• c#游戏之路-wpf版本开发


    中间实在忙啊,隔了几天,终于有点时间,本来以为写博客很简单,不过现在感觉怎么把意思表达清楚真是件难事啊,所以,写的不好,园友们不要介意啊。

    话说,上回我打算开发游戏,考虑了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或者自己部署一套代码系统,希望能更方便一点。另外我发现自己平常得我基本上都是别人问我问题我答得头头是道,真要让我自己写文章还真是一大挑战啊,大家有什么好得思路都欢迎评论。

  • 相关阅读:
    WebApi 接口参数:传参详解
    WebApi 身份认证解决方案:Basic基础认证
    EF 底层封装方法(供参考)
    接口和抽象类的区别
    EF Code First 连接MySql
    EntityFramework Code-First—领域类配置之DataAnnotations
    驱动基本框架和与ring3通信
    必备理论知识
    通过远程代码注入实现不死进程
    PE文件中插入程序
  • 原文地址:https://www.cnblogs.com/csharp-game/p/6961151.html
Copyright © 2020-2023  润新知