WPF贪吃蛇游戏开发

目录

 

源码可以在github上获取https://github.com/ZombieAllen/WPF.git

基于WPF的贪吃蛇游戏设计

1 引言

2 功能设计

2.1游戏区域

2.2 贪吃蛇动画

2.3 键盘控制方向

2.4游戏区随机出现蛇果(不能出现在蛇身上)

2.5碰撞监控

2.5.1 与果实碰撞

2.5.2 与边界或者蛇身碰撞

2.6 界面优化1

2.7 高分榜

2.8 界面优化2

3 总结


源码可以在github上获取https://github.com/ZombieAllen/WPF.git

 

基于WPF的贪吃蛇游戏设计

摘要:针对现有的网上关于WPF以及C#开发缺乏系统化的学习资料,现在设计一款大家耳熟能详的贪吃蛇的小游戏,来比较好的把XMAL界面设计,XML文件的信息写入与读取,C#面向对象的程序设计整合到一个游戏里。

 

关键字:WPF;C#;XML;界面设计;Visual Studio 2019

 

1 引言

      随着工厂信息化以及数字化的飞速发展,对于工厂的自动化工程师,掌握一门高级的开发语言,来开发界面美观的windows客户端,实现数据的采集,存储,展示已经变得越来越必须。跨学科的人才需求也在日渐加大。

      C#语言是现如今比较流行的一门面向对象的编程语言,在微软新一代界面设计技术WPF的加持下,设计出一款界面美观,功能强大的windows客户端应用变得非常方便。

      这款基于WPF的贪吃蛇游戏,会利用Canvas来绘制界面的背景以及蛇和果实,并利用障眼法来实现蛇的动画效果,并利用事件来实现方向控制,最后会利用xml技术来存储高分榜,游戏开始的时候可以列出玩家排名。

 

2 功能设计

2.1游戏区域

      首先我们打开Visual Studio2019 , 新建WPF项目

短短的几行xmal代码,就实现了游戏窗口

 

接下来我们在这个Canvas区域画出网格,来规范蛇以及果实出现的位置

技巧:通过isOdd变量的值通过嵌套For循环来在画布上画出一个个颜色相间的方块。

最终实现的代码以及效果

        private void Window_ContentRendered(object sender, EventArgs e)

        {

            DrawGameArea();

        }

        private void DrawGameArea()

        {

            for (int y = 0; y < GameArea.ActualHeight; y+=squareSize)

            {

                for (int x = 0; x < GameArea.ActualWidth; x += squareSize)

                {

                    Rectangle rectangle = new Rectangle() { Width = squareSize, Height = squareSize };

                    rectangle.Fill = isOdd ? Brushes.White : Brushes.WhiteSmoke;

                    GameArea.Children.Add(rectangle);

                    Canvas.SetLeft(rectangle, x);

                    Canvas.SetTop(rectangle, y);

                    isOdd = !isOdd;

                }

                isOdd = !isOdd;

            }

        }  

 

2.2 贪吃蛇动画

这里我们会接触第一个面向对象设计的套路:类-à实例

对于一条蛇来说,C#自身并没有这个实例。 这时候我们需要自己创建一个SnakePart的类,然后进行实例化出一条蛇。

实现方法:

在项目里添加一个SnakePart的类,定义其主要属性:UI元素,位置,是否是蛇头

代码如下

namespace WPF贪吃蛇游戏设计

{

    public  class SnakePart

    {

        public UIElement  uIElement { get; set; }

        public Point point { get; set; }

        public Boolean isHead { get; set; }

    }

}

 

然后我们用泛型集合List把一个个蛇的部分串起来,就成了一条蛇了。

List<SnakePart> snakeParts = new List<SnakePart>();

 

那么我们先画一条静态的蛇

定义蛇的初始位置以及长度

        //初始位置

        Point startPos = new Point(120, 120);

        //初始长度

        int snakeLength = 3;

 

然后判断是否是蛇头来画不同颜色的方框,并把方框添加到蛇的uIElement上,然后在Canvas里画出来

具体实现代码:

        private void DrawSnake(Point startPos, int snakelength)

        {

            Boolean isHead = false;

            double nextX = startPos.X;

            double nextY = startPos.Y;

            for (int i = 0; i < snakelength; i++)

            {

                if (i < snakelength - 1)

                {

                    isHead = false;

                    DrawSnake_sub(isHead, nextX, nextY);

                    nextX += squareSize;

                }

                else

                {

                    isHead = true;

                    DrawSnake_sub(isHead, nextX, nextY);

                }

            }

        }

        private void DrawSnake_sub(Boolean isHead , double nextX,double nextY)

        {

            

                Rectangle rectangle = new Rectangle() { Width = squareSize, Height = squareSize };

                rectangle.Fill = isHead ? snakeHeadColor : snakeBodyColor;

                SnakePart snakepart = new SnakePart();

                snakepart.uIElement = rectangle;

                snakeParts.Add(snakepart);

                GameArea.Children.Add(rectangle);

                Canvas.SetLeft(rectangle, nextX);

                Canvas.SetTop(rectangle, nextY);

        }

然后在页面渲染的时候调用

DrawSnake(startPos, snakeLength);

这样我们就初始化了一条蛇,具体实现结­­­­­果如下(红色为蛇头,绿色为蛇身)

 

接下来我们的任务就是让蛇动起来:控制蛇的方向和蛇的速度

蛇的方向:

首先我们定义一个枚举类型SnakeDirection,来表示蛇移动的四个方向。

namespace WPF贪吃蛇游戏设计

{

  public  enum SnakeDirection

    {

        left,

        top,

        right,

        down

    }

}

然后我们设定蛇的初始行进方向为向右

接下来我们定义蛇的行进速度,这时候就得用到定时器以及事件了

首先我们实例化一个定时器dispatch

        //定时器

        DispatcherTimer dispatcher = new DispatcherTimer();

       

为其绑定一个事件MoveSnake(),定时器的间隔时间设定以及开始计时

  dispatcher.Interval = TimeSpan.FromMilliseconds(1000);

  dispatcher.Tick += Dispatcher_Tick;

  dispatcher.Start();

 

接下来就是MoveSnake函数的编写了。

对于蛇的移动来说,其实用到的就是障眼法,分为4步

  1. 移去蛇尾
  2. 在原先蛇头的位置画蛇身
  3. 移去原先的蛇头
  4. 根据蛇头原先的位置,在其行进方向一个单元格的位置重新画一个蛇头

 

具体实现代码

        private void MoveSnake()

        {

            //移去最后一节身子

            GameArea.Children.Remove(snakeParts[0].uIElement);

            snakeParts.Remove(snakeParts[0]);

            //获取原先蛇头的位置,并重新画一个身子

            double exHeadPosX = snakeParts[snakeParts.Count-1].point.X;

            double exHeadPosY = snakeParts[snakeParts.Count - 1].point.Y;

            DrawSnake_sub(false, exHeadPosX, exHeadPosY);

            //移去原先的蛇头

            GameArea.Children.Remove(snakeParts[snakeParts.Count - 2].uIElement);

            snakeParts.Remove(snakeParts[snakeParts.Count - 2]);

            //重新画一个蛇头

            DrawSnake_sub(true, exHeadPosX+squareSize, exHeadPosY);

        }

经过测试,蛇就可以沿着向右的方向每隔1s移动一格了

 

2.3 键盘控制方向

首先在窗体上定义Key_Up事件

KeyUp="Window_KeyUp"

然后在事件处理函数里定义不同的key值对应的操作

提示:对于贪吃蛇游戏来说,不存在直接回头的操作。

代码如下:

        private void Window_KeyUp(object sender, KeyEventArgs e)

        {

            switch (e.Key)

            {

                case Key.Left:

                    if (snakeDirection!=SnakeDirection.right && snakeDirection != SnakeDirection.left)

                    {

                        snakeDirection = SnakeDirection.left;

                        MoveSnake();

                    }

                    break;

                case Key.Up:

                    if (snakeDirection!=SnakeDirection.down && snakeDirection != SnakeDirection.up)

                    {

                        snakeDirection = SnakeDirection.up;

                        MoveSnake();

                    }

                    break;

                case Key.Right:

                    if (snakeDirection != SnakeDirection.right && snakeDirection != SnakeDirection.left)

                    {

                        snakeDirection = SnakeDirection.right;

                        MoveSnake();

                    }

                    break;

                case Key.Down:

                    if (snakeDirection != SnakeDirection.down && snakeDirection != SnakeDirection.up)

                    {

                        snakeDirection = SnakeDirection.down;

                        MoveSnake();

                    }

                    break;

                default:

                    break;

            }

        }

根据在游戏过程中获取到的snakeDirection的值,我们把之前添加蛇头的逻辑做了些修改,增加了方向的判断

代码如下:

//重新画一个蛇头

            switch (snakeDirection)

            {

                case SnakeDirection.left:

                    DrawSnake_sub(true, exHeadPosX - squareSize, exHeadPosY);

                    break;

                case SnakeDirection.up:

                    DrawSnake_sub(true, exHeadPosX , exHeadPosY - squareSize);

                    break;

                case SnakeDirection.right:

                    DrawSnake_sub(true, exHeadPosX + squareSize, exHeadPosY);

                    break;

                case SnakeDirection.down:

                    DrawSnake_sub(true, exHeadPosX , exHeadPosY + squareSize);

                    break;

                default:

                    break;

            }

这样就实现了蛇根据键盘的输入来进行移动的程序

实现效果如下:

2.4游戏区随机出现蛇果(不能出现在蛇身上)

原理:生成一个squaresize整数倍的数来定位蛇果的位置,然后通过canvas画出来,当然蛇果的位置不能出现在蛇身上以及出游戏边界

 

实现代码:

        private void GenerateFood()

        {

            start:

            Random random = new Random();

            double foodPosX = random.Next(0, (int)GameArea.ActualWidth / squareSize)*squareSize;

            double foodPosY = random.Next(0, (int)GameArea.ActualHeight / squareSize) * squareSize;

            foreach (var snakePart in snakeParts)

            {

                if (snakePart.point.X==foodPosX && snakePart.point.Y==foodPosY)

                {

                    goto start;

                }

            }

            Ellipse food = new Ellipse() { Width = squareSize, Height = squareSize };

            food.Fill = Brushes.Chocolate;

            GameArea.Children.Add(food);

            Canvas.SetLeft(food, foodPosX);

            Canvas.SetTop(food, foodPosY);

        }

 

实现效果如下

 

2.5碰撞监控

碰撞监控的效果:蛇与果实碰撞则果实消失,蛇身长度+1。如果蛇与边界碰撞,游戏结束

2.5.1 与果实碰撞

先来实现蛇与果实碰撞,蛇身长度+1的逻辑

思路:判断蛇头与蛇果的位置重合,则清除果实,重新生成果实,返回一个值,根据这个值来判断是否需要去掉蛇尾

代码实现:

        private int CollisionCheck()

        {

            if (snakeParts[snakeParts.Count-1].point.X==foodPosX && snakeParts[snakeParts.Count - 1].point.Y==foodPosY)

            {

                GameArea.Children.Remove(food);

                GenerateFood();

                return 1;

            }

            else

            {

                return 0;

            }

        }

原来的MoveSnake函数里增加一个判断条件,来决定是否需要砍掉蛇尾

代码如下:

        private void MoveSnake()

        {

            if (CollisionCheck()!=1)

            {

                //移去最后一节身子

                GameArea.Children.Remove(snakeParts[0].uIElement);

                snakeParts.Remove(snakeParts[0]);

 

            }

实现效果如下:

2.5.2 与边界或者蛇身碰撞

如果蛇头与蛇身碰撞,或者蛇头超出边界,则游戏结束。

在CollisionCheck方法里添加如下代码:

            else

            {

                foreach (var snakeBody in snakeParts.Take(snakeParts.Count - 1))

                {

                    if (snakeBody.point.X == snakeParts[snakeParts.Count - 1].point.X && snakeBody.point.Y == snakeParts[snakeParts.Count - 1].point.Y)

                    {

                        EndGame();

                    }

                }

                if (snakeParts[snakeParts.Count - 1].point.X >= GameArea.ActualWidth ||

                   snakeParts[snakeParts.Count - 1].point.X < 0 ||

                   snakeParts[snakeParts.Count - 1].point.Y >= GameArea.ActualHeight ||

                   snakeParts[snakeParts.Count - 1].point.Y < 0)

                {

                    EndGame();

                }

                return 0;

            }

并为EndGame方法添加内容,当游戏结束的时候,停止定时器,并且弹出提示框

        private void EndGame()

        {

            //dispatcher.Stop();

            dispatcher.IsEnabled = false;

            dispatcher.Stop();

            MessageBox.Show("游戏结束","WPF贪吃蛇游戏",MessageBoxButton.OK,MessageBoxImage.Warning);

        }

 

最终的效果

 

 

2.6 界面优化1

基本功能实现后,我们来对界面来美化一下。

首先我们重新设计下游戏结束的时候要弹出的窗体样式

在Canvas里添加如下代码:

            <Border x:Name="bdr_EndGame" Visibility="Collapsed" Panel.ZIndex="1" BorderThickness="3" BorderBrush="AliceBlue" Width="400" Height="400" Margin="200" Background="Azure" Padding="100">

                <StackPanel>

                    <TextBlock FontSize="30" Foreground="Red" TextAlignment="Center" Margin="0,20">游戏结束</TextBlock>

                    <TextBlock  FontSize="30" Foreground="Green" TextAlignment="Center" Margin="0,10">您的得分是:</TextBlock>

                    <TextBlock x:Name="txt_score" FontSize="30" Foreground="Black" TextAlignment="Center" Width="90"></TextBlock>

                </StackPanel>

            </Border>

然后我们得到如下效果

然后我们在后端替换下原来的messagebox.设定该border的属性为visual

bdr_EndGame.Visibility = Visibility.Visible;

为了显示最终的得分,我们需要在程序里定义一个变量score,然后每次蛇头与食物重合的时候,score+1,然后把这个值转换成string类型,传给txt_score.Text

txt_score.Text = Score.ToString();

最终当游戏结束的时候,显示的效果入下图所示

 

接下来我们实现的功能是让游戏具有开始画面,当用户按下“S”键,游戏开始。

当游戏结束的时候,用户按下”S”键,游戏又重复开始。

 

首先我们设计下开始的画面

对应的XMAL代码如下:

            <Border x:Name="bdr_Welcome" Visibility="Visible" Panel.ZIndex="1" BorderThickness="3" BorderBrush="AliceBlue" Width="600" Height="600" Margin="100" Background="Azure" Padding="100">

                <StackPanel>

                    <TextBlock FontSize="50" Foreground="Red" TextAlignment="Center" Margin="0,20">WPF贪吃蛇</TextBlock>

                    <TextBlock FontSize="20" Foreground="Red" TextAlignment="Center" Margin="0,30" TextWrapping="Wrap">大多数游戏都会通关,但贪吃蛇不一样,像极了人的一生,不停奔波,不停索取,但,最终只会死亡</TextBlock>

                    <TextBlock  FontSize="20" Foreground="Green" TextAlignment="Center" Margin="0,40">“S”键开始这段结局已注定的游戏</TextBlock>

                </StackPanel>

            </Border>

 

在程序后台,我们定义一个变量isGameRunning来作为游戏的运行状态。

我们首先来让程序接受键盘S的输入

case Key.S:

                    if (isGameRuning==false)

                    {

                         StartGame();

 

                    }

                    break;

当程序接收到键盘的key_up事件,并且是又S键触发的

我们就执行StartGame方法

大体思路:如果蛇和果实都还有残余在画布上,我们首先会清除这部分Ui元素,初始化游戏,然后开始计时。

 

实现代码如下:

        private void StartGame()

        {

            //欢迎画面消失

            bdr_Welcome.Visibility = Visibility.Collapsed;

            //清除上一局游戏里的蛇身

            if (snakeParts.Count!=0)

            {

                int snakeLength = snakeParts.Count;

                for (int i = 0; i < snakeLength; i++)

                {

                    GameArea.Children.Remove(snakeParts[0].uIElement);

                    snakeParts.Remove(snakeParts[0]);

                }

            }

            //清除上一局游戏里的食物

            if (food!=null)

            {

                GameArea.Children.Remove(food);

                food = null;

            }

 

            bdr_EndGame.Visibility = Visibility.Collapsed;

 

            //初始方向l

            snakeDirection = SnakeDirection.right;

            snakeDirectionPre = SnakeDirection.right;

            //重新生成蛇身以及食物

            DrawSnake(startPos, snakeLength);

            GenerateFood();

            //开始计时

            dispatcher.Interval = TimeSpan.FromMilliseconds(500);

            dispatcher.Start();

            isGameRuning = true;

 

        }

 

到现在为止,这已经是一个完整的WPF贪吃蛇程序了。

2.7 高分榜

对于一个游戏来说,游戏分数的记录,以及高分榜的排序也是吸引玩家的关键,可以让玩家有成就感,当他的名字出现在高分榜上。

现在我们就来实现这个功能

首先我们创建一个HighScore的类,代码如下

    public class HighScore

    {

        public string Name { get; set; }

        public int Score { get; set; }

}

 

在后台的定义下一个泛型集合highScoreList以及xmlName

        //高分榜

        List<HighScore> highScoreList = new List<HighScore>();

        //xml文件路径

        string xmlName = "HighScoreList.xml";

接下来的思路是:当程序载入的时候,判断在程序的路径下是否存在这个名为xml的文件,如果没有就创建一个,如果有就读取文件内容到highScoreList这个列表里。

对应的代码:

if (File.Exists(xmlName) == false)

            {

                CreateXMLFile();

            }

            else

            {

                StreamReader read = new StreamReader(xmlName);

                ReadXML(read);

            }

对应的CreateXMLFile方法和ReadXML的方法代码:

CreateXMLFile方法

        private void CreateXMLFile()

        {

            //创建名为xmlName的文件流

            Stream write = new FileStream(xmlName, FileMode.Create);

            //设定xml的内部格式

            GenerateXML(write);

        }

        private void GenerateXML(Stream write)

        {

            XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<HighScore>));

            

            xmlSerializer.Serialize(write, highScoreList);

         

            write.Close();

        }

 

ReadXMLFile方法

        private void ReadXML(StreamReader readStream)

        {

            XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<HighScore>));

           

            highScoreList = (List<HighScore>)xmlSerializer.Deserialize(readStream);

            highScoreList = new List<HighScore>(SortByScore(highScoreList));

            Item_TopList.ItemsSource = highScoreList.Take(5);

            readStream.Close();

        }

 

当游戏结束的时候,需要判断下这次的成绩score是否在highScoreList这个集合的top5,如果是,需要弹出一个让玩家输入名字的对话框。不是则直接弹出成绩的对话框。

实现代码:

        private IOrderedEnumerable<HighScore> SortByScore(List<HighScore> highscorelist)

        {

           var   highScoreOrderList= highscorelist.OrderByDescending(x => x.Score);

            return highScoreOrderList;

        }

在我们程序载入并读取XML文件的时候,我们会把highScoreList的元素按照score降序排列,并重新赋给highScoreList,当游戏结束的时候,我们只需要比较这次的成绩score和highScoreList[4].score的大小,如果大于则弹出输入名字的对话框,不是则显示成绩

对应代码:

            if (isGameRuning==false)

            {

                if (highScoreList.Count!=0 && score > highScoreList[4].Score)

                {

                    bdr_AddHighScore.Visibility = Visibility.Visible;

                }

 

 

            }

            else

            {

                bdr_TopList.Visibility = Visibility.Collapsed;

                bdr_AddHighScore.Visibility = Visibility.Collapsed;

            }

        }

 

现在我们设计下两个界面,输入姓名的对话框以及高分榜的排名

输入姓名的对话框:

 

XMAL代码:

            <Border x:Name="bdr_AddHighScore" Visibility="Visible" Panel.ZIndex="1" BorderThickness="3" BorderBrush="AliceBlue" Width="600" Height="600" Margin="100" Background="Azure" Padding="100">

                <StackPanel>

                    <TextBlock FontSize="50" Foreground="Red" TextAlignment="Center" Margin="0,20">恭喜进入高分榜</TextBlock>

                    <TextBlock FontSize="20" Foreground="Red" TextAlignment="Center" Margin="0,30" TextWrapping="Wrap">请输入您的名字</TextBlock>

                    <TextBox x:Name="txt_PlayName"  FontSize="20" Foreground="Green" TextAlignment="Center" Margin="0,40" Width="200"></TextBox>

                    <Button FontSize="20" Foreground="Green"  Margin="0,10" Width="200" Background="Transparent" Click="btn_submit" >提交</Button>

                </StackPanel>

            </Border>

 

高分榜对话框:

 

对应的XMAL代码:

            <Border x:Name="bdr_TopList"  Visibility="Visible" Panel.ZIndex="1" BorderThickness="3" BorderBrush="AliceBlue" Width="600" Height="600" Margin="100" Background="Azure" Padding="100">

                <StackPanel>

                    <TextBlock FontSize="50" Foreground="Red" TextAlignment="Center" Margin="0,20">高分榜</TextBlock>

                    <ItemsControl x:Name="Item_TopList" >

                        <ItemsControl.ItemTemplate>

                            <DataTemplate>

                                <DockPanel>

                                    <TextBlock DockPanel.Dock="Left" TextAlignment="Left" FontSize="30" Text="{Binding Name}"></TextBlock>

                                    <TextBlock DockPanel.Dock="Right" TextAlignment="Right" FontSize="30" Text="{Binding Score}"></TextBlock>

                                </DockPanel>

                            </DataTemplate>

                        </ItemsControl.ItemTemplate>

                    </ItemsControl>

                </StackPanel>

            </Border>

 

接下来就是输入姓名对话框弹出后,我们需要把输入的姓名以及对应的结果写入highScoreList里,输入完成后,当按下提交按钮,则会跳转到高分榜的界面。再次按下“S”键,则游戏重新开始

对应代码:

        private void btn_submit(object sender, RoutedEventArgs e)

        {

            AddInfoToXML(txt_PlayName.Text);

 

            StreamReader read = new StreamReader(xmlName);

            ReadXML(read);

            bdr_TopList.Visibility = Visibility.Visible;

            bdr_AddHighScore.Visibility = Visibility.Collapsed;

        }

        private void AddInfoToXML(string name)

        {

            HighScore highScore = new HighScore() {Name = name,Score = score };

            highScoreList.Add(highScore);

            highScoreList = new List<HighScore>(SortByScore(highScoreList));

            Stream write= new FileStream(xmlName, FileMode.Open);

            GenerateXML(write);

        }

        private void GenerateXML(Stream write)

        {

            XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<HighScore>));

           

            xmlSerializer.Serialize(write, highScoreList);

         

            write.Close();

        }

这样我们利用XML的数据存储属性就完成了高分榜功能的制作。像这样数据量不大的情况下,没有必要用专业的数据库来做,XML轻量易操作,用在小型的程序里特别适合

2.8 界面优化2

最后一点工作:我们美化下窗体。

隐去windows默认的窗体外框,重新在Canvas上Dock一个stackPanel

XMAL代码如下:

        <StackPanel DockPanel.Dock="Top" Background="Black">

            <Grid>

                <Grid.ColumnDefinitions>

                    <ColumnDefinition Width="*"></ColumnDefinition>

                    <ColumnDefinition Width="*"></ColumnDefinition>

                    <ColumnDefinition Width="auto"></ColumnDefinition>

                </Grid.ColumnDefinitions>

                <Grid.RowDefinitions>

                    <RowDefinition Height="40"></RowDefinition>

                    <RowDefinition Height="40"></RowDefinition>

                </Grid.RowDefinitions>

 

                <TextBlock Grid.Row="0" Grid.Column="0" FontSize="30" Foreground="White" FontWeight="Bold">贪吃蛇游戏</TextBlock>

                <TextBlock Grid.Row="0" Grid.Column="1" FontSize="30" Foreground="White" FontWeight="Bold">作者:Zombie</TextBlock>

                <Button Grid.Row="0" Grid.Column="2" FontSize="30" Background="Transparent" Foreground="White" Click="btn_Close">X</Button>

 

                <TextBlock Grid.Row="1" Grid.Column="0" FontSize="30" Foreground="Yellow" FontWeight="Bold"  x:Name="txt_current_score"></TextBlock>

                <TextBlock Grid.Row="1" Grid.Column="1" FontSize="30" Foreground="Yellow" FontWeight="Bold"  x:Name="txt_current_speed"></TextBlock>

            </Grid>

        </StackPanel>

 

界面效果如下:

 

并增加了游戏难度,没吃一个果实,速度提高50,最快是时间间隔100ms

实现代码:

                speed = speed > 100 ? speed - 50 : 100;

                dispatcher.Interval = TimeSpan.FromMilliseconds(speed);

 

并最终在标题栏的第二行显示出分数以及速度

效果如下:

高分榜:

 

3 总结

到这里,我们就实现了一个比较完整的贪吃蛇游戏。贪吃蛇游戏在各个计算机语言设计中都是一个综合性比较强的项目。其中涉及了泛型,集合,Lambda表达式,Canvas这些比较难的概念,也有动画实现的小技巧。还涉及到利用XML进行数据的存储,调用来实现高分榜的部分。WPF特有的 XMAL语言,可以很好的实现非常精致的画面,以及和后端代码的分离。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页