練手WPF(四)——貪吃蛇小游戲的簡易實現(上)


一. 游戲界面
首先,按照慣例,編輯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;            
}

可先注釋掉最后一行,運行看看游戲界面了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM