分类
开元棋牌正版

【C++】工程项目两栖作战:植物混战丧尸丨源代码+讲义/课设-毕设必选工程项目

真菌混战丧尸,是两个十分经典之作的迷你格斗游戏,新手从头开始,合作开发两个他们的真菌混战丧尸,却是十分值得称赞期盼的!能做为他们的课设,也能用以加速提高他们的工程项目合作开发潜能。

工程项目效用(详尽音频讲义>「镜像」

表明:即使完备Zopfli递交后提示信息违法,因此这儿仅截屏起身。假如须要模拟音频,在文章中申明方可。

工程项目预备

加装Visual Studio的任一版(所推荐VS2019街道社区版、VS2022街道社区版)加装easyx程序库申领工程项目片断(申明“真菌混战丧尸”,方可申领)

建立工程项目

采用VS建立工程项目,采用空工程项目模版:

引入片断:在工程项目产品目录下,建立res配置文件,把Cogl后的片断复制到res产品目录下。

同时实现格斗游戏如上所述情景

标识符如下表所示(须要由上而下标识符音频传授,可申明“标识符传授“)。

#include <stdio.h> #include <graphics.h> #include “tools.h” #include <mmsystem.h> #pragma comment(lib, “winmm.lib”) #define WIN_WIDTH 900 #define WIN_HEIGHT 600 enum { WAN_DOU, XIANG_RI_KUI, ZHI_WU_COUT }; IMAGE imgBg; IMAGE imgBar; IMAGE imgCards[ZHI_WU_COUT]; IMAGE* imgZhiWu[ZHI_WU_COUT][20]; int curZhiWu; int curX, curY; //现阶段选上真菌在终端操作过程中的座标 struct zhiWu { int type; // >=1 0:没有真菌 int frameIndex; }; struct zhiWu map[3][9]; int sunshine; int sunshineTable[ZHI_WU_COUT] = { 100, 50 }; void gameInit() { loadimage(&imgBg, “res/bg.jpg”); loadimage(&imgBar, “res/bar.png”); sunshine = 150; curZhiWu = 0; memset(imgZhiWu, 0, sizeof(imgZhiWu)); memset(map, 0, sizeof(map)); char name[64]; for (int i = 0; i < ZHI_WU_COUT; i++) { sprintf_s(name, sizeof(name), “res/Cards/card_%d.png”, i + 1); loadimage(&imgCards[i], name); for (int j = 0; j < 20; j++) { sprintf_s(name, sizeof(name), “res/zhiwu/%d/%d.png”, i, j + 1); imgZhiWu[i][j] = new IMAGE; loadimage(imgZhiWu[i][j], name); if (imgZhiWu[i][j]->getwidth() == 0) { delete imgZhiWu[i][j]; imgZhiWu[i][j] = NULL; } } } initgraph(WIN_WIDTH, WIN_HEIGHT, 1); // 设置字体: LOGFONT f; gettextstyle(&f); // 获取现阶段字体设置 f.lfHeight = 30; // 设置字体高度为 48 f.lfWidth = 15; strcpy(f.lfFaceName, “Segoe UI Black”); f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效用为抗锯齿 settextstyle(&f); // 设置字体样式 setbkmode(TRANSPARENT); setcolor(BLACK); mciSendString(“play res/bg.mp3 repeat”, 0, 0, 0); } void updateWindow() { BeginBatchDraw(); putimage(0, 0, &imgBg); putimagePNG(250, 0, &imgBar); for (int i = 0; i < ZHI_WU_COUT; i++) { int x = 338 + i * 64; int y = 6; putimage(x, y, &imgCards[i]); } if (curZhiWu > 0) { // 绘制正在终端的真菌 IMAGE* img = imgZhiWu[curZhiWu – 1][0]; putimagePNG(curX – img->getwidth() * 0.5, curY – img->getheight() * 0.5, img); } for (int i = 0; i < 3; i++) { for (int j = 0; j < 9; j++) { if (map[i][j].type > 0) { int x = 260 + j * 81.6; // (msg.x – 260) / 81.6; int y = 180 + i * 103.6 + 14; // (msg.y – 210) / 103.6; int zhiWuIndex = map[i][j].type; int frameIndex = map[i][j].frameIndex; putimagePNG(x, y, imgZhiWu[zhiWuIndex – 1][frameIndex]); } } } char scoreText[8]; sprintf_s(scoreText, sizeof(scoreText), “%d”, sunshine); outtextxy(28210 + 4, 50 + 15 + 2, scoreText); EndBatchDraw(); } void userClick() { ExMessage msg; static int status = 0; if (peekmessage(&msg)) { if (msg.message == WM_LBUTTONDOWN) { if (msg.x > 338 && msg.x < 338 + 64 * ZHI_WU_COUT && msg.y>6 && msg.y < 96) { int index = (msg.x – 338) / 64; printf(“%d\n”, index); status = 1; curZhiWu = index + 1; // 1, 2 curX = msg.x; curY = msg.y; } } else if (msg.message == WM_MOUSEMOVE && status == 1) { curX = msg.x; curY = msg.y; } else if (msg.message == WM_LBUTTONUP && status == 1) { printf(“up\n”); if (msg.x > 260 && msg.y < 995 && msg.y > 180 && msg.y < 491) { if (sunshine >= sunshineTable[curZhiWu – 1]) { sunshine -= sunshineTable[curZhiWu – 1]; int col = (msg.x – 260) / 81.6; int row = (msg.y – 210) / 103.6; printf(“[%d,%d]\n”, row, col); if (map[row][col].type == 0) { map[row][col].type = curZhiWu; map[row][col].frameIndex = 0; } } } status = 0; curZhiWu = 0; } } } void updateGame() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 9; j++) { if (map[i][j].type > 0) { map[i][j].frameIndex++; if (imgZhiWu[map[i][j].type – 1][map[i][j].frameIndex] == NULL) { map[i][j].frameIndex = 0; } } } } } int main(void) { gameInit(); int timer = 0; bool flag = true; while (1) { userClick(); timer += getDelay(); if (timer > 20) { timer = 0; flag = true; } if (flag) { flag = false; updateWindow(); updateGame(); } } return 0; }

添加启动菜单

建立菜单界面,标识符如下表所示:

void startUI() { IMAGE imgBg, imgMenu1, imgMenu2; loadimage(&imgBg, “res/menu.png”); loadimage(&imgMenu1, “res/menu1.png”); loadimage(&imgMenu2, “res/menu2.png”); int flag = 0; while (1) { BeginBatchDraw(); putimage(0, 0, &imgBg); putimagePNG(474, 75, flag ? &imgMenu2 : &imgMenu1); ExMessage msg; if (peekmessage(&msg)) { if (msg.message == WM_LBUTTONDOWN && msg.x > 474 && msg.x < 474 + 300 && msg.y > 75 && msg.y < 75 + 140) { flag = 1; EndBatchDraw(); } else if (msg.message == WM_LBUTTONUP && flag) { return; } } EndBatchDraw(); } }

在main函数中调用菜单,标识符如下表所示:

int main(void) { gameInit(); startUI(); int timer = 0; bool flag = true; while (1) { userClick(); timer += getDelay(); if (timer > 20) { timer = 0; flag = true; } if (flag) { flag = false; updateWindow(); updateGame(); } } return 0; }

生产阳光

熟悉真菌混战丧尸的同学都知道,种植真菌才能消灭丧尸,但是种植真菌,须要先具备一定数量的阳光值。如上所述的阳光值很小。有两种方式生成阳光:第一种,随机降落少量的阳光;第二种,通过种植向日葵,让向日葵自动生产阳光。我们先同时实现第一种方式。

定义两个结构体,来表示阳光球。即使阳光是以旋转的方式运动的,因此定义两个图片帧数组,通过循环播放图片帧来同时实现旋转效用。

IMAGE imgSunshineBall[29]; struct sunshineBall { int x, y; int frameIndex; bool used; int destY; int timer = 0; }; struct sunshineBall balls[10];

在gameInit函数中,如上所述化阳光帧数组。

memset(balls, 0, sizeof(balls)); for (int i = 0; i < 29; i++) { sprintf_s(name, sizeof(name), “res/sunshine/%d.png”, i + 1); loadimage(&imgSunshineBall[i], name); }

建立阳光,标识符如下表所示。

void createSunshine() { int ballMax = sizeof(balls) / sizeof(balls[0]); static int frameCount = 0; static int fre = 400; frameCount++; if (frameCount >= fre) { fre = 200 + rand() % 200; frameCount = 0; int i; for (i = 0; i < ballMax && balls[i].used; i++); if (i >= ballMax) return; balls[i].used = true; balls[i].frameIndex = 0; balls[i].x = 260 + rand() % (905 260); balls[i].y = 60; balls[i].destY = 180 + (rand() % 4) * 90 + 20; balls[i].timer = 0; } }

修改阳光的位置和帧序号,标识符如下表所示。

void updateSunshine() { int ballMax = sizeof(balls) / sizeof(balls[0]); for (int i = 0; i < ballMax; i++) { if (balls[i].used) { balls[i].frameIndex = (balls[i].frameIndex + 1) % 29; if(balls[i].timer == 0) balls[i].y += 2; if (balls[i].y >= balls[i].destY) { balls[i].timer++; if (balls[i].timer > 100) balls[i].used = false; } } } }

在updateGame函数中调用以上两个函数 ,以建立阳光并更新阳光的状态。

createSunshine(); updateSunshine();

在updateWindow函数中,渲染阳光。

for (int i = 0; i < 10; i++) { if (balls[i].used) { putimagePNG(balls[i].x, balls[i].y, &imgSunshineBall[balls[i].frameIndex]); } }

收集阳光

当“阳光球”出现的时候,用户点击阳光球,就能“收集”这个阳光,现阶段总的阳光值就会增加25点。在原版的真菌混战丧尸格斗游戏中,阳光球被收集后,会慢慢终端到顶部的“工具栏”的左侧。这个阳光球的“终端操作过程”,我们后续再同时实现。

定义两个全局变量,表示现阶段总的阳光值。

int sunshine;

在如上所述化gameInit中,设置两个如上所述值。

sunshine = 150;

建立收集阳光的函数,如下表所示:

void collectSunshine(ExMessage* msg) { int count = sizeof(balls) / sizeof(balls[0]); int w = imgSunshineBall[0].getwidth(); int h = imgSunshineBall[0].getheight(); for (int i = 0; i < count; i++) { if (balls[i].used) { int x = balls[i].x; int y = balls[i].y; if (msg->x > x && msg->x < x + w && msg->y > y && msg->y < y + h) { balls[i].used = false; sunshine += 25; mciSendString(“play res/sunshine.mp3”, 0, 0, 0); } } } }

在用户点击处理中,调用收集阳光的函数。

#include <mmsystem.h> #pragma comment(lib, “winmm.lib”) void userClick() { ExMessage msg; static int status = 0; if (peekmessage(&msg)) { if (msg.message == WM_LBUTTONDOWN) { if (msg.x > 338 && msg.x < 338 + 65 * ZHI_WU_COUNT && msg.y < 96) { int index = (msg.x – 338) / 65; status = 1; curZhiWu = index + 1; } else { collectSunshine(&msg); } } // …… } }

显示现阶段总的阳光值

在gameInit如上所述化中,设置字体。

LOGFONT f; gettextstyle(&f); // 获取现阶段字体设置 f.lfHeight = 30; // 设置字体高度为 48 f.lfWidth = 15; strcpy(f.lfFaceName, “Segoe UI Black”); f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效用为抗锯齿 settextstyle(&f); // 设置字体样式 setbkmode(TRANSPARENT); setcolor(BLACK);

在updateWindow中绘制阳光值。

char scoreText[8]; sprintf_s(scoreText, sizeof(scoreText), “%d”, sunshine); outtextxy(276, 67, scoreText);

建立丧尸

建立丧尸的数据模型。这儿一共建立了10个丧尸,这10个丧尸全部被消灭后,这个关卡就胜利了。

struct zm { int x, y; int frameIndex; bool used; int speed; }; struct zm zms[10]; IMAGE imgZM[22];

丧尸数组,以及丧尸序列帧图片数组,在gameInit函数中进行如上所述化,如下表所示。注意:把丧尸的片断图片保存到src/zm产品目录下。)

memset(zms, 0, sizeof(zms)); srand(time(NULL)); for (int i = 0; i < 22; i++) { sprintf_s(name, sizeof(name), “res/zm/%d.png”, i + 1); loadimage(&imgZM[i], name); }

建立丧尸,标识符如下表所示:

void createZM() { static int zmFre = 500; static int count = 0; count++; if (count > zmFre) { zmFre = rand() % 200 + 300; count = 0; int i; int zmMax = sizeof(zms) / sizeof(zms[0]); for (i = 0; i < zmMax && zms[i].used; i++); if (i < zmMax) { zms[i].used = true; zms[i].x = WIN_WIDTH; zms[i].y = 180 + (1 + rand() % 3) * 100 8; zms[i].speed = 1; } } }

更新丧尸的数据(丧尸的图片帧序号、丧尸的位置),标识符如下表所示:

void updateZM() { int zmMax = sizeof(zms) / sizeof(zms[0]); static int count1 = 0; count1++; if (count1 > 2) { count1 = 0; for (int i = 0; i < zmMax; i++) { if (zms[i].used) { zms[i].x -= zms[i].speed; if (zms->x < 23666) { printf(“GAME OVER!\n”); MessageBox(NULL, “over”, “over”, 0); //TO DO break; } } } } static int count2 = 0; count2++; if (count2 > 4) { count2 = 0; for (int i = 0; i < zmMax; i++) { if (zms[i].used) { zms[i].frameIndex = (zms[i].frameIndex + 1) % 22; } } } }

在updateGame函数中,建立丧尸并更新丧尸数据,如下表所示:

createZM(); updateZM();

建立绘制丧尸的接口, 如下表所示:

void drawZM() { int zmCount = sizeof(zms) / sizeof(zms[0]); for (int i = 0; i < zmCount; i++) { if (zms[i].used) { IMAGE* img = &imgZM[zms[i].frameIndex]; int x = zms[i].x; int y = zms[i].y – img->getheight(); putimagePNG(x, y, img); } } }

在updateWindow函数中,绘制丧尸,如下表所示:

drawZM();

同时实现阳光球的飞跃

现在的同时实现效用是,阳光被点击后,阳光球直接消失了!而原版的真菌混战丧尸中,阳光被点击后,阳光会自动飞向左上角的位置,飞到终点后,阳光值才增加25点。我们的同时实现方式是,阳光球每次飞跃4个点,直到飞到终点,

如下表所示图:

给阳光的结构体添加两个成员,表示飞跃操作过程中的偏移量:

struct sunshineBall { int x, y; int frameIndex; bool used; int destY; int timer; //添加以下两个成员 float xOff; float yOff; };

在阳光被建立时,把变异量设置为0, 如下表所示:

void createSunshine() { int ballMax = sizeof(balls) / sizeof(balls[0]); static int frameCount = 0; static int fre = 200; frameCount++; if (frameCount >= fre) { //…略 balls[i].xOff = 0; balls[i].yOff = 0; } }

阳光被点击后,马上修改阳光球的xoff和yoff:

#include <math.h> void collectSunshine(ExMessage* msg) { int count = sizeof(balls) / sizeof(balls[0]); int w = imgSunshineBall[0].getwidth(); int h = imgSunshineBall[0].getheight(); for (int i = 0; i < count; i++) { if (balls[i].used) { int x = balls[i].x; int y = balls[i].y; if (msg->x > x && msg->x < x + w && msg->y >y && msg->y < y + h) { balls[i].used = false; sunshine += 25; mciSendString(“play res/sunshine.mp3”, 0, 0, 0); // 设置如上所述偏移量 float destX = 262; float destY = 0; float angle = atan((y – destY) / (x – destX)); balls[i].xOff = 4 * cos(angle); balls[i].yOff = 4 * sin(angle); } } } }

在阳光飞跃操作过程中更新阳光的位置,如下表所示:(注意是在飞跃操作过程中,不断计算偏移量,效用更好。)

void updateSunshine() { int ballMax = sizeof(balls) / sizeof(balls[0]); for (int i = 0; i < ballMax; i++) { if (balls[i].used) { //略… } else if (balls[i].xOff) { float destX = 263; float destY = 0; float angle = atan((balls[i].y – destY) / (balls[i].x – destX)); balls[i].xOff = 4 * cos(angle); balls[i].yOff = 4 * sin(angle); balls[i].x -= balls[i].xOff; balls[i].y -= balls[i].yOff; if (balls[i].y < 0 || balls[i].x < 262) { balls[i].xOff = 0; balls[i].yOff = 0; sunshine += 25; } } } }

删除原来被点击后,立即更新阳光值的标识符。

//sunshine += 25;

修改渲染阳光的判断条件,如下表所示:

for (int i = 0; i < ballMax; i++) { if (balls[i].used || balls[i].xOff) { //添加这个条件 IMAGE* img = &imgSunshineBall[balls[i].frameIndex]; putimagePNG(balls[i].x, balls[i].y, img); } }

此时已经能够同时实现阳光的飞跃了,但是飞跃动作太慢了,后期我们再优化。

发射豌豆

丧尸靠近时,已经种植的真菌豌豆就会自动发射“子弹”,我们先为子弹定义数据类型,如下表所示:

struct bullet { int x, y; int row; bool used; int speed; }; struct bullet bullets[30]; IMAGE imgBulletNormal;

在gameInit函数中,如上所述化“豌豆子弹池”和子弹的图片,如下表所示:

loadimage(&imgBulletNormal, “res/bullets/bullet_normal.png”); memset(bullets, 0, sizeof(bullets));

在丧尸结构体中,添加成员row, 表示该丧尸所在的“行”,方便后续的判断。也能不加,直接根据丧尸的y座标来计算。

struct zm { int x, y; int frameIndex; bool used; int speed; int row; //0..2 };

在createZM函数中,建立丧尸的时候,设置row成员的值,如下表所示:

…… if (i < zmMax) { zms[i].used = true; zms[i].x = WIN_WIDTH; zms[i].row = rand() % 3; // 0..2; zms[i].y = 172 + (1 + zms[i].row) * 100; zms[i].speed = 1; } ……

建立shoot函数,同时实现豌豆发射子弹,如下表所示:

void shoot() { int zmCount = sizeof(zms) / sizeof(zms[0]); int directions[3] = { 0 }; int dangerX = WIN_WIDTH imgZM[0].getwidth(); for (int i = 0; i < zmCount; i++) { if (zms[i].used && zms[i].x < dangerX) { directions[zms[i].row] = 1; } } for (int i = 0; i < 3; i++) { for (int j = 0; j < 9; j++) { if (map[i][j].type == WAN_DOU+1 && directions[i]) { static int count = 0; count++; if (count > 20) { count = 0; int k; int maxCount = sizeof(bullets) / sizeof(bullets[0]); for (k = 0; k < maxCount && bullets[k].used; k++); if (k < maxCount) { bullets[k].row = i; bullets[k].speed = 4; bullets[k].used = true; int zwX = 260 + j * 81.6; // (msg.x 260) / 81.6; int zwY = 180 + i * 103.6 + 14; // (msg.y 210) / 103.6; bullets[k].x = zwX + imgZhiWu[map[i][j].type 1][0]->getwidth()-10; bullets[k].y = zwY + 5; } } } } } }

更新子弹的位置,如下表所示:

void updateBullets() { int countMax = sizeof(bullets) / sizeof(bullets[0]); for (int i = 0; i < countMax; i++) { if (bullets[i].used) { bullets[i].x += bullets[i].speed; if (bullets[i].x > WIN_WIDTH) { bullets[i].used = false; } } } }

在updateGame函数中,发射子弹并更新子弹的位置,如下表所示:

shoot(); updateBullets();

在updateWindow中绘制子弹,如下表所示:

int bulletMax = sizeof(bullets) / sizeof(bullets[0]); for (int i = 0; i < bulletMax; i++) { if (bullets[i].used) { putimagePNG(bullets[i].x, bullets[i].y, &imgBulletNormal); } }

子弹和丧尸的碰撞

子弹碰到丧尸之后,子弹会“爆炸”,同时丧尸会“掉血”。我们先给丧尸添加血量成员。

struct zm { //略… int blood; };

并在建立丧尸的时候,把血量如上所述化为100,如下表所示:

//… zms[i].speed = 1; zms[i].blood = 100;

子弹在碰到丧尸之后才会爆炸,并显示爆炸图片:

因此,我们在子弹的结构体中添加两个成员,分别表示现阶段是否已经爆炸,以及爆炸的帧图片序号,如下表所示:

struct bullet { //… bool blast; int frameIndex; }; IMAGE imgBulletBlast[4];

在gameInit函数中对子弹帧图片数组,进行如上所述化,如下表所示:

loadimage(&imgBulletBlast[3], “res/bullets/bullet_blast.png”); for (int i = 0; i < 3; i++) { float k = (i + 1) * 0.2; loadimage(&imgBulletBlast[i], “res/bullets/bullet_blast.png”, imgBulletBlast[3].getwidth()*k, imgBulletBlast[3].getheight()*k, true); }

在发射子弹shoot函数中,对子弹的blast和帧序号frameIndex进行如上所述化,如下表所示:

bullets[k].row = i; bullets[k].speed = 4; bullets[k].used = true; bullets[k].blast = false; bullets[k].blastTime = 0;

在更新子弹的updateBullets函数中,更新子弹爆炸的帧序号,如下表所示:

bullets[i].x += bullets[i].speed; if (bullets[i].x > WIN_WIDTH) { bullets[i].used = false; } if (bullets[i].blast) { bullets[i].blastTime++; if (bullets[i].blastTime >= 4) { bullets[i].used = false; } }

进行碰撞检测,检查子弹和丧尸是否发生碰撞,如下表所示:

void collisionCheck() { int bCount = sizeof(bullets) / sizeof(bullets[0]); int zCount = sizeof(zms) / sizeof(zms[0]); for (int i = 0; i < bCount; i++) { if (bullets[i].used == false || bullets[i].blast)continue; for (int k = 0; k < zCount; k++) { int x1 = zms[k].x + 80; int x2 = zms[k].x + 110; if (bullets[i].row == zms[k].row && bullets[i].x > x1 && bullets[i].x < x2) { zms[i].blood -= 20; bullets[i].blast = true; bullets[i].speed = 0; } } } }

在updateGame函数中,调用碰撞检测函数,如下表所示:

collisionCheck();

渲染子弹的爆炸效用,如下表所示:

int bulletMax = sizeof(bullets) / sizeof(bullets[0]); for (int i = 0; i < bulletMax; i++) { if (bullets[i].used) { if (bullets[i].blast) { IMAGE* img = &imgBulletBlast[bullets[i].blastTime]; int x = bullets[i].x + 12 – img->getwidth() / 2; int y = bullets[i].y + 12 – img->getheight() / 2; putimagePNG(x, y, img); /*bullets[i].used = false;*/ } else { putimagePNG(bullets[i].x, bullets[i].y, &imgBulletNormal); } } }

丧尸死亡

丧尸被豌豆子弹击中后,会“掉血”,血量掉光了,就直接KO了,同时变成一堆“黑沙”。

给丧尸结构体添加dead成员,表示是否已经死亡,另外添加两个图片帧数组,用以表示变成成黑沙的操作过程。

struct zm { …… bool dead; }; IMAGE imgZmDead[20];

在gameInit中对这个图片帧数组进行如上所述化。

for (int i = 0; i < 20; i++) { sprintf_s(name, sizeof(name), “res/zm_dead/%d.png”, i + 1); loadimage(&imgZmDead[i], name); }

在碰撞检测中对丧尸的血量做检测,假如血量降到0,就设置为死亡状态。如下表所示:

void collisionCheck() { int bCount = sizeof(bullets) / sizeof(bullets[0]); int zCount = sizeof(zms) / sizeof(zms[0]); for (int i = 0; i < bCount; i++) { if (bullets[i].used == false || bullets[i].blast)continue; for (int k = 0; k < zCount; k++) { int x1 = zms[k].x + 80; int x2 = zms[k].x + 110; if (zms[k].dead==false && //添加这个条件 bullets[i].row == zms[k].row && bullets[i].x > x1 && bullets[i].x < x2) { zms[k].blood -= 20; bullets[i].blast = true; bullets[i].speed = 0; //对血量进行检测 if (zms[k].blood <= 0) { zms[k].dead = true; zms[k].speed = 0; zms[k].frameIndex = 0; } break; } } } }

丧尸死亡后,在updateZM中,更新丧尸的状态(变成黑沙发)。如下表所示:

static int count2 = 0; count2++; if (count2 > 4) { count2 = 0; for (int i = 0; i < zmMax; i++) { if (zms[i].used) { //判断是否已经死亡 if (zms[i].dead) { zms[i].frameIndex++; if (zms[i].frameIndex >= 20) { zms[i].used = false; } } else { zms[i].frameIndex = (zms[i].frameIndex + 1) % 22; } } } }

绘制丧尸的黑沙状态,如下表所示:

void drawZM() { int zmCount = sizeof(zms) / sizeof(zms[0]); for (int i = 0; i < zmCount; i++) { if (zms[i].used) { //选择对应的渲染图片 IMAGE* img = (zms[i].dead) ? imgZmDead : imgZM; img += zms[i].frameIndex; int x = zms[i].x; int y = zms[i].y – img->getheight(); putimagePNG(x, y, img); } } }

C词汇工程项目更新中……