【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
easyx本身非常适合小游戏的开发。因为它可以画图,可以有键盘输入,可以有鼠标输入,也可以贴图,也可以做出各种效果,所以用来开发小游戏也是非常合适的。即使用来做大型游戏的demo,也是可以的。很多同学去学习easyx的api,本质上就是尝试开发游戏,当然这也无可厚非。今天我们可以简单开发一个贪吃蛇游戏。

1、贪吃蛇
贪吃蛇是一个很经典的游戏。本身贪吃蛇的长度很短,但是它每吃一个食物之后,身体就会变长。这样随着蛇的身体越来越长,它的行动也越来越受限,比如它不能碰壁,也不能碰到自己。一旦出现这些问题,那么游戏就会结束。
2、功能分解
要实现贪吃蛇也不难,整个功能可以切分成这几个部分。首先,初始化蛇的身体,比如分解成几个正方形。然后初始化食物的位置。接着就是绘制蛇的身体,绘制食物。这些都ok之后,就可以移动小蛇。如果无法移动,程序会结束;如果可以移动,则游戏继续。
3、初始化蛇的位置
整个小蛇是按照正方形来进行设计的,所以可以设几个正方形就可以了,当然也要给出初始运动方向,
// basic function defined here
void initSnake(Snake &snake)
{
snake.length = 3;
snake.x[0] = WIDTH / 2 / BLOCK_SIZE * BLOCK_SIZE;
snake.y[0] = HEIGHT / 2 / BLOCK_SIZE * BLOCK_SIZE;
snake.x[1] = WIDTH / 2 / BLOCK_SIZE * BLOCK_SIZE + BLOCK_SIZE ;
snake.y[1] = HEIGHT / 2 / BLOCK_SIZE * BLOCK_SIZE + BLOCK_SIZE;
snake.x[2] = WIDTH / 2 / BLOCK_SIZE * BLOCK_SIZE + BLOCK_SIZE * 2;
snake.y[2] = HEIGHT / 2 / BLOCK_SIZE * BLOCK_SIZE + BLOCK_SIZE * 2;
snake.dir = RIGHT;
}
4、设置食物的位置
有两个时间点需要设置食物的位置。一个是初始化的时候,还有一个就是当小蛇吃掉食物的时候,需要更新一个食物位置。过程中,注意食物的位置不能在蛇的身上。
void generateFood(Food &food, const Snake &snake)
{
bool valid = false;
while (!valid)
{
valid = true;
food.x = (rand() % (WIDTH / BLOCK_SIZE)) * BLOCK_SIZE;
food.y = (rand() % (HEIGHT / BLOCK_SIZE)) * BLOCK_SIZE;
// check food in snake body
for (int i = 0; i < snake.length; ++i)
{
if (food.x == snake.x[i] && food.y == snake.y[i])
{
valid = false;
break;
}
}
}
}
5、绘制小蛇
小蛇的绘制其实不复杂,主要就是绘制几个正方形即可,
// draw snake
void drawSnake(const Snake &snake)
{
for (int i = 0; i < snake.length; ++i)
{
setfillcolor(i == 0 ? BLUE : GREEN);
fillrectangle(snake.x[i], snake.y[i], snake.x[i] + BLOCK_SIZE, snake.y[i] + BLOCK_SIZE);
}
}
6、食物的绘制
相对而言,食物的绘制则更加简单,就是绘制一个正方形,注意颜色的区分,
// draw food
void drawFood(const Food &food)
{
setfillcolor(RED);
fillrectangle(food.x, food.y, food.x + BLOCK_SIZE, food.y + BLOCK_SIZE);
}
7、小蛇的移动
小蛇的移动稍微复杂一点。这里面有一个小诀窍。首先把所有的小蛇身体,往后移动一格。再根据当前运动方向,确定第一个小格中的x和y怎么修改。修改的时候判断有没有食物,有实物的话,反而简单,直接长度+1,重新生成食物,退出继续循环。
如果不是食物,需要判断有没有出界,或者碰到自己。前面两个没遇到,就继续行走,遇到的话,就程序退出了。
// move snake
bool moveSnake(Snake &snake, Food &food)
{
// update snake
for (int i = snake.length - 1; i > 0; --i)
{
snake.x[i] = snake.x[i - 1];
snake.y[i] = snake.y[i - 1];
}
// adjust position of header
switch (snake.dir) {
case UP:
snake.y[0] -= BLOCK_SIZE;
break;
case DOWN:
snake.y[0] += BLOCK_SIZE;
break;
case LEFT:
snake.x[0] -= BLOCK_SIZE;
break;
case RIGHT:
snake.x[0] += BLOCK_SIZE;
break;
default:
break;
}
// check food whether being eaten
if (snake.x[0] == food.x && snake.y[0] == food.y)
{
snake.length++;
generateFood(food, snake); // re-generate food
return true; //already eat food
}
// hit wall
if (snake.x[0] < 0 || snake.x[0] >= WIDTH || snake.y[0] < 0 || snake.y[0] >= HEIGHT)
{
return false;
}
// hit snake itself
for (int i = 1; i < snake.length; ++i)
{
if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i])
{
return false;
}
}
return true;
}
8、键盘输入
整个小蛇的控制是通过键盘进行的。控制的按键是a、w、s、d,玩过cs的同学应该不会陌生。每一次按键确认之后,都要设置下小蛇的前进方向,根据小蛇是否正常运行,对小蛇和食物进行重新绘制,这样整个游戏就可以慢慢做好了。
当然实际开发的时候,我们也会显示当前的得分,以及提示用户,什么时候程序已经结束了。最后这里给出完整的代码,有兴趣的同学可以好好看一下代码,
#define _CRT_SECURE_NO_WARNINGS
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctime>
static const int WIDTH = 640;
static const int HEIGHT = 480;
static const int BLOCK_SIZE = 20;
static const int SNAKE_MAX_LENGTH = 100;
enum Direction { UP, DOWN, LEFT, RIGHT };
// struct of Snake
struct Snake
{
int x[SNAKE_MAX_LENGTH];
int y[SNAKE_MAX_LENGTH];
int length;
Direction dir;
};
// struct of Food
struct Food
{
int x;
int y;
};
// basic function defined here
void initSnake(Snake &snake)
{
snake.length = 3;
snake.x[0] = WIDTH / 2 / BLOCK_SIZE * BLOCK_SIZE;
snake.y[0] = HEIGHT / 2 / BLOCK_SIZE * BLOCK_SIZE;
snake.x[1] = WIDTH / 2 / BLOCK_SIZE * BLOCK_SIZE + BLOCK_SIZE ;
snake.y[1] = HEIGHT / 2 / BLOCK_SIZE * BLOCK_SIZE + BLOCK_SIZE;
snake.x[2] = WIDTH / 2 / BLOCK_SIZE * BLOCK_SIZE + BLOCK_SIZE * 2;
snake.y[2] = HEIGHT / 2 / BLOCK_SIZE * BLOCK_SIZE + BLOCK_SIZE * 2;
snake.dir = RIGHT;
}
void generateFood(Food &food, const Snake &snake)
{
bool valid = false;
while (!valid)
{
valid = true;
food.x = (rand() % (WIDTH / BLOCK_SIZE)) * BLOCK_SIZE;
food.y = (rand() % (HEIGHT / BLOCK_SIZE)) * BLOCK_SIZE;
// check food in snake body
for (int i = 0; i < snake.length; ++i)
{
if (food.x == snake.x[i] && food.y == snake.y[i])
{
valid = false;
break;
}
}
}
}
// draw snake
void drawSnake(const Snake &snake)
{
for (int i = 0; i < snake.length; ++i)
{
setfillcolor(i == 0 ? BLUE : GREEN);
fillrectangle(snake.x[i], snake.y[i], snake.x[i] + BLOCK_SIZE, snake.y[i] + BLOCK_SIZE);
}
}
// draw food
void drawFood(const Food &food)
{
setfillcolor(RED);
fillrectangle(food.x, food.y, food.x + BLOCK_SIZE, food.y + BLOCK_SIZE);
}
// move snake
bool moveSnake(Snake &snake, Food &food)
{
// update snake
for (int i = snake.length - 1; i > 0; --i)
{
snake.x[i] = snake.x[i - 1];
snake.y[i] = snake.y[i - 1];
}
// adjust position of header
switch (snake.dir) {
case UP:
snake.y[0] -= BLOCK_SIZE;
break;
case DOWN:
snake.y[0] += BLOCK_SIZE;
break;
case LEFT:
snake.x[0] -= BLOCK_SIZE;
break;
case RIGHT:
snake.x[0] += BLOCK_SIZE;
break;
default:
break;
}
// check food whether being eaten
if (snake.x[0] == food.x && snake.y[0] == food.y)
{
snake.length++;
generateFood(food, snake); // re-generate food
return true; //already eat food
}
// hit wall
if (snake.x[0] < 0 || snake.x[0] >= WIDTH || snake.y[0] < 0 || snake.y[0] >= HEIGHT)
{
return false;
}
// hit snake itself
for (int i = 1; i < snake.length; ++i)
{
if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i])
{
return false;
}
}
return true;
}
int main(int argc, char* argv)
{
Snake snake;
Food food;
char buf[32] = {0};
bool gameOver = false;
srand((unsigned int)time(0)); // random seed
initgraph(WIDTH, HEIGHT);
setbkmode(TRANSPARENT);
BeginBatchDraw();
initSnake(snake);
generateFood(food, snake);
while (!gameOver)
{
if (_kbhit()) // non-block function
{
// check keyboard
char ch = _getch();
switch (ch)
{
case 'w': // up
if (snake.dir != DOWN)
snake.dir = UP;
break;
case 's': // down
if (snake.dir != UP)
snake.dir = DOWN;
break;
case 'a': // left
if (snake.dir != RIGHT)
snake.dir = LEFT;
break;
case 'd': // right
if (snake.dir != LEFT)
snake.dir = RIGHT;
break;
default:
break;
}
}
// logical check
gameOver = !moveSnake(snake, food);
if (gameOver)
{
break;
}
// re-draw picture
cleardevice();
drawSnake(snake);
drawFood(food);
memset(buf, 0, 32); // draw score here
sprintf(buf, "Score: %d", snake.length -3);
outtextxy(500, 50, buf);
EndBatchDraw();
Sleep(250); // left some time to gamer
BeginBatchDraw();
}
// update to clear screen
cleardevice();
outtextxy(260, 220,"Game Over!!!");
EndBatchDraw();
_getch();
closegraph(); //close window
return 0;
}
3949

被折叠的 条评论
为什么被折叠?



