蛇身的绘制
这应该是贪吃蛇游戏中最主要的部分了,在程序中使用数组来保存组成蛇身的每个方块的坐标值。
// 保存组成蛇身的每个方格位置
int[] x = new int[snake_length_max];
int[] y = new int[snake_length_max];
当蛇头位置改变以后,我们从尾部开始遍历数组,将每个方块坐标位置向后移动,然后将新的蛇头位置赋值到数组第一元素,然后重新绘制所有方块。
void draw_snake()
{
//从尾部开始更新蛇身方块坐标
for (int i=snake_length-1; i>0; i--) {
x = x[i-1];
y = y[i-1];
}
// 设置蛇头新的坐标
x[0] = snake_head_x;
y[0] = snake_head_y;
// 设置蛇身填充颜色
fill(#3874F6);
// 开始画蛇
for (int i=0; i<snake_length; i++) {
rect(x, y, grid, grid);
}
}
食物的产生
当食物被吃掉以后,我们需要在指定长宽的区域内随机生成食物的坐标数据,这里使用了random()函数,用来生成坐标值,这个坐标值需要是方块边长的整数倍。
void draw_food(int max_width, int max_high)
{
//食物填充颜色
fill(#F71E1E);
//如果食物被吃掉,则随机生成一个
if (food_eaten)
{
food_x = int(random(0, max_width) / grid) * grid;
food_y = int(random(0, max_high) / grid) * grid;
}
rect(food_x, food_y, grid, grid);
food_eaten = false;
}
方向控制
这里使用前篇介绍的按键识别操作。对方向按键进行判断,然后对应改变蛇的运动方向。
if (snake_direction != 'P'&& keyPressed && key == CODED)
{
switch(keyCode) {
case LEFT:
if (snake_direction != 'R') {
snake_direction = 'L';
}
break;
case RIGHT:
if (snake_direction != 'L') {
snake_direction = 'R';
}
break;
case DOWN:
if (snake_direction != 'U') {
snake_direction = 'D';
}
break;
case UP:
if (snake_direction != 'D') {
snake_direction = 'U';
}
break;
}
在刷新显示的时候,会根据移动方向的不同,对蛇头的坐标进行改变,当重新对蛇身进行绘制的时候,整个蛇就进行了一次移动。
//移动方向选择
switch(snake_direction) {
case 'L':
snake_head_x -= grid;
break;
case 'R':
snake_head_x += grid;
break;
case 'D':
snake_head_y += grid;
break;
case 'U':
snake_head_y -= grid;
break;
}
此外,增加了暂停键“P”或“p”,以及运行键“R”或“r”的判断。
if (key == 'p' || key == 'P')
{
game_pause++;
if (game_pause%2 == 1)
{
snake_direction_temp = snake_direction;
snake_direction = 'P';
} else {
snake_direction = snake_direction_temp;
}
}
....
if (keyPressed && (key == 'r' || key == 'R'))
{
...
}
值得注意的是,这些对于按键的监听,没有放在draw()函数中,而是使用了键盘事件函数keyPressed(),每当按下一个键,其中的代码就会运行一次,使用这种方式监听按键更加方便灵活。
判断游戏结束
当蛇头坐标超过显示区域,即蛇撞墙,或蛇头位置坐标与蛇身其他方块坐标相同,即自己吃了自己,都会导致游戏结束。
boolean check_snake_die()
{
// 撞墙了
if ( snake_head_x < 0 || snake_head_x >= width || snake_head_y < 0 || snake_head_y >= height) {
show_game_over();
return true;
}
// 自己吃自己
if ( snake_length > 2 ) {
for ( int i=1; i<snake_length; i++ ) {
if ( snake_head_x == x && snake_head_y == y ) {
show_game_over();
return true;
}
}
}
return false;
}
移动速度
程序中使用millis()函数来获取自程序开始到当前的时间。每当draw()中代码运行一次,我们都重新获取一次当前时间,然后减掉之前的时间来计算出经过的时间,然后与移动间隔时间进行比较,当大于间隔时间时,说明需要刷新移动蛇身一次,然后重新获取一次时间,为后续比较做准备。
time_passed = millis() - time_start; //计算出经过的时间
time_interval = 1000 / speed; //计算移动间隔时间
if (time_passed > time_interval && snake_direction != 'P' && game_start)//游戏刷新条件
{
...
time_start = millis(); //重新获取时间
}
吃到食物
当蛇头坐标移动到与食物坐标相同时,就代表食物被吃到,这时蛇身长度要加1,重新生成食物。程序中每吃掉5个食物,移动速度就会增加1。
//蛇吃到食物
if (snake_head_x == food_x && snake_head_y == food_y)
{
food_eaten = true; //可重新生成食物
snake_length++;
if ( snake_length%5 == 1) {
speed++;
}
speed = min(20, speed);//控制最大速度
}
实现效果
贪吃蛇的实现还是非常简单的,当然代码还有很多需要优化的地方,比如说我们在随机生成食物坐标的时候,需要排除蛇身中方块的坐标,即食物不能直接出现在蛇身中,你可以试着来优化下。
我们还可以直接将代码导出成exe可执行文件,你也来试一试吧。
编辑:Tony来源:公众号TonyCode