基于 EasyX 库的大鱼吃小鱼游戏

        这是进入大学本科之后完成的第一个比较完整的程序,通过EasyX库使用CPP实现一个大鱼吃小鱼的游戏。
 
工程文件下载: https://github.com/lgc0208/BigFishEatSmallFish
 

一.使用的库和IDE

EasyX库

        EasyX 是针对 C++ 的图形库,可以帮助 C 语言初学者快速上手图形和游戏编程。

  1. EasyX库地址https://easyx.cn/

  2. 使用版本 2018春分版;

IDE:Visual Studio 2019

二.源代码

1.Struct.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Struct.h

struct player
{
int x;
int y;
bool live = true;
int size = 1;
int score = 0;
};

struct fish
{
int x;
int y;
int size;
bool live = true;
};

2.Functions.h

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//Functions.h
#ifndef _Functions_H
#define _Functions_H

#include <easyx.h>
#include <conio.h>
#include <time.h>
#include <iostream> //头文件
#pragma comment(lib,"winmm.lib") //引用静态库
#include"Struct.h"
using namespace std;
/*************************************全局变量****************************************************/
const short ScreenWidth = 480;
const short ScreenHeight = 640;
short ret = 0;
int ToBig = 10;
/**************NPC鱼的数量****************/
const int numB = 1;//大鱼数量
const int numL = 2;//小鱼数量
const int numS = 3;//虾米数量
IMAGE Fish[3];//鱼的图片
IMAGE lplayerfish[2];//玩家鱼的图片
IMAGE rplayerfish[2];//玩家鱼的图片
IMAGE background;//背景
short Formerx = 0;//用于转身
short Formery = 0;//用于转身
const int N = 50;//图片大小

//加载图片
extern void loadImages()
{
loadimage(&Fish[2], L"bigfish.jpg", 2 * N, 2 * N);
loadimage(&Fish[1], L"littlefish.jpg", N, N);
loadimage(&Fish[0], L"shrimp.jpg", N, N);
loadimage(&background, L"background.jpg", ScreenHeight, ScreenWidth);
loadimage(&lplayerfish[0], L"lplayerlittlefish.jpg", N, N);
loadimage(&lplayerfish[1], L"lplayerbigfish.jpg", 2 * N, 2 * N);
loadimage(&rplayerfish[0], L"rplayerlittlefish.jpg", N, N);
loadimage(&rplayerfish[1], L"rplayerbigfish.jpg", 2 * N, 2 * N);
}

//制作掩码图
extern void changeImage(IMAGE imagePut, int x, int y)
{
IMAGE image(N, N); //存储掩码
DWORD* pmemflower = GetImageBuffer(&imagePut);
DWORD* image1 = GetImageBuffer(&image);
DWORD* fish1 = GetImageBuffer(&imagePut);
COLORREF backcolor = pmemflower[0];
for (int i = 0; i < N * N; i++)
{
if (pmemflower[i] >= backcolor)
image1[i] = WHITE;
else
image1[i] = BLACK;
}
for (int i = 0; i < N * N; i++)
{
if (pmemflower[i] >= backcolor)
fish1[i] = BLACK;
}
putimage(x, y, &image, SRCAND);
putimage(x, y, &imagePut, SRCPAINT);
}

//制作特定规格掩码图
extern void changeImage2(IMAGE imagePut, int x, int y)
{
IMAGE image(100, 100); //存储掩码
DWORD* pmemflower = GetImageBuffer(&imagePut);
DWORD* image1 = GetImageBuffer(&image);
DWORD* fish1 = GetImageBuffer(&imagePut);
COLORREF backcolor = pmemflower[0];
for (int i = 0; i < 100 * 100; i++)
{
if (pmemflower[i] >= backcolor)
image1[i] = WHITE;
else
image1[i] = BLACK;
}
for (int i = 0; i < 100 * 100; i++)
{
if (pmemflower[i] >= backcolor)
fish1[i] = BLACK;
}
putimage(x, y, &image, SRCAND);
putimage(x, y, &imagePut, SRCPAINT);
}

//玩家移动
extern void move(player* player2)
{
if (GetAsyncKeyState(65) & 0x8000) player2->x -= 10; // 左边
if (GetAsyncKeyState(87) & 0x8000) player2->y -= 10; // 上面
if (GetAsyncKeyState(83) & 0x8000) player2->y += 10; // 下面
if (GetAsyncKeyState(68) & 0x8000) player2->x += 10; // 右边
}

//NPC移动
extern void movefish(fish* fish1)
{
fish1->x += (rand() % 21) - 10;
fish1->y += (rand() & 21) - 10;
}

//形态升级
extern void biggerplayer(player* player2)
{
if (player2->score > ToBig)
player2->size = 2;
}

//NPC重生
extern void reborn(fish* fish1)
{
fish1->x = rand() % (ScreenHeight - 50);
fish1->y = rand() % (ScreenWidth - 50);
}

//判断玩家是否死亡
extern void ifDead(fish* fish1, player* player2)
{
if (fish1->size < player2->size)
{
player2->score++;
biggerplayer(player2);
reborn(fish1);
}
else if (fish1->size >= player2->size)
{
player2->live = false;
}
}

//判断玩家与NPC是否相遇
extern void iftouch(fish* fish1, player* player2)
{
if (((abs(fish1->x - player2->x) < N)) && (abs(fish1->y - player2->y) < N))//距离小于N,则相遇
ifDead(fish1, player2);
}

extern void iftouch2(fish* fish1, player* player2)
{
if (((abs(fish1->x - player2->x) < N)) && (abs(fish1->y - player2->y) < N))//距离小于N,则相遇
{
player2->score++;
biggerplayer(player2);
reborn(fish1);
}
}

//判断鱼是否出界
extern void out(fish* fish1)
{
if ((fish1->x < 0) || (fish1->x > (ScreenHeight - 50)))
{
reborn(fish1);
}
if ((fish1->y < 0) || (fish1->y > (ScreenWidth - 50)))
{
reborn(fish1);
}
}

//判断玩家是否出界
extern void playerout(player* player2)
{
if ((player2->x < 0) || (player2->x > ScreenHeight - 50))
player2->live = false;
if ((player2->y < 0) || (player2->y > ScreenWidth - 50))
player2->live = false;
}

//打印计分板
extern void ScorePrint(player* player2)
{
setbkmode(TRANSPARENT); //设置字体背景色为透明
settextcolor(BLACK); //设置字体颜色为红色
TCHAR str[32] = { 0 };
_stprintf_s(str, _T("击杀:%d"), player2->score);
outtextxy(5, 5, str);
_stprintf_s(str, _T("形态:%d"), player2->size);
outtextxy(5, 25, str);
}

#endif // !_Functions_H

3.main.cpp

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//main.cpp
#include"Functions.h"

int main()
{
player player1;
fish bigfish[numB];
fish littlefish[numL];
fish shrimp[numS];
srand(unsigned(time(0)));
//玩家初始位置
player* player2 = &player1;
player2->x = rand() % (ScreenHeight - 50);
player2->y = rand() % (ScreenWidth - 50);
//鱼初始位置
fish* shrimp1[15];//shrimp指针数组
for (int i = 0; i < numS; i++)
{
shrimp1[i] = &shrimp[i];
shrimp1[i]->x = rand() % (ScreenHeight - 50);
shrimp1[i]->y = rand() % (ScreenWidth - 50);
shrimp1[i]->size = 0;
}
fish* littlefish1[numL];//littlefish指针数组
for (int i = 0; i < numL; i++)
{
littlefish1[i] = &littlefish[i];
littlefish1[i]->x = rand() % (ScreenHeight - 50);
littlefish1[i]->y = rand() % (ScreenWidth - 50);
littlefish1[i]->size = 1;

}
fish* bigfish1[numB];
for (int i = 0; i < numB; i++)
{
bigfish1[i] = &bigfish[i];
bigfish1[i]->x = rand() % (ScreenHeight - 50);
bigfish1[i]->y = rand() % (ScreenWidth - 50);
bigfish1[i]->size = 2;

}

loadImages();//加载图片
initgraph(ScreenHeight, ScreenWidth);//加载窗口界面
putimage(0, 0, &background);//放置窗口背景板
mciSendString(L"open music.mp3 ", 0, NULL, NULL);//将音乐文件放在与源文件同一目录下,alias music的意思就是将这个音乐文件重新命名为music
mciSendString(L"play music.mp3 repeat", NULL, NULL, NULL);//播放音乐,play意为播放,同样可换做pause:暂停,stop:停止,close:关闭,Resume:继续。Repeat意为重复播放。
////mciSendstring(“stop music repeat”, NULL, NULL, NULL);//暂停播放音乐。
setbkmode(TRANSPARENT); //设置字体背景色为透明
settextcolor(BLACK); //设置字体颜色为红色
/*****************游戏规则*****************/
outtextxy(290, 200, L"游戏规则:");
outtextxy(290, 230, L"玩家操作鱼吃比自己等级低的鱼");
outtextxy(290, 260, L"遇到比自己等级高或同等级的鱼即死亡");
outtextxy(290, 290, L"出界即死亡");
outtextxy(290, 320, L"按回车键开始");
outtextxy(290, 350, L"按P键暂停");
outtextxy(290, 380, L"按ESC键自毁");
outtextxy(290, 410, L"持续按C开启无敌模式");
getchar();//使得开始界面静止,当用户按下Enter键时继续执行指令
//char s[] = "blacksheepwall";

//程序运行
do
{

putimage(0, 0, &background);//放置游戏界面背景板
ScorePrint(player2);//打印计分板

//如果用户没有按C键,按此模式运行
if (!(GetAsyncKeyState('C') & 0x8000))
{
//打印NPC-BOSS鱼
for (int i = 0; i < numS; i++)
{
movefish(shrimp1[i]);//NPC移动函数
changeImage(Fish[0], shrimp1[i]->x, shrimp1[i]->y);//制作掩码图
iftouch(shrimp1[i], player2);//判断玩家与NPC是否相遇
out(shrimp1[i]);//判断鱼是否出界
}
//打印NPC-小鱼
for (int i = 0; i < numL; i++)
{
movefish(littlefish1[i]);
changeImage(Fish[1], littlefish1[i]->x, littlefish1[i]->y);
iftouch(littlefish1[i], player2);
out(littlefish1[i]);
}
//打印NPC-大鱼
for (int i = 0; i < numB; i++)
{
movefish(bigfish1[i]);
changeImage2(Fish[2], bigfish1[i]->x, bigfish1[i]->y);
iftouch(bigfish1[i], player2);
out(bigfish1[i]);
}
}
//若用户按下C键,进入无敌模式
else
{
setbkmode(TRANSPARENT); //设置字体背景色为透明
settextcolor(BLACK); //设置字体颜色为红色
TCHAR str[32] = { 0 };
_stprintf_s(str, _T("已开始作弊模式"));//提示操作者已进入无敌模式
outtextxy(5, 45, str);
for (int i = 0; i < numS; i++)
{
movefish(shrimp1[i]);
changeImage(Fish[0], shrimp1[i]->x, shrimp1[i]->y);
iftouch2(shrimp1[i], player2);//若接触,NPC必死
out(shrimp1[i]);
}
for (int i = 0; i < numL; i++)
{
movefish(littlefish1[i]);
changeImage(Fish[1], littlefish1[i]->x, littlefish1[i]->y);
iftouch2(littlefish1[i], player2);//若接触,NPC必死
out(littlefish1[i]);
}
for (int i = 0; i < numB; i++)
{
movefish(bigfish1[i]);
changeImage2(Fish[2], bigfish1[i]->x, bigfish1[i]->y);
iftouch2(bigfish1[i], player2);//若接触,NPC必死
out(bigfish1[i]);
}
}

//玩家的移动
move(player2);
if (Formerx > player2->x)//是否转弯
{
if (player2->size == 1)
changeImage(lplayerfish[player2->size - 1], player2->x, player2->y);
else changeImage2(lplayerfish[player2->size - 1], player2->x, player2->y);
}
else
{
if (player2->size == 1)
changeImage(rplayerfish[player2->size - 1], player2->x, player2->y);
else changeImage2(rplayerfish[1], player2->x, player2->y);
}
Formerx = player2->x;

playerout(player2);
if (GetAsyncKeyState('P') & 0x8000)//若按下P,则暂停
ret = _getwch();
Sleep(40);//帧率
} while (player2->live == true && !(GetAsyncKeyState(VK_ESCAPE) & 0x8000));//在没有按下ESC或者玩家未死亡时,持续执行

//设置结束页面
putimage(0, 0, &background);
setbkmode(TRANSPARENT); //设置字体背景色为透明
settextcolor(BLACK); //设置字体颜色为红色
outtextxy(290, 290, L"游戏结束:");
outtextxy(290, 320, L"按任意键退出:");
while (_kbhit())//键盘敲击,接受到任意键后退出
ret = _getwch();
ret = _getwch();
closegraph();
return 0;
}


三.后记

        当初写这个代码前前后后约摸花了一周半的时间,主要的难点在于刚接触完CPP的基础语法,就开始去编写这样一个对当时的我来说“十分不可思议”的“大程序”。包括要去学习一个新的库的用法,程序的组织……

        其实一开始想去尝试使用类来编写这个程序,但是编写的过程中发现类的继承之类的知识都还没有掌握,==基础不牢,地动山摇==,所以就还是采用了面向过程的编程方式。

        总而言之,感觉最终的程序虽然做出来了,但是组织程序的过程还是挺凌乱的,有些“不知道怎么了就做出来了”的感觉。谨以此文留作纪念。

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2024 lgc0208@foxmail.com
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信