八、生成新的单节蛇身
我们这里先说说游戏小原理好了,游戏运行后,通过计时器事件不断生成新的单节蛇身类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. 初识Lock与AbstractQueuedSynchronizer(AQS)

    本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...

  2. 史上最全Winform中使用ZedGraph教程与资源汇总整理(附资源下载)

    场景 C#窗体应用中使用ZedGraph曲线插件绘制图表: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/99716066 Win ...

  3. JS基础语法---循环语句之:for 循环 + 9个练习

    for循环 语法: for(表达式1;表达式2;表达式3){ 循环体; } 执行过程: 先执行一次表达式1,然后判断表达式2;如果不成立则直接跳出循环 如果表达式2成立,执行循环体的代码,结束后,跳到 ...

  4. arcgis api for javascript 学习(二) 发布并调用地图切片

    文章将从发布切片地图到调用切片地图整个过程都展示出来. (一).切片地图的发布 1.还是前面的arcgis展示的地图 2.与发布动态地图前面的步骤是一样的 打开分享后,如图 3.一切就绪后,到达缓存的 ...

  5. 强大的Charles的使用,强大的flutter1.9

    <a href="http://www.cocoachina.com/articles/37551?filter=ios"> 强大的Charles 强大的flutter

  6. Android 蓝牙开发(3)——蓝牙的详细介绍

    前面的两篇文章,主要是在 Android 官网关于蓝牙介绍的基础上加上自己的理解完成的.主要针对的是 Android 开发中的一些 API 的使用. 第一篇文章 Android 蓝牙开发(1) 主要是 ...

  7. 第十五周博客作业 <西北师范大学| 周安伟>

    第十五周作业 助教博客链接https://home.cnblogs.com/u/zaw-315/ 作业要求链接https://www.cnblogs.com/nwnu-daizh/p/10946673 ...

  8. python的wraps函数

    当使用@修饰符修饰函数时,会存在这样一个问题:被修饰的函数会消失(这是因为修饰函数没有设置返回值,如果设置了返回值,则就把返回值赋给被修饰函数,比如,test1函数的返回值设置为 return 6, ...

  9. Windows环境下搭建Redis集群(Redis-x64-3.2.100)

    一 .前期准备Redis.Ruby语言运行环境.Redis的Ruby驱动redis-xxxx.gem.创建Redis集群的工具redis-trib.rb 二.安装配置redisredis下载地址 ht ...

  10. [C3W2] Structuring Machine Learning Projects - ML Strategy 2

    第二周:机器学习策略(2)(ML Strategy(2)) 误差分析(Carrying out error analysis) 你好,欢迎回来,如果你希望让学习算法能够胜任人类能做的任务,但你的学习算 ...