前言:
由于网上有很多贪吃蛇的框架,我们基本不用自己写,只需找到符合自己预期的框架,充分理解后复制粘贴过来为己所用就好了,我的框架来源:用C++实现经典的贪吃蛇游戏_c++贪吃蛇-CSDN博客
但是,我觉得这框架太简单了,虽然能玩,但也只是能玩的地步,而且这个游戏它没有主题,不行,我要赋予它一个主题,刚好我喜欢牢大,牢大又名黑曼巴,干脆就来个贪吃曼巴吧!(本人没有对曼巴的任何不尊敬,本文的曼巴仅指蛇,与篮球运动员科比并无干系)
首先我们要把框架复制粘贴进我们的项目里,并进行充分的理解,然后再加入我们自己的部分,大部分想理解都不难,我就简单说说比较难看懂的部分。
曼巴的移动
首先理清思路,在这个结合easyx的游戏中,蛇的移动是我们每时每刻都依据蛇的坐标把蛇画出来,下一刻再清理掉画面,更新坐标,再把蛇画出来,这样的循环,看起来就是蛇在移动,跟动画是一个原理。
SnakeNode tmp[MAXLEN]; //用另外一个数组来存储蛇原来的位置
//移动
void Snake::Move() {
//将原来的蛇结点拷贝一份
for (int i = 0; i < this->length; i++) {
tmp[i].x = this->node[i].x;
tmp[i].y = this->node[i].y;
}
int status = 0;//用来判断是否点击了转向按键
if (this->dirt == RIGHT) {
//判断是否转向
if (GetAsyncKeyState('W') && status == 0) {
//this->node[0].y -= SIZE;
this->dirt = UP;
status = 1;
}
else if (GetAsyncKeyState('S') && status == 0) {
this->dirt = DOWN;
status = 1;
}
else {
this->node[0].x += SIZE;
}
}
if (this->dirt == DOWN) {
//判断是否转向
if (GetAsyncKeyState('A') && status == 0) {
//this->node[0].x -= SIZE;
this->dirt = LEFT;
status = 1;
}
else if (GetAsyncKeyState('D') && status == 0) {
this->node[0].x += SIZE;
this->dirt = RIGHT;
status = 1;
}
else {
this->node[0].y += SIZE;
}
}
if (this->dirt == LEFT) {
//判断是否转向
if (GetAsyncKeyState('W') && status == 0) {
//this->node[0].y -= SIZE;
this->dirt = UP;
status = 1;
}
else if (GetAsyncKeyState('S') && status == 0) {
this->node[0].y += SIZE;
this->dirt = DOWN;
status = 1;
}
else {
this->node[0].x -= SIZE;
}
}
if (this->dirt == UP) {
//判断是否转向
if (GetAsyncKeyState('A') && status == 0) {
this->node[0].x -= SIZE;
this->dirt = LEFT;
status = 1;
}
else if (GetAsyncKeyState('D') && status == 0) {
this->node[0].x += SIZE;
this->dirt = RIGHT;
status = 1;
}
else {
this->node[0].y -= SIZE;
}
}
//移动
for (int i = 1; i < this->length; i++) {
this->node[i].x = tmp[i - 1].x;
this->node[i].y = tmp[i - 1].y;
}
Sleep(speed);
}
问题1 (转向)
在以上框架中,我产生了第一个疑惑,为什么同是检查是否按下转向按键,有的蛇头坐标更新,有的不更新呢?后来仔细一看发现,原作者用来判断按下按键的语句都是if语句,这意味着如果第一个if判断结束后仍会进入第二个if,如果按了转向,蛇头坐标改变,后面又会进入其他if判断语句,容易出现混乱,所以,在这里我做了一些修改如下。(把if改成else if)
void Snake::Move() {
//将原来的蛇结点拷贝一份
for (int i = 0; i < this->length; i++) {
tmp[i].x = this->node[i].x;
tmp[i].y = this->node[i].y;
}
int status = 0;//用来判断是否点击了转向按键
int flag = 0;
if (this->dirt == RIGHT) {
//判断是否转向
if (GetAsyncKeyState('W') && status == 0) {
this->node[0].y -= 2 * SIZE;
this->dirt = UP;
status = 1;
}
else if (GetAsyncKeyState('S') && status == 0) {
this->node[0].y += 2 * SIZE;
this->dirt = DOWN;
status = 1;
}
else {
if (GetAsyncKeyState('A') && status == 0)
flag -= 10;
if (GetAsyncKeyState('D') && status == 0)
flag += 10;
this->node[0].x += 2 * SIZE;
}
}
else if (this->dirt == DOWN) {
//判断是否转向
if (GetAsyncKeyState('A') && status == 0) {
this->node[0].x -= 2 * SIZE;
this->dirt = LEFT;
status = 1;
}
else if (GetAsyncKeyState('D') && status == 0) {
this->node[0].x += 2 * SIZE;
this->dirt = RIGHT;
status = 1;
}
else {
if (GetAsyncKeyState('W') && status == 0)
flag -= 10;
if (GetAsyncKeyState('S') && status == 0)
flag += 10;
this->node[0].y += 2 * SIZE;
}
}
else if (this->dirt == LEFT) {
//判断是否转向
if (GetAsyncKeyState('W') && status == 0) {
this->node[0].y -= 2 * SIZE;
this->dirt = UP;
status = 1;
}
else if (GetAsyncKeyState('S') && status == 0) {
this->node[0].y += 2 * SIZE;
this->dirt = DOWN;
status = 1;
}
else {
if (GetAsyncKeyState('D') && status == 0)
flag -= 10;
if (GetAsyncKeyState('A') && status == 0)
flag += 10;
this->node[0].x -= 2 * SIZE;
}
}
else if (this->dirt == UP) {
//判断是否转向
if (GetAsyncKeyState('A') && status == 0) {
this->node[0].x -= 2 * SIZE;
this->dirt = LEFT;
status = 1;
}
else if (GetAsyncKeyState('D') && status == 0) {
this->node[0].x += 2 * SIZE;
this->dirt = RIGHT;
status = 1;
}
else {
if (GetAsyncKeyState('S') && status == 0)
flag -= 10;
if (GetAsyncKeyState('W') && status == 0)
flag += 10;
this->node[0].y -= 2 * SIZE;
}
}
//移动
for (int i = 1; i < this->length; i++) {
this->node[i].x = tmp[i - 1].x;
this->node[i].y = tmp[i - 1].y;
}
Sleep(speed - flag * 15);
}
曼巴的诞生
蛇的移动函数搞懂了,接下来就看蛇身的绘制,在原代码中,蛇身是正方形的,而且还会变色,但是黑曼巴应该是黑色的,而且我也不喜欢正方形的蛇身,所以我用把蛇身画成圆的,并且设置为黑色。
//绘制蛇
void Snake::Draw() {
cleardevice();//清空原先的绘图
putimage(0, 0, &bk);
settextcolor(BLACK);
char scorestr[2000], twostr[1000], threestr[1000], str[8];
sprintf_s(twostr, "二分球:%2d", two);
sprintf_s(threestr, "三分凉:%2d", three);
sprintf_s(scorestr, "总得分:%2d", score);
outtextxy(900, 90, scorestr);
outtextxy(900, 70, threestr);
outtextxy(900, 50, twostr);
srand((unsigned)time(NULL));//设置随机数种子
for (int i = 0; i < this->length; i++) {
setfillcolor(BLACK);
fillcircle(this->node[i].x, this->node[i].y, SIZE);
}
}
曼巴碰撞判断
问题2(蛇身坐标)
但是圆形蛇身就意味这蛇的碰撞检测得重做了,因为对于正方形来说,左上角的点是这个正方形的坐标,由于食物也是正方形的,只需判断两者左上角的坐标是否重合即可,但是对于圆形蛇身和圆形食物来说,圆心才是坐标,如果简单判断坐标重合,就会出现吃到食物会有视觉延迟的情况。因此我们需要创造一个判断碰撞的函数coli(float x0,float y0)
//创建食物的类
class Food
{
friend class Snake; //食物的友元为蛇
friend class Block;
public:
Food(Snake& snake, Block& block); //食物初始化
void Draw(); //绘制食物
bool coli(float x0, float y0)//碰撞判断
{
if (x0 == x && y0 == y) return true;
if (x0 == x + 2 * SIZE && y0 == y) return true;
if (x0 == x + 4 * SIZE && y0 == y) return true;
if (x0 == x && y0 == y + 2 * SIZE) return true;
if (x0 == x && y0 == y + 4 * SIZE) return true;
if (x0 == x + 2 * SIZE && y0 == y + 2 * SIZE) return true;
if (x0 == x + 4 * SIZE && y0 == y + 2 * SIZE) return true;
if (x0 == x + 2 * SIZE && y0 == y + 4 * SIZE) return true;
if (x0 == x + 4 * SIZE && y0 == y + 4 * SIZE) return true;
return false;
}
private:
int x, y; //坐标
int score; //分数
int type;
IMAGE ima1, ima2, ima3, ima4;
};
函数传入蛇头的坐标x0,y0,只要蛇头碰到食物周围的八个点之一,就返回碰撞。
(ps:我试过直接用食物所在区间来判断,但是结果不理想,搞不清楚为啥。)
接下来我们在食物判定和死亡判定中用这个coli()函数来判断是否与食物(食物判断),自身,墙体(死亡判断),障碍物发生碰撞即可,但要注意,由于死亡判断是snake类中的函数,食物判断是food类中函数,所以我们要分别在两个类中都定义coli()函数,但是,考虑到后面我还会改变障碍物的形状,图案和数量等复杂操作,我就另起了一个障碍物类block,并在其中定义coli()函数。
bool coli(int x0, int y0)//碰撞判定
{
for (int i = 0; i < count; i++)
{
int x = vc[i][0];
int y = vc[i][1];
if (x0 == x && y0 == y) return true;
if (x0 == x + 2 * SIZE && y0 == y) return true;
if (x0 == x + 4 * SIZE && y0 == y) return true;
if (x0 == x && y0 == y + 2 * SIZE) return true;
if (x0 == x && y0 == y + 4 * SIZE) return true;
if (x0 == x + 2 * SIZE && y0 == y + 2 * SIZE) return true;
if (x0 == x + 4 * SIZE && y0 == y + 2 * SIZE) return true;
if (x0 == x + 2 * SIZE && y0 == y + 4 * SIZE) return true;
if (x0 == x + 4 * SIZE && y0 == y + 4 * SIZE) return true;
}
return false;
}
//失败判定
bool Snake::Defeat(Block& block) {
//1.碰到边界
if (this->node[0].x < 0 || this->node[0].x >= WIDTH || this->node[0].y < 0 || this->node[0].y >= HEIGHT) {
return true;
}
//2.碰到自己的身体
for (int i = 1; i < this->length; i++) {
if (this->node[0].x == this->node[i].x && this->node[0].y == this->node[i].y) {
return true;
}
}
//3.被肘击
if (block.coli(node[0].x, node[0].y))
{
Sleep(1500);
if (GetAsyncKeyState('1')) {
wuditime = 5;
return false;
}
return true;
}
return false;
}
曼巴的食物
接下来是食物的生成,我觉得原来的食物太单调了,我要把他们改为冰红茶和篮球!
网上搜到的照片不符合预期,那就自己画!然后通过easyx的函数导入到程序。
食物(想要多少种都可以自行添加,这里只演示两种):
肘子(障碍物):
背景:(我知道我画的很好)
哦对了,各种图片都按如下操作放入文件夹就好了
问题3(图案遮挡背景)
这里有个小插曲,在导入自定义图案时,由于图片是正方形的,会导致图案四周的背景被遮挡,总体上就不美观,我在csdn上找到了解决方法(可能要用到照片剪辑软件):
关于Easyx如何显示透明无背景贴图_easyx图片背景透明-CSDN博客
问题4(吃食物无反应)
//食物的初始化
Food::Food(Snake& snake, Block& block)
{
loadimage(&ima1, "basketball.png", 4 * SIZE, 4 * SIZE);
loadimage(&ima2, "baskrv.png", 4 * SIZE, 4 * SIZE);
loadimage(&ima3, "bhc.png", 4 * SIZE, 4 * SIZE);
loadimage(&ima4, "rv.png", 4 * SIZE, 4 * SIZE);
table:
srand((unsigned)time(0));
do
{
x = (rand() % (WIDTH / SIZE));
y = (rand() % (HEIGHT / SIZE));
} while (x % 2 == 0 || y % 2 == 0 || x >= WIDTH / 10 - 4 || y >= HEIGHT / 10 - 4 || block.onblock(x, y));//一直生成随机坐标直到该坐标不与障碍物重叠并且为能被蛇头碰到
this->x *= SIZE;
this->y *= SIZE;
this->type = rand() % 100;
if (type >= 0 && type <= 80) {
type = 1;
}
else {
type = 2;
}
for (int i = 0; i < snake.length; i++) {
if (snake.node[i].x == this->x && snake.node[i].y == this->y) {
goto table;
}
}
}
为什么看起来吃到了食物,但是没有任何反应
(注意:代码中之所以要求食物能被蛇头碰到,是因为蛇头为圆形,半径为10,而每次移动都会往要移动的方向画一个同样的圆,因此,每次蛇头移动的距离是20,所以其坐标变化为1,3,5,7,9,……,如果食物的坐标是2,4,6,……,就没法判定吃到食物,尽管从视觉上看起来是吃到了,因此我们限制食物的坐标必须是1,3,5,7,……此类规律。而且,由于食物图案是以左上角为坐标点,不能生成在右下边界上,否则食物图案就刷新在视野之外了)
//绘制食物
void Food::Draw() {
if (type == 1)//篮球
{
putimage(this->x, this->y, &ima2, SRCAND);
putimage(x, y, &ima1, SRCPAINT);
}
else if (type == 2)//冰红茶
{
putimage(x, y, &ima4, SRCAND);
putimage(x, y, &ima3, SRCPAINT);
}
}
如上,我们只需在对应的地方putimage就行。
//吃食物
int Snake::Eat(Food& food) {
if (food.coli(node[0].x, node[0].y)) {
if (food.type == 1) {
two++;
score += 2;
}
else if (food.type == 2) {
three++;
score += 3;
}
if (this->node[length - 1].x - this->node[length - 2].x == 0 && this->node[length - 1].y - this->node[length - 2].y == -20) {
this->length++;
this->node[length - 1].x = this->node[length - 2].x;
this->node[length - 1].y = this->node[length - 2].y - 2 * SIZE;
}
if (this->node[length - 1].x - this->node[length - 2].x == 0 && this->node[length - 1].y - this->node[length - 2].y == 20) {
this->length++;
this->node[length - 1].x = this->node[length - 2].x;
this->node[length - 1].y = this->node[length - 2].y + 2 * SIZE;
}
if (this->node[length - 1].x - this->node[length - 2].x == 20 && this->node[length - 1].y - this->node[length - 2].y == 0) {
this->length++;
this->node[length - 1].x = this->node[length - 2].x + 2 * SIZE;
this->node[length - 1].y = this->node[length - 2].y;
}
if (this->node[length - 1].x - this->node[length - 2].x == -20 && this->node[length - 1].y - this->node[length - 2].y == 0) {
this->length++;
this->node[length - 1].x = this->node[length - 2].x - 2 * SIZE;
this->node[length - 1].y = this->node[length - 2].y;
}
return true;
}
return false;
}
问题5(蛇尾长度增加)
对于这个部分,有的人可能开始会看不懂,首先我们判断蛇头碰撞到食物了,判断此时食物的种类,并更新相关的分数,对于下面四个又臭又长的if,看第一个,实际上就是通过蛇倒数两个节点来判断此时蛇尾部的总体走向,如果倒数第一和倒数第二个节点的x坐标相同,则可以知道蛇尾是在竖直方向,此时我们只需在判断y的坐标就知道它是向下运动还是向上运动,那我们也就知道新的尾节点坐标应该设在哪里了。(有时候看不懂代码为啥这么写,可以根据代码一行行模拟推测,整个过程完成后看起来就明朗多了。)
曼巴的手肘(障碍物)
直接生成block类
class Block
{
friend class Snake;
friend class Food;
private:
vector<vector<int>> vc;//用vector数组存放批量障碍物的坐标
int x, y;
int count;
int difficulty;//难度越高,障碍物数量越多
IMAGE b, bv;//障碍物的图案
public:
Block(); //初始化8个障碍物
void Draw(int num);//负责障碍物相关的绘画
bool coli(int x0, int y0);//碰撞判定
bool onblock(int x, int y);//判断一个东西是否与障碍物重叠
void modify(int num);//改变障碍物数量
void diff();//改变显示难度
};
问题6(障碍物生成在食物上)
在游戏中,我们可以通过按h键增加障碍物的数量,但是由于要避免生成在食物上面,我原本想先生成食物再生成障碍物,但是这样一来就下一个食物就有可能生成在障碍物上,因为我是先初始化block类再初始化food类,并且将block类中障碍物的坐标传给食物类,让食物类来判断坐标是否重合,这样,每次重新生成食物时才能避免生成在障碍物上,因此我没法先生成食物再生成障碍物,能否让后面添加的障碍物不在食物上呢?可以,但是食物又必须在障碍物后面刷新,有了!每次我们按h提升难度,难度不是立马提升,而是等我们消灭掉现有的食物后,添加更多的障碍物再刷新食物,这样就能避免中途添加的障碍物生成在现有的食物上面了,这个办法也许不是最好的,肯定有更好的办法,但确实解决了眼前的问题,况且还算合理。
问题7(障碍物生成在脸上)
问题真是一个接一个地跳出来,不过也好,我们正闲着没事干,击破几个问题涨涨士气。由上面视频可以看到,生成的障碍物离我们只有一步之遥,这样死了也太冤枉了吧!所以我索性开挂,吃到食物后曼巴直接无敌!设定无敌帧,其实就是曼巴在吃到食物时设定一个时间,在蛇的移动过程中递减,当时间殆尽时,恢复肉身。 这样吃到食物后,进入了金身,就算当面被肘击100次都没事!
if (snake.Eat(food)) {
eat = 1;
music.play_eatingsound();
wuditime = 5;//无敌帧
goto table2;
}
snake.Move();
wuditime--;
现在游戏已经大体完成了,看好了,接下来是画龙点睛,赋予灵魂之时。
曼巴之魂
music~
说到曼巴,遥远的天边仿佛飘来伤感的声音,我难以分辨究竟是天使的哭泣,还是恶魔的得意,于是我侧耳倾听,随着渺远的声音离我愈来愈近,我不禁心头一颤,接着我感到双腿无力,顿时坐到了地上。此时我才意识到,自始自终都是身体的求生本能,体内超量分布的肾上腺素在支撑着我早已被恶魔击穿的身体。我清楚地感受到它瞬间来到我的右后侧,贴在我的耳边,用低沉沙哑又未曾听闻的嗓音对我低语:see you again……此时我浑身颤抖地连连点头,我以为它走了,结果它右出现在我左后侧!“两倍速……”我第一次体会到如山一般的压力,压得我差点喘不过气来,迫于威压,我只得服从。。。
关于如何添加音频,我参考了这篇文章:
mciSendString函数简介(播放音乐以及录音相关操作)-CSDN博客
简单来说就是mcisendstring(“打开音乐”),然后再mcisendstring(“播放音乐”)。
后来朋友说打开的时候吓了一跳,音量太大了,我又添加了相应的音量调节功能。
void Music::bk_volumn()
{
if (GetAsyncKeyState(38)) vol += 100;
else if (GetAsyncKeyState(40)) vol -= 100;
char com1[41];
sprintf(com1, "setaudio BGM1 volume to %d", vol);
mciSendString(com1, NULL, 0, NULL);
}
void Music::play_bkmusic()
{
mciSendString("open syagp.mp3 alias BGM1", 0, 0, 0);//see you again加速版
mciSendString("play BGM1 repeat", 0, 0, 0);
}
问题8(音量调节无响应)
ps:我们需要先播放音乐再调节音量,调换顺序就无效了。’38‘和’40‘分别对应箭头上下键的ascii码,我们只需在Music类中初始化音量vol,然后循环执行音量调节函数,看是否检测到上下箭头键的输入,再调节vol就行了。
相同的道理也适用于吃到食物的音效和被肘击及死亡的音效。
void Music::play_eatingsound()
{
mciSendString("close EAT", 0, 0, 0);
mciSendString("open eatingsound.mp3 alias EAT", 0, 0, 0);
mciSendString("play EAT", 0, 0, 0);
if (GetAsyncKeyState(38)) vol += 100;
else if (GetAsyncKeyState(40)) vol -= 100;
char com2[41];
sprintf(com2, "setaudio EAT volume to %d", vol);
mciSendString(com2, NULL, 0, NULL);
}
void Music::play_colisound()
{
mciSendString("close BGM1", 0, 0, 0);
mciSendString("close BGM2", 0, 0, 0);
mciSendString("open ah.mp3 alias BGM2", 0, 0, 0);//man!
mciSendString("play BGM2", 0, 0, 0);
if (GetAsyncKeyState(38)) vol += 100;
else if (GetAsyncKeyState(40)) vol -= 100;
char com3[41];
sprintf(com3, "setaudio BGM2 volume to %d", vol);
mciSendString(com3, NULL, 0, NULL);
}
void Music::play_endmusic()
{
mciSendString("close BGM1", 0, 0, 0);
mciSendString("open mambaout.mp3 alias END", 0, 0, 0);//mambaout
mciSendString("play END", 0, 0, 0);
if (GetAsyncKeyState(38)) vol += 100;
else if (GetAsyncKeyState(40)) vol -= 100;
char com4[41];
sprintf(com4, "setaudio END volume to %d", vol);
mciSendString(com4, NULL, 0, NULL);
}
问题9(播放音频无响应)
注意:音乐不能只加载一次,而是每次都必须加载再播放,因为他们感情太好了,而且如果要播放下一个音频,如果不想冲突,或者想隔一段时间播放同一个音频,就必须把上一个音频关掉,要新建一段关系总得先分手嘛·—·
退役
有的人不同意,说曼巴不会死亡只会受伤,所以我们就假定它每次都是受伤暂时退役,那就必须要有相应的动画来致敬,如下:
左下角是个人臭美签名,因为不知道空白处放啥图片合适。
(如果你也想放自己的照片,可以利用一些照片剪辑软件,先在软件里调节各照片的大小和坐标,这样方便直接添加,否则每次都要运行程序看效果再调整照片大小坐标挺麻烦的)
菜单
有了结束画面,感觉没有菜单太简陋了,那就来做一个菜单Menu
菜单类的声明
class Menu
{
private:
IMAGE menu_bk, detail_bk, history_bk, control_bk, respect_bk;
MOUSEMSG m;
public:
int goon = 1;
Menu();
void window();
void showmenu();
void showhistory();
void showdetail();
void showcontrol();
void showrespect();
bool goback();
void showreturn();
};
绘制菜单和选项并判断鼠标信息
void Menu::showmenu()
{
menubegin:
window();
setbkcolor(WHITE);
cleardevice();
putimage(0, 0, &menu_bk);
setfillcolor(WHITE);
fillrectangle(200, 140, 600, 160);
fillrectangle(200, 200, 600, 220);
fillrectangle(200, 260, 600, 280);
fillrectangle(200, 320, 600, 340);
fillrectangle(200, 380, 600, 400);
fillrectangle(200, 440, 600, 460);
fillrectangle(200, 500, 600, 520);
setbkmode(TRANSPARENT);
settextcolor(BLACK);
outtextxy(250, 142, "开始游戏");//goto begin
outtextxy(250, 202, "历史得分榜");//goto history
outtextxy(250, 262, "操作");//goto control
outtextxy(250, 322, "游戏说明");//goto detail
outtextxy(250, 382, "作者好帅");//就喜欢你这种有眼力的玩家
outtextxy(250, 442, "作者好丑");//游戏崩溃
outtextxy(250, 502, "mamba out");//退出游戏
settextcolor(WHITE);
while (1) {
m = GetMouseMsg();
if (m.x >= 200 && m.x <= 600 && m.y >= 140 && m.y <= 160) {//检测鼠标的位置 是否满足条件
setlinecolor(YELLOW);//满足后 设置新的边框为红色
rectangle(190, 135, 610, 165);//画新的边框
if (m.uMsg == WM_LBUTTONDOWN) {
break;
}
}
else if (m.x >= 200 && m.x <= 600 && m.y >= 200 && m.y <= 220) {
setlinecolor(YELLOW);
rectangle(190, 195, 610, 225);
if (m.uMsg == WM_LBUTTONDOWN) {
showhistory();
goto menubegin;
}
}
else if (m.x >= 200 && m.x <= 600 && m.y >= 260 && m.y <= 280) {
setlinecolor(YELLOW);
rectangle(190, 255, 610, 285);
if (m.uMsg == WM_LBUTTONDOWN) {
showcontrol();
goto menubegin;
}
}
else if (m.x >= 200 && m.x <= 600 && m.y >= 320 && m.y <= 340) {
setlinecolor(YELLOW);
rectangle(190, 315, 610, 345);
if (m.uMsg == WM_LBUTTONDOWN) {
showdetail();
goto menubegin;
}
}
//
else if (m.x >= 200 && m.x <= 600 && m.y >= 380 && m.y <= 400) {
setlinecolor(YELLOW);
rectangle(190, 375, 610, 405);
if (m.uMsg == WM_LBUTTONDOWN) {
showrespect();
goto menubegin;
}
}
else if (m.x >= 200 && m.x <= 600 && m.y >= 440 && m.y <= 460) {
setlinecolor(YELLOW);
rectangle(190, 435, 610, 465);
if (m.uMsg == WM_LBUTTONDOWN) {
goon = 0;
cout << "谎言不会伤人,\n真相才是快刀。\n别玩了,一点人情世故都不懂-_-"<<endl<<endl;
break;
}
}
else if (m.x >= 200 && m.x <= 600 && m.y >= 500 && m.y <= 520) {
setlinecolor(YELLOW);
rectangle(190, 495, 610, 525);
if (m.uMsg == WM_LBUTTONDOWN) {
goon = 0;
break;
}
}
else {
setlinecolor(WHITE);
rectangle(190, 135, 610, 165);
rectangle(190, 195, 610, 225);
rectangle(190, 255, 610, 285);
rectangle(190, 315, 610, 345);
rectangle(190, 375, 610, 405);
rectangle(190, 435, 610, 465);
rectangle(190, 495, 610, 525);
}
}
}
其中MOUSEMSG定义了一个鼠标变量m,菜单肯定用鼠标嘛,m用来接收鼠标是按了左键还是右键,再返回信息,此时我们再做出相应反应就好了。
问题10(窗口乱跑)
在初始化菜单窗口或者游戏窗口时,有时候窗口会跑到屏幕以外去,我也不知道具体因为啥,然后我通过这个函数解决了
void Menu::window()
{
initgraph(WIDTH, HEIGHT);
// 确定窗口的初始位置,使其位于屏幕中央
int windowPosX = 0;
int windowPosY = 0;
// 获取当前窗口句柄
HWND hwnd = GetHWnd();
// 设置窗口位置
SetWindowPos(hwnd, HWND_TOP, windowPosX, windowPosY, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
简单来说就是不用最基础的initgraph,而是自己写一个初始化窗口的函数,在里面调用initgraph后再手动设置窗口的位置,保持窗口不跑到屏幕外面(这里我把窗口的左上角放在(0,0)点上)。
当然,菜单也可以有它的专属图片和音乐
Menu::Menu()
{
loadimage(&menu_bk, "menubk.png", WIDTH, HEIGHT);
loadimage(&detail_bk, "detailbk.jpg", WIDTH, HEIGHT);
loadimage(&history_bk, "historybk.jpg", WIDTH, HEIGHT);
loadimage(&control_bk, "controlbk.jpg", WIDTH, HEIGHT);
}
图片放在构造函数就行了,因为只需要加载一次,这里我没加音乐,你们可以自己加。
做完的效果是这样的:
图片是我乱翻的一张游戏对马岛之魂(这游戏挺好玩的,期末周让我玩上了,期末都没复习)的截图。
关于历史得分榜,这是文件的读取和写入方面的基础知识,没啥需要讲的。
可以看看:C++对txt文件的写入读取操作_c++读取txt-CSDN博客
打包分享装b (适当的自我激励)
问题11(我打包的游戏别人运行不了或者图片音频消失了)
如果做了一个游戏只有自己玩的了别人都玩不了,谈何乐趣?!当时我发给好几个朋友,他们的问题都不尽相同,有的少dll,有的少easyx,有的就是直接的运行错误,我一边下载dll一边打包给他们,一边让他们安装vs和easyx,试了好几次都失败了,那叫一个苦恼,如果你也有一样的苦恼,来,照我这样来!
右击解决方案,打开所在文件夹,把里面全部东西都选中打开。(包括那个x64文件夹)
选择新建的setup,右击,选择生成,接着右击解决方案如下图,选择打开文件夹再返回上一级就能看见setup了, 此时直接打开安装即可,到时候会在桌面看见游戏,当然也可以设置游戏的图标,自己去搜。
源代码
好了,难点大概就这些,以下是所有的源代码,分为4个头文件和1个cpp文件:
block类头文件
#include <iostream>
#include <stdio.h>
#include <graphics.h>
#ifndef BLOCK_H
#define BLOCK_H
#pragma once
#define WIDTH 1500
#define HEIGHT 800
//定义食物以及蛇的大小
#define SIZE 10
using namespace std;
class Block
{
friend class Snake;
friend class Food;
private:
vector<vector<int>> vc;//用vector数组存放批量障碍物的坐标
int x, y;
int count;
int difficulty;//难度越高,障碍物数量越多
IMAGE b, bv;//障碍物的图案
public:
Block(); //初始化8个障碍物
void Draw(int num);//负责障碍物相关的绘画
bool coli(int x0, int y0);//碰撞判定
bool onblock(int x, int y);//判断一个东西是否与障碍物重叠
void modify(int num);//改变障碍物数量
void diff();//改变显示难度
};
Block::Block():vc(100, vector<int>(2)), count(8), difficulty(1)
{
srand(time(0));//改变随机数种子
for (int i = 0; i < 100; i++)
{
do
{
x = rand() % (WIDTH / SIZE);
y = rand() % (HEIGHT / SIZE);
} while (x % 2 == 0 || y % 2 == 0 || x >= WIDTH / 10 - 5 || y >= HEIGHT / 10 - 5);
x *= 10;
y *= 10;
vc[i][0] = x;
vc[i][1] = y;
}
loadimage(&b, "block.png", 5 * SIZE, 5 * SIZE);//初始化图案
loadimage(&bv, "blockrv.png", 5 * SIZE, 5 * SIZE);
}
void Block::Draw(int num)
{
char difficultystr[10];
settextcolor(BLACK);
sprintf_s(difficultystr, "难度:%2d", difficulty);
outtextxy(900, 20, difficultystr);
for (int i = 0; i < num; i++)
{
putimage(vc[i][0], vc[i][1], &bv, SRCAND);
putimage(vc[i][0], vc[i][1], &b, SRCPAINT);
}
}
bool Block::coli(int x0,int y0)
{
for (int i = 0; i < count; i++)
{
int x = vc[i][0];
int y = vc[i][1];//循环每个障碍物,判断蛇头坐标x0,y0是否碰到障碍物
if (x0 == x && y0 == y) return true;
if (x0 == x + 2 * SIZE && y0 == y) return true;
if (x0 == x + 4 * SIZE && y0 == y) return true;
if (x0 == x && y0 == y + 2 * SIZE) return true;
if (x0 == x && y0 == y + 4 * SIZE) return true;
if (x0 == x + 2 * SIZE && y0 == y + 2 * SIZE) return true;
if (x0 == x + 4 * SIZE && y0 == y + 2 * SIZE) return true;
if (x0 == x + 2 * SIZE && y0 == y + 4 * SIZE) return true;
if (x0 == x + 4 * SIZE && y0 == y + 4 * SIZE) return true;
}
return false;
}
bool Block::onblock(int x,int y)
{
x *= 10;
y *= 10;
for (int i = 0; i < count; i++)
{
if (x >= vc[i][0] && x <= vc[i][0] + 4 * SIZE && y >= vc[i][1] && y <= vc[i][1] + 4 * SIZE) return true;
}return false;
}
void Block::modify(int num)
{
count = num;
}
void Block::diff()
{
difficulty++;
}
#endif
end类头文件
#include <iostream>
#include <stdio.h>
#include <graphics.h>
#ifndef END_H
#define END_H
#pragma once
#define WIDTH 1500
#define HEIGHT 800
//定义食物以及蛇的大小
#define SIZE 10
using namespace std;
class End
{
private:
IMAGE kobe1, kobe2, kobe3, champ;
public:
End()
{
loadimage(&kobe1, "ending.jpg", 552, 551, 0);
loadimage(&kobe2, "hurt2.jpg", 539, 839, 0);
loadimage(&kobe3, "hurt.jpg", 570, 856, 0);
loadimage(&champ, "champ.png", 520, 359, 0);
}
void draw()
{
cleardevice();
putimage(19, 497, &champ);
putimage(0, 0, &kobe1);
putimage(552, 0, &kobe2);
putimage(1010, 0, &kobe3);
Sleep(9000);
}
};
#endif
menu类头文件
#include <iostream>
#include <stdio.h>
#include <graphics.h>
#ifndef MENU_H
#define MENU_H
#pragma once
#define WIDTH 1500
#define HEIGHT 800
//定义食物以及蛇的大小
#define SIZE 10
using namespace std;
class Menu
{
private:
IMAGE menu_bk, detail_bk, history_bk, control_bk, respect_bk;
MOUSEMSG m;
public:
int goon = 1;
Menu();
void window();
void showmenu();
void showhistory();
void showdetail();
void showcontrol();
void showrespect();
bool goback();
void showreturn();
};
void Menu::window()
{
initgraph(WIDTH, HEIGHT);
// 确定窗口的初始位置,使其位于屏幕中央
int windowPosX = 0;
int windowPosY = 0;
// 获取当前窗口句柄
HWND hwnd = GetHWnd();
// 设置窗口位置
SetWindowPos(hwnd, HWND_TOP, windowPosX, windowPosY, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
Menu::Menu()
{
loadimage(&menu_bk, "menubk.png", WIDTH, HEIGHT);
loadimage(&detail_bk, "detailbk.jpg", WIDTH, HEIGHT);
loadimage(&history_bk, "historybk.jpg", WIDTH, HEIGHT);
loadimage(&control_bk, "controlbk.jpg", WIDTH, HEIGHT);
}
void Menu::showmenu()
{
menubegin:
window();
setbkcolor(WHITE);
cleardevice();
putimage(0, 0, &menu_bk);
setfillcolor(WHITE);
fillrectangle(200, 140, 600, 160);
fillrectangle(200, 200, 600, 220);
fillrectangle(200, 260, 600, 280);
fillrectangle(200, 320, 600, 340);
fillrectangle(200, 380, 600, 400);
fillrectangle(200, 440, 600, 460);
fillrectangle(200, 500, 600, 520);
setbkmode(TRANSPARENT);
settextcolor(BLACK);
outtextxy(250, 142, "开始游戏");//goto begin
outtextxy(250, 202, "历史得分榜");//goto history
outtextxy(250, 262, "操作");//goto control
outtextxy(250, 322, "游戏说明");//goto detail
outtextxy(250, 382, "作者好帅");//就喜欢你这种有眼力的玩家
outtextxy(250, 442, "作者好丑");//游戏崩溃
outtextxy(250, 502, "mamba out");//退出游戏
settextcolor(WHITE);
while (1) {
m = GetMouseMsg();
if (m.x >= 200 && m.x <= 600 && m.y >= 140 && m.y <= 160) {//检测鼠标的位置 是否满足条件
setlinecolor(YELLOW);//满足后 设置新的边框为红色
rectangle(190, 135, 610, 165);//画新的边框
if (m.uMsg == WM_LBUTTONDOWN) {
break;
}
}
else if (m.x >= 200 && m.x <= 600 && m.y >= 200 && m.y <= 220) {
setlinecolor(YELLOW);
rectangle(190, 195, 610, 225);
if (m.uMsg == WM_LBUTTONDOWN) {
showhistory();
goto menubegin;
}
}
else if (m.x >= 200 && m.x <= 600 && m.y >= 260 && m.y <= 280) {
setlinecolor(YELLOW);
rectangle(190, 255, 610, 285);
if (m.uMsg == WM_LBUTTONDOWN) {
showcontrol();
goto menubegin;
}
}
else if (m.x >= 200 && m.x <= 600 && m.y >= 320 && m.y <= 340) {
setlinecolor(YELLOW);
rectangle(190, 315, 610, 345);
if (m.uMsg == WM_LBUTTONDOWN) {
showdetail();
goto menubegin;
}
}
//
else if (m.x >= 200 && m.x <= 600 && m.y >= 380 && m.y <= 400) {
setlinecolor(YELLOW);
rectangle(190, 375, 610, 405);
if (m.uMsg == WM_LBUTTONDOWN) {
showrespect();
goto menubegin;
}
}
else if (m.x >= 200 && m.x <= 600 && m.y >= 440 && m.y <= 460) {
setlinecolor(YELLOW);
rectangle(190, 435, 610, 465);
if (m.uMsg == WM_LBUTTONDOWN) {
goon = 0;
cout << "谎言不会伤人,\n真相才是快刀。\n别玩了,一点人情世故都不懂-_-"<<endl<<endl;
break;
}
}
else if (m.x >= 200 && m.x <= 600 && m.y >= 500 && m.y <= 520) {
setlinecolor(YELLOW);
rectangle(190, 495, 610, 525);
if (m.uMsg == WM_LBUTTONDOWN) {
goon = 0;
break;
}
}
else {
setlinecolor(WHITE);
rectangle(190, 135, 610, 165);
rectangle(190, 195, 610, 225);
rectangle(190, 255, 610, 285);
rectangle(190, 315, 610, 345);
rectangle(190, 375, 610, 405);
rectangle(190, 435, 610, 465);
rectangle(190, 495, 610, 525);
}
}
}
void Menu::showdetail()
{
setbkcolor(WHITE);
cleardevice();
/*putimage(0, 0, &detail_bk);*/
char line1[1000] = "游戏说明:";
char line2[1000] = "1. 游戏压缩包包没有病毒的,请放心使用,如果游玩发现bug,请转告给世界上最帅的人,欢迎各位转告给我。";
char line3[1000] = "2. 长按前进方向加速突破,反向减速慢节奏。";
char line4[1000] = "3. 曼巴具有剧毒性,咬到自己会马上死亡。";
char line5[1000] = "4. 曼巴速度很快,动能太大,撞墙马上死亡。";
char line6[1000] = "5. 游戏中的肘子应尽量避免,如被肘击,在1.5秒内扣1可以复活劳大,此时劳大会艰难爬起来前进。";
char line7[1000] = "6. 如果闲游戏太简单,按h增加难度,难度会在吃到下一个食物后刷新。";
char line8[1000] = "7. 游戏为本人制作,欢迎转载或用于商业行为。(神头鬼脸)";
char line9[1000] = "8. 游戏达到81分有彩蛋;";
char line10[1000] ="9. 游戏后续会更新完善,3.0版本仅为第三代版本,望理解,敬请期待4.0版本。";
while (1)
{
outtextxy(100, 50, line1);
outtextxy(100, 80, line2);
outtextxy(100, 110, line3);
outtextxy(100, 140, line4);
outtextxy(100, 170, line5);
outtextxy(100, 200, line6);
outtextxy(100, 230, line7);
outtextxy(100, 260, line8);
outtextxy(100, 290, line9);
outtextxy(100, 320, line10);
showreturn();
if (goback()) break;
}
}
void Menu::showhistory()
{
setbkcolor(WHITE);
cleardevice();
/*putimage(0, 0, &history_bk);*/
setfillcolor(WHITE);
while (1) //文件输出
{
showreturn();
if (goback()) break;
}
}
void Menu::showcontrol()
{
setbkcolor(WHITE);
cleardevice();
/*putimage(0, 0, &control_bk);*/
char line1[100] = "WASD : 上下左右,长按加减速";
char line2[100] = "H : 增加难度";
char line3[100] = "扣1 : 复活牢大";
char line4[100] = "空格 : 暂停";
char line5[100] = "详见游戏说明";
while (1)
{
outtextxy(200, 30, line1);
outtextxy(200, 60, line2);
outtextxy(200, 90, line3);
outtextxy(200, 120, line4);
outtextxy(200, 150, line5);
showreturn();
if (goback()) break;
}
}
void Menu::showrespect()
{
setbkcolor(WHITE);
cleardevice();
/*putimage(0, 0, &respect_bk);*/
while (1)
{
char line1[100] = "就喜欢爱说真话的人>_<";
outtextxy(200, 30, line1);
showreturn();
if (goback()) break;
}
cout << endl;
}
bool Menu::goback()
{
m = GetMouseMsg();
if (m.x >= 0 && m.x <= 1800 && m.y >= 0 && m.y <= 800)
if (m.uMsg == WM_RBUTTONDOWN) {
return 1;
}
return 0;
}
void Menu::showreturn()
{
settextcolor(BLACK);
outtextxy(900, 202, "右");
outtextxy(900, 222, "击");
outtextxy(900, 242, "鼠");
outtextxy(900, 262, "标");
outtextxy(900, 282, "返");
outtextxy(900, 302, "回");
}
//
// int main()
// {
// Menu m;
// m.showmenu();
// return 0;
// }
#endif
music类头文件
#include <iostream>
#include <stdio.h>
#include <graphics.h>
#include <conio.h>
#ifndef MUSIC_H
#define MUSIC_H
#pragma once
#define WIDTH 1500
#define HEIGHT 800
//定义食物以及蛇的大小
#define SIZE 10
using namespace std;
class Music
{
private:
int vol;
public:
Music() :vol(100) {};
void bk_volumn();
void play_bkmusic();
void play_eatingsound();
void play_colisound();
void play_endmusic();
void play_menumusic();
void play_detailmusic();
void play_controlmusic();
void play_respectmusic();//你干嘛~~哎呦
void play_norespectmusic();
void play_historymusic();//三十年河西三十年河东,莫欺少年穷!
};
void Music::bk_volumn()
{
if (GetAsyncKeyState(38)) vol += 100;
else if (GetAsyncKeyState(40)) vol -= 100;
char com1[41];
sprintf(com1, "setaudio BGM1 volume to %d", vol);
mciSendString(com1, NULL, 0, NULL);
}
void Music::play_bkmusic()
{
mciSendString("open syagp.mp3 alias BGM1", 0, 0, 0);//see you again加速版
mciSendString("play BGM1 repeat", 0, 0, 0);
}
void Music::play_eatingsound()
{
mciSendString("close EAT", 0, 0, 0);
mciSendString("open eatingsound.mp3 alias EAT", 0, 0, 0);
mciSendString("play EAT", 0, 0, 0);
if (GetAsyncKeyState(38)) vol += 100;
else if (GetAsyncKeyState(40)) vol -= 100;
char com2[41];
sprintf(com2, "setaudio EAT volume to %d", vol);
mciSendString(com2, NULL, 0, NULL);
}
void Music::play_colisound()
{
mciSendString("close BGM1", 0, 0, 0);
mciSendString("close BGM2", 0, 0, 0);
mciSendString("open ah.mp3 alias BGM2", 0, 0, 0);//man!
mciSendString("play BGM2", 0, 0, 0);
if (GetAsyncKeyState(38)) vol += 100;
else if (GetAsyncKeyState(40)) vol -= 100;
char com3[41];
sprintf(com3, "setaudio BGM2 volume to %d", vol);
mciSendString(com3, NULL, 0, NULL);
}
void Music::play_endmusic()
{
mciSendString("close BGM1", 0, 0, 0);
mciSendString("open mambaout.mp3 alias END", 0, 0, 0);//mambaout
mciSendString("play END", 0, 0, 0);
if (GetAsyncKeyState(38)) vol += 100;
else if (GetAsyncKeyState(40)) vol -= 100;
char com4[41];
sprintf(com4, "setaudio END volume to %d", vol);
mciSendString(com4, NULL, 0, NULL);
}
#endif
源文件
#include <iostream>
#include <easyx.h>
#include<time.h>
#include <stdlib.h>
#include <vector>
#include"menu.h"
#include"music.h"
#include "end.h"
#include "block.h"
#pragma comment(lib,"winmm.lib")
#include <mmsystem.h>
using namespace std;
//定义场景大小
#define WIDTH 1500
#define HEIGHT 800
//定义食物以及蛇的大小
#define SIZE 10
//定义蛇的朝向
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
int speed = 175; //蛇的速度(用在睡眠函数里面)
#define MAXLEN 1600 //蛇的最大长度
int wuditime = 0;//无敌帧
typedef struct {
int x;
int y;
}SnakeNode;
//创建蛇的类
class Snake
{
friend class Food; //蛇的友元为食物
friend class Block;
public:
Snake()//初始化
{
this->dirt = RIGHT;
this->length = 3;
score = 0;
two = three = 0;
//下标是0的位置为蛇的头部
for (int i = 0; i < length; i++) {
this->node[i].x = 70 - ((i + 1) * 2 * SIZE);
this->node[i].y = SIZE;
}
loadimage(&bk, "bk.png", WIDTH, HEIGHT, 1);
};
void Move(); //移动
void Draw(); //绘制蛇
int Eat(Food& food); //吃食物
bool Defeat(Block& block); //失败判定
private:
int dirt; //朝向
int length; //长度
int score; //总得分
int two;//两分球个数
int three;//三分球个数
SnakeNode node[MAXLEN]; //蛇的结点
IMAGE bk;
};
//创建食物的类
class Food
{
friend class Snake; //食物的友元为蛇
friend class Block;
public:
Food(Snake& snake, Block& block); //食物初始化
void Draw(); //绘制食物
bool coli(float x0, float y0)//碰撞判断
{
if (x0 == x && y0 == y) return true;
if (x0 == x + 2 * SIZE && y0 == y) return true;
if (x0 == x + 4 * SIZE && y0 == y) return true;
if (x0 == x && y0 == y + 2 * SIZE) return true;
if (x0 == x && y0 == y + 4 * SIZE) return true;
if (x0 == x + 2 * SIZE && y0 == y + 2 * SIZE) return true;
if (x0 == x + 4 * SIZE && y0 == y + 2 * SIZE) return true;
if (x0 == x + 2 * SIZE && y0 == y + 4 * SIZE) return true;
if (x0 == x + 4 * SIZE && y0 == y + 4 * SIZE) return true;
return false;
}
private:
int x, y; //坐标
int score; //分数
int type;
IMAGE ima1, ima2, ima3, ima4;
};
SnakeNode tmp[MAXLEN]; //用另外一个数组来存储蛇原来的位置
//移动
void Snake::Move() {
//将原来的蛇结点拷贝一份
for (int i = 0; i < this->length; i++) {
tmp[i].x = this->node[i].x;
tmp[i].y = this->node[i].y;
}
int status = 0;//用来判断是否点击了转向按键
int flag = 0;
if (this->dirt == RIGHT) {
//判断是否转向
if (GetAsyncKeyState('W') && status == 0) {
this->node[0].y -= 2 * SIZE;
this->dirt = UP;
status = 1;
}
else if (GetAsyncKeyState('S') && status == 0) {
this->node[0].y += 2 * SIZE;
this->dirt = DOWN;
status = 1;
}
else {
if (GetAsyncKeyState('A') && status == 0)
flag -= 10;
if (GetAsyncKeyState('D') && status == 0)
flag += 10;
this->node[0].x += 2 * SIZE;
}
}
else if (this->dirt == DOWN) {
//判断是否转向
if (GetAsyncKeyState('A') && status == 0) {
this->node[0].x -= 2 * SIZE;
this->dirt = LEFT;
status = 1;
}
else if (GetAsyncKeyState('D') && status == 0) {
this->node[0].x += 2 * SIZE;
this->dirt = RIGHT;
status = 1;
}
else {
if (GetAsyncKeyState('W') && status == 0)
flag -= 10;
if (GetAsyncKeyState('S') && status == 0)
flag += 10;
this->node[0].y += 2 * SIZE;
}
}
else if (this->dirt == LEFT) {
//判断是否转向
if (GetAsyncKeyState('W') && status == 0) {
this->node[0].y -= 2 * SIZE;
this->dirt = UP;
status = 1;
}
else if (GetAsyncKeyState('S') && status == 0) {
this->node[0].y += 2 * SIZE;
this->dirt = DOWN;
status = 1;
}
else {
if (GetAsyncKeyState('D') && status == 0)
flag -= 10;
if (GetAsyncKeyState('A') && status == 0)
flag += 10;
this->node[0].x -= 2 * SIZE;
}
}
else if (this->dirt == UP) {
//判断是否转向
if (GetAsyncKeyState('A') && status == 0) {
this->node[0].x -= 2 * SIZE;
this->dirt = LEFT;
status = 1;
}
else if (GetAsyncKeyState('D') && status == 0) {
this->node[0].x += 2 * SIZE;
this->dirt = RIGHT;
status = 1;
}
else {
if (GetAsyncKeyState('S') && status == 0)
flag -= 10;
if (GetAsyncKeyState('W') && status == 0)
flag += 10;
this->node[0].y -= 2 * SIZE;
}
}
//移动
for (int i = 1; i < this->length; i++) {
this->node[i].x = tmp[i - 1].x;
this->node[i].y = tmp[i - 1].y;
}
Sleep(speed - flag * 15);
}
//绘制蛇
void Snake::Draw() {
cleardevice();//清空原先的绘图
putimage(0, 0, &bk);//先画出背景
settextcolor(BLACK);
char scorestr[2000], twostr[1000], threestr[1000], str[8];
sprintf_s(twostr, "二分球:%2d", two);
sprintf_s(threestr, "三分凉:%2d", three);
sprintf_s(scorestr, "总得分:%2d", score);//画出当前战绩
outtextxy(900, 90, scorestr);
outtextxy(900, 70, threestr);
outtextxy(900, 50, twostr);
srand((unsigned)time(NULL));//设置随机数种子
for (int i = 0; i < this->length; i++) {
setfillcolor(BLACK);//“黑”曼巴
fillcircle(this->node[i].x, this->node[i].y, SIZE);
}
}
//食物的初始化
Food::Food(Snake& snake, Block& block)
{
loadimage(&ima1, "basketball.png", 4 * SIZE, 4 * SIZE);
loadimage(&ima2, "baskrv.png", 4 * SIZE, 4 * SIZE);
loadimage(&ima3, "bhc.png", 4 * SIZE, 4 * SIZE);
loadimage(&ima4, "rv.png", 4 * SIZE, 4 * SIZE);
table:
srand((unsigned)time(0));
do
{
x = (rand() % (WIDTH / SIZE));
y = (rand() % (HEIGHT / SIZE));
} while (x % 2 == 0 || y % 2 == 0 || x >= WIDTH / 10 - 4 || y >= HEIGHT / 10 - 4 || block.onblock(x, y));//一直生成随机坐标直到该坐标不与障碍物重叠并且为能被蛇头碰到
this->x *= SIZE;
this->y *= SIZE;
this->type = rand() % 100;
if (type >= 0 && type <= 80) {
type = 1;
}
else {
type = 2;
}
for (int i = 0; i < snake.length; i++) {
if (snake.node[i].x == this->x && snake.node[i].y == this->y) {
goto table;
}
}
}
//绘制食物
void Food::Draw() {
if (type == 1)//篮球
{
putimage(this->x, this->y, &ima2, SRCAND);
putimage(x, y, &ima1, SRCPAINT);
}
else if (type == 2)//冰红茶
{
putimage(x, y, &ima4, SRCAND);
putimage(x, y, &ima3, SRCPAINT);
}
}
//吃食物
int Snake::Eat(Food& food) {
if (food.coli(node[0].x, node[0].y)) {
if (food.type == 1) {
two++;
score += 2;
}
else if (food.type == 2) {
three++;
score += 3;
}
if (this->node[length - 1].x - this->node[length - 2].x == 0 && this->node[length - 1].y - this->node[length - 2].y == -20) {
this->length++;
this->node[length - 1].x = this->node[length - 2].x;
this->node[length - 1].y = this->node[length - 2].y - 2 * SIZE;
}
if (this->node[length - 1].x - this->node[length - 2].x == 0 && this->node[length - 1].y - this->node[length - 2].y == 20) {
this->length++;
this->node[length - 1].x = this->node[length - 2].x;
this->node[length - 1].y = this->node[length - 2].y + 2 * SIZE;
}
if (this->node[length - 1].x - this->node[length - 2].x == 20 && this->node[length - 1].y - this->node[length - 2].y == 0) {
this->length++;
this->node[length - 1].x = this->node[length - 2].x + 2 * SIZE;
this->node[length - 1].y = this->node[length - 2].y;
}
if (this->node[length - 1].x - this->node[length - 2].x == -20 && this->node[length - 1].y - this->node[length - 2].y == 0) {
this->length++;
this->node[length - 1].x = this->node[length - 2].x - 2 * SIZE;
this->node[length - 1].y = this->node[length - 2].y;
}
return true;
}
return false;
}
//失败判定
bool Snake::Defeat(Block& block) {
//1.碰到边界
if (this->node[0].x < 0 || this->node[0].x >= WIDTH || this->node[0].y < 0 || this->node[0].y >= HEIGHT) {
return true;
}
//2.碰到自己的身体
for (int i = 1; i < this->length; i++) {
if (this->node[0].x == this->node[i].x && this->node[0].y == this->node[i].y) {
return true;
}
}
//3.被肘击
if (block.coli(node[0].x, node[0].y))
{
Sleep(1500);
if (GetAsyncKeyState('1')) {
wuditime = 5;
return false;
}
return true;
}
return false;
}
int main() {
Menu menu;
Music music;
End ending;
begin:
menu.showmenu();
if (menu.goon)
{
menu.window();
table1:
setbkcolor(WHITE);
cleardevice();
music.play_bkmusic();
Block block;
Snake snake;
int num = 8;
int newnum = 8;
int eat = 0;
table2:
Food food(snake, block);
while (1) {
BeginBatchDraw();
FlushBatchDraw();
music.bk_volumn();
snake.Draw();
if (eat)
{
block.modify(newnum);
block.Draw(newnum);
num = newnum;
}
else block.Draw(num);
if (GetAsyncKeyState('H'))
{
newnum += 16;
block.diff();
}
if (GetAsyncKeyState(' ')) system("Pause");
food.Draw();
FlushBatchDraw();
EndBatchDraw();//双缓冲,防止屏幕一闪一闪的
if (snake.Eat(food)) {
eat = 1;
music.play_eatingsound();
wuditime = 5;//无敌帧
goto table2;
}
if (wuditime<=0 &&snake.Defeat(block) ) {
music.play_colisound();
break;
}
snake.Move();
wuditime--;
eat = 0;
}
HWND window = GetHWnd();
SetWindowText(window, "死亡");
int end = MessageBox(window, "mamba out,see you again?", "提示", MB_OKCANCEL);
if (end == IDOK) goto table1;
else if (end==IDCANCEL)
{
music.play_endmusic();
ending.draw();
goto begin;
}
}
return 0;
}