C语言手写植物大战僵尸

创建主场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<stdio.h>
#include<graphics.h>

#define WIN_WIDTH 900
#define WIN_HEIGHT 600

IMAGE imgBg;

//游戏初始化
void gameInit() {
loadimage(&imgBg, "res/bg.jpg");

initgraph(WIN_WIDTH, WIN_HEIGHT);
}

//更新窗口
void updateWindow() {
putimage(0, 0, &imgBg);
}

int main(void) {
//游戏初始化
gameInit();

//更新窗口
updateWindow();

system("pause");
return 0;
}

实现植物卡牌

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include "tools.h"

//#define ZHI_WU_CUNT 3

enum { WAN_DOU, XIANG_RI_KUI, ZHI_WU_COUNT };

IMAGE imgBar;
IMAGE imgCards[ZHI_WU_COUNT];

void gameInit() {
loadimage(&imgBar, "res/bar.png");

//初始化植物卡牌
char name[64];
for (int i = 0; i < ZHI_WU_COUNT; i++) {
//生成植物卡牌的文件名
sprintf_s(name, sizeof(name), "res/Cards/card_%d.png", i + 1);
loadimage(&imgCards[i], name);
}
}

void updateWindow() {
//putimage(250, 0, &imgBar); //有黑边
putimagePNG(250, 0, &imgBar);

for (int i = 0; i < ZHI_WU_COUNT; i++) {
int x = 338 + i * 65;
int y = 6;
putimage(x, y, &imgCards[i]);
}
}

实现植物的选择和拖动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
IMAGE* imgZhiWu[ZHI_WU_COUNT][20];

int curX, curY; //当前选中的植物,在移动过程中的位置
int curZhiWu; //0:没有选中,1:选择了第一种植物 etc.

bool fileExist(const char* name) {
FILE* fp = fopen(name, "r");
if (fp == NULL) {
return false;
}
else {
fclose(fp);
return true;
}
}

void gameInit() {
memset(imgZhiWu, 0, sizeof(imgZhiWu));

//初始化植物卡牌
char name[64];
for (int i = 0; i < ZHI_WU_COUNT; i++) {
for (int j = 0; j < 20; j++) {
sprintf_s(name, sizeof(name), "res/zhiwu/%d/%d.png", i, j + 1);
//先判断这个文件是否存在
if (fileExist(name)) {
imgZhiWu[i][j] = new IMAGE;
loadimage(imgZhiWu[i][j], name);
}
else {
break;
}
}
}

curZhiWu = 0;
//创建游戏的图形窗口
initgraph(WIN_WIDTH, WIN_HEIGHT, 1); //加一个参数1,保留后台界面,方便调试
}

void updateWindow() {
BeginBatchDraw(); //开始缓冲(先打印到内存中)

//渲染 拖动过程中的植物
if (curZhiWu > 0) {
IMAGE* img = imgZhiWu[curZhiWu - 1][0];
putimagePNG(curX - img->getwidth() / 2, curY - img->getheight() / 2, img);
}

EndBatchDraw(); //结束双缓冲(再一次性打印到窗口)
}
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; //选择卡牌序号
printf("%d\n", index);
status = 1; //可以拖动
curZhiWu = index + 1;
}
}
else if (msg.message == WM_MOUSEMOVE && status == 1) { //鼠标移动
curX = msg.x;
curY = msg.y;

}
else if (msg.message == WM_LBUTTONUP) { //左键抬起

}
}

}
int main(void) {
gameInit();

while (1) {
userClick();

updateWindow();
}

system("pause");
return 0;
}

实现植物的种植

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
struct zhiwu {
int type; //0:没有选中,1:第一种植物 etc.
int frameIndex; //序列帧的序号
};

struct zhiwu map[3][9];

void gameInit() {
memset(map, 0, sizeof(map));
}

void updateWindow() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 9; j++) {
if (map[i][j].type > 0) {
int x = 256 + j * 81;
int y = 179 + i * 102 + 14;
int zhiWuType = map[i][j].type - 1;
int index = map[i][j].frameIndex;
putimagePNG(x, y, imgZhiWu[zhiWuType][index]);
}
}
}
}
void userClick() {
if (peekmessage(&msg)) {
else if (msg.message == WM_LBUTTONUP) { //左键抬起
if (msg.x > 256 && msg.y > 179 && msg.y < 489) {
int row = (msg.y - 179) / 102;
int col = (msg.x - 256) / 81;
//printf("%d, %d\n", row, col);
if (map[row][col].type == 0) {
map[row][col].type = curZhiWu;
map[row][col].frameIndex = 0;
}
}
curZhiWu = 0;
status = 0;
}
}
}

实现植物的摇摆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//更新游戏相关数据
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++;
int zhiWuType = map[i][j].type - 1;
int index = map[i][j].frameIndex;
if (imgZhiWu[zhiWuType][index] == NULL) {
map[i][j].frameIndex = 0;
}
}
}
}
}

int main(void) {
gameInit();
while (1) {
userClick();

updateWindow();
updateGame(); //更新游戏相关数据

Sleep(10);//帧等待
}

system("pause");
return 0;
}

优化游戏循环和游戏渲染循序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void updateWindow() {
BeginBatchDraw(); //开始缓冲

//渲染 拖动过程中的植物(放到了最后)
if (curZhiWu > 0) {
IMAGE* img = imgZhiWu[curZhiWu - 1][0];
putimagePNG(curX - img->getwidth() / 2, curY - img->getheight() / 2, img);
}

EndBatchDraw(); //结束双缓冲
}

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

if (flag) {
flag = false;
updateWindow();
updateGame();
}
}

system("pause");
return 0;
}

制作启动菜单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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();
}
}

int main(void) {
gameInit();

startUI();

int timer = 0;
bool flag = true;
while (1) {
userClick();
timer += getDelay();
if (timer > 20) {
flag = true;
timer = 0;
}

if (flag) {
flag = false;
updateWindow();
updateGame();
}

Sleep(10);
}

system("pause");
return 0;
}

创建随机阳光

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include<time.h>

struct sunshineBall {
int x, y; //阳光球在飘落过程中的坐标位置(x不变)
int frameIndex; //当前显示的图片帧的序号
int destY; //飘落的目标位置的y坐标
bool used; //是否在使用
};

//阳光池
struct sunshineBall balls[10];
IMAGE imgSunshineBall[29];


void 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);
}

//配置随机种子
srand(time(NULL));
}

void creatSunshine() {
static int count = 0;
static int fre = 400;
count++;
if (count >= fre) { //每fre帧创建一个阳光,fre随机改变
fre = 200 + rand() % 200;
count = 0;

//从阳光池中取一个可以使用的
int ballMax = sizeof(balls) / sizeof(balls[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() % (900 - 260);
balls[i].y = 60;
balls[i].destY = 200 + (rand() % 4) * 90;
}
}

void updateGame() {
creatSunshine();
}

显示随机阳光

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
struct sunshineBall {
int x, y; //阳光球在飘落过程中的坐标位置(x不变)
int frameIndex; //当前显示的图片帧的序号
int destY; //飘落的目标位置的y坐标
bool used; //是否在使用
int timer; //计时器
};

void updateWindow() {
int ballMax = sizeof(balls) / sizeof(balls[0]);
for (int i = 0; i < ballMax; i++) {
if (balls[i].used) {
IMAGE* img = &imgSunshineBall[balls[i].frameIndex];
putimagePNG(balls[i].x, balls[i].y, img);
}
}
}

void creatSunshine() {
if (count >= fre) {
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].used = false;
balls[i].timer++;
if (balls[i].timer > 100) { //掉到地面100帧之后
balls[i].used = false;
}
}
}
}
}

void updateGame() {
creatSunshine(); //创建阳光
updateSunshine(); //更新阳光的状态
}

收集阳光、显示阳光值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib")

int sunshine; //总阳光值

void gameInit() {
sunshine = 150;

//设置字体
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); //字体颜色
}

void updateWindow() {
char scoreText[8];
sprintf_s(scoreText, sizeof(scoreText), "%d", sunshine);
outtextxy(276, 67, scoreText); //在指定位置输出文本--分数
}

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); //添加音效
}
}
}
}

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) {

}
else {
collectSunshine(&msg);
}
}

}
}