八、生成新的单节蛇身
我们这里先说说游戏小原理好了,游戏运行后,通过计时器事件不断生成新的单节蛇身类SnakeNode,添加到List中的0位置,原来的蛇头变成了第二节。该节新蛇头的坐标通过蛇头前进方向Direct进行判断,即如果原蛇头往左运行,则新蛇头在原蛇头位置的左一格生成,其他方向以此类推。最后将该节添加到SnakeNodes列表中,并将相应图形添加到游戏区。

private void GenNewSnakeNode()
{
SnakeNode snakeNode = null;
switch (Direct)
{
case Direction.UP:
snakeNode = new SnakeNode(new Point(SnakeNodes[SnakeHead]._pos.X,
SnakeNodes[SnakeHead]._pos.Y - ));
break; case Direction.DOWN:
snakeNode = new SnakeNode(new Point(SnakeNodes[SnakeHead]._pos.X,
SnakeNodes[SnakeHead]._pos.Y + ));
break; case Direction.LEFT:
snakeNode = new SnakeNode(new Point(SnakeNodes[SnakeHead]._pos.X - ,
SnakeNodes[SnakeHead]._pos.Y));
break; case Direction.RIGHT:
snakeNode = new SnakeNode(new Point(SnakeNodes[SnakeHead]._pos.X + ,
SnakeNodes[SnakeHead]._pos.Y));
break;
} if (snakeNode != null)
{
SnakeNodes.Insert(, snakeNode);
myCanvas.Children.Add(SnakeNodes[]._rect);
}
}

九、随机水果位置
注意该新位置不得与蛇身任何一节重叠,如果重叠则另外生成新位置,直到成功为止。

private Point SetFruitToRandomPos()
{
bool flag = true;
Point pos = new Point();
while (flag)
{
flag = false;
pos = new Point(rnd.Next(, CellWidth), rnd.Next(, CellHeight));
foreach (var node in SnakeNodes)
{
if (pos.X == node._pos.X && pos.Y == node._pos.Y)
{
flag = true;
break;
}
}
} return pos;
}

十、碰撞检测(蛇头与水果)
因为只有蛇头会最先触碰到水果,所以只需判断蛇头坐标与水果坐标是否相同即可。如果撞到水果,则将水果随机生成新位置;如果没有碰到,则删除蛇尾一节(因为之后会通过计时器会生成新一节蛇头,这样就会保存蛇身长度,感觉位置前进了一格)。

private void CheckCollision()
{
if (SnakeNodes[SnakeHead]._pos.X == fruit._pos.X && SnakeNodes[SnakeHead]._pos.Y == fruit._pos.Y)
{
fruit.SetPostion(SetFruitToRandomPos());
}
else
{
if (myCanvas.Children.Contains(SnakeNodes[SnakeNodes.Count - ]._rect))
myCanvas.Children.Remove(SnakeNodes[SnakeNodes.Count - ]._rect); SnakeNodes.RemoveAt(SnakeNodes.Count - );
}
}

十一、判断是否游戏结束
很简单,看看蛇头是否触碰游戏区四边或蛇身的任何一节,如果是则返回true。
注意蛇身循环是从1开始的,如果从0开始你知道会出现什么情况。

        private bool IsGameOver()
{
if (SnakeNodes[SnakeHead]._pos.X == - || SnakeNodes[SnakeHead]._pos.X == CellWidth
|| SnakeNodes[SnakeHead]._pos.Y == - || SnakeNodes[SnakeHead]._pos.Y == CellHeight)
{
return true;
} foreach (var node in SnakeNodes)
{
if (node == SnakeNodes[SnakeHead])
continue; if (node._pos.X == SnakeNodes[SnakeHead]._pos.X && node._pos.Y == SnakeNodes[SnakeHead]._pos.Y)
{
return true;
}
} return false;
}

十三、删除游戏区的所有蛇身节
这个会在重新开始游戏时使用到:

private void RemoveSnakeNodeAll()
{
for (int i = ; i < SnakeNodes.Count; i++)
{
if (myCanvas.Children.Contains(SnakeNodes[i]._rect))
{
myCanvas.Children.Remove(SnakeNodes[i]._rect);
}
}
}

顺便再来一发删除游戏区的水果:

private void RemoveFruit()
{
if (fruit == null)
{
return;
} if (myCanvas.Children.Contains(fruit._ellipse))
{
myCanvas.Children.Remove(fruit._ellipse);
}
}

十四、按键操作
通过方向键,设置Direct值即可。

private void MyCanvas_PreviewKeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Left:
if (Direct != Direction.RIGHT)
{
Direct = Direction.LEFT;
}
break; case Key.Right:
if (Direct != Direction.LEFT)
{
Direct = Direction.RIGHT;
}
break; case Key.Up:
if (Direct != Direction.DOWN)
{
Direct = Direction.UP;
}
break; case Key.Down:
if (Direct != Direction.UP)
{
Direct = Direction.DOWN;
}
break; case Key.Escape:
Application.Current.Shutdown();
break; case Key.Space:
if (CurrGameState == GameState.NONE)
return; if (CurrGameState == GameState.PAUSE)
{
CurrGameState = GameState.GAMEING;
timer.Start();
MenuControl_Pause.Header = "暂停";
}
else if (CurrGameState == GameState.GAMEING)
{
CurrGameState = GameState.PAUSE;
timer.Stop();
MenuControl_Pause.Header = "继续";
}
break;
}
}

十四、定义开始游戏方法
先删除界面中的蛇身和水果(保留底部暗格),随机位置生成水果和三节蛇身。

private void StartGame()
{
RemoveSnakeNodeAll();
RemoveFruit(); int startX = rnd.Next(, CellWidth - );
int startY = rnd.Next(, CellHeight - );
Direct = Direction.RIGHT; fruit = new Fruit(SetFruitToRandomPos(), myCanvas); SnakeNodes = new List<SnakeNode>();
SnakeNodes.Add(new SnakeNode(new Point(startX, startY)));
GenNewSnakeNode();
GenNewSnakeNode();
}

十五、开始游戏菜单

调用StartGame()方法,并设定计时器开始。

private void MenuFile_NewGame_Click(object sender, RoutedEventArgs e)
{
StartGame();
timer.Start();
CurrGameState = GameState.GAMEING;
MenuControl_Pause.Header = "暂停";
}

十六、暂停与继续
直接上代码,没有要说的了。

private void MenuControl_Pause_Click(object sender, RoutedEventArgs e)
{
if (CurrGameState == GameState.GAMEING)
{
CurrGameState = GameState.PAUSE;
timer.Stop();
MenuControl_Pause.Header = "继续";
}
else if (CurrGameState == GameState.PAUSE)
{
CurrGameState = GameState.GAMEING;
timer.Start();
MenuControl_Pause.Header = "暂停";
}
}

十七、退出游戏

private void MenuFile_Exit_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}

十八、其他菜单功能
自己实现好了

private void MenuHelp_About_Click(object sender, RoutedEventArgs e)
{
}

[2019.12.23]更新:添加计时器事件代码(我不知道这个方法上次没有贴出来)

十九、计时器事件

/// <summary>
/// 计时器事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Timer_Tick(object sender, EventArgs e)
{
CheckCollision();
GenNewSnakeNode(); if (IsGameOver())
{
CurrGameState = GameState.STOP;
timer.Stop();
MessageBox.Show("游戏结束", "警告", MessageBoxButton.OK); return;
}
}

练手WPF(四)——贪吃蛇小游戏的简易实现(下)的更多相关文章

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

    一. 游戏界面首先,按照惯例,编辑MainWindow.xaml,先将游戏界面制作好.非常简单:(1)主游戏区依然使用我们熟悉的Canvas控件,大小为640X480像素,设定每小格子为20px,所以 ...

  2. 练手WPF(二)——2048游戏的简易实现(上)

    1.创建游戏界面编辑MainWindow.xaml,修改代码如下: <Window.Resources> <Style TargetType="Label"> ...

  3. 练手WPF(二)——2048游戏的简易实现(下)

    接着上一篇继续~~~ 6.动画显示增加分数 /// <summary> /// 动画显示增加得分 /// </summary> /// <param name=" ...

  4. C++ 简单的控制台贪吃蛇小游戏

    由于比较懒,所以不怎么写,觉得这样不应该.我应该对自己学的做出整理,不管是高端低端,写出来是自己的. // 贪吃蛇.cpp : 定义控制台应用程序的入口点. // #include "std ...

  5. 贪吃蛇小游戏-----C语言实现

    1.分析 众所周知,贪吃蛇游戏是一款经典的益智游戏,有PC和手机等多平台版本,既简单又耐玩.该游戏通过控制蛇头方向吃食物,从而使得蛇变得越来越长,蛇不能撞墙,也不能装到自己,否则游戏结束.玩过贪吃蛇的 ...

  6. JS高级---案例:贪吃蛇小游戏

    案例:贪吃蛇小游戏 可以玩的小游戏,略复杂,过了2遍,先pass吧 先创建构造函数,再给原型添加方法.分别创建食物,小蛇和游戏对象. 食物,小蛇的横纵坐标,设置最大最小值,运动起来的函数,按上下左右键 ...

  7. Java GUI学习,贪吃蛇小游戏

    JAVA GUI练习 贪吃蛇小游戏 前几天虽然生病了,但还是跟着狂神学习了GUI的方面,跟着练习了贪吃蛇的小项目,这里有狂神写的源码点我下载,还有我跟着敲的点我下载,嘿嘿,也就注释了下重要的地方,这方 ...

  8. 用GUI实现java版贪吃蛇小游戏

    项目结构 新建一个JFrame窗口,作为程序入口 public class GameStart{ public static void main(String[] args) { JFrame jFr ...

  9. Java 用java GUI写一个贪吃蛇小游戏

    目录 主要用到 swing 包下的一些类 上代码 游戏启动类 游戏数据类 游戏面板类 代码地址 主要用到 swing 包下的一些类 JFrame 窗口类 JPanel 面板类 KeyListener ...

随机推荐

  1. python3的reload(sys)

    import sys reload(sys) sys.setdefaultencoding(‘utf-8’) 以上是python2的写法,但是在python3中这个需要已经不存在了,这么做也不会什么实 ...

  2. 用 Python 自动监测 GitHub 项目更新

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: GitPython PS:如有需要Python学习资料的小伙伴可以加 ...

  3. Python--方法/技巧在哪用的典型例子

    就我个人在学习Python的过程中,经常会出现学习了新方法后,如果隔上几天不用,就忘了的情况,或者刚学习的更好的方法没有得到应用,还是沿用已有的方法,这样很不利于学习和掌握新姿势,从而拉长学习时间,增 ...

  4. Java 实践:生产者与消费者

    实践项目:生产者与消费者[经典多线程问题] 问题引出: 生产者和消费者指的是两个不同的线程类对象,操作同一个空间资源的情况. 需求引出: —— 生产者负责生产数据,消费者负责取走数据 —— 生产者生产 ...

  5. Lnmp架构部署动态网站环境.2019-7-3-1.4

    安装wiki开源产品 一.创建数据库 [root@Lnmp bbs]# mysql -uroot -p123456 mysql> create database wiki; #创建wiki数据库 ...

  6. wsl笔记

    目录 环境 修改更新源 新建用户 设置超级用户 环境变量 zsh美化终端 设zsh为默认shell 环境变量PATH 开启 ssh 远程连接 apt命令与问题 windows 和 wsl 互相访问文件 ...

  7. CodeForces - 1250L(思维+贪心)

    题意 https://vjudge.net/problem/CodeForces-1250L 有三类人,a,b,c,现在要把这些人分成三个组,a和c类的不能在同一组,问分组后三组的最大的人数的最小值是 ...

  8. Windows | Ubuntu18.04分别安装Matlab 2017b破解版

    首先下载好Windows和Ubuntu 版本的MATLAB 2017b 安装包, 1.Windows上安装,解压文件R2017b_win64_dvd1.iso和R2017b_win64_dvd2.is ...

  9. JS阻止冒泡和取消默认事件(默认行为)

    本文链接:http://caibaojian.com/javascript-stoppropagation-preventdefault.html 阻止事件冒泡 function(e){ if( e ...

  10. SpringCloud琐碎内容

    SpringCloud提供了很多监控端点,可以使用http://{ip}:{port}/{endpoint}得形式来访问这些端点,从而了解应用程序的运行状况. Actuator提供的端点,如表3-2所 ...