silverlight游戏设计(四)角色/精灵篇之 -- 精灵的设计
2010-12-26 19:06 姜 萌@cnblogs 阅读(1467) 评论(1) 编辑 收藏 举报精灵的呈现基础
silverlight的那套api并不是一个为游戏设计的,你找不到现成的”精灵”相关的类。
最简单的精灵用一个Image就可以充当,但游戏中总是存在各式各样的精灵,不同精灵又有不同的逻辑,为了方便设计我们有必要定义一些接口。
呈现器接口—IPresenter
{
/// <summary>
/// 呈现器
/// </summary>
public interface IPresenter : IDisposable
{
/// <summary>
/// 用于标识咯咯呈现器
/// </summary>
string Id { get; set; }
/// <summary>
///
/// </summary>
EmRoleBehavior CurrentBehavior { get; set; }
/// <summary>
///
/// </summary>
EmRoleDir CurrentDir { get; set; }
/// <summary>
/// 附加到容器中以显示
/// </summary>
void AttachOnContainer(Panel container);
/// <summary>
/// 从容器中脱离
/// </summary>
void RemoveFromContainer();
/// <summary>
/// 是否可见
/// </summary>
bool IsVisiable { get; set; }
/// <summary>
/// 是否启用特效
/// </summary>
bool IsEnableEffect { get; set; }
/// <summary>
/// 特效
/// </summary>
Effect Effect { get; set; }
/// <summary>
/// 相对父容器的X轴偏移
/// </summary>
double X { get; set; }
/// <summary>
/// 相对父容器的Y轴偏移
/// </summary>
double Y { get; set; }
/// <summary>
/// 相对父容器的Z轴值
/// </summary>
int ZIndex { get; set; }
/// <summary>
/// X偏移
/// </summary>
double OffsetX { get; set; }
/// <summary>
/// Y偏移
/// </summary>
double OffsetY { get; set; }
/// <summary>
/// 运动控制模式
/// </summary>
EmSpriteControlMode ControlMode { get; set; }
/// <summary>
///
/// </summary>
PresenterResource Resource { get; set; }
/// <summary>
/// 显示的帧速
/// </summary>
TimeSpan FrameFrequency { get; set; }
/// <summary>
///
/// </summary>
void SetViewSource(ImageSource source);
/// <summary>
/// ControlMode为RefreshOnPerFrame时调用此方法执行逻辑
/// </summary>
void OnFrame();
/// <summary>
/// 播放一套完整的动画序列,直到将ControlMode设置为非AniSequenceMode值
/// </summary>
/// <param name="behavior"></param>
/// <param name="dir"></param>
/// <param name="?"></param>
void DoAniSequence(EmRoleBehavior behavior, EmRoleBehavior dir, AniTimes time, Action onAniCompleted);
}
}
Id:精灵的标识值
CurrentBehavior:精灵动作
CurrentDir:精灵方向
IsVisiable:是否可见
IsEnableEffect:是否开启特Effect效
Effect:对呈现器进行渲染的BitmapEffect
X:相对呈现器父容器的X轴偏移
Y:相对呈现器父容器的Y轴偏移
ZIndex:Z轴权重值
OffsetX:在整个精灵容器内部X偏移
OffsetY:在整个精灵容器内部Y偏移
CenterX:X轴中心点偏移量
CenterY:Y轴中心点偏移量
ControlMode:运动模式。OnFrame/Story/混合
Resource:这里是精灵序列帧的图片资源,我从其精灵本身给分离出来了。
FrameFrequency:帧速设定
可添加部件的精灵
稍微玩过一些rpg游戏的人都知道,游戏中有个概念叫“装备”,顽疾通常会在通过打怪、任务、升级奖励等来得到新的装备,不同装备下角色的外观是不同的,同样的也会需求出现在”纸娃娃”、装备预览等需求上。我们在设计之处就应该考虑到这种复合的精灵角色,所以要进一步加入一个接口:IPartable。、
{
/// <summary>
/// 支持部件添加的呈现器
/// </summary>
public interface IPartable : IPresenter
{
void AddPart(IPresenter part);
void AddPart(string partKey, IPresenter part);
void RemovePart(string partKey);
}
}
进一步,具体的实现
上面把接口定义好,接下来只需实现即可。对于silverlight,任何一个UIElement的子类都可以作为呈现器,而如果要添加部件的话很容易想到的是Canvas,因为Canvas一方面本身就是个容器,其次是一种绝对布局,我们可以通过设定UIElement对应的Canvas.LeftProperty调整其位置。有了这样的思路,具体实现如下:
{
public abstract class PresenterBase : IPresenter
{
#region Fields
private Panel _preContainer;
private bool _isEnableEffect;
private long _preRefreshTimeTicks;
#endregion
#region ctors
public PresenterBase()
{
CurrentBehavior = EmRoleBehavior.站立;
CurrentDir = EmRoleDir.默认;
}
#endregion
protected abstract UIElement InnerUIElement { get; }
#region IPresenter Members
public virtual EmRoleBehavior CurrentBehavior { get; set; }
public virtual EmRoleDir CurrentDir { get; set; }
public string Id
{
get;
set;
}
public double X
{
get
{
return Canvas.GetLeft(InnerUIElement) - OffsetX;
}
set
{
Canvas.SetLeft(InnerUIElement, value + OffsetX);
}
}
public double Y
{
get
{
return Canvas.GetTop(InnerUIElement) - OffsetY;
}
set
{
Canvas.SetTop(InnerUIElement, value + OffsetY);
}
}
public double OffsetX
{
get;
set;
}
public double OffsetY
{
get;
set;
}
public int ZIndex
{
get
{
return Canvas.GetZIndex(InnerUIElement);
}
set
{
Canvas.SetZIndex(InnerUIElement, value);
}
}
public void AttachOnContainer(Panel container)
{
RemoveFromContainer();
container.Children.Add(container);
_preContainer = container;
}
public void RemoveFromContainer()
{
if (_preContainer != null)
_preContainer.Children.Remove(InnerUIElement);
}
public bool IsEnableEffect
{
get
{
return _isEnableEffect;
}
set
{
if (_isEnableEffect == value)
return;
_isEnableEffect = value;
InnerUIElement.Effect = Effect;
}
}
public System.Windows.Media.Effects.Effect Effect
{
get;
set;
}
public bool IsVisiable
{
get
{
return InnerUIElement.Visibility.ToBoolean();
}
set
{
InnerUIElement.Visibility = value.ToVisibility();
}
}
public abstract void SetViewSource(ImageSource source);
/// <summary>
/// 呈现器的图片资源
/// </summary>
public PresenterResource Resource
{
get; set;
}
public TimeSpan FrameFrequency
{
get; set;
}
public virtual void OnFrame()
{
var span = TimeSpan.FromTicks(DateTime.Now.Ticks - _preRefreshTimeTicks);
if (span >= FrameFrequency)
{
RefreshOnFrameImmediately();
}
}
public virtual void Dispose()
{
RemoveFromContainer();
_preContainer = null;
}
public abstract EmSpriteControlMode ControlMode
{
get; set;
}
public abstract void DoAniSequence(EmRoleBehavior behavior, EmRoleBehavior dir, AniTimes time, Action onAniCompleted);
/// <summary>
/// 立即刷新
/// </summary>
protected abstract void RefreshOnFrameImmediately();
#endregion
}
}
{
/// <summary>
/// 可添加部件的呈现器
/// </summary>
public class AddPartablePresenter : PresenterBase, IPartable
{
#region Fields
protected Dictionary<string, IPresenter> _parts = new Dictionary<string, IPresenter>();
public static readonly string DEFAULT_PARTKEY = "unipart";
private static int autoId = 0;
protected EmSpriteControlMode _controlMode;
protected Canvas _container = new Canvas();
#endregion
#region IPresenter Members
public override EmSpriteControlMode ControlMode
{
get
{
return _controlMode;
}
set
{
_controlMode = value;
}
}
public override void SetViewSource(ImageSource source)
{
_container.Background = new ImageBrush()
{
ImageSource = source
};
}
#endregion
#region IPartable Members
public void AddPart(IPresenter part)
{
AddPart(CURRENT_DEFAULT_PARTKEY, part);
}
public void AddPart(string partKey, IPresenter part)
{
attachSelf(partKey, part);
}
public void RemovePart(string partKey)
{
deteachSelf(partKey);
}
#endregion
#region IDisposable Members
public override void Dispose()
{
base.Dispose();
}
#endregion
protected override UIElement InnerUIElement
{
get { return _container; }
}
#region Private Helper Methods
private void attachSelf(string partKey, IPresenter part)
{
part.AttachOnContainer(_container);
_parts.Add(partKey, part);
}
private void deteachSelf(string partKey)
{
if(_parts.ContainsKey(partKey))
{
_parts[partKey].RemoveFromContainer();
}
_parts.Remove(partKey);
}
private string CURRENT_DEFAULT_PARTKEY
{
get
{
return string.Format("{0}-{1}", DEFAULT_PARTKEY, ++autoId);
}
}
#endregion
public override void DoAniSequence(EmRoleBehavior behavior, EmRoleBehavior dir, AniTimes time, Action onAniCompleted)
{
throw new NotImplementedException();
}
protected override void RefreshOnFrameImmediately()
{
foreach (var part in _parts.Values)
{
part.OnFrame();
}
}
}
}
{
public class GameRole : AddPartablePresenter
{
public string RoleId
{
get;
set;
}
public override void OnFrame()
{
if(base.ControlMode == EmSpriteControlMode.RefreshPerFrame)
{
foreach (var part in base._parts.Values)
{
part.OnFrame();
}
}
}
public override EmRoleBehavior CurrentBehavior
{
get
{
return base.CurrentBehavior;
}
set
{
base.CurrentBehavior = value;
//更新所有部件的行为
foreach (var part in base._parts.Values)
{
part.CurrentBehavior = CurrentBehavior;
}
}
}
public override EmRoleDir CurrentDir
{
get
{
return base.CurrentDir;
}
set
{
base.CurrentDir = value;
//更新所有部件的方向
foreach (var part in base._parts.Values)
{
part.CurrentDir = CurrentDir;
}
}
}
#region Private Helper Methods
#endregion
}
}