C++实战 -大鱼吃小鱼
这是进入大学本科之后完成的第一个比较完整的程序,通过EasyX库使用CPP实现一个大鱼吃小鱼的游戏。
工程文件下载:
一.使用的库和IDE
EasyX库
EasyX 是针对 C++ 的图形库,可以帮助 C 语言初学者快速上手图形和游戏编程。
- EasyX库地址 :https://easyx.cn/
- 使用版本 2018春分版;
IDE:Visual Studio 2019
二.源代码
1.Struct.h
//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
//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
//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的基础语法,就开始去编写这样一个对当时的我来说“十分不可思议”的“大程序”。包括要去学习一个新的库的用法,程序的组织……
其实一开始想去尝试使用类来编写这个程序,但是编写的过程中发现类的继承之类的知识都还没有掌握,基础不牢,地动山摇,所以就还是采用了面向过程的编程方式。
总而言之,感觉最终的程序虽然做出来了,但是组织程序的过程还是挺凌乱的,有些“不知道怎么了就做出来了”的感觉。谨以此文留作纪念。