话说,上回我打算开发游戏,考虑了4种技术,后来我发现无论哪种技术应该是都能实现,区别大概就是性能以及占用的资源吧,所以,我后来实现用的是wpf。当时在网上搜索了一下,发现曾经有人写过,也在国外的网站上找到了部分代码,所以就这么拼拼凑凑就把效果跑起来了。
下面先上个效果图吧,算是这几天的一个成果。
我这个是完全模仿《传奇》,屏幕所能容纳的物件数量是17*17,所以我在每一个格子上放置一格人物,并让这个人物进行运动,我的预想是,如果全屏幕人物运动的情况下还能达到10FPS,那么理论上实现《传奇》这样的游戏应该是可行的。
下面是我的代码部分,要说实现上面这个效果,不算简单也不算难,主要应该是一些思想。我的项目名字叫做LightDarkLegend.
典型的WPF项目,App.xaml是wpf默认的启动页,MainWindow.xaml是游戏的登录器页面,GameBox.xaml是游戏运行的页面,结构上来说还是非常简单的。
其实最主要的核心就是这个MyDraw,也就是自定义的绘制控件。MyDraw的主要逻辑就是处理传入的人物、魔法、怪物、地图等参数,与原始数据进行比较,假如发现数据不同,则触发绘制,最终有自定义控件的OnRender方法呈现图像。
核心代码MyMap类
public class MyMap : INotifyPropertyChanged { private long x; public long X { get { return this.x; } set { if (this.x != value) { this.x = value; this.OnPropertyChanged("X"); } } } private long y; public long Y { get { return this.y; } set { if (this.y != value) { this.y = value; this.OnPropertyChanged("Y"); } } } private long moveX; private long moveY; public long MoveX { get => moveX; set => moveX = value; } public long MoveY { get => moveY; set => moveY = value; } private Map map; public Map Map { get { return this.map; } set { if (this.map != value) { this.map = value; this.OnPropertyChanged("Map"); } } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }
MyDraw类
public class MyDraw : FrameworkElement { public WriteableBitmap mapBitMap; public WriteableBitmap magicBitMap; public WriteableBitmap monsterBitMap; public WriteableBitmap personBitMap; public WriteableBitmap itemBitMap; public WriteableBitmap controlBoxBitMap; public static int pixelWidth = 2040; //(int)myMap.Map.width; public static int pixelHeight = 1530; // (int)myMap.Map.height; public static int xGezi = 120;//一个砖块占用像素x public static int yGezi = 90;//一个砖块占用像素y public static int moveWidth = pixelWidth / xGezi;//人物x坐标移动格子数一屏幕 public static int moveHeight = pixelHeight / yGezi;//人物y坐标移动格子数一屏幕 public bool isLoadObject = false;//是否不进行绘制,进行元素加载。 #region 我的地图 public static readonly DependencyProperty myMapProperty = DependencyProperty.Register("map", typeof(MyMap), typeof(MyDraw), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, OnMyMapPropertyChanged)); private static void OnMyMapPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { MyDraw draw = (MyDraw)d; MyMap myMap = (MyMap)e.NewValue; MyMap oldMap = (MyMap)e.OldValue; if (myMap != null && myMap.Map != null) { if (oldMap != null && myMap.Map.id != oldMap.Map.id) { //LoadAllObject();//载入所有当前地图相关元素(地砖元素,建筑物,怪物图片) } //重新绘图 if (oldMap != null && oldMap.X == myMap.X && oldMap.Y == myMap.Y)//仅仅移动 { draw.MoveMap((int)myMap.MoveX, (int)myMap.MoveY); return; } draw.DrawMap(); } } public MyMap myMap { get { return (MyMap)GetValue(myMapProperty); } set { SetValue(myMapProperty, value); } } Image GetImage(int x, int y) { int index = x * (int)myMap.Map.width + y; return myMap.Map.layouts[0].metros[index].img; } void DrawMap() { int x = (int)myMap.X;//人物x坐标 int y = (int)myMap.Y;//人物y坐标 //需要根据人物所在坐标,取得地图的点阵数据 int moveMinX = x - moveWidth / 2-1; int moveMaxX = x + moveWidth / 2 + 2; int moveMinY = y - moveHeight / 2-1; int moveMaxY = y + moveHeight / 2 + 2; mapBitMap.Lock(); //双重缓存 using (Bitmap backBufferBitmap = new Bitmap(pixelWidth, pixelHeight, mapBitMap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format32bppPArgb, mapBitMap.BackBuffer)) { using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap)) { backBufferGraphics.Clear(System.Drawing.Color.Black); DateTime de = DateTime.Now; //绘制地表 int drawX = -1; for (int i = moveMinX; i < moveMaxX; i++)//获得数据进行绘制 { int drawY = -1; for (int j = moveMinY; j < moveMaxY; j++) { if (i < 0 || j < 0) { //超出地图范围,以空元素代替 //不进行绘制 } else { backBufferGraphics.DrawImage(GetImage(i, j), drawX * xGezi, drawY * yGezi); } drawY += 1; } drawX += 1; } backBufferGraphics.Flush(); DateTime de2 = DateTime.Now; TimeSpan dm = de2 - de; } } //结束绘制 mapBitMap.AddDirtyRect(new Int32Rect(0, 0, pixelWidth, pixelHeight)); mapBitMap.Unlock(); } #endregion public MyDraw() { if (mapBitMap == null || (mapBitMap != null && (mapBitMap.PixelWidth != pixelWidth || mapBitMap.PixelHeight != pixelHeight))) { mapBitMap = new WriteableBitmap(pixelWidth, pixelHeight, 96, 96, PixelFormats.Pbgra32, null); } } protected override void OnRender(DrawingContext drawingContext) { drawingContext.DrawImage(mapBitMap, new Rect(0, 0, RenderSize.Width, RenderSize.Height)); } }
发现,粘贴代码确实比较麻烦,尤其是代码量非常大的时候,看来下次我得考虑使用git或者自己部署一套代码系统,希望能更方便一点。