今天写一个非常有意思的游戏,我记得小时候玩过一款fc游戏叫《兵蜂》游戏的玩法是控制一架飞机不断的发射子弹攻击敌人,非常好玩。那现在就用c控制台来模拟一下这个游戏。先上效果图:
击败一个敌人可以加1分,身体碰到敌人生命数量减1
吃到S buff可用子弹加1,吃到L buff 生命值加1 ,当生命值为0的时候游戏结束
要注意你的子弹可以摧毁buff
吃到F buff可以发动杀招 获得10秒火力全开时间,这个期间子弹无限而且是3个倍火力,
要注意buff失效后要还清欠下的子弹才能继续发射
主玩法程序开始会显示
DEMO:
game_1.7.exe.zip
下面开始详解代码:
(代码写的不怎么样 还有很多地方需要优化 欢迎指教)
#include
#include "windows.h"
#include
#include
#define random(x) (rand()%x) //定义随机数范围
//定义windows控制台可以显示的颜色
#define blue 0x01
#define green 0x02
#define red 0x04
#define hig 0x08
#define yellow red|green
#define lightblue blue|green
#define purple red|blue
#define white blue|green|red
//键盘对于的键值
//4个方向键
#define dd 80
#define ll 75
#define rr 77
#define uu 72
//esc与空格
#define esc 27
#define space 32
//定义初始数据
#define player_life 5 //玩家生命5
#define enemy_max 5 //敌人数量,为什么要用max?因为我写的是判断地图中敌人的数量是否大于这个值,下面同理
#define enemy_speed 300 //敌人移动速度 ms
#define bullet_max 2
#define bullet_speed 100
#define buff_max 20
#define buff_int 3
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); //获取Windows控制台资源句柄
CONSOLE_CURSOR_INFO CursorInfo; //设置控制台用的
struct
{
int max; //最大数量
int number = 0; //当前数量
char y[50]; //在地图上的哪一个位置
char x[50];
int speed; //移动速度
}enemy,bullet; //定义给敌人与子弹的结构体
struct
{
char y = 15; //玩家在地图的位置
char x = 10;
int life = player_life; //玩家生命值
char buff[buff_max] = { 0 }; //玩家身上的buff
long buff_time[buff_max] = { 10000 }; //玩家身上buff的时间 使用时需要加上系统运行时间所以要用long类型
}player;
struct
{
int number = 0; //当前数量
char y[buff_max] = { 0 }; //在地图上的位置
char x[buff_max] = { 0 };
char z[buff_max] = { 0 }; //类型 4火力全开 5增加生命 6增加子弹数量
char chance[buff_max] = { 0 };
int speed = 500; //移动速度
int add_time = 5000; //每隔n/ms刷一次buff
}buffer; //buff结构体
char map[20][20]; //地图缓存 0空 1#(敌人) 2*(玩家) 3|子弹 现有的3种buff是456
//这种结构的好处是可以自己随意添加buff种类不需要改太多东西
void set_colo(char colo)
{
SetConsoleTextAttribute(hOut, colo | hig); //通过上面获得的控制台资源句柄设置控制台的字符的颜色
}
void colo_print(char colo, const char* p) //可以打印不同颜色的字符串
{
SetConsoleTextAttribute(hOut, colo | hig);
printf(p);
SetConsoleTextAttribute(hOut, white | hig);
}
void set_y_x(int y, int x) //设置光标位置
{
COORD pos = { x,y };
SetConsoleCursorPosition(hOut, pos);
}
void y_x_colo_print(int y, int x, char colo, const char* p) //可以在设置的坐标内打印彩色字符串
{
set_y_x(y, x);
colo_print(colo, p);
}
//在程序中我们只是操作结构体中的变量所以我们需要把结构体中的数据对应的写进map[20][20]
//例如变量中enemy是这样的:
//enemy.number=3;
//enemy.y={0,1,2};
//enemy.x={0,1,2};
//那么它们经过下面的函数以后在map[20][20]中就是这样的:
//map[20][20]=
//{
// {1,0,0,0,},
// {0,1,0,0,},
// {0,0,1,0,},
//}
void buff_in_map()
{
char y, x, a;
for (y = 0; y < 20; y++) //先清空buff的数据
{
for (x = 0; x < 20; x++)
{
map[y][x] = 0;
}
}
for (a = 0; a < buffer.number; a++) //把buff对应的位置写进map
{
map[buffer.y[a]][buffer.x[a]] = buffer.z[a];//z是类型
}
for (a = 0; a < enemy.number; a++) //然后就是敌人
{
map[enemy.y[a]][enemy.x[a]] = 1;
}
for (a = 0; a < bullet.number; a++) //子弹
{
map[bullet.y[a]][bullet.x[a]] = 3;
}
map[player.y][player.x] = 2; //为了防止玩家一直站在一个地方按空格,玩家是一个三角形 两边是有碰撞体积的
map[player.y + 1][player.x - 1] = 2;
map[player.y + 1][player.x + 1] = 2;
}
void dis_map() //将map的数据打印出来,这条函数和上面的函数放一起循环调用就可以了
{
char y, x;
for (y = 0; y < 20; y++)
{
for (x = 0; x < 20; x++)
{
switch (map[y][x])
{
case 0:
y_x_colo_print(y, x, white, " ");
break;
case 1:
y_x_colo_print(y, x, red, "#");
break;
case 2:
y_x_colo_print(y, x, green, "*");
break;
case 3:
y_x_colo_print(y, x, lightblue, "|");
break;
case 4:
y_x_colo_print(y, x, lightblue, "F");
break;
case 5:
y_x_colo_print(y, x, green, "L");
break;
case 6:
y_x_colo_print(y, x, lightblue, "S");
break;
default:
break;
}
}
}
}
char get_key() //获得按键键值
{
char a;
if (_kbhit() == 1) //由于单使用 _getch()会导致程序堵塞,所以用先用_kbhit()获得是否有按键按下
{
a = _getch();
return a;
}
return 0;
}
/**************************************************上面是驱动部分,下面就是业务逻辑**/
char state = 0; //状态机的第n个状态 应该用nume会更好
char ch; //保存按键键值用的
int fs = 0, lv_fs = 0; //分数与等级(起名鬼才)
void move_player() //控制玩家的移动
{
switch (ch)
{
case uu:
player.y = (player.y == 0) ? 0 : player.y - 1; //边界限制
break;
case dd:
player.y = (player.y == 18) ? 18 : player.y + 1;//由于玩家是个三角形所以 不能走到19 不然数组会溢出
break;
case ll:
player.x = (player.x == 1) ? 1 : player.x - 1;
break;
case rr:
player.x = (player.x == 18) ? 18 : player.x + 1;
break;
default:
break;
}
}
void fire() //发射
{
if (ch == space) //如果空格被按下
{
if ((bullet.number < bullet.max || player.buff[0] == 1) && bullet.number < 48) //如果子弹数量小于最大子弹数量或者正处于火力全开状态
{ //无限火力状态并不是无限子弹,因为数组长度只有50,因为是三连发 所以最大不能超过48
bullet.y[bullet.number] = player.y; //在角色当前位置放置一颗子弹
bullet.x[bullet.number] = player.x;
bullet.number += 1; //每放一颗 当前数量加1
if (player.buff[0] == 1) //如果是火力全开状态,两边也放置子弹
{
bullet.y[bullet.number] = player.y + 1;
bullet.x[bullet.number] = player.x - 1;
bullet.number += 1;
bullet.y[bullet.number] = player.y + 1;
bullet.x[bullet.number] = player.x + 1;
bullet.number += 1;
}
}
}
}
char hit(int y, int x, char mod)//干掉这个位置的敌人成功返回1,如果mod为1会加分数 用于区分是子弹碰撞还是玩家身体碰撞
{
char a, b;
for (a = 0; a < enemy.number; a++)
{
if (enemy.y[a] == y && enemy.x[a] == x)
{
for (b = a; b < enemy.number - 1; b++)
{
enemy.y[b] = enemy.y[b + 1];
enemy.x[b] = enemy.x[b + 1];
}
enemy.number -= 1;
if (mod == 1)
{
fs += 1;
lv_fs += 1;
}
return 1;
}
}
return 0;
}
char hit_buff(int y, int x, char mod)//干掉这个位置的buff成功返回1,如果mod为1会获得这个buff 用于区分是子弹碰撞还是玩家身体碰撞
{
char a, b;
for (a = 0; a < buffer.number; a++)
{
if (buffer.y[a] == y && buffer.x[a] == x)
{
if (mod == 1)
{
if (buffer.z[a] == 4)
{
player.buff[0] = 1;
player.buff_time[0] = clock() + 10000;
}
if (buffer.z[a] == 5)
{
player.life += 1;
}
if (buffer.z[a] == 6)
{
bullet.max += 1;
//bullet.speed-=20;
}
}
for (b = a; b < buffer.number - 1; b++)
{
buffer.y[b] = buffer.y[b + 1];
buffer.x[b] = buffer.x[b + 1];
buffer.z[b] = buffer.z[b + 1];
}
buffer.number -= 1;
return 1;
}
}
return 0;
}
char jc_e(int y, int x)//检查这个位置是否有敌人 生成敌人的时候放置同一个地方产生多个敌人
{
char a;
for (a = 0; a < enemy.number; a++)
{
if (enemy.y[a] == y && enemy.x[a] == x)
{
return 1;
}
}
return 0;
}
long rand_buff_time = 1000;//游戏运行1s后开始随机生成buff
void rand_buff()
{
char c, y, x;
if (clock() > rand_buff_time) //如果系统时间大于这个变量说明时间过了buffer.add_time ms
{
rand_buff_time = clock() + buffer.add_time; //这个变量重新赋值 系统时间加上等待时间,这样就可以延时一段时间 而且不会阻塞程序
c = random(buff_int + 1); //随机获得一个范围的数,目前只有3种buff,+1是为了让0也算一种,c等于0不产生任何buff
do
{
x = random(20);
y = random(2);
} while (jc_e(y, x)); //获得一个坐标而且坐标里不能有敌人
buffer.y[buffer.number] = y;
buffer.x[buffer.number] = x;
if (c == 1) //不同类型buff转义存进数组
{
buffer.z[buffer.number] = 4;
}
if (c == 2)
{
buffer.z[buffer.number] = 5;
}
if (c == 3)
{
buffer.z[buffer.number] = 6;
}
buffer.number += 1;
}
}
void rand_e() //随机产生敌人
{
int y, x;
if (enemy.number < enemy.max) //判断需不需要产生
{
do
{
x = random(20);
y = random(5);
} while (jc_e(y, x)); //获得一个没有敌人的坐标
enemy.y[enemy.number] = y; //写进数组
enemy.x[enemy.number] = x;
enemy.number += 1; //当前数量加1
}
}
long move_time, e_move_time, bu_move_time;
void move_e_b() //自动移动敌人、buff还有子弹
{
int y, x, a, b;
hit_buff(player.y, player.x, 1); //玩家的3个点都可以与buff碰撞
hit_buff(player.y + 1, player.x - 1, 1);
hit_buff(player.y + 1, player.x + 1, 1);
//玩家的3个点也都能碰到敌人 碰到生命就减1
if (hit(player.y, player.x, 0) || hit(player.y + 1, player.x - 1, 0) || hit(player.y + 1, player.x + 1, 0))
{
player.life -= 1;
if (player.life == 0)
{
state = 1;
}
}
for (a = 0; a < bullet.number;) //扫描地图上的全部子弹
{
if (hit(bullet.y[a], bullet.x[a], 1) || hit_buff(bullet.y[a], bullet.x[a], 0)) //哪颗子弹碰到buff或者敌人
{
for (b = a; b < bullet.number - 1; b++) //就让哪颗子弹消失
{
bullet.y[b] = bullet.y[b + 1];
bullet.x[b] = bullet.x[b + 1];
}
bullet.number -= 1;
}
else
{
a++;
}
}
if (clock() > move_time) //系统时间大于某个值
{
move_time = clock() + bullet.speed; //某个值重新赋值 系统时间加上子弹移动速度 等待下一次大于
for (a = 0; a < bullet.number;) //扫描所有子弹
{
bullet.y[a] -= 1; //所有子弹都向前移动一格
if (bullet.y[a] <= 0)
{
for (b = a; b < bullet.number - 1; b++) //如果有子弹跑到地图尽头就把它从数组中剔除
{
bullet.y[b] = bullet.y[b + 1];
bullet.x[b] = bullet.x[b + 1];
}
bullet.number -= 1;
}
else
{
a++;
}
}
}
if (clock() > e_move_time) //敌人的移动 与子弹的移动同理
{
e_move_time = clock() + enemy.speed;
for (a = 0; a < enemy.number;)
{
enemy.y[a] += 1;
if (enemy.y[a] >= 19)
{
hit(enemy.y[a], enemy.x[a], 0);
}
else
{
a++;
}
}
}
if (clock() > bu_move_time)
{
bu_move_time = clock() + buffer.speed;
for (a = 0; a < buffer.number;)
{
buffer.y[a] += 1;
if (buffer.y[a] >= 19)
{
hit_buff(buffer.y[a], buffer.x[a], 0);
}
else
{
a++;
}
}
}
}
void dis_keep_out()//画一个边框
{
char a;
for (a = 0; a < 20; a++)
{
y_x_colo_print(a, 20, white, "|");
}
y_x_colo_print(20, 0, white, "=====================");
}
void dis_data() //显示游戏数据
{
y_x_colo_print(19, 22, white, "得分:");
printf("%3d", fs);
y_x_colo_print(17, 22, white, "生命:");
printf("%3d", player.life);
y_x_colo_print(15, 22, white, "可用子弹:");
printf("%3d", bullet.max - bullet.number);
y_x_colo_print(13, 22, white, "子弹速度:(ms/格)");
printf("%3d", bullet.speed);
y_x_colo_print(11, 22, white, "敌人数量:");
printf("%3d", enemy.number);
y_x_colo_print(9, 22, white, "敌人速度:(ms/格)");
printf("%3d", enemy.speed);
if (player.buff[0] == 1)
{
y_x_colo_print(7, 22, lightblue, "火力全开 ");
printf("%d", (player.buff_time[0] - clock()) / 1000);
}
else
{
y_x_colo_print(7, 22, lightblue, " ");
}
}
void buff_c() //buff控制 某些有时间限制的buff 如果时间到了就把这个buff去掉
{
char a;
for (a = 0; a < buff_max; a++) //与上面子弹的移动差不多原理 这里是扫描玩家的所有buff
{
if (player.buff[a] == 1)
{
if (clock() > player.buff_time[a])
{
player.buff[a] = 0;
}
}
}
}
long lv_c_time = 20000;
void lv_c() //游戏节奏控制
{
if (clock() > lv_c_time) //每隔20秒敌人移动速度加快 -20ms
{
lv_c_time = clock() + 20000;
if (enemy.speed > 20)
{
enemy.speed -= 20;
}
if (bullet.speed > 10)
{
bullet.speed -= 10;
}
}
if (lv_fs >= 20) //每打死20个敌人 敌人数量+1
{
lv_fs = 0;
enemy.max += 1;
}
}
void game_info() //游戏说明
{
y_x_colo_print(0, 0, white, "C控制台兵蜂小游戏");
y_x_colo_print(1, 0, white, "游戏说明:");
y_x_colo_print(2, 0, white, "方向键控制玩家移动,空格射击");
y_x_colo_print(3, 0, white, "玩家初始生命为"); printf("%d", player_life);
y_x_colo_print(4, 0, white, "初始可用子弹为"); printf("%d", bullet_max);
y_x_colo_print(5, 0, white, "初始敌人数量为"); printf("%d", enemy_max);
y_x_colo_print(6, 0, white, "每隔20秒,敌人移动速度-20ms,每击败20个敌人,敌人数+1。可用拾取地图中的buff提高战斗力");
y_x_colo_print(7, 0, white, "BUFF说明:");
y_x_colo_print(8, 0, lightblue, "F 10秒火力全开,子弹无限,3连发");
y_x_colo_print(9, 0, lightblue, "S 可用子弹+1");
y_x_colo_print(10, 0, green, "L 生命+1");
y_x_colo_print(11, 0, red, "注意 你发射的子弹可以摧毁buff");
y_x_colo_print(13, 0, white, "power by:吴文峰 QQ 1337087466");
y_x_colo_print(15, 0, white, "按任意键开始游戏");
_getch();
system("cls");
}
int main()
{
GetConsoleCursorInfo(hOut, &CursorInfo); //获取控制台光标信息
CursorInfo.bVisible = false; //隐藏控制台光标
SetConsoleCursorInfo(hOut, &CursorInfo); //设置控制台光标状态
srand((int)time(0)); //设置随机数种子
game_info(); //显示游戏说明
dis_keep_out(); //打印一个边框
enemy.max = enemy_max; //给一些变量赋初值
enemy.speed = enemy_speed;
bullet.max = bullet_max;
bullet.speed = bullet_speed;
while (1)
{
ch = get_key(); //获得键值
switch (state)
{
case 0: //游戏运行模式
rand_e();
rand_buff();
buff_c();
move_player();
fire();
move_e_b();
lv_c();
buff_in_map();
dis_map();
dis_data();
break;
case 1: //游戏结束
y_x_colo_print(1, 22, red, "GAME OVER ");
y_x_colo_print(2, 22, red, "One More? Y/N");
//printf("%d",ch);
if (ch == 121) //游戏重开 变量重新赋初值
{
y_x_colo_print(1, 22, red, " ");
y_x_colo_print(2, 22, red, " ");
bullet.max = bullet_max;
bullet.speed = bullet_speed;
bullet.number = 0;
enemy.max = enemy_max;
enemy.speed = enemy_speed;
enemy.number = 0;
player.y = 15;
player.x = 10;
player.life = 5;
buffer.number = 0;
fs = 0;
lv_fs = 0;
state = 0;
}
if (ch == 110)//退出游戏
{
y_x_colo_print(22, 0, red, "EXITnpower by:吴文峰nQQ 1337087466");
return fs;
}
break;
default:
break;
}
}
}