欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

练手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;            
}

可先注释掉最后一行,运行看看游戏界面了。

练手WPF(四)——贪吃蛇小游戏的简易实现(上)