文章目录
说明
此设计可以作为C语言课程设计,主要用的是easyx图形库;
在VS2019成功运行,注释比较全面,对没有C语言基础的小白也很友好;
若有些地方还可以完善,请指正。
设计游戏的思路主要从以下几个当面:
1.游戏图片的保存及绘制;其中主要包括玩家、子弹、敌机图片;
2.封装的函数包括玩家的移动、子弹的创建、
子弹的移动、敌机的创建、敌机的移动;
3.使用的逻辑主要有边界的处理,子弹击中敌机;
4.在主函数运行所有的功能函数
下面开始介绍主要功能函数吧
枚举出设计中所需要的常量
enum TYPE
{
WIDTH = 480, //窗口的宽度
HEIGHT = 600, //窗口的高度
PLAYERHEIGHT = 60, //玩家飞机的高度
PLAYYERWIDTH = 90, //玩家飞机的宽度
BULLET_NUM = 15, //玩家子弹的数量
ENEMY_NUM=6, //敌机的数量
BIG,
SMALL,
ENEMY_1_WIDTH=70, //Boss的宽度
ENEMY_1_HEIGHT=78, //Boss的高度
ENEMY_2_WIDTH=60,
ENEMY_2_HEIGHT=60
};
创建结构体,其中包括的成员有玩家、敌机、玩家
struct Plane
{
int x;
int y;
bool live;
int width;
int hegiht;
int hp; //敌机的血量
int type; //敌机的类别
}player,bullet[BULLET_NUM],enemy[ENEMY_NUM];
主要的功能函数
图片加载
void LoadIm()
游戏绘制
void gameDraw();
//游戏初始化
void initGanme();
数据的创建
void creatEnemy();
/*这里创建的敌机判断敌机的类型及生命状态*/
void creatBullet();
/*子弹是通过获取键盘信息创建的*/
逻辑处理
1.void playerMove();
/*主要的对玩家的坐标处理:
*左边界坐标要大于玩家宽度的1/2,
*右边界的坐标要小于主界面的宽度减去玩家宽度的1/2;
*上边界的坐标要大于玩家的高度;
*下边界的坐标应该小于主界面的高度减去玩家高度1/2;
*/
2.void playPlane();
/*这里的逻辑是子弹的坐标应该在敌机区域内
*主要实现的代码如下
*/
if (bullet[k].x > enemy[i].x
&& bullet[k].x<enemy[i].x + enemy[i].width
&& bullet[k].y>enemy[i].y
&& bullet[k].y < enemy[i].y + enemy[i].hegiht)
{
bullet[k].live = false; //事件发生,消除子弹
enemy[i].hp--; //敌机的血量-1
}
程序所用代码(已测试)
#include<stdio.h>
#include<graphics.h>
#include<conio.h>
#include<time.h>
enum My
{
WIDTH = 480, //窗口的宽度
HEIGHT = 600, //窗口的高度
PLAYERHEIGHT = 60, //玩家飞机的高度
PLAYYERWIDTH = 90, //玩家飞机的宽度
BULLET_NUM = 15, //玩家子弹的数量
ENEMY_NUM=6, //敌机的数量
BIG,
SMALL,
ENEMY_1_WIDTH=70, //Boss的宽度
ENEMY_1_HEIGHT=78, //Boss的高度
ENEMY_2_WIDTH=60,
ENEMY_2_HEIGHT=60
};
struct Plane
{
int x;
int y;
bool live;
int width;
int hegiht;
int hp;
int type; //敌机的类别
}player,bullet[BULLET_NUM],enemy[ENEMY_NUM];
//保存背景图片
IMAGE bk;
//保存玩家图片
IMAGE img_role[2];
//保存子弹图片
IMAGE img_bullet[2];
//保存敌机图片
IMAGE img_enemy[2][2];
//保存继续
IMAGE img_continue;
//保存退出
IMAGE img_exit;
//把图片加载到程序中
void LoadIm()
{
//加载背景图片
loadimage(&bk, "./images/background.jpg");
//加载玩家图片
loadimage(&img_role[0], "./images/planeNormal_1.jpg");
loadimage(&img_role[1], "./images/planeNormal_2.jpg");
//加载子弹图片
loadimage(&img_bullet[0], "./images/bullet_1.jpg");
loadimage(&img_bullet[1], "./images/bullet_2.jpg");
//加载敌机图片
loadimage(&img_enemy[0][0], "./images/enemy_1.jpg");
loadimage(&img_enemy[0][1], "./images/enemy_2.jpg");
loadimage(&img_enemy[1][0], "./images/enemy_3.jpg");
loadimage(&img_enemy[1][1], "./images/enemy_4.jpg");
//加载游戏结束图片
loadimage(&img_continue, "./images/continue.jpg");
//加载退出图片
loadimage(&img_exit, "./images/exit.jpg");
}
//敌机的类型
void enemyHp(int i)
{
int flag = rand() % 10;
if (flag>=0&&flag<=2) //0~9 这里是有3/10的几率生成大飞机
{
enemy[i].type = BIG;
enemy[i].hp = 3;
enemy[i].width = ENEMY_1_WIDTH;
enemy[i].hegiht = ENEMY_1_HEIGHT;
}
else
{
enemy[i].type = SMALL;
enemy[i].hp = 1;
enemy[i].width = ENEMY_2_WIDTH;
enemy[i].hegiht = ENEMY_2_HEIGHT;
}
}
//初始化游戏数据
void gameInit()
{
//初始化玩家数据
player.x = WIDTH / 2 - PLAYYERWIDTH/2;
player.y = HEIGHT - PLAYERHEIGHT;
player.hp = 4;
player.live = true;
//初始化子弹
for (int i = 0; i < BULLET_NUM; i++)
{
bullet[i].x = 0;
bullet[i].y = 0;
bullet[i].live = false; //设置为false可以让子弹不在画面中展示
}
//初始化敌机
for (int i = 0; i < ENEMY_NUM; i++)
{
enemy[i].live = false;
enemyHp(i);
}
}
//游戏的绘制函数
void gameDraw()
{
LoadIm();
//贴窗口文件
putimage(0, 0, &bk);
//绘制玩家图片
if (player.live)
{
putimage(player.x, player.y, &img_role[0], NOTSRCERASE);
putimage(player.x, player.y, &img_role[1], SRCINVERT);
}
for (int i = 0; i < BULLET_NUM; i++)
{
if (bullet[i].live)
{
//绘制子弹图片
putimage(bullet[i].x, bullet[i].y, &img_bullet[0], NOTSRCERASE);
putimage(bullet[i].x, bullet[i].y, &img_bullet[1], SRCINVERT);
}
}
//绘制敌机
for (int i = 0; i < ENEMY_NUM; i++)
{
if (enemy[i].live)
{
if (enemy[i].type == SMALL)
{
putimage(enemy[i].x, enemy[i].y, &img_enemy[1][0], NOTSRCERASE);
putimage(enemy[i].x, enemy[i].y, &img_enemy[1][1], SRCINVERT);
}
else
{
putimage(enemy[i].x, enemy[i].y, &img_enemy[0][0], NOTSRCERASE);
putimage(enemy[i].x, enemy[i].y, &img_enemy[0][1], SRCINVERT);
}
}
}
//游戏结束界面
if (player.live == false)
{
putimage(40, 220, &img_continue);
putimage(290, 220, &img_exit);
}
//播放音乐
//mciSendString(TEXT("open music.mp3 alias music"), 0, 0, 0);
//mciSendString(TEXT(" play music"), 0, 0, 0);
}
//定时器
bool Timer(int ms, int id)
{
static DWORD t[10];
if (clock()-t[id]>ms)
{
t[id] = clock();
return true;
}
return false;
}
//创建子弹
void creatBullet()
{
for (int i = 0; i < BULLET_NUM; i++)
{
if (!bullet[i].live)
{
bullet[i].x = player.x + 35;
bullet[i].y = player.y;
bullet[i].live = true;
break;
}
}
}
//敌机的移动
void enemyMove(int speed)
{
for (int i = 0; i < ENEMY_NUM; i++)
{
if (enemy[i].live)
{
enemy[i].y += speed;
if (enemy[i].y > HEIGHT)
{
enemy[i].live = false;
}
}
}
}
//产生敌机
void creatEnemy()
{
for (int i = 0; i < ENEMY_NUM; i++)
{
if (!enemy[i].live)
{
enemy[i].live = true;
enemy[i].x = rand() % (WIDTH - ENEMY_1_WIDTH/2);
enemy[i].y = 0;
enemyHp(i);
break;
}
}
enemyMove(3);
}
//玩家移动,判断键盘消息
void playerMove(int speed)
{
#if 0
//如果有键盘按下,则返回真
if (_kbhit)
{
//1.方式一 _getch() 阻塞函数,和scanf一样,如果没有输入,就会卡在主程序,一直等待输入,且不是标准C的函数
char key = _getch();
switch (key)
{
case 'w':
case 'W':
player.y -= speed;
break;
case 's':
case 'S':
player.y += speed;
break;
case 'a':
case 'A':
player.x -= speed;
break;
case 'd':
case 'D':
player.x += speed;
break;
default:
break;
}
}
#elif 1
//2.使用Windows函数获取键盘的输入 GetAsyncKeyState 非阻塞函数
//如果用字母,可以用大写;则键盘的大小写都可以被检测到
if (GetAsyncKeyState(VK_UP)||GetAsyncKeyState('W'))
{
if (player.y > 0-PLAYERHEIGHT/2)
{
player.y -= speed;
}
printf("(%d,%d),Live:%d,Hp:%d\n", player.x, player.y, player.live, player.hp);
}
if (GetAsyncKeyState(VK_DOWN)||GetAsyncKeyState('S'))
{
if (player.y < HEIGHT - PLAYERHEIGHT/2)
{
player.y += speed;
}
printf("(%d,%d),Live:%d,Hp:%d\n", player.x, player.y, player.live, player.hp);
}
if (GetAsyncKeyState(VK_LEFT)||GetAsyncKeyState('A'))
{
if (player.x > 0-PLAYYERWIDTH/2)
{
player.x -= speed;
}
printf("(%d,%d),Live:%d,Hp:%d\n", player.x, player.y, player.live, player.hp);
}
if (GetAsyncKeyState(VK_RIGHT)||GetAsyncKeyState('D'))
{
if (player.x < WIDTH - PLAYYERWIDTH/2)
{
player.x += speed;
}
printf("(%d,%d),Live:%d,Hp:%d\n", player.x, player.y, player.live, player.hp);
}
#endif // 0
if (GetAsyncKeyState(VK_SPACE)&& Timer(200, 1) ||GetAsyncKeyState('J')&&Timer(200,1)) //延时时间,毫秒
{
if (player.live)
{
creatBullet();
}
}
}
//子弹的移动
void bulletMove()
{
for (int i = 0; i < BULLET_NUM; i++)
{
if (bullet[i].live)
{
bullet[i].y -= 2;
//bullet[i].x -= 1;
if (bullet[i].y < 0)
{
bullet[i].live = false;
}
}
}
}
//击中飞机,碰撞算法
void playPlane()
{
for (int i = 0; i < ENEMY_NUM; i++)
{
if (!enemy[i].live)
{
continue;
}
for (int k = 0; k < BULLET_NUM; k++)
{
if (!bullet[i].live)
{
continue;
}
if (bullet[k].x > enemy[i].x && bullet[k].x<enemy[i].x + enemy[i].width
&& bullet[k].y>enemy[i].y && bullet[k].y < enemy[i].y + enemy[i].hegiht)
{
bullet[k].live = false;
enemy[i].hp--;
}
}
if (enemy[i].hp<=0)
{
enemy[i].live = false;
}
}
}
int main()
{
//创建一个窗口
initgraph(WIDTH, HEIGHT,SHOWCONSOLE);
gameInit();
//双缓冲绘图
BeginBatchDraw();
while (1)
{
gameDraw();
FlushBatchDraw();
playerMove(5);
bulletMove();
if (Timer(100,0))
{
creatEnemy();
}
if (Timer(200, 2))
{
enemyMove(2);
}
playPlane();
}
EndBatchDraw();
return 0;
}
程序运行截图
问题处理
图片
1.在项目里面创建images文件夹;
2.加入原图和掩码图,黑白的为掩码图;
字符集
打开-“项目”-“属性”,按照上图将“字符集”改成上图模式然后可以运行了;