本系列所有代码都是使用Microsoft Visual Studio 2008开发,为基于Silverlight的游戏开发技术,如果您看完之后觉得不错,回复顶一下,万分感激:)
在上一次,介绍了面向对象在怪物上的应用,比较简单的代码完成了多重怪物的不同逻辑,然而并不是非常明显的使用了面向对象,因为数量较小,这次我们搞一个对象数目繁多的应用,来证明面向对象在游戏开发中是多么的好用。还是老规矩,仍然提供一个实例。代码在这里下载
是否还记得,这个系列中的《Flyer》小游戏,那个系列为了更好的介绍Silverlight的基础知识没有使用面向对象方法,所有的对象都是独立编程,因此我们发现所有的基础对象都有这么一段代码。
public double Speed = 4;
public double X
{
get { return Canvas.GetLeft(this); }
set { Canvas.SetLeft(this, value); }
}
public double Y
{
get { return Canvas.GetTop(this); }
set { Canvas.SetTop(this, value); }
}
public Rect MyRect
{
get
{
return new Rect(X, Y, _rectangle.Width, _rectangle.Height);
}
}
恐怕非常的不爽,先不说维护起来麻烦,就是来回的复制粘贴也够费劲的,以后要是增加了各种各样的物体,这个工作就够手疼的,但是我们在面向对象的面前就省去了很多力气,然而……这个世界没有公平的事情,手不疼了,脑袋就要疼了,嘿嘿,具体为什么,下面就知道。
我们的目的是,用面向对象实现之前《Flyer》的功能,为了更好的说明,这个游戏就不写太多的逻辑代码,有兴趣的朋友自行研究:)
建立工程……(请参阅其他文章)
为了更加方便快速的完成,这次我们用上Blend,请参照下图设置:

然后加入之前的图片,这次的图片没有做动画,一切从简,否则复杂的代码不利于大家读代码。
这个时候,我们开始研究如何创造类的结构,使用面向对象的方法来完成这个项目,这需要编程人员的经验以及基础的共同作用,所以,在写之前先要想好,经过思考,我们发现很多可以抽象出来的东西,咱们画了一个类结构图:

ClassActivityRole类更加高级了一点,它可以做一些行为,提供检测还有移动等方法。
ClassSolid类是不需要操作的物体集合。
ClassFlyer与ClassSolid有本质的不同,ClassSolid只需要向上移动就行,但是Flyer却不行,它需要键盘的控制,所以,作为ClassSolid的子类不合适,所以我们将ClassActivityRole作为父类。
ClassCloud 、ClassFood、、ClassScrew从ClassSolid继承获得了有用的部分,而且告诉大家,我是一个Solid,但是他们之间不同的是三种完全不同的作用,Cloud什么都不做只是向上飘,Food在碰撞的时候会加血,Screw则是损血,这是完全不同的三种逻辑,这个部分和上一个篇里打怪物的情况几乎一样,所以可以使用同一的碰撞逻辑(这部分的代码未实现,如要请参考其他篇)
那么下面就是Coding的时间,我在这里给出部分代码,详细的请下载源文件。
public class ClassBaseRole : Canvas
{
public Image ResImage = new Image();
public ClassBaseRole()
{
this.Children.Add(ResImage);
InitializtionRole();
}
public virtual void InitializtionRole()
{
}
/// <summary>
/// 移动速度
/// </summary>
public double Speed = 1;
/// <summary>
/// 修改或获取X坐标
/// </summary>
public double X
{
get { return Canvas.GetLeft(this); }
set { Canvas.SetLeft(this, value); }
}
/// <summary>
/// 修改或获取Y坐标
/// </summary>
public double Y
{
get { return Canvas.GetTop(this); }
set { Canvas.SetTop(this, value); }
}
}
public class ClassActivityRole : ClassBaseRole
{
public ClassActivityRole()
{
MyRectangle = new Rectangle() { Width = 32, Height = 32, Stroke = new SolidColorBrush(Colors.Red) };
this.Children.Add(MyRectangle);
}
protected Rectangle MyRectangle;
/// <summary>
/// 取得自身的碰撞区域
/// </summary>
public Rect MyRect
{
get
{
return new Rect(X, Y, MyRectangle.Width, MyRectangle.Height);
}
}
/// <summary>
/// 碰撞测试
/// </summary>
/// <param name="objective">目标物体</param>
/// <returns></returns>
public bool CollidedTest(ClassActivityRole objective)
{
Rect rt = objective.MyRect;
rt.Intersect(this.MyRect);
if (!double.IsInfinity(rt.Height) && !double.IsInfinity(rt.Width))
return true;
else
return false;
}
public virtual void DownWard()
{
Y += base.Speed;
}
public virtual void UpWard()
{
Y -= base.Speed;
}
public virtual void RightWard()
{
X += base.Speed;
}
public virtual void LeftWard()
{
X -= base.Speed;
}
public virtual void LoopLogic()
{
}
}
后面还有更多,在这里不一一列举,直接看效果:


public partial class MainPage : UserControl
{
public static Random random = new Random((int)DateTime.Now.Ticks);
public static double ScreenWidth = 400;
public static double ScreenHeight = 400;
public ClassFlyer Hero = new ClassFlyer();
public MainPage()
{
InitializeComponent();
for (int i = 0; i < 20; i++)
{
Sky.Children.Add(new ClassCloud());
Sky.Children.Add(new ClassFood());
Sky.Children.Add(new ClassScrew());
}
Sky.Children.Add(Hero);
DispatcherTimer GameLoop = new DispatcherTimer();
GameLoop.Interval = TimeSpan.FromMilliseconds(40);
GameLoop.Tick += new EventHandler(GameLoop_Tick);
GameLoop.Start();
base.KeyDown += new KeyEventHandler(MainPage_KeyDown);
base.KeyUp += new KeyEventHandler(MainPage_KeyUp);
}
void MainPage_KeyUp(object sender, KeyEventArgs e)
{
Hero.FylerState = EmFlyerState.正常;
}
void MainPage_KeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Up:
Hero.FylerState = EmFlyerState.向上;
break;
case Key.Down:
Hero.FylerState = EmFlyerState.向下;
break;
case Key.Left:
Hero.FylerState = EmFlyerState.向左;
break;
case Key.Right:
Hero.FylerState = EmFlyerState.向右;
break;
}
}
void GameLoop_Tick(object sender, EventArgs e)
{
foreach (var item in Sky.Children)
{
if (item is ClassActivityRole)
{
ClassActivityRole temp = (item as ClassActivityRole);
temp.LoopLogic();
if (temp.Y <= -32)
{
temp.InitializtionRole();
}
else
{
if (temp.CollidedTest(Hero) && !(temp is ClassCloud))
{
temp.InitializtionRole();
}
}
}
}
}
}
综述,我们从这个例子中,可以看到有关于多重的类别对象在整体管理时候的应用,在做整体的游戏管理时候,只需要调用基本对象,基本类的方法会直接调用继承下来的类,比如说LoopLogic,对于不同的对象Solid、Flyer、Cloud、Food处理不同的逻辑,在CollidedTest中更加明显,只需要传入基类即可,不需要做单独的判定条件。
本代码中没有做损血和加血,主要是为了更加直观,损血和加血只需要写在碰撞检测里就可以,也不需要做太多的代码,甚至碰撞检测可以写在自身的逻辑中,而不需要每次都调用迭代器做判定
它在实际开发中非常重要,它能很好的简化代码,让结构更加清晰,而且修改起来非常容易,即便是增加功能也是非常简单的事情,比如我们搞一个横着飞的火球,只需要简单继承,修改一下飞行轨迹即可,可能在其他方面的应用更加广泛和舒适,欢迎共同探讨,各位高手可能有更加面向对象的例子,我也想好好学习啊:)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架