引言
实际游戏开发中我们肯定不会将所有逻辑代码都方在一个文件中,不仅不利于阅读最重要的是非常不利于拓展与重用。面向对象的游戏开发思想告诉我们,是时候对游戏中的对象进行封装了。
3.1通过用户控件(UserControl)封装游戏对象(交叉参考:精灵控件横空出世!① 精灵控件横空出世!② )
在2.2节的基础上首先我们为解决方案添加一个新的项目:Controls(解决方案上右键->添加->新建项目->Silverlight类库)。默认该项目中会包含一个Class1.cs文件,我们删除掉它。(注:解决方案中项目与项目之间交互必须添加引用,例如在MainPage中要使用Controls项目中的控件,那么我们需要在MainPage所在的项目上点右键->添加引用->项目->Controls->确定;后续课程还有创建新的项目,使用方法同样。)
关键时刻到了,在刚新建好的Controls项目上点击右键->添加->新建项,此时我们选择Silverlight用户控件并取名为Sprite。Sprite(精灵) - 游戏中第一个伟大的对象控件诞生了。
当然首先要做的还是把Sprite.xaml中的Grid换成Canvas,然后将2.2中关于精灵的所有逻辑代码均放进Sprite控件中以实现独立封装,转移及修改内容如下:
1)同创建Controls项目一样的方式创建一个游戏逻辑类库Logic,接下来在其中再新建一个名Enum的文件夹以保存枚举类型,创建两个枚举类分别为:SpriteDirection.cs(精灵朝向)和SpriteState.cs(精灵状态):
///
/// 精灵朝向
///
public enum SpriteDirection {
North = 0,
NorthEast = 1,
East = 2,
SouthEast = 3,
South = 4,
SouthWest = 5,
West = 6,
NorthWest = 7
} ///
/// 精灵状态
///
public enum SpriteState {
///
/// 站立(停止)
///
Stand = 0,
///
/// 跑动(移动)
///
Run = 1,
///
/// 攻击(物理)
///
Attack = 2,
///
/// 施法(魔法)
///
Casting = 3,
///
/// 无
///
None = 9,
} 枚举类型不仅直观而且使用起来非常方便,后面的课程大家会逐渐感受到它非凡的魔力。
2)移植原先MainPage中关于精灵的所有属性到Sprite控件内部并公开:
代码 #region 属性
///
/// 获取或设置速度系数
///
public double Speed { get; set; }
///
/// 获取或设置脚底中心
///
public Point Center { get; set; }
///
/// 获取或设置朝向
///
public SpriteDirection Direction { get; set; }
///
/// 获取或设置状态
///
public SpriteState State { get; set; }
#endregion 3)移植原先MainPage中的精灵动作动画计时器及相关实行方法到精灵内部:
代码 Image body = new Image();
DispatcherTimer dispatcherTimer = new DispatcherTimer() {
Interval = TimeSpan.FromMilliseconds(120)
};
public Sprite() {
this.Children.Add(body);
Stand();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Start();
}
int currentFrame, startFrame, endFrame;
void dispatcherTimer_Tick(object sender, EventArgs e) {
if (currentFrame > endFrame) { currentFrame = startFrame; }
body.Source = new BitmapImage(new Uri(string.Format(@"/{0};component/Res/Sprite/{1}-{2}-{3}.png", Global.ProjectName, (int)State, (int)Direction, currentFrame), UriKind.Relative));
currentFrame++;
} #region 方法
///
/// 计算当前坐标与目标点之间的正切值以获取朝向
///
/// 当前坐标
/// 目标坐标
/// 朝向代号
public void SetDirection(Point current, Point target) {
double tan = (target.Y - current.Y) / (target.X - current.X);
if (Math.Abs(tan) >= Math.Tan(Math.PI * 3 / 8) && target.Y Math.Tan(Math.PI / 8) && Math.Abs(tan) current.X && target.Y Math.Tan(Math.PI / 8) && Math.Abs(tan) current.X && target.Y > current.Y) {
Direction = SpriteDirection.SouthEast;
} else if (Math.Abs(tan) >= Math.Tan(Math.PI * 3 / 8) && target.Y >= current.Y) {
Direction = SpriteDirection.South;
} else if (Math.Abs(tan) > Math.Tan(Math.PI / 8) && Math.Abs(tan) |