This article has been localized into Chinese by the community.
In this article series, we're building a complete Snake game from scratch. It makes sense to start with the Introduction and then work your way through the articles one by one, to get the full understanding.
If you want to get the complete source code for the game at once, to get started modifying and learning from it right now, consider downloading all our samples!
用DispatchTimer持续移动
在上面几篇文章中,我们为我们的贪吃蛇创建了一个好看的游戏区域,然后我们添加代码去执行贪吃蛇的创建和移动。然而,正如前面提到的,移动的代码不应该只被调用一次,它应该被一直调用,当游戏在运行的时候去保持贪吃蛇的移动。换句话来说,我们将需要一个Timer
一般而言,在程序中,一个Timer通常是一种机制,它允许任务跟根据间隔去去一次又一次地重复,换句话来说,每次Timer计时器“滴答”时,都会去执行一部分代码,然后这个Timer的滴答声是基于定义的时间间隔的.它恰好是我们需要保持贪吃蛇移动的东西,所以我们将会去添加一个DispatcherTimer到我们的Window
public partial class SnakeWPFSample : Window
{
private System.Windows.Threading.DispatcherTimer gameTickTimer = new System.Windows.Threading.DispatcherTimer();
....
有了这些,我们现在需要去订阅它的唯一事件:Tick 事件。我们将会在Window的构造器中这么做。
public SnakeWPFSample()
{
InitializeComponent();
gameTickTimer.Tick += GameTickTimer_Tick;
}
这里是事件的实现
private void GameTickTimer_Tick(object sender, EventArgs e)
{
MoveSnake();
}
因此,每次Timer定时器滴答,Tick事件就会被调用,该事件反过来去调用我们之前实现的MoveSnake() 方法.终于看我们所有人辛勤劳动的结果,我们有了一条在视觉上移动的蛇,我们只需要创建了蛇的初始部分和开始计时器Timer。我们将会创建一个叫StartNewGame()的方法,当玩家死亡的时候,我们将会用它去启动第一款游戏和其他额外数量的新款游戏,我们将会从一个非常基础的版本开始,然后,随着我们继续,我将会去扩展更多的功能-现在,让我们让这条蛇移动起来吧!
第一步是去添加另外一组常量,以便给我们去开始新的游戏:
public partial class SnakeWPFSample : Window
{
const int SnakeSquareSize = 20;
const int SnakeStartLength = 3;
const int SnakeStartSpeed = 400;
const int SnakeSpeedThreshold = 100;
......
此刻只使用前三个常量,去控制大小,长度和贪吃蛇的开始速度.我们将会之后会使用到SnakeSpeedThreshold ,但是现在,让我们按照承诺去添加一个StartNewGame() 方法的简单实现:
private void StartNewGame()
{
snakeLength = SnakeStartLength;
snakeDirection = SnakeDirection.Right;
snakeParts.Add(new SnakePart() { Position = new Point(SnakeSquareSize * 5, SnakeSquareSize * 5) });
gameTickTimer.Interval = TimeSpan.FromMilliseconds(SnakeStartSpeed);
// Draw the snake
DrawSnake();
// Go!
gameTickTimer.IsEnabled = true;
}
我们首先根据初始值设置snakeLength 和 snakeDirection.然后我们添加一个部分到snakeParts中(之后会更多),为了正确移动给它一个好的开始位置-我们将再一次使用SnakeSquareSize常量去帮助计算正确位置.有了这个,我们能够通过调用DrawSnake() 方法绘制贪吃蛇和激活timer,这将基本启动蛇的移动.
我们现在终于可以坐下去享受实际看起来像一个游戏的第一个版本-事实上,我们现在必须去做的就是去调用StartNewGame()方法.这个应该在用户准备好时去完成,但是现在,去检查一切是否正常工作,我们将在其他内容初始化收立即执行该操作-我们将再一次依赖于ContentRendered事件在Window类上,我们在其中一篇文章中添加了该事件.只需添加一个我们的StartNewGame()方法的调用,然后我们终于可以去编译和执行起来了:
private void Window_ContentRendered(object sender, EventArgs e)
{
DrawGameArea();
StartNewGame();
}
如果你如描述说的做了每件事,你现在应该能够启动游戏,看到贪吃蛇正在被创建和立即开始移动:
注意贪吃蛇如何作为一个正方形凭空出现的,然后成长成一个三个正方形长度.会这么发生时因为我们只添加了一部分到snakeParts 集合,但是Timer会每时每刻调用MoveSnake() 方法,一个新的部分会添加进去(去让它成长),如何当前长度超过蛇所需长度,则移去蛇的尾部,开始长度为3(通过设定 SnakeStartLength 常量).
小结
我们现在有了一条移动的蛇,这非常地棒!但正如你从上面的动图看到的那样,任然还有一些事情没被做-这里没有给贪吃蛇吃的食物,然后当贪吃蛇撞到墙上,什么也没发生.我们将在下篇文章去研究这些方面.