使用 C 语言和 windows 的键盘检测函数和延迟函数,开发的控制台 roguelike 游戏
点开 .exe 文件立即进入游戏
AWSD 移动
J 攻击
K 加成buff
没有结束条件,除非碰到敌人。
其他模块功能还没来得及开发
author : 民用级脑的研发记录
DEVC++ 项目工程代码副本:
链接:https://pan.baidu.com/s/1HVjKRDJrzxtRlddyfrBZow
提取码:jhip
VS2022 项目工程代码副本
链接:https://pan.baidu.com/s/1vWCI2EIdNwpyqdoeNV2-DA
提取码:aexz
CSDN 源代码
https://mp.csdn.net/mp_blog/creation/editor?spm=1001.2101.3001.5352
代码请随意使用,在下抛砖引玉,给其他初学者的一点参考代码
BUG 说明:
在第一行时,敌人不会接触。
原理暂不明确,可能是坐标计算错了。
不打算修正了,直接当安全区可还行?
Have a good time
未完待续,后续若整好了 C 语言的 RPG 地图编辑器也一并开源
cpp
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <windows.h>
#define KEY_DOWN(vKey) ((GetAsyncKeyState(vKey) & 0x8000) ? 1:0) // 判断是否按下按键,按下之后,最高位变成1,所以需要位运算去除其他位,只检测最高位
#define KEY_DOWN_FOREGROUND(hWnd,vk) (KEY_DOWN(vk) && GetForegroundWindow() == hWnd) // 前景窗口判断
#pragma warning(disable : 4996) // VS2022 报错某些不安全函数,这里禁用 VS 的某些警告 方便在其他函数移植进 Visualstdio2022
struct enemys
{
int x;
int y;
int is_live;
};
inline void showgame(int** gamemap, int high, int wide, char* player, char* ai)
{ // 更新游戏,由于printf只能一行接着一行打印,所以不能覆盖先前字符,只能增加缓冲区,在缓冲区里实现覆盖
char p[10000] = { '\0' };
char pen[2];
int tile;
for (int j = 0; j < wide; j++)
strcat(p, "#");
strcat(p, "\n");
for (int i = 0; i < high; i++)
{
strcat(p, "#");
for (int j = 0; j < wide; j++)
{
tile = gamemap[i][j];
switch (tile)
{
case 0:
strcpy(pen, " ");
break;
default:
sprintf(pen, "%d", tile);
break;
}
strcat(p, pen);
}
strcat(p, "#\n");
}
for (int j = 0; j < wide; j++)
{
strcat(p, "#");
}
strcat(p, "\n");
printf("%s", p); // 加载进缓存数组,减少 printf 使用,解决 printf 速度慢导致的屏闪
}
int main()
{
int** bkmap; // 游戏背景图
int** gamemap; // 记录当前游戏地图形式
int high; // 地图高,宽
int wide;
int limitx;
int limity;
high = 20;
wide = 100;
limitx = 100;
limity = 20;
bkmap = new int* [limity]; // 初始化游戏地图
gamemap = new int* [high];
for (int i = 0; i < limity; i++)
bkmap[i] = new int[limitx];
for (int i = 0; i < high; i++)
gamemap[i] = new int[wide];
for (int i = 0; i < limity; i++)
for (int j = 0; j < limitx; j++)
bkmap[i][j] = 0;
for (int i = 0; i < high; i++)
for (int j = 0; j < wide; j++)
gamemap[i][j] = 0;
int playerx; // 玩家位置
int playery;
int gamex; // 显示区域
int gamey;
int player; // 玩家记号
player = 1;
playerx = 0;
playery = 0;
gamex = 0;
gamey = 0;
int field = 9;
int enemy; // 敌人记号
int indix; // 刷新敌人时更新数组
int enemynum; // 敌人个数
int enemymax; // 敌人上限
enemy = 2;
enemynum = 10;
enemymax = 100;
enemys ai[100]; // 敌人数组
for (int i = 0; i < 100; i++)
{
ai[i].is_live = 0;
ai[i].x = 0;
ai[i].y = 0;
}
char playerview[100] = "6";
char enemyview[100] = "2";
printf("%s", playerview);
int characterx; // 敌人位置
int charactery;
charactery = 10;
characterx = 10;
printf("%s", enemyview);
showgame(gamemap, high, wide, playerview, enemyview);
int flag_x;
int flag_y;
int is_atk;
int atk_area;
int is_buff;
flag_x = 0;
flag_y = 0;
is_atk = 0;
atk_area = 1;
is_buff = 0;
HWND hwnd;
hwnd = GetForegroundWindow(); // 获取前端窗口句柄,由于程序刚运行时是在前端,所以这就是本程序的窗口句柄
srand(time(NULL)); // 设置随机数种子
int gameover;
gameover = 0;
int cnt = 0; // 敌人计算位置,每两帧移动一次
while (1)
{
flag_x = 0; // 默认不按键时速度为0
flag_y = 0;
if (KEY_DOWN_FOREGROUND(hwnd, 0x41))
{
flag_x -= 1;
}
if (KEY_DOWN_FOREGROUND(hwnd, 0x57))
{
flag_y -= 1;
}
if (KEY_DOWN_FOREGROUND(hwnd, 0x44))
{
flag_x += 1;
}
if (KEY_DOWN_FOREGROUND(hwnd, 0x53))
{
flag_y += 1;
}
if (is_atk == 0 && KEY_DOWN_FOREGROUND(hwnd, 0x4A))
{
is_atk = 1;
}
if (is_buff == 0 && KEY_DOWN_FOREGROUND(hwnd, 0x4B))
{
is_buff = 1;
}
if (KEY_DOWN_FOREGROUND(hwnd, VK_ESCAPE))
{
printf("游戏退出\n");
break;
}
if (KEY_DOWN_FOREGROUND(hwnd, 0x30)) // 绘制地图
bkmap[playery][playerx] = 0;
if (KEY_DOWN_FOREGROUND(hwnd, 0x36)) // 绘制地图
bkmap[playery][playerx] = 6;
if (KEY_DOWN_FOREGROUND(hwnd, 0x37)) // 绘制地图
bkmap[playery][playerx] = 7;
if (KEY_DOWN_FOREGROUND(hwnd, 0x38)) // 绘制地图
bkmap[playery][playerx] = 8;
if (KEY_DOWN_FOREGROUND(hwnd, 0x39)) // 绘制地图
bkmap[playery][playerx] = 9;
if (flag_x > 1) // 速度限制
flag_x = 1;
else if (flag_x < -1)
flag_x = -1;
if (flag_y > 1)
flag_y = 1;
else if (flag_y < -1)
flag_y = -1;
if (flag_x) // 位移改变
playerx += flag_x;
if (flag_y)
playery += flag_y;
if (playerx >= limitx) // 角色位置限制
playerx = limitx - 1;
else if (playerx < 0)
playerx = 0;
if (playery >= limity)
playery = limity - 1;
else if (playery < 0)
playery = 0;
for (int i = 0; i < high; i++)
for (int j = 0; j < wide; j++)
if (gamemap[i][j] != bkmap[i + gamey][j + gamex]) // 去除上次动作记录
gamemap[i][j] = bkmap[i + gamey][j + gamex];
if (is_atk % 12 != 0) // 攻击
{
if (playerx < atk_area || playerx >= limitx - atk_area || playery < atk_area || playery >= limity - atk_area)
{
is_atk = 0;
}
else
{
is_atk++;
if (characterx - playerx >= -atk_area && charactery - playery >= -atk_area && characterx - playerx <= atk_area && charactery - playery <= atk_area) // 是否攻击到敌人
field = 0;
for (int n = 0; n < 100; n++)
{
if (ai[n].is_live)
{
if (ai[n].x - playerx >= -atk_area && ai[n].y - playery >= -atk_area && ai[n].x - playerx <= atk_area && ai[n].y - playery <= atk_area) // 是否攻击到敌群
ai[n].is_live = 0;
}
}
for (int i = playery - atk_area; i <= playery + atk_area; i++)
for (int j = playerx - atk_area; j <= playerx + atk_area; j++) // 范围攻击记号
{
gamemap[i][j] = is_atk;
if (bkmap[i][j] != 0)
bkmap[i][j] = 0;
}
}
}
else is_atk = 0;
for (int n = 0; n < 100; n++) // 打印存活敌人
if (ai[n].is_live)
gamemap[ai[n].y][ai[n].x] = enemy;
gamemap[charactery][characterx] = field; // 敌人比攻击晚赋值,所以敌人可以覆盖攻击图标
if (is_buff % 22 != 0) // buff 持续时间 12 帧
{
is_buff++;
atk_area = 3;
player = is_buff;
}
else
{
is_buff = 0;
atk_area = 1;
player = 1;
}
indix = 0;
enemynum = 0;
cnt++;
while (indix < enemymax) // 加入新的敌人
{
if (ai[indix].is_live)
{
enemynum++;
if (cnt % 2 == 0)
{
if (ai[indix].x < playerx)
ai[indix].x++;
else if (ai[indix].x > playerx)
ai[indix].x--;
if (ai[indix].y < playery)
ai[indix].y++;
else if (ai[indix].y > player)
ai[indix].y--;
cnt = cnt % 2;
}
if (playerx == ai[indix].x && playery == ai[indix].y)
{
gameover = 1;
player = 1;
}
}
else
{
ai[indix].x = rand() % 100;
ai[indix].y = rand() % 20;
ai[indix].is_live = 1;
enemynum++;
}
indix++;
if (enemynum >= 15)
break;
}
printf("player x %d y %d\n", playerx, playery);
gamemap[playery][playerx] = player;
showgame(gamemap, high, wide, playerview, enemyview);
while (gameover == 1)
{
printf("game over\n");
printf("按 R 重新开始\n按 ESC 退出游戏\n");
if (KEY_DOWN_FOREGROUND(hwnd, 0x52))
{
gameover = 0;
playerx = 0;
playery = 0;
enemynum = 0;
break;
}
else if (KEY_DOWN_FOREGROUND(hwnd, VK_ESCAPE))
{
break;
}
showgame(gamemap, high, wide, playerview, enemyview);
Sleep(50);
system("cls");
}
if (gameover == 1)
break;
Sleep(100);
system("cls"); // 清屏
}
system("cls");
printf("Have a good time\n");
system("pause");
Sleep(50);
return 0;
}