代码改变世界

silverlight游戏设计(四)角色/精灵篇之 -- 精灵的设计

2010-12-26 19:06  姜 萌@cnblogs  阅读(1467)  评论(1编辑  收藏  举报

精灵的呈现基础

silverlight的那套api并不是一个为游戏设计的,你找不到现成的”精灵”相关的类。

最简单的精灵用一个Image就可以充当,但游戏中总是存在各式各样的精灵,不同精灵又有不同的逻辑,为了方便设计我们有必要定义一些接口。

呈现器接口—IPresenter

image

IPresenter
namespace Sopaco.Silverlight.GameFramewrok.Sprite
{
    
/// <summary>
    
/// 呈现器
    
/// </summary>
    public interface IPresenter : IDisposable
    {
        
/// <summary>
        
/// 用于标识咯咯呈现器
        
/// </summary>
        string Id { getset; }
        
/// <summary>
        
/// 
        
/// </summary>
        EmRoleBehavior CurrentBehavior { getset; }
        
/// <summary>
        
/// 
        
/// </summary>
        EmRoleDir CurrentDir  { getset; }
        
/// <summary>
        
/// 附加到容器中以显示
        
/// </summary>
        void AttachOnContainer(Panel container);
        
/// <summary>
        
/// 从容器中脱离
        
/// </summary>
        void RemoveFromContainer();
        
/// <summary>
        
/// 是否可见
        
/// </summary>
        bool IsVisiable { getset; }
        
/// <summary>
        
/// 是否启用特效
        
/// </summary>
        bool IsEnableEffect { getset; }
        
/// <summary>
        
/// 特效
        
/// </summary>
        Effect Effect { getset; }
        
/// <summary>
        
/// 相对父容器的X轴偏移
        
/// </summary>
        double X { getset; }
        
/// <summary>
        
/// 相对父容器的Y轴偏移
        
/// </summary>
        double Y { getset; }
        
/// <summary>
        
/// 相对父容器的Z轴值
        
/// </summary>
        int ZIndex { getset; }
        
/// <summary>
        
/// X偏移
        
/// </summary>
        double OffsetX { getset; }
        
/// <summary>
        
/// Y偏移
        
/// </summary>
        double OffsetY { getset; }
        
/// <summary>
        
/// 运动控制模式
        
/// </summary>
        EmSpriteControlMode ControlMode { getset; }
        
/// <summary>
        
/// 
        
/// </summary>
        PresenterResource Resource { getset; }
        
/// <summary>
        
/// 显示的帧速
        
/// </summary>
        TimeSpan FrameFrequency { getset; }
        
/// <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。、

image

IPartable
namespace Sopaco.Silverlight.GameFramewrok.Sprite
{
    
/// <summary>
    
/// 支持部件添加的呈现器
    
/// </summary>
    public interface IPartable : IPresenter
    {
        
void AddPart(IPresenter part);
        
void AddPart(string partKey, IPresenter part);
        
void RemovePart(string partKey);
    }
}

 

 

 

 

进一步,具体的实现

 

image 

上面把接口定义好,接下来只需实现即可。对于silverlight,任何一个UIElement的子类都可以作为呈现器,而如果要添加部件的话很容易想到的是Canvas,因为Canvas一方面本身就是个容器,其次是一种绝对布局,我们可以通过设定UIElement对应的Canvas.LeftProperty调整其位置。有了这样的思路,具体实现如下:

PresenterBase
namespace Sopaco.Silverlight.GameFramewrok.Sprite
{
    
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 { getset; }
        
public virtual EmRoleDir CurrentDir { getset; }
        
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 
        {
            
getset
        }
        
public TimeSpan FrameFrequency 
        {
            
getset
        }
        
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 
        {
            
getset;
        }
        
public abstract void DoAniSequence(EmRoleBehavior behavior, EmRoleBehavior dir, AniTimes time, Action onAniCompleted);
        
/// <summary>
        
/// 立即刷新
        
/// </summary>
        protected abstract void RefreshOnFrameImmediately();
        
#endregion
    }
}

 

 

 

AddPartablePresenter
namespace Sopaco.Silverlight.GameFramewrok.Sprite
{
    
/// <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();
            }
        }
    }
}

 

 

GameRole
namespace Sopaco.Silverlight.GameFramewrok.Sprite
{
    
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
    }
}