在Windows上将桌面图标做成贪吃蛇游戏

这次,又是一个全新的小玩意。就是把windows桌面上的图标,让他们整齐划一,变成贪吃蛇小游戏(没错,我就是和贪吃蛇杠上了)。

这个游戏,可以称之为“没有界面的贪吃蛇”。下面放上效果图。

 下面介绍详细过程。

0.准备工作

  • 系统:Win10
  • 开发工具:Visual Studio2019
  • 语言:C#

1.关键点

  • 如何生成食物(动态创建桌面图标)
  • 如何移动蛇身体和食物(移动桌面图标到指定位置)

创建桌面图标用到了接口IWshShortcut,具体代码如下

        public void _createShortcut()
        {
            //选择随机图标
            var rnd = new Random().Next(0, ICONS.Length);

            var LinkPathName = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            var IconPath = ICONS[rnd];
            var guid = Guid.NewGuid().ToString();
            //获取图标名字
            var name = ICONNAMES[rnd];
            WshShell shell = new WshShell();            
            IWshShortcut link = (IWshShortcut)shell.CreateShortcut($"{LinkPathName}/{name+guid}.lnk");
            link.IconLocation = IconPath;
            link.TargetPath = IconPath;
            link.Save();            
                         
        }

而移动桌面图标则用到了WindowsAPI中的函数,代码如下

        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        public static extern int SendMessage(int hWnd, int Msg, int wParam, int lParam);

        private void _movdDeskTopIons(int i, int x, int y)
        {
            WindowsAPIs.SendMessage((int)_shortCut, (int)LVM_SETITEMPOSITION, i, (int)WindowsAPIs.MakeLParam(x, y));            
        }

解决了这两个问题,还存在一个隐藏的问题,即通过IWshShortcut创建的图标,在一定的时间之后或创建第二个图标之后,这个图标才能被移动。所以你会发现桌面左上角有一个多余的图标,那就是先将图标创建好放在左上角,当游戏需要食物时,才将该图标移动到随机位置,同时再创建一个备用图标放在左上角。

解决了这个问题,再加上贪吃蛇的基础游戏规则代码,这个游戏就可以完整独立流畅地运行了。

2.代码片段

下面贴出一些关键类代码,有需要完整工程文件或游戏执行文件的小伙伴可以私信我。

WindowsAPI类

    public class WindowsAPIs
    {        
        const int WM_MOUSEWHEEL = 0x020A; //鼠标滚轮
        const int WM_LBUTTONDOWN = 0x0201;//鼠标左键
        const int WM_LBUTTONUP = 0x0202;
        const int WM_KEYDOWN = 0x0100;//模拟按键
        const int WM_KEYUP = 0x0101;
        const int MOUSEEVENTF_MOVE = 0x0001;//用于琴台鼠标移动
        const int MOUSEEVENTF_LEFTDOWN = 0x0002;//前台鼠标单击
        const int MOUSEEVENTF_LEFTUP = 0x0004;
        const int WM_SETTEXT = 0x000C;//设置文字
        const int WM_GETTEXT = 0x000D;//读取文字
        [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        public static extern int SendMessage(int hWnd, int Msg, int wParam, int lParam);

        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        public static extern int SendMessage(int hWnd, int Msg, int wParam, string lParam);

        [DllImport("user32.dll")]//在窗口列表中寻找与指定条件相符的第一个子窗口
        public static extern int FindWindowEx(int hwndParent, // handle to parent window
                                                int hwndChildAfter, // handle to child window
                                                string className, //窗口类名            
                                                string windowName);

        [DllImport("user32.DLL")]
        public static extern IntPtr FindWindowEx(IntPtr hwndParent,
            IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        public static IntPtr MakeLParam(int wLow, int wHigh)
        {
            return (IntPtr)(((short)wHigh << 16) | (wLow & 0xffff));
        }
    }

Snake类

    public class Snake
    {
        //0123 左上右下
        public int Direction = 2;

        //组成蛇身的位置信息
        public List<Pos> Body;

        //蛇身宽度 食物宽度
        public int Width=76;
        //蛇身高度 食物高度
        public int Height= 104;

        //未消化的食物
        private Queue<Pos> _digestion = new Queue<Pos>();

        public Snake()
        {
            Body = new List<Pos>();
            Body.Add(new Pos { x = 0, y = 0 });
        }

        public void TurnDirection(int direction)
        {
            if (Math.Abs(direction - Direction) == 2) return;
            Direction = direction;
        }

        public void Move()
        {
            Pos head = new Pos
            {
                x=Body.First().x,
                y=Body.First().y
            };
            switch (Direction)
            {
                case 0:
                    {
                        head.x -= Width;
                        break;
                    }
                case 1:
                    {
                        head.y -= Height;
                        break;
                    }
                case 2:
                    {
                        head.x += Width;
                        break;
                    }
                case 3:
                    {
                        head.y += Height;
                        break;
                    }
            }

            Pos food = null;
            if (_digestion.Count > 0)
            {                
                food = _digestion.Dequeue();
                food.x = Body.Last().x;
                food.y = Body.Last().y;
            }
            for(var i=Body.Count-1;i>0;i--)
            {
                Body[i] =new Pos { x = Body[i - 1].x, y = Body[i - 1].y };
            }
            Body[0] = head;
            if (food != null) Body.Add(food);
        }

        public void Eat(Pos food)
        {
            _digestion.Enqueue(food);
        }
    }

Map类

    public class Map
    {
        public double  Width { get; set; }
        public double Height { get; set; }

        public Map()
        {
            Width = SystemParameters.WorkArea.Width;//25  1920    76.8
            Height = SystemParameters.WorkArea.Height;//10  1040   104
        }

    }

Game类

    public class Game
    {
        public string[] ICONS= new string[] {
            @"C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\devenv.exe",
            @"D:\Program Files\Microsoft VS Code\Code.exe",
            @"C:\Program Files\JetBrains\IntelliJ IDEA 2020.2.3\bin\idea64.exe",
            @"D:\Program Files\Android Studio\Studio\bin\studio64.exe",
            @"D:\Program Files (x86)\DingDing\DingtalkLauncher.exe",
            @"D:\Program Files\YouDaoDic\Dict\YoudaoDict.exe",
            @"D:\Program Files (x86)\Steam\steam.exe",
            @"D:\Program Files\WeGame\wegame.exe",            
            @"C:\Program Files\Adobe\Adobe Photoshop CC 2017\Photoshop.exe",
        };
        public string[] ICONNAMES = new string[]
        {
            "Visual Studio","VSCode","IDEA","Android Studio","Dingtalk","YoudaoDict","Steam","WeGame","Photoshop"
        };

        private const uint LVM_SETITEMPOSITION = 0x1000 + 15;
        private IntPtr _shortCut;

        private bool _gaming=false;
        private Snake _snake;
        private Map _map;
        private Pos _food;


        public delegate void GameNotify(bool Gaming);
        public GameNotify GameStoped;

        public Game()
        {
            _map = new Map();
            _shortCut = _getIconHandle();
        }

        public void Start()
        {
            _snake = new Snake();
            _gaming = true;
                     
            new Thread(() => {
                _createShortcut();
                Thread.Sleep(1000);
                while (_gaming)
                {
                    _snake.Move();

                    if (_food == null)
                        _createFood();

                    if (!_checkCollision())
                    {
                        MessageBox.Show("游戏结束!");
                        _gaming = false;
                        Application.Current.Dispatcher.Invoke(GameStoped,_gaming);
                        break;
                    }
                    _drawBody();                    
                    Thread.Sleep(250);
                }
            }).Start();
        }

        public void TurnDirection(int direction)
        {
            _snake.TurnDirection(direction);
        }

        public void _createFood()
        {
            var rnd = new Random();
            var x = rnd.Next(0, (int)_map.Width-_snake.Width);
            var y = rnd.Next(0, (int)_map.Height-_snake.Height);
            _createShortcut();            
            _movdDeskTopIons(_snake.Body.Count, x, y);            
            _food = new Pos { x = x, y = y };
        }

        private bool _checkCollision()
        {
            var head = _snake.Body.First();
            //碰撞墙壁            
            if (head.x < 0 || head.x > _map.Width-_snake.Width)
                return false;
            if (head.y < 0 || head.y > _map.Height-_snake.Height)
                return false;
            //碰撞自身
            for (var i = 1; i < _snake.Body.Count; i++)
            {
                if (
                Math.Abs(head.x - _snake.Body[i].x) < _snake.Width
                &&
                Math.Abs(head.y - _snake.Body[i].y) < _snake.Height
                )
                {
                    return false;
                }
            }

            //碰撞食物
            if (
                  Math.Abs(head.x - _food.x) < _snake.Width
                  &&
                  Math.Abs(head.y - _food.y) < _snake.Height
                )
            {
                _snake.Eat(new Pos { x = _food.x, y = _food.y });
                _food = null;
            }

            return true;
        }
        private void _drawBody()
        {
            for(var i = 0; i < _snake.Body.Count; i++)
            {
                var pos = _snake.Body[i];
                _movdDeskTopIons(i, pos.x, pos.y);
            }
        }



        public void _createShortcut()
        {
            //选择随机图标
            var rnd = new Random().Next(0, ICONS.Length);

            var LinkPathName = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            var IconPath = ICONS[rnd];
            var guid = Guid.NewGuid().ToString();
            //获取图标名字
            var name = ICONNAMES[rnd];
            WshShell shell = new WshShell();            
            IWshShortcut link = (IWshShortcut)shell.CreateShortcut($"{LinkPathName}/{name+guid}.lnk");
            link.IconLocation = IconPath;
            link.TargetPath = IconPath;
            link.Save();            
                         
        }
        private IntPtr _getIconHandle()
        {
            IntPtr vHandle = WindowsAPIs.FindWindow("Progman", "Program Manager");
            vHandle = WindowsAPIs.FindWindowEx(vHandle, IntPtr.Zero, "SHELLDLL_DefView", null);
            vHandle = WindowsAPIs.FindWindowEx(vHandle, IntPtr.Zero, "SysListView32", "FolderView");
            return vHandle;
        }
        private void _movdDeskTopIons(int i, int x, int y)
        {
            WindowsAPIs.SendMessage((int)_shortCut, (int)LVM_SETITEMPOSITION, i, (int)WindowsAPIs.MakeLParam(x, y));            
        }
    }

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值