一个简单的 Tank C++ 游戏

A simple Tank C++ game

我现在对 c++ 有点陌生,我必须编写一个控制台 Tank 游戏。我自己做了很多部分,大部分部分都没有错。这是我的坦克 class:

    class Tank  {
    protected:
         string name;
         static int number;
         static int row;
         static int col;
         static char direction;

public:
    Tank(int _number, string _name, int _row, int _col, char _direction){
        this->name = _name;
        this->number = _number;
        setRow(_row);
        setCol(_col);
        setDirection(_direction);
    }

    ///////////////////////////////
    ///----GETTERS FUNCTIONS----///
    ///////////////////////////////
    char getdirection(){ return direction; }
    int getnumber(){ return number; }
    int getCol(){ return col; }
    int getRow(){ return row; }

    void setDirection(char _direction)
    {
        direction = _direction;
    }
    void setCol(int _col)
    {
        col = _col;
    }
    void setRow(int _row)
    {
        row = _row;
    }
    ////////////////////////
    ///To String Function///
    ////////////////////////
    string _toString(){
        string r = "";
        r = ("Tank No. " + toString(number + 1) + " " + name + " At (" + toString(row) + "," + toString(col) + ") " + direction);
        return r;
    }
};

int Tank::col = 0;
int Tank::row = 0;
int Tank::number = 0;
char Tank::direction = '[=10=]';

问题是当我创建对象数组时:

Tank *players[4];

因为我需要一个四人游戏和一个坦克对象,所以它只使用最后一个对象。这是我想在每个回合中使用的代码,让每个玩家说出他们将要做什么:

static char action;
        static int turn = 1;
        static Game game(*players, map);
        for (int count = 0; players[count] != NULL; count++)
        {
            clr_screen();
            gotoxy(100, 15); cout << "Turn " << turn;
            gotoxy(100, 16); cout << "Player " << players[count]->getnumber() + 1 << " Action: ";
            cin >> action;
            game.move(action, count);
        }
        turn++;

如您所见,代码存在问题。

您可能想知道,这是游戏 class:

class Game{
private:
    Tank *p_tank[5];
    Map map;
public:
    Game(Tank *tanks, Map _map)
    {
        *p_tank = tanks; map = _map;
    }

    //Tank getTank(){ return p_tank; }
    Map getMap(){ return map; }

如果您需要此处的完整代码,它是:

    #include <iostream>
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include <fstream>
#include <sstream>

using namespace std;

void SetWindow(int Width, int Height)
{
    _COORD coord;
    coord.X = Width;
    coord.Y = Height;

    _SMALL_RECT Rect;
    Rect.Top = 0;
    Rect.Left = 0;
    Rect.Bottom = Height - 1;
    Rect.Right = Width - 1;

    HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE);      // Get Handle
    SetConsoleScreenBufferSize(Handle, coord);            // Set Buffer Size
    SetConsoleWindowInfo(Handle, TRUE, &Rect);            // Set Window Size
}

void gotoxy(int x, int y)
{
    static HANDLE h = NULL;
    if (!h)
        h = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD c = { x, y };
    SetConsoleCursorPosition(h, c);
}

string toString(int x) {
    stringstream ss;
    ss << x;
    return ss.str();
}
class Tank  {
protected:
    string name;
     static int number;
     static int row;
     static int col;
     static char direction;

public:
    Tank(int _number, string _name, int _row, int _col, char _direction){
        this->name = _name;
        this->number = _number;
        setRow(_row);
        setCol(_col);
        setDirection(_direction);
    }

    ///////////////////////////////
    ///----GETTERS FUNCTIONS----///
    ///////////////////////////////
    char getdirection(){ return direction; }
    int getnumber(){ return number; }
    int getCol(){ return col; }
    int getRow(){ return row; }

    void setDirection(char _direction)
    {
        direction = _direction;
    }
    void setCol(int _col)
    {
        col = _col;
    }
    void setRow(int _row)
    {
        row = _row;
    }
    ////////////////////////
    ///To String Function///
    ////////////////////////
    string _toString(){
        string r = "";
        r = ("Tank No. " + toString(number + 1) + " " + name + " At (" + toString(row) + "," + toString(col) + ") " + direction);
        return r;
    }
};

int Tank::col = 0;
int Tank::row = 0;
int Tank::number = 0;
char Tank::direction = '[=14=]';

class Bullet : public Tank{
private:
    Tank tank;
public:
    Tank getTank(){ return tank; }
    void setRow(int _row){ row = _row; }
    void setCol(int _col){ col = _col; }
    char getDirection(){ return direction; }
    string __toString(){
        string r = "";
        r = ("Bullet At (" + toString(row) + "," + toString(col) + ") From Tank No " + toString(number));
        return r;
    }
};

class Block{//Characters: according to legends Lines: 316 - 322
protected:
    int row;
    int col;
    char character;
public:
    Block(char block)
    {
        this->character = block;
    }
    virtual string _toString(){
        string r = "";
        r = (character + (" at (" + toString(row) + toString(col) + "). "));
        return r;
    }
    int getRow(){ return row; }
    int getCol(){ return col; }
    void setRow(int _row){ row = _row; }
    void setCol(int _col) { row = _col; }
    virtual char getCharacter() { return character; }
};

/*class Ground : public Block{
public:
Ground(){
character = '.';
Block block;
block.getCharacter();
}
};

class Wall : public Block{
public:
Wall(){
character = 'W';
Block block;
block.getCharacter();
}
};

class Box : public Block{
public:
Box(){
character = 'B';
Block block;
block.getCharacter();
}
};

class Ice : public Block{
public:
Ice(){
character = 'I';
Block block;
block.getCharacter();
}
};

class Trap : public Block{
public:
Trap(){
character = 'T';
Block block;
block.getCharacter();
}
};

class Random : public Block{
public:
Random(){
character = '?';
Block block;
block.getCharacter();
}
};

class Tblock : public Block{
public:
Tblock(){
character = 'T';
Block block;
block.getCharacter();
}
};*/

class Map{
private:
    int rows;
    int cols;
    static const int MaxSize = 100;
    Block *map[MaxSize][MaxSize];
public:
    Map(int row = MaxSize, int col = MaxSize){
        this->rows = row;
        this->cols = col;
        for (int i = 0; i < row; i++){
            for (int j = 0; j < col; j++)
                map[i][j] = new Block(' ');
        }
    }

    void edit(int row, int col, char legend)
    {
        if (legend == '%')//bullet sign
            map[row][col] = new Block('O');
        else if (legend == '0')
            map[row][col] = new Block('1');
        else if (legend == '1')
            map[row][col] = new Block('2');
        else if (legend == '2')
            map[row][col] = new Block('3');
        else if (legend == '3')
            map[row][col] = new Block('4');
        else if (legend == 'G' || legend == 'g')
            map[row][col] = new Block(' ');
        else if (legend == 'w' || legend == 'W')
            map[row][col] = new Block('W');
        else if (legend == '?')
            map[row][col] = new Block('?');
        else if (legend == 'B' || legend == 'b')
            map[row][col] = new Block('B');
        else if (legend == 'X' || legend == 'x')
            map[row][col] = new Block('X');
        else if (legend == 'I' || legend == 'i')
            map[row][col] = new Block('I');
        else
            map[row][col] = new Block('B');
        _toGUI(row, col);
    }

    void _toGUI(int x, int y)
    {
        int X = 7 + 8 * x;
        int Y = 6 + 4 * y;
        gotoxy(X, Y);
        cout << map[x][y]->getCharacter();
    }

    bool moveOk(int r, int c, char mov)
    {
        switch (mov)
        {
        case 'l':
            if (r - 1 < 0) return false;
            else return true;
            break;

        case 'r':
            if (r > rows - 2) return false;
            else return true;
            break;

        case 'd':
            if (c < cols - 1) return true;
            else return false;
            break;

        case 'u':
            if (c > 0) return true;
            else return false;
            break;
        }
    }

    int getRows(){ return cols; }
    int getCols(){ return rows; }

};

class Game{
private:
    Tank *p_tank[5];
    Map map;
public:
    Game(Tank *tanks, Map _map)
    {
        *p_tank = tanks; map = _map;
    }

    //Tank getTank(){ return p_tank; }
    Map getMap(){ return map; }
    void move(char move, int whoToPlay)
    {
        string wtp;
        wtp = toString(whoToPlay);
        int m_row, m_col;
        switch (move)
        {
        case 'l':
            m_row = p_tank[whoToPlay]->getRow() - 1;
            m_col = p_tank[whoToPlay]->getCol() - 1;
            if (map.moveOk(m_row, m_col, 'l')){
                map.edit(m_row, m_col, 'G');
                map.edit(--m_row, m_col, wtp[0]);
                p_tank[whoToPlay]->setRow(m_row + 1);
                p_tank[whoToPlay]->setCol(m_col + 1);
            }
            break;

        case 'r':
            m_row = p_tank[whoToPlay]->getRow() - 1;
            m_col = p_tank[whoToPlay]->getCol() - 1;
            if (map.moveOk(m_row, m_col, 'r')){
                map.edit(m_row, m_col, 'G');
                map.edit(++m_row, m_col, wtp[0]);
                p_tank[whoToPlay]->setRow(m_row + 1);
                p_tank[whoToPlay]->setCol(m_col + 1);
            }
            break;

        case 'd':
            m_row = p_tank[whoToPlay]->getRow() - 1;
            m_col = p_tank[whoToPlay]->getCol() - 1;
            if (map.moveOk(m_row, m_col, 'd')){
                map.edit(m_row, m_col, 'G');
                map.edit(m_row, ++m_col, wtp[0]);
                p_tank[whoToPlay]->setRow(m_row + 1);
                p_tank[whoToPlay]->setCol(m_col + 1);
            }
            break;

        case 'u':
            m_row = p_tank[whoToPlay]->getRow() - 1;
            m_col = p_tank[whoToPlay]->getCol() - 1;
            if (map.moveOk(m_row, m_col, 'u')){
                map.edit(m_row, m_col, 'G');
                map.edit(m_row, --m_col, wtp[0]);
                p_tank[whoToPlay]->setRow(m_row + 1);
                p_tank[whoToPlay]->setCol(m_col + 1);
            }
            break;
        }
    }
};

void draw_field(Map map)
{
    ////////////////////////
    //-------Border-------//
    ////////////////////////
    for (int i = 3; i <= 2 * (4 * map.getRows() + 3); i++)
    {
        gotoxy(i, 3);
        printf("%c", 219);
        gotoxy(i, (4 * map.getCols() + 7));
        printf("%c", 219);
    }

    for (int i = 6; i < (4 * map.getCols() + 7); i++){
        gotoxy(3, i);
        printf("%c", 219);
        gotoxy(2 * (4 * map.getRows() + 3), i);
        printf("%c", 219);
    }

    gotoxy(3, 5);
    printf("%c", 219);
    gotoxy(3, 4);
    printf("%c", 219);
    gotoxy(3, 4 * map.getCols() + 7);
    printf("%c", 219);
    gotoxy(2 * (4 * map.getRows() + 3), 4);
    printf("%c", 219);
    gotoxy(2 * (4 * map.getRows() + 3), 5);
    printf("%c", 219);
    gotoxy(2 * (4 * map.getRows() + 3), map.getCols() + 7);
    printf("%c", 219);
    //end of BORDER

    //****************\
    //---COORD GUI----\
    //****************\

    //numbers:
    int j = 1;
    int z = 1;
    for (int i = 8; i < 2 * (4 * map.getRows() + 3); i += 8)
    {
        gotoxy(i, 2); cout << j;
        j++;
    }

    for (int i = 6; i < (4 * map.getCols() + 6); i += 4)
    {
        gotoxy(1, i);
        cout << z;
        z++;
    }
    //lines:
    for (int j = 8; j < (4 * map.getCols() + 6); j += 4)
    for (int i = 4; i < 2 * (4 * map.getRows() + 3); i++)
    {
        gotoxy(i, j);
        cout << "-";
    }
    for (int j = 12, c = 0; c < 4 * map.getRows() / 4 - 1; j += 8, c++)
    for (int i = 4, t = 0; t < 4 * map.getCols() + 1; i++, t++)
    {
        Sleep(10);
        gotoxy(j, i);
        cout << "|";
    }

}

void clr_screen()
{
    for (int i = 100; i <= 136; i++){
        for (int j = 15; j <= 55; j++){
            gotoxy(i, j); cout << " ";
        }
    }
}

void legend()
{
    gotoxy(100, 2); cout << "Legends:             ";
    gotoxy(100, 4); cout << "Tanks: #\tBullet: *";
    gotoxy(100, 5); cout << "Walls: W\tRandom: ?";
    gotoxy(100, 6); cout << "Box: B\tIce: I";
    gotoxy(100, 7); cout << "Trap: X\t";
    gotoxy(100, 9); cout << "Actions:";
    gotoxy(100, 10); cout << "U D L R\tFire: F";
    gotoxy(100, 11); cout << "----------------------";
}

void action_display(string str, Map &map)
{
    Tank *players[5];
    char reply = 'n';

    if (str == "%edit%" || str == "%Edit%"){
        while (reply != 'y'){
            char legend; int c; int r;
            clr_screen();
            gotoxy(100, 14);
            cout << "Actions:";
            gotoxy(100, 15);
            cout << "Edit Section:";
            gotoxy(100, 16); cout << "Enter the legend: ";
            gotoxy(100, 17); cin >> legend;
            if (legend == '1' || legend == '2' || legend == '3' || legend == '4')
                legend = '!';
            gotoxy(100, 18); cout << "Col: "; cin >> r;
            gotoxy(100, 19); cout << "Row: "; cin >> c;
            map.edit(r - 1, c - 1, legend);
            gotoxy(100, 20); cout << "Done?(Y|N)"; cin >> reply;
        }
    }

    if (str == "%NewGame%")
    {
        static int P;
        string name;
        int r, c;
        clr_screen();
        gotoxy(100, 15);
        cout << "How many players?";
        cin >> P;
        if (P >= 4) P = 4;
        for (int i = 0; i < P; i++)
        {
            clr_screen();
            gotoxy(100, 16); cout << "Enter player " << i + 1 << " start: ";
            gotoxy(100, 17); cout << "col: "; cin >> r;
            gotoxy(100, 18); cout << "row: "; cin >> c;
            gotoxy(100, 19); cout << "Name: "; cin >> name;
            players[i] = new Tank(i, name, r, c, 'u');
            string a;
            a = toString(i);
            map.edit(r - 1, c - 1, a[0]);
            clr_screen();
            gotoxy(100, 15); cout << players[i]->_toString();
            _getch();
        }
    }

    if (str == "%%MovementDuringGame%%")
    {
        static char action;
        static int turn = 1;
        static Game game(*players, map);
        for (int count = 0; players[count] != NULL; count++)
        {
            clr_screen();
            gotoxy(100, 15); cout << "Turn " << turn;
            gotoxy(100, 16); cout << "Player " << players[count]->getnumber() + 1 << " Action: ";
            cin >> action;
            game.move(action, count);
        }
        turn++;
        clr_screen();
    }

}
void menu()
{
    cout << "Welcome to Tank CPP Game.\n";
    cout << "At first, Please Enter the field's Size.\n!!Attention!! We Recommend 10x10. Max Row Size: 11 and Max Column Size: 11\n\npress Any Key to continue...";
    _getch();
    int r; int c;
    system("cls");
    cout << "Row:"; cin >> r; cout << "Col:"; cin >> c;
    if (r >= 11) r = 11;
    if (c >= 11) c = 11;
    SetWindow(138, 60);
    system("cls");
    Map map(r, c);
    draw_field(map);
    _getch();
    clr_screen();
    legend();

    //**********************//
    //****** Editing *******//
    //**********************//
    action_display("%edit%", map);
    clr_screen();

    /****************************
    ******GAME START-Players*****
    ****************************/
    action_display("%NewGame%", map);
    _getch();

    /************************
    ******* MOVEMENTS *******
    *************************/
    while (1)
        action_display("%%MovementDuringGame%%", map);
    _getch();
}

void main()
{
    menu();
}

这很危险:

for (int count = 0; players[count] != NULL; count++)

无法保证 players[4] 或原始数组之外的任何其他内存为 null,因为它可能被不同的东西使用或仅保存旧的和未初始化的内存值,导致您的程序意外继续并导致你遇到的问题。

改进:

  • 将硬编码的玩家数量存储在常量中。
  • 使用 sizeofcountof function or macro
  • 检索数组中的项目数
  • 使用 std::vector 或类似的存储它持有的当前项目数的东西

*p_tank = tanks; 是失败的行,您应该告诉我们。

现在我可以告诉您如何解决这个问题,但您真的不应该使用原始指针和数组。当您使用 std::vector<Tank>.

时,整个问题就消失了

我没有阅读完整的代码示例,但我注意到您正在使用静态成员变量来保存 类 状态。当然这是行不通的,因为如果你有多个对象,它们共享相同的数字、行、列和方向内存。这意味着如果您有一个 Tank 对象数组并且您只更改这些数组对象中的任何一个成员,它们都将被设置为完全相同的值。这意味着你的所有坦克总是在彼此之上,朝同一个方向看。可能有四个,但你只能看到其中一个。

我并不是说这就是问题所在,但这是您可能会考虑的问题。