1 Star 0 Fork 3

北京昂首科技有限公司/cgames

forked from Anbang24/cgames 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
cgame8(bricks)v3.cpp 13.26 KB
一键复制 编辑 原始数据 按行查看 历史
Anbang24 提交于 2022-11-23 00:17 . 第8章 打砖块
//https://gitee.com/devcpp/cgames anbangli@foxmail.com GNU GPL v3
//cgame8(bricks)v3.cpp 打砖块 版本3:挡板和弹球的数据脱离出地图
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <Windows.h>
#include <conio.h>
using namespace std;
const int WD = 20, HT = 20; //游戏地图的宽度(Width)和高度(Height)
char map[HT][WD];//地图二维数组 ' '-空白,'#'-围墙,'B'-砖块,'='-挡板,'O'-弹球
int X0 = 10, Y0 = 0; //绘制地图时的左上角坐标原点
int barx, bary, barwd; //挡板的左侧x位置、y位置和宽度
int x, y, vx, vy; //弹球的坐标x和y,速度vx和vy
//控制台文本窗口游戏中需要移动光标到屏幕特定位置,自定义相应的函数 gotoxy。
//屏幕左上角为(0,0),横向向右为 X 轴,竖向向下为 Y 轴。
//void gotoxy(short x, short y) { //控制台窗口光标定位
// static COORD crd; //定义 COORD 类型(窗口坐标)的静态局部变量 crd
// crd.X = x;
// crd.Y = y;
// SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), crd);
// //GetStdHandle获取控制台输出句柄并用SetConsoleCursorPosition设置光标位置
// return;
//}
//void showcursor(bool visible) { //显示或隐藏光标
// CONSOLE_CURSOR_INFO cursor = {20, visible};
////CONSOLE_CURSOR_INFO结构体包含控制台光标信息,两个成员分别表示光标厚度和是否可见
// SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor);
//}
//void clrscr() { //清空屏幕
// HANDLE hdout = GetStdHandle(STD_OUTPUT_HANDLE); //获取标准输出设备的句柄
// CONSOLE_SCREEN_BUFFER_INFO csbi; //定义表示屏幕缓冲区属性的变量
// GetConsoleScreenBufferInfo(hdout, &csbi); //获取标准输出设备的屏幕缓冲区属性
// DWORD size = csbi.dwSize.X * csbi.dwSize.Y, num = 0; //定义双字节变量
// COORD pos = {0, 0}; //表示坐标的变量(初始化为左上角(0, 0)点)
// //把窗口缓冲区全部填充为空格并填充为默认颜色(清屏)
// FillConsoleOutputCharacter(hdout, ' ', size, pos, &num);
// FillConsoleOutputAttribute (hdout, csbi.wAttributes, size, pos, &num );
// SetConsoleCursorPosition(hdout, pos); //光标定位到窗口左上角
//}
void initMap(int &bricks) { //初始化地图和砖块,并对砖块计数
int iy, ix;
for (iy = 0; iy < HT; iy++) { //行循环
for (ix = 0; ix < WD; ix++) { //列循环
if (ix == 0 || ix == WD - 1 || iy == HT - 1 || iy == 0)
map[iy][ix] = '#'; //围墙
else
map[iy][ix] = ' '; //内部空白区域
}
}
//砖块随机地零散分布在地图内部空白区域上方部分
bricks = 0;
for (iy = 2; iy < 7; iy++)
for (ix = 1; ix < WD - 1; ix++) {
map[iy][ix] = (rand() % 2 ? 'B' : ' ');
bricks += (map[iy][ix] == 'B'); //砖块计数
}
}
void initBarBall() { //初始化挡板和弹球
//挡板初始时处于内部空白区域的底行
barwd = 7;
barx = WD / 2 - barwd / 2,
bary = HT - 2;
int j;
for (j = 1; j < WD - 1; j++)
map[bary][j] = ' '; //空格
for (j = barx; j <= barx + barwd; j++)
map[bary][j] = '='; //挡板
//弹球初始时静止在挡板上面中央位置
x = WD / 2;
y = HT - 3;
map[y][x] = 'O';
vx = vy = 0; //弹球的初始速度为0
}
void drawxy(int x, int y, char val) { //根据val的值绘制地图格点(x,y)
gotoxy(X0 + x * 2, Y0 + y);
if (val == ' ') //空白
printf(" ");
else if (val == '#') //围墙
printf("█");
else if (val == 'B') //砖块
printf("□");
else if (val == '=') //挡板
printf("▓");
else if (val == 'O') //弹球
printf("●");
else if (val == 'o') //失败时的弹球
printf("○");
else
printf("×");
}
void drawMap() { //绘制整个地图
int iy, ix;
for (iy = 0; iy < HT; iy++) { //行
for (ix = 0; ix < WD; ix++) { //列
drawxy(ix, iy, map[iy][ix]);
}
}
}
void showMsg(int lives, int bricks, int score, int lines) {
gotoxy(X0 + 0, Y0 + HT);
cout << "lives: " << lives << " bricks: " ;
cout << bricks << " scores: " << score << " ";
for (int i = 0; i < lines; i++) //清空下面几行的残余信息
cout << "\n ";
}
bool wantMore() { //是否继续游戏
int ch;
gotoxy(X0 + 0, Y0 + HT + 1);
//cout << " \r"; //清空本行
cout << "再玩一局吗? (y/n): ";
while ((ch = tolower(getchar())) != 'y' && ch != 'n')
; //空循环体
cin.sync(); //清空缓冲区
gotoxy(X0 + 0, Y0 + HT + 1);
cout << " \r"; //清空本行
return (ch == 'y' ? true : false);
}
void showHelp() {
clrscr(); //清屏
showcursor(true); //显示光标
cout << "\n";
cout << "[] [] [] [] [] [] [] [] [] [] [] [] []\n";
cout << " [] [] [] [] [] [] [] [] [] [] [] [] \n";
cout << "[] [] [] [] [] [] [] ● [] [] [] [] [] \n";
cout << " [] [] [] [] [] []   [] [] [] [] [] \n";
cout << "打砖块(anbangli@foxmail.com, 2021.1)\n\n";
cout << "操作说明:\n A← 左移 D→ 右移 空格 发射/暂停 H 帮助 ESC 退出\n\n";
cout << "\n按回车键开始";
getch(); //用户按键
clrscr(); //清屏
showcursor(false); //隐藏光标
}
int main() {
int lives = 3, score = 0; //生命数,得分(胜利时可以带到下一局)
int bricks = 0, hits = 0; //本局的生命数,砖块数,已击中砖块数
int loops; //玩家按键时间与弹球运动时间间隔的倍数
int key; //用户按键
showHelp();
srand(time(0)); //初始化随机数种子
initMap(bricks); //初始化地图
initBarBall(); //初始化挡板和弹球
drawMap(); //主循环之前绘制整个地图(游戏过程中只绘制地图中发生变化的内容)
showMsg(lives, bricks, score, 0); //输出信息
showcursor(0);
while (true) { //游戏主循环
//drawMap(); //绘制地图
//showMsg(lives, bricks, score); //输出信息
if ((lives > 0 && bricks <= 0) || lives <= 0) { //胜利或失败
gotoxy(X0, Y0 + HT);
if (lives > 0)
cout << "\n击碎所有砖块,您胜利了!";
else { //失败
cout << "\n本局失败,还有砖块尚未击中。 ";
score = 0; //本局失败,得分清0
}
if (wantMore()) {//是否继续
initMap(bricks); //初始化地图
initBarBall(); //初始化挡板和弹球
lives = 3;
drawMap(); //绘制地图
showMsg(lives, bricks, score, 2);
continue;
} else
break;
}
//既非胜利也非失败,则游戏正在进行
if (kbhit()) { //有键盘输入
key = getch(); //接收用户按键
if (key <= 0 || key > 127) //读取方向键时,第一次读得的不是实际键值
key = getch(); //读取方向键时,第二次才读得实际键值
if (key == ' ') { //空格
if (vx == 0 && vy == 0) { //开局发球。初速度随机斜向左右上方
vx = (rand() % 2 ? 1 : -1);
vy = -1;
} else //中途暂停
getch(); //按任意键继续
} else if ((key == 75 || key == 'a' || key == 'A') && barx > 1) { //left
//map[bary][barx + barwd] = ' '; //挡板右端空白
drawxy(barx + barwd, bary, ' '); //绘制挡板右端空白
//barx--;
//map[bary][barx] = '='; //挡板左端点
drawxy(--barx, bary, '='); //绘制挡板左端点
if (vx == 0 && vy == 0) { //开局时尚未发球,则球随之挡板移动
//map[y][x] = ' ';
drawxy(x, y, ' ');
//x--;
//map[y][x] = 'O';
drawxy(--x, y, 'O');
}
} else if ((key == 77 || key == 'd' || key == 'D') && (barx + barwd < WD - 2)) { //right
//map[bary][barx] = ' '; //挡板左端空白
drawxy(barx, bary, ' '); //绘制挡板左端空白
//barx++;
//map[bary][barx + barwd] = '='; //挡板右端点
drawxy(++barx + barwd, bary, '='); //绘制挡板右端点
if (vx == 0 && vy == 0) { //开局时尚未发球,则球随之挡板移动
//map[y][x] = ' '; //弹球原位置
drawxy(x, y, ' '); //清除原位置的弹球
//x++;
//map[y][x] = 'O'; //弹球新位置
drawxy(++x, y, 'O'); //在新位置绘制弹球
}
} else if (key == 'h' || key == 'H') {
showHelp();
drawMap(); //绘制地图
} else if (key == 27) { //是否退出游戏(ESC键的ASCII码为27)
showcursor(1);
gotoxy(0, HT + 1 );
cout << "是否退出游戏?(y/n): ";
while ((key = tolower(getch())) != 'y' && key != 'n')
; //空循环体
cin.sync(); //清空缓冲区
showMsg(lives, bricks, score, 1); //输出信息并清空下方1行
if (key == 'y')
break;
else
showcursor(0);
}
}
//处理弹球运动时间间隔是处理用户按键时间间隔的多倍
Sleep(50);
loops = (++loops) % 3;
if (loops > 0) //仅在loops等于0时处理弹球运动, 否则跳过
continue; //跳过下面的代码,执行下一循环
hits = 0; //在此循环中击中砖块数
//先判断左右或竖直碰撞,如果发生判断则不再判断斜碰
if (map[y][x + vx] == 'B') { //横向运动方向为砖块
map[y][x + vx] = ' '; //打碎砖块变为空白
drawxy(x + vx, y, ' '); //重绘原有砖块位置为空白
hits++; //横向碰撞记数
vx = -vx; //横向反弹
cout << '\a'; //响铃
}
if (map[y][x + vx] == '#') { //围墙
vx = -vx; //横向反弹
cout << '\a';
}
if (y + vy > bary) { //如果下方是底部边界,则生命数减少
map[y][x] = 'o';
drawxy(x, y, 'o');
lives--; //生命数减少
showMsg(lives, bricks, score, 0);
if (lives > 0) { //生命值尚未减少到0
showcursor(1);
gotoxy(X0, Y0 + HT + 1);
cout << "失败,按任意键继续";
getch();
map[y][x] = ' ';
initBarBall(); //初始化挡板和弹球
drawMap();
showMsg(lives, bricks, score, 2);
}
continue;
}
if (map[y + vy][x] == 'B') { //如果竖直运动方向是砖块
map[y + vy][x] = ' '; //打碎砖块变为空白
drawxy(x, y + vy, ' '); //重绘原有砖块位置为空白
hits++; //竖直碰撞记数
vy = -vy; //竖直反弹
cout << '\a';
}
//if (map[y + vy][x] == '#' || map[y + vy][x] == '=' ) { //围墙,挡板
if (map[y + vy][x] == '#') { //与围墙竖直相碰,挡板
vy = -vy; //竖直反弹
cout << '\a';
}
if (((y + vy) == bary && (x > barx) && (x - barx < barwd)) ) { //与挡板竖直相碰
vy = -vy; //竖直反弹
cout << '\a';
}
//未发生横向或竖直碰撞,则判断是否发生斜碰
if (hits == 0 && map[y + vy][x + vx] == 'B') {
map[y + vy][x + vx] = ' ';
drawxy(x + vx, y + vy, ' '); //重绘原有砖块位置为空白
hits++;
vy = -vy;
vx = -vx;
cout << '\a';
}
bricks -= hits; //剩余砖块数
score += hits; //得分
if (vx != 0 || vy != 0) {
//map[y][x] = ' '; //弹球原位置变为空
drawxy(x, y, ' '); //清除原位置的弹球
}
if ((vy < 0 && y > 1) || (vy > 0 && y < HT - 1))
y = y + vy;
if ((vx < 0 && x > 1) || (vx > 0 && x < WD - 2))
x = x + vx;
if (vx != 0 || vy != 0) {
//map[y][x] = 'O';
drawxy(x, y, 'O'); //在新位置绘制弹球
}
showMsg(lives, bricks, score, 0); //输出信息
} //主循环结束
cout << "\n游戏结束,谢谢使用!";
showcursor(1);
getch();
return 0;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C
1
https://gitee.com/facese/cgames.git
git@gitee.com:facese/cgames.git
facese
cgames
cgames
master

搜索帮助