练手WPF(四)——贪吃蛇小游戏的简易实现(上)
程序员文章站
2022-06-19 16:45:27
一. 游戏界面首先,按照惯例,编辑MainWindow.xaml,先将游戏界面制作好。非常简单:(1)主游戏区依然使用我们熟悉的Canvas控件,大小为640X480像素,设定每小格子为20px,所以横坚坐标的格子数为32x24。见源代码的最后位置。(2)定位控件我们使用DockPanel,方便放置 ......
一. 游戏界面
首先,按照惯例,编辑mainwindow.xaml,先将游戏界面制作好。非常简单:
(1)主游戏区依然使用我们熟悉的canvas控件,大小为640x480像素,设定每小格子为20px,所以横坚坐标的格子数为32x24。见源代码的最后位置。
(2)定位控件我们使用dockpanel,方便放置主菜单。
(3)将按键事件previewkeydown放在window内。
<window x:class="moonsnake.mainwindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:moonsnake" mc:ignorable="d" previewkeydown="mycanvas_previewkeydown" title="moon snake game" height="540" width="660" windowstartuplocation="centerscreen" resizemode="canminimize"> <dockpanel> <menu dockpanel.dock="top"> <menuitem header="文件"> <menuitem name="menufile_newgame" header="新游戏" click="menufile_newgame_click" /> <separator/> <menuitem name="menufile_exit" header="退出" click="menufile_exit_click" /> </menuitem> <menuitem header="控制"> <menuitem name="menucontrol_pause" header="暂停" click="menucontrol_pause_click" /> </menuitem> <menuitem header="帮助"> <menuitem name="menuhelp_about" header="关于..." click="menuhelp_about_click" /> </menuitem> </menu> <canvas x:name="mycanvas" height="480" width="640" background="#222222" focusable="true" previewkeydown="mycanvas_previewkeydown" /> </dockpanel> </window>
二、添加水果fruit类
因为我们不打算使用任何图片,所以为了简单起见,就只使用红色的实心圆代表水果好了。
看下面的代码:功能简单,主要通过两个属性指定水果的位置和图形。
public class fruit { public point _pos { get; set; } public ellipse _ellipse { get; set; } public canvas _canvas { get; set; } public fruit(point point, canvas canvas) { _pos = point; _canvas = canvas; _ellipse = new ellipse { width = 20, height = 20, fill = brushes.red }; _ellipse.setvalue(canvas.leftproperty, _pos.x * 20); _ellipse.setvalue(canvas.topproperty, _pos.y * 20); _canvas.children.add(_ellipse); } public void setpostion(point pos) { _pos = pos; _ellipse.setvalue(canvas.leftproperty, _pos.x * 20); _ellipse.setvalue(canvas.topproperty, _pos.y * 20); } }
三、添加单节蛇身snakenode类
每个snakenode代表蛇身的一节,之后我们会通过list<snakenode>列表代表整条蛇。
看代码就知道了,与水果类非常相似,甚至比它更简单,构造函数没有传递canvas参数,因为我们打算在主程序实现添加图形到主游戏区的功能,只要指定它的位置和形状即可,形状则使用了有边线的矩形代替。
public class snakenode { public point _pos { get; set; } public rectangle _rect { get; set; } public snakenode(point point) { _pos = point; _rect = new rectangle { width = 20, height = 20, stroke = new solidcolorbrush(colors.dodgerblue), strokethickness = 3, fill = brushes.skyblue }; _rect.setvalue(canvas.leftproperty, _pos.x * 20); _rect.setvalue(canvas.topproperty, _pos.y * 20); } }
四、定义四个常量和两个枚举
看注释:
const int cellsize = 20; // 小格子大小 const int snakehead = 0; // 蛇头位置(永远位于列表0) const int cellwidth = 640 / cellsize; // 游戏区横格数 const int cellheight = 480 / cellsize; // 游戏区纵格数 // 蛇身前进方向 enum direction { up, down, left, right } direction direct = direction.up; // 游戏状态 enum gamestate { none, gameing, pause, stop } gamestate currgamestate = gamestate.none;
五、很少的几个字段变量
list<snakenode> snakenodes = new list<snakenode>(); // 蛇身列表 fruit fruit; // 水果 random rnd = new random((int)datetime.now.ticks); // 随机数 system.windows.threading.dispatchertimer timer = new system.windows.threading.dispatchertimer(); // 计时器
六、画游戏区暗格线
主要使用path控件,通过循环每隔20px画一根横线和纵线。
private void drawgrid() { path gridpath = new path(); gridpath.stroke = new solidcolorbrush(color.fromargb(255, 50, 50, 50)); gridpath.strokethickness = 1; stringbuilder data = new stringbuilder(); for (int x = 0; x < 640; x += cellsize) { data.append($"m{x},0 l{x},480 "); } for (int y = 0; y<480; y += cellsize) { data.append($"m0,{y} l640,{y} "); } gridpath.data = geometry.parse(data.tostring()); mycanvas.children.add(gridpath); }
七、我是构造方法
这里画底线和设置计时器。
public mainwindow() { initializecomponent(); drawgrid(); timer.interval = new timespan(0, 0, 0, 0, 260); timer.tick += timer_tick; }
可先注释掉最后一行,运行看看游戏界面了。