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!
为贪吃蛇添加食物
在WPF贪吃蛇文章系列的这个阶段,我们现在已经有了一个棋盘背景作为游戏区域,同时一个漂亮的看起来绿色的蛇在周围移动。然而,正如介绍的一样,游戏的意图是为了蛇去吃一些食物-在我们的版本将会是红苹果!
因此现在是时候去开始添加一些食物在游戏区域中。我们将会通过随机添加一个红色圆形在游戏区域Canvas的边界内来实现,但是我们需要去确定我们没有放置它在被逐渐成长的贪吃蛇占领的其中一个正方形在。换句话说,放置一个苹果在游戏的一个最重要的方面是决定下一个位置的代码。这是我们即将用来执行此操作的代码:
private Point GetNextFoodPosition()
{
int maxX = (int)(GameArea.ActualWidth / SnakeSquareSize);
int maxY = (int)(GameArea.ActualHeight / SnakeSquareSize);
int foodX = rnd.Next(0, maxX) * SnakeSquareSize;
int foodY = rnd.Next(0, maxY) * SnakeSquareSize;
foreach(SnakePart snakePart in snakeParts)
{
if((snakePart.Position.X == foodX) && (snakePart.Position.Y == foodY))
return GetNextFoodPosition();
}
return new Point(foodX, foodY);
}
一定要在Window类声明顶部上添加这行代码,以及其他字段/常量:
public partial class SnakeWPFSample : Window
{
private Random rnd = new Random();
......
因此,去快速地扩展代码:我们再一次使用SnakeSquareSize常量去帮助我们计算我们食物的下一个位置,在Random类的内部,它提供给我们random X和Y 方位。一旦我们有了一些,我们浏览所有当前蛇的部件以及检查如果它们的位置是否和我们刚刚创建的X和Y坐标匹配-如果他们这么做,这意味着我们以及击中当前被蛇占领的一个区域,以及我们通过再一次简单地调用方法寻求一个新的方位(使之成为一个递归方法)
这也意味着这个方法能够无限次被自己调用,理论上,导致一个无限循环。我们能够为其做一些检查,但是这应该没啥必要,因为这需要蛇长到没有空的地方了-我打赌游戏将会结束在这发生之前。
有了这些,我们准备去添加代码,它将添加食物在一个新的被计算好的位置-我们将会调用一个DrawSnakeFood()方法做到。感谢所有工作已经被GetNextFoodPosition()处理,这非常简单,但是首先,一定要去声明字段用来保存食物的引用,以及绘制苹果的SolidColorBrush ,以及其他字段/常量声明:
public partial class SnakeWPFSample : Window
{
private UIElement snakeFood = null;
private SolidColorBrush foodBrush = Brushes.Red;
......
这里是方法的实现:
private void DrawSnakeFood()
{
Point foodPosition = GetNextFoodPosition();
snakeFood = new Ellipse()
{
Width = SnakeSquareSize,
Height = SnakeSquareSize,
Fill = foodBrush
};
GameArea.Children.Add(snakeFood);
Canvas.SetTop(snakeFood, foodPosition.Y);
Canvas.SetLeft(snakeFood, foodPosition.X);
}
正如承诺那样,一旦我们有了方位这非常简单,我们简单地创建一个新的椭圆形实例,以及再一次使用SnakeSquareSize常量去确定它已经有了背景和每个蛇部件一样的大小。我们保存一个椭圆形实例的引用在snakeFood 字段中,因为我们之后需要它。
有了这些,我们真的只需要去调用DrawSnakeFood() 方法看看执行的结果。这将会在两种情况下执行:在游戏的开始和当贪吃蛇‘吃’到食物的时候(稍后会详细讲到)。现在,让我们在我们的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 and the snake food
DrawSnake();
DrawSnakeFood();
// Go!
gameTickTimer.IsEnabled = true;
}
是的!如果你现在启动游戏,你应该会看到贪吃蛇终于有食物去追逐了:
相当多的工作去安置一个红点在屏幕上,对吧?
小结
在这篇文章中,我们终于在餐桌上添加一些食物去给贪吃蛇吃了,但是这里任然有工作要去做:我们需要能够去控制贪吃蛇,然后我们需要去知道当它什么时候撞击到什么东西(一个墙,它自己的尾部或者食物).更多内容将放在下篇文章讲解。