在这个1D C++游戏中射出多颗子弹

Shoot more than one bullet in this 1D C++ game

本文关键字:子弹 游戏 1D C++      更新时间:2023-10-16

首先,让我向您展示我的代码(假设它在主函数中):

#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <conio.h>
int main(){
bool exit = false;
int g_x = 33;
int g_brx = 40;
int g_blx[5];
CONSOLE_SCREEN_BUFFER_INFO csbi;
int columns, rows;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
const int WIDTH = columns;//info->dwSize.X;

do {
//Render
for (int i = 0; i < WIDTH; i++) {
if (i == g_x) printf("x");
else if (i == g_blx[0]) printf("<");
else if (i == g_blx[1]) printf("<");
else if (i == g_blx[2]) printf("<");
else if (i == g_blx[3]) printf("<");
else if (i == g_blx[4]) printf("<");
else if (i == g_brx) printf(">");
else printf("-");
}
for (int i = 0; i < 5; i++) {
//Process input
if (_kbhit()) {
char ch = _getch();
switch (ch) {
case 'h': if (g_x > 0) g_x--; break;
case 'l': if (g_x < WIDTH - 1) g_x++; break;
case 'j': if (g_blx[i] < 0) g_blx[i] = g_x - 1; break;
case 'k': if (g_brx < 0) g_brx = g_x + 1; break;
case 27: exit = true; break;
}
}
}
//Move bullets
for(int i = 0; i < 5; i++)
if (g_blx[i] >= 0) g_blx[i]--;
if (g_brx >= 0) g_brx++;
if (g_brx >= WIDTH) g_brx = -1;
Sleep(50);
} while (!exit);
}

此代码在每次迭代中都会打印一行WIDTH大小的行,可以是"x"(播放器)、"<"(子弹射向左侧)、">"(子弹射向右侧)或"-"(这是在没有任何先前内容可打印时使用的符号)。

然后,它处理玩家的输入,并判断玩家是否移动或子弹被击中。然后,它只是移动子弹。(我不知道我是否正确地解释了自己,因为英语不是我的第一语言。不管怎样,我都邀请你运行它,看看它是如何工作的)。

我想做的是(没有成功)为每一侧发射1颗以上的子弹,尽管我首先尝试的是一侧,最多5颗子弹。但我似乎无法解决这个问题。我曾尝试使用g_blxg_brx(项目符号变量)的数组,然后对它们进行循环,但它无法正确获得我的输入,因此在按键后无法打印1个项目符号。

如有任何帮助,我们将不胜感激。事先非常感谢。

我会使用一个标准的C++容器,如std::vector,来跟踪项目符号,并创建一个Bullet类来存储每个项目符号的属性,如位置、方向和绘制方式(目前只有<>)。以下是VS2017的一个示例,代码中有注释:

#include "pch.h"
#include <iostream>
#include <string>
#include <vector>
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <conio.h>
// a class describing a bullet
class Bullet {
int speed; // direction
int x;     // current position
char ch;   // its visual representation
public:
// constructor
Bullet(int X, int Speed, int Ch) : speed(Speed), x(X), ch(Ch) {}
// accessors
inline int getX() const { return x; }
inline char getChar() const { return ch; }
// telling the bullet to do its thing
void Move() { x += speed; }
};
// a class to maintain a number of bullets
class BulletList {
static constexpr size_t MaxNumberOfBullets = 5;
std::vector<Bullet> bullets; // storage for Bullet objects
int max_x; // the last column
public:
// constructor
BulletList(int columns) : bullets(), max_x(columns-1) {
bullets.reserve(MaxNumberOfBullets);
}
// method to add a bullet (if we haven't reached MaxNumberOfBullets yet)
bool AddBullet(int x, int speed, char ch) {
if (bullets.size() < MaxNumberOfBullets) {
bullets.emplace_back(x, speed, ch);
return true;
}
return false;
}
// method to tell all bullets to move and remove
// those who's left the playing ground
void MoveBullets() {
for (auto it = bullets.begin(); it != bullets.end();) {
it->Move();
if (it->getX() < 0 || it->getX() > max_x) it = bullets.erase(it);
else ++it;
}
}
// method to "paint" all bullets in a prepared graphical buffer
void addBulletsToGraph(std::string& line) const {
for (const auto&b : bullets) 
line[b.getX()] = b.getChar();
}
};
int main() {
bool exit = false;
int g_x = 33;
CONSOLE_SCREEN_BUFFER_INFO csbi;
int columns, rows;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
const int WIDTH = columns;//info->dwSize.X;
BulletList bl(columns);
do {
//Render
std::string graph(columns, '-');  // create a line of '-'
bl.addBulletsToGraph(graph);      // paint the bullets
graph[g_x] = 'x';                 // paint the player
std::cout << graph << std::flush; // print the whole graph at once
//Process input
if (_kbhit()) {
char ch = _getch();
switch (ch) {
case 'h': if (g_x > 0) g_x--; break;
case 'l': if (g_x < WIDTH - 1) g_x++; break;
case 'j': bl.AddBullet(g_x, 1, '>'); break;
case 'k': bl.AddBullet(g_x, -1, '<'); break;
case 27: exit = true; break;
}
}
//Move bullets
bl.MoveBullets();
Sleep(50);
} while (!exit);
}

您在这里犯的第一个错误是试图区分向左项目符号和向右项目符号。应该只有一个项目符号数组,其中的条目是项目符号的位置,然后使用它们的位置来确定它们是向右还是向左。这很容易,因为您已经定义了两个关键点,一个用于向左拍摄,另一个用于向右拍摄。"棘手"的部分是处理子弹离开世界时会发生什么:我做出了让它们完全消失的设计决定,这意味着每次离开屏幕都会少一颗子弹。我实现这一点的方式是迭代每个项目符号并"将它们拉回来",即下一个项目符号覆盖当前项目符号。

第二个错误是您使用的渲染基础。重复一个for循环,直到你基本上偶然发现了你想画的东西,这是不太可持续的。为了解决这个问题,我引入了一个char数组,我们在其中存储所有想要绘制的东西(播放器、项目符号和'-'),然后渲染只需要一个printf。

我还自由地让游戏只在输入时进行,因为这对我来说不那么令人困惑。我还把键改为asdf,因为这更符合人体工程学。

#include <stdio.h>
#include <string.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <conio.h>
int main() {
bool exit = false;
int PlayerPosition   = 33;
const int MaxBullets = 50;
int Bullets[MaxBullets] = {};
int CurrentBullets = 0;
CONSOLE_SCREEN_BUFFER_INFO csbi;
int columns, rows;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
rows    = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
const int WIDTH = columns;//info->dwSize.X;
char* RenderLine = (char*) HeapAlloc(
GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS, columns + 1
);
// Initial render
for (int i = 0; i < WIDTH; i++) {
RenderLine[i] = '-';
}
RenderLine[PlayerPosition] = 'x';
printf(RenderLine);
do {
//Process input
char ch = getch();
if (ch) {
switch (ch) {
case 'a': {
if (PlayerPosition > 0) {
RenderLine[PlayerPosition]   = '-';
RenderLine[--PlayerPosition] = 'x';
}
break;
}
case 'f': {
if (PlayerPosition < WIDTH - 1) {
RenderLine[PlayerPosition]   = '-';
RenderLine[++PlayerPosition] = 'x';
}
break;
}
case 'd': {
if (CurrentBullets < MaxBullets) {
RenderLine[PlayerPosition + 1] = '>';
Bullets[CurrentBullets++] = PlayerPosition + 1;
}
break;
}
case 's': {
if (CurrentBullets < MaxBullets) {
RenderLine[PlayerPosition - 1] = '<';
Bullets[CurrentBullets++] = PlayerPosition - 1;
}
break;
}
case 27: exit = true; break; // press escape
}
//Render
printf(RenderLine);
//Move bullets
for (int i = 0; i < CurrentBullets; i++) {
if (Bullets[i] > PlayerPosition) { // move right
RenderLine[Bullets[i]]   = '-';
if (Bullets[i] == (WIDTH - 1)) { // a bullet has left the screen on the right
Bullets[i] = Bullets[--CurrentBullets];
} else {
RenderLine[++Bullets[i]] = '>';
}
} else if (Bullets[i] < PlayerPosition) { // move right
RenderLine[Bullets[i]]   = '-';
if (Bullets[i] == 0) { // a bullet has left the screen on the left
Bullets[i] = Bullets[--CurrentBullets];
} else {
RenderLine[--Bullets[i]] = '<';
}
}
}
}
} while (!exit);
}