练手WPF(四)——贪吃蛇小游戏的简易实现(下)
八、生成新的单节蛇身
我们这里先说说游戏小原理好了,游戏运行后,通过计时器事件不断生成新的单节蛇身类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(四)——贪吃蛇小游戏的简易实现(下)的更多相关文章
- 练手WPF(四)——贪吃蛇小游戏的简易实现(上)
一. 游戏界面首先,按照惯例,编辑MainWindow.xaml,先将游戏界面制作好.非常简单:(1)主游戏区依然使用我们熟悉的Canvas控件,大小为640X480像素,设定每小格子为20px,所以 ...
- 练手WPF(二)——2048游戏的简易实现(上)
1.创建游戏界面编辑MainWindow.xaml,修改代码如下: <Window.Resources> <Style TargetType="Label"> ...
- 练手WPF(二)——2048游戏的简易实现(下)
接着上一篇继续~~~ 6.动画显示增加分数 /// <summary> /// 动画显示增加得分 /// </summary> /// <param name=" ...
- C++ 简单的控制台贪吃蛇小游戏
由于比较懒,所以不怎么写,觉得这样不应该.我应该对自己学的做出整理,不管是高端低端,写出来是自己的. // 贪吃蛇.cpp : 定义控制台应用程序的入口点. // #include "std ...
- 贪吃蛇小游戏-----C语言实现
1.分析 众所周知,贪吃蛇游戏是一款经典的益智游戏,有PC和手机等多平台版本,既简单又耐玩.该游戏通过控制蛇头方向吃食物,从而使得蛇变得越来越长,蛇不能撞墙,也不能装到自己,否则游戏结束.玩过贪吃蛇的 ...
- JS高级---案例:贪吃蛇小游戏
案例:贪吃蛇小游戏 可以玩的小游戏,略复杂,过了2遍,先pass吧 先创建构造函数,再给原型添加方法.分别创建食物,小蛇和游戏对象. 食物,小蛇的横纵坐标,设置最大最小值,运动起来的函数,按上下左右键 ...
- Java GUI学习,贪吃蛇小游戏
JAVA GUI练习 贪吃蛇小游戏 前几天虽然生病了,但还是跟着狂神学习了GUI的方面,跟着练习了贪吃蛇的小项目,这里有狂神写的源码点我下载,还有我跟着敲的点我下载,嘿嘿,也就注释了下重要的地方,这方 ...
- 用GUI实现java版贪吃蛇小游戏
项目结构 新建一个JFrame窗口,作为程序入口 public class GameStart{ public static void main(String[] args) { JFrame jFr ...
- Java 用java GUI写一个贪吃蛇小游戏
目录 主要用到 swing 包下的一些类 上代码 游戏启动类 游戏数据类 游戏面板类 代码地址 主要用到 swing 包下的一些类 JFrame 窗口类 JPanel 面板类 KeyListener ...
随机推荐
- python基础(15):内置函数(一)
1. 内置函数 什么是内置函数? 就是python给你提供的,拿来直接⽤的函数,比如print,input等等,截⽌到python版本3.6.2 python⼀共提供了68个内置函数.他们就是pyth ...
- Python笔记:设计模式之工厂模式
工厂模式:“工厂”即表示一个负责创建其他类型的对象的类,通常情况下,一个工厂的对象会有一个或多个方法与之关联,这些方法用于创建不同类型的对象,工厂对象会根据客户端给方法传递的不同的参数或者客户端调用不 ...
- 1G内存VPS安装 mysql5.6 经常挂
背景介绍 去年3月份的时候参加了腾讯云主机活动,5年362,非常优惠.当时的想法是买来可以瞎整一波,虽然配置不高,但是搞点事情也够用. 配置如下,上海机房 1 核 1 GB 1 Mbps 系统盘:普通 ...
- JS基础语法---continue关键字
continue: 在循环中如果遇到continue关键字,直接开始下一次循环 如下:continue在计数器之前,会一直循环,浏览器会崩溃,我的就崩溃了 var i = 0; while (i &l ...
- haproxy动静分离的验证
线上出现过项目的静态文件被拦截,不能直接访问.所以想到了haproxy指向对应的url来访问静态文件,想到这里在网络搜索了下,确实有此功能.立即上测试环境验证下: 在listen中增加两行: #定 ...
- Linux 安装并配置zsh
1. 安装zsh,配置agnoster主题 1.1 安装zsh $ sudo apt-get install -y zsh 1.2 安装oh-my-zsh $ sh -c "$(curl - ...
- Houdini设置
关闭网络编辑器动画 Animate network changes 平移时保持视口枢轴 Maintain Viewport Pivot when Panning 通知帮助提示将出现在Houdini的状 ...
- centos7虚拟机端口命令
cat /etc/redhat-release # 查看centos 版本 Centos7端口常见命令 虚拟机新开了5005端口,系统内部是显示开了的,但是外部不能访问端口. 一些需要用到的命令: ...
- Ant风格路径表达式
ANT通配符有三种: ? 匹配任何单字符 * 匹配0或者任意数量的字符 ** 匹配0或者更多的目录 举例: /project/*.a 匹配项目根路径下所有在project路径下的.a文件 /proje ...
- 对象锁和class锁
对象锁:就是这个锁属于这个类的对象实例,可以通过为类中的非静态方法加synchronized关键字 或者使用 synchronized(this) 代码块,为程序加对象锁. Class锁:就是这个锁属 ...