這次,又是一個全新的小玩意,就是把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));
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/293584.html
標籤:其他
