1.飞行棋游戏控制台实践

1.飞行棋游戏控制台实践


1.1 知识点

需求分析

理清小项目的功能需求

本项目是一个简单的飞行棋小游戏,包含以下功能需求:

  1. 控制台初始化:设置控制台大小,隐藏光标。
  2. 多个场景:包括开始场景、游戏场景和结束场景。
  3. 开始场景逻辑:玩家可以选择开始游戏或退出游戏。
  4. 游戏场景逻辑:玩家和电脑轮流扔骰子,根据骰子的点数在格子上移动,遇到不同类型的格子触发相应的事件。
  5. 结束场景逻辑:游戏结束,显示胜利者并提供返回主菜单的选项。

原型图



整理流程图

流程图应包括以下主要部分:

  1. 控制台初始化:初始化控制台,设置游戏的屏幕尺寸和相关配置。
  2. 场景选择:玩家在开始场景选择开始游戏或退出游戏。
  3. 游戏场景:在游戏场景中,玩家和电脑依次扔骰子,移动到相应的格子,触发格子的效果。
  4. 结束场景:游戏结束,显示游戏结果。



Visio

可以使用 Visio 工具绘制流程图,将各场景逻辑、格子类型、玩家与电脑的交互等流程可视化。

控制台基础设置

定义控制台初始化函数,设置控制台的大小并隐藏光标,提供一个清晰的游戏界面。

void consoleInit(int w, int h)
{
    //控制台大小
    setConsoleSize(w, h);
    //隐藏光标
    setCursorVisibility(false);
}

主函数中调用

int main()
{
    int w = 50;
    int h = 30;
    consoleInit(w, h);
}

多个场景

游戏包括三个主要场景:开始场景、游戏场景和结束场景。通过 E_SceneType 枚举管理场景切换。

enum E_SceneType
{
    Begin, // 开始场景
    Game,  // 游戏场景
    End,   // 结束场景
};

主函数中进行选择进行选择,声明beginOrEndScene和gameScene函数,等一下再实现

int main()
{
    //...
    E_SceneType nowSceneType = Begin;
    while (true)
    {
        //根据当前场景ID来决定更新哪一个场景的逻辑
        switch (nowSceneType)
        {
        case Begin:
            //开始场景逻辑
            //先把之前的内容清理掉
            system("cls");
            //我们将开始场景逻辑封装到了函数当中
            beginOrEndScene(w, h, nowSceneType);
            break;
        case Game:
            //游戏场景逻辑
            //先把之前的内容清理掉
            system("cls");
            gameScene(w, h, nowSceneType);
            break;
        case End:
            //结束场景逻辑
            //先把之前的内容清理掉
            system("cls");
            beginOrEndScene(w, h, nowSceneType);
            break;
        }
    }
}

开始场景和结束逻辑实现

在开始场景中,玩家可以选择开始游戏或退出游戏。以下是实现该场景的核心逻辑。

//颜色枚举 用来设置颜色 方便阅读
enum E_Color
{
    Blue = 1,
    Green = 2,
    Red = 4,
    White = 7,
};

//开始场景选择索引
enum E_SceneIndex
{
    One,
    Two,
};

void beginOrEndScene(int w, int h, E_SceneType& nowSceneType)
{
    //显示不会一直更新变化的内容
    setCursorPosition(nowSceneType == Begin ? w / 2 - 3 : w / 2 - 4, 8);
    setTextColor(White);
    cout << (nowSceneType == Begin ? "飞行棋" : "游戏结束");

    //表示当前选择的是哪个选项
    E_SceneIndex nowSelIndex = One;
    //是否退出开始场景循环
    bool isQuitBegin = false;
    //开始场景的逻辑循环
    while (true)
    {
        //开始游戏
        setCursorPosition(nowSceneType == Begin ? w / 2 - 4 : w / 2 - 5, 13);
        setTextColor(nowSelIndex == One ? Red : White);
        cout << (nowSceneType == Begin ? "开始游戏" : "回到主菜单");
        //退出游戏
        setCursorPosition(w / 2 - 4, 15);
        setTextColor(nowSelIndex == Two ? Red : White);
        cout << "退出游戏";

        //输入信息的askii码
        int input = _getch();
        switch (input)
        {
        case 'W':
        case 'w':
            //由于这里只有两个选项 所以它可以直接设置类型
            //如果超过两个 就应该像C++入门当中一样去进行+-运算
            //判断当前选中的是哪个索引了
            nowSelIndex = One;
            break;
        case 'S':
        case 's':
            nowSelIndex = Two;
            break;
        case 'J':
        case 'j':
            //选中谁 处理不同的逻辑
            switch (nowSelIndex)
            {
            case One:
                //开始游戏 就应该切换场景类型
                //当选择第一个选项 如果是开始场景 就应该前往游戏场景
                //否则前往开始场景
                nowSceneType = nowSceneType == Begin ? Game : Begin;
                isQuitBegin = true;
                break;
            case Two:
                closeConsole();
                break;
            }
            break;
        }
        //和while循环配对的break
        //只有当我们选择开始游戏后 才会跳出开始场景循环 
        //来到主循环当中
        if (isQuitBegin)
            break;
    }
}

游戏场景逻辑实现

游戏场景函数

绘制红墙,地图和玩家

//游戏场景逻辑处理
void gameScene(int w, int h, E_SceneType& nowSceneType)
{
    //绘制不变的内容
    //红墙 文字提示 等等
    drawWall(w, h);

    //绘制地图信息
    Map map = Map(14, 3, 80);
    map.draw();

    //绘制玩家和电脑
    Player player = Player(0, E_PlayerType::Player);
    Player computer = Player(0, E_PlayerType::Computer);
    drawPlayer(player, computer, map);

    while (true)
    {
        //玩家扔骰子逻辑
        if (playerRandomMove(w, h, player, computer, map, nowSceneType))
            break;

        //电脑扔骰子逻辑
        if (playerRandomMove(w, h, computer, player, map, nowSceneType))
            break;
    }
}

不变的红墙

游戏场景中有不变的红墙作为边界。

void drawWall(int w, int h)
{
    //绘制红墙
    //设置为红色
    setTextColor(Red);
    //横向
    for (int i = 0; i < w; i += 2)
    {
        //第一排红墙
        setCursorPosition(i, 0);
        cout << "■";
        //最底部的红墙
        setCursorPosition(i, h - 1);
        cout << "■";
        //另外两行墙体
        setCursorPosition(i, h - 6);
        cout << "■";
        setCursorPosition(i, h - 11);
        cout << "■";
    }
    //竖向
    for (int i = 0; i < h; i++)
    {
        //最左边
        setCursorPosition(0, i);
        cout << "■";
        //最右边
        setCursorPosition(w - 2, i);
        cout << "■";
    }

    //下方提示内容绘制
    setCursorPosition(2, h - 10);
    setTextColor(White);
    cout << "□:普通格子";

    setCursorPosition(2, h - 9);
    setTextColor(Blue);
    cout << "‖:暂停,一回合不动";

    setCursorPosition(26, h - 9);
    setTextColor(Red);
    cout << "●:炸弹,倒退5格";

    setCursorPosition(2, h - 8);
    setTextColor(Red | Green);
    cout << "¤:时空隧道,随机倒退,暂停,换位置";

    setCursorPosition(2, h - 7);
    setTextColor(Red | Blue);
    cout << "★:玩家";
    setCursorPosition(10, h - 7);
    setTextColor(Green | Blue);
    cout << "▲:电脑";
    setCursorPosition(20, h - 7);
    setTextColor(White);
    cout << "◎:玩家和电脑重合";

    setCursorPosition(2, h - 5);
    setTextColor(White);
    cout << "按任意键开始扔色子";
}

格子类型枚举和格子结构体

格子类型包括普通格子、炸弹、暂停和时空隧道。每个格子都有对应的坐标和类型。

//格子的类型
enum E_Grid_Type
{
    Normal,//普通格子
    Boom,//炸弹
    Pause,//暂停
    Tunnel,//时空隧道
};

//位置结构体
struct Vector2
{
    int x;
    int y;

    Vector2()
    {
        x = 0;
        y = 0;
    }

    Vector2(int x, int y) : x(x), y(y) {}
};

//格子结构体
struct Grid
{
    E_Grid_Type type;//格子的类型
    Vector2 pos;//格子的位置

    Grid()
    {
        type = Normal;
        pos = {};
    }

    Grid(int x, int y, E_Grid_Type type)
    {
        pos = { x, y };
        this->type = type;
    }

    //根据格子类型和位置 绘制格子
    void draw()
    {
        //设置格子所在的位置 去进行颜色设置会图像绘制
        setCursorPosition(pos.x, pos.y);
        //根据格子的位置 去绘制对应类型的颜色的图像
        switch (type)
        {
        case Normal:
            setTextColor(White);
            cout << "□";
            break;
        case Boom:
            setTextColor(Red);
            cout << "●";
            break;
        case Pause:
            setTextColor(Blue);
            cout << "‖";
            break;
        case Tunnel:
            setTextColor(Red | Green);
            cout << "¤";
            break;
        }
    }
};

地图结构体

地图由多个格子组成,每个格子的位置和类型是动态生成的。

struct Map
{
    Grid* grids = nullptr;//格子数组首地址
    int gridNum;//格子数组数量

    //传入 地图的起始位置 以及 有多少个格子
    Map(int x, int y, int num)
    {
        gridNum = num;//记录格子数量
        grids = new Grid[gridNum];//动态在堆上分配格子数组

        //表示x变化的次数
        int indexX = 0;
        //表示y变化的次数
        int indexY = 0;
        //x的步长
        int stepNum = 2;

        //对地图信息进行初始化
        int randomNum;
        for (int i = 0; i < gridNum; i++)
        {
            //初始化每一个格子信息
            //初始化格子类型
            randomNum = getRandom(0, 100);
            //普通格子 85% 或者 这个格子是第一个或者最后一个格子 那么就必须是普通格子
            if (randomNum < 85 || i == 0 || i == gridNum - 1)
                grids[i].type = Normal;
            //炸弹 5%
            else if (randomNum >= 85 && randomNum < 90)
                grids[i].type = Boom;
            //暂停 5%
            else if (randomNum >= 90 && randomNum < 95)
                grids[i].type = Pause;
            //时空隧道 5%
            else
                grids[i].type = Tunnel;

            //初始化格子位置
            grids[i].pos = Vector2(x, y);

            if (indexX == 10)
            {
                //加Y
                y += 1;
                ++indexY;
                //Y变化结束
                if (indexY == 2)
                {
                    //y加了2次过后 就应该进行下一轮变化了
                    indexX = 0;
                    indexY = 0;
                    //步长反向
                    stepNum = -stepNum;
                }
            }
            else
            {
                //X变化
                x += stepNum;
                ++indexX;
            }

        }
    }

    ~Map()
    {
        if (grids != nullptr)
        {
            delete[] grids;
            grids = nullptr;
        }
    }

    //绘制地图
    void draw()
    {
        //通过循环命令自己管理的格子们去执行一个绘制的行为
        for (int i = 0; i < gridNum; i++)
        {
            //调用格子数组中每一个格子的绘制方法
            grids[i].draw();
        }
    }
};

玩家和电脑结构体

玩家和电脑在游戏中的位置由 Player 结构体管理。

//玩家电脑枚举
enum class E_PlayerType
{
    Player,//玩家
    Computer,//电脑
};

struct Player
{
    //玩家类型
    E_PlayerType type;
    //当前所在格子位置
    int nowIndex;
    //是否暂停
    bool isPause;

    Player(int index, E_PlayerType type) : nowIndex(index), type(type)
    {
        isPause = false;
    }

    void draw(Map& mapInfo)
    {
        //所在的位置
        //获取到地图信息
        Grid grid = mapInfo.grids[nowIndex];
        setCursorPosition(grid.pos.x, grid.pos.y);
        //根据类型去绘制图像和颜色
        switch (type)
        {
        case E_PlayerType::Player:
            setTextColor(Red | Blue);
            cout << "★";
            break;
        case E_PlayerType::Computer:
            setTextColor(Green | Blue);
            cout << "▲";
            break;
        }
    }
};

要对玩家进行绘制

void drawPlayer(Player& player, Player& computer, Map& map)
{
    //重合时
    if (player.nowIndex == computer.nowIndex)
    {
        //绘制重合时的图像
        //得到位置
        Grid grid = map.grids[player.nowIndex];
        setCursorPosition(grid.pos.x, grid.pos.y);
        setTextColor(White);
        cout << "◎";
    }
    //不重合时
    else
    {
        player.draw(map);
        computer.draw(map);
    }
}

扔骰子

玩家和电脑通过扔骰子来决定在格子上移动。

//擦除提示位置的信息
void clearInfo(int w, int h)
{
    //擦除之前显示的提示信息
    for (int j = 5; j >= 2; j--)
    {
        for (int i = 2; i < w - 2; i++)
        {
            setCursorPosition(i, h - j);
            cout << " ";
        }
    }
}

bool randomMove(int w, int h, Player& player, Player& otherPlayer, Map& map)
{
    //擦除之前显示的提示信息
    clearInfo(w, h);

    //根据扔色子的对象决定提示信息的文本颜色
    setTextColor(player.type == E_PlayerType::Player ? Red | Blue : Green | Blue);

    //暂停相关逻辑
    if (player.isPause)
    {
        //提示暂停相关
        setCursorPosition(2, h - 5);
        cout << "处于暂停状态," << (player.type == E_PlayerType::Player ? "你" : "电脑") << "需要暂停过一回合";
        setCursorPosition(2, h - 4);
        cout << "请按任意键,让" << (player.type == E_PlayerType::Player ? "电脑" : "你") << "开始扔骰子";

        //直接不扔色子 直接返回 
        //提示暂停
        player.isPause = false;
        return false;
    }

    //随机扔骰子
    int randomNum = getRandom(1, 6);
    //让对象移动对应随机扔出的步数
    player.nowIndex += randomNum;

    //提示扔了多少点数
    setCursorPosition(2, h - 5);
    cout << (player.type == E_PlayerType::Player ? "你" : "电脑") << "扔出的点数为:" << randomNum;

    //移动 判断所在格子是什么 处理对应逻辑
    if (player.nowIndex >= map.gridNum - 1)
    {
        //如果超过了终点 就应该拉回来
        player.nowIndex = map.gridNum - 1;
        setCursorPosition(2, h - 4);
        cout << (player.type == E_PlayerType::Player ? "恭喜你,你率先到达了终点" : "很遗憾,电脑率先到达了终点");
        setCursorPosition(2, h - 3);
        cout << "请按任意键结束游戏";
        //应该结束游戏了 到达了终点
        return true;
    }
    else
    {
        //没有到终点,就判断当前对象所在格子是什么格子
        //处理对应逻辑
        Grid grid = map.grids[player.nowIndex];
        switch (grid.type)
        {
        case Normal:
            //不处理任何逻辑
            setCursorPosition(2, h - 4);
            cout << (player.type == E_PlayerType::Player ? "你" : "电脑") << "到达了一个安全位置";
            setCursorPosition(2, h - 3);
            cout << "请按任意键,让" << (player.type == E_PlayerType::Player ? "电脑" : "你") << "开始扔骰子";
            break;
        case Boom:
            player.nowIndex -= 5;
            //遇到炸弹 最多退掉起点 不能比起点还小
            if (player.nowIndex < 0)
                player.nowIndex = 0;
            setCursorPosition(2, h - 4);
            cout << (player.type == E_PlayerType::Player ? "你" : "电脑") << "踩到了炸弹,退后5格";
            setCursorPosition(2, h - 3);
            cout << "请按任意键,让" << (player.type == E_PlayerType::Player ? "电脑" : "你") << "开始扔骰子";

            break;
        case Pause:
            //暂停一回合
            //必须添加一个暂停标识 才能知道下一回合需要暂停
            //不能扔色子
            player.isPause = true;
            setCursorPosition(2, h - 4);
            cout << (player.type == E_PlayerType::Player ? "你" : "电脑") << "到了暂停点,你需要暂停一回合";
            setCursorPosition(2, h - 3);
            cout << "请按任意键,让" << (player.type == E_PlayerType::Player ? "电脑" : "你") << "开始扔骰子";
            break;
        case Tunnel:
            //随机 退5格   暂停一回合  交换位置
            randomNum = getRandom(1, 3);
            setCursorPosition(2, h - 4);
            cout << (player.type == E_PlayerType::Player ? "你" : "电脑") << "到达了时空隧道";
            setCursorPosition(2, h - 3);
            //退5格
            if (randomNum == 1)
            {
                player.nowIndex -= 5;
                if (player.nowIndex < 0)
                    player.nowIndex = 0;
                cout << "触发了退5格";
            }
            //暂停
            else if (randomNum == 2)
            {
                player.isPause = true;
                cout << "触发了暂停一回合";
            }
            //交换位置
            else
            {
                int temp = player.nowIndex;
                player.nowIndex = otherPlayer.nowIndex;
                otherPlayer.nowIndex = temp;
                cout << "触发了交换位置";
            }
            setCursorPosition(2, h - 2);
            cout << "请按任意键,让" << (player.type == E_PlayerType::Player ? "电脑" : "你") << "开始扔骰子";
            break;
        }
    }
    //默认就没有到达终点
    return false;
}


bool playerRandomMove(int w, int h, Player& player, Player& otherPlayer, Map& map, E_SceneType& nowSceneType)
{
    //玩家扔骰子逻辑
    //检测输入
    _getch();
    //扔骰子的逻辑
    bool isGameOver = randomMove(w, h, player, otherPlayer, map);
    //绘制地图
    map.draw();
    //绘制玩家
    drawPlayer(player, otherPlayer, map);
    //判断是否要结束游戏
    if (isGameOver)
    {
        nowSceneType = End;
        _getch();
    }

    return isGameOver;
}

1.2 知识点代码

基础实践.cpp

#include <iostream>
#include <conio.h>
#include "CustomConsole.h"
using namespace std;

#pragma region 1 控制台初始化
void consoleInit(int w, int h)
{
    //控制台大小
    setConsoleSize(w, h);
    //隐藏光标
    setCursorVisibility(false);
}
#pragma endregion

#pragma region 2 场景选择相关
enum E_SceneType
{
    Begin,//开始场景
    Game,//游戏场景
    End,//结束场景
};
#pragma endregion

#pragma region 3 开始场景逻辑相关 and 9 结束场景逻辑相关
//颜色枚举 用来设置颜色 方便阅读
enum E_Color
{
    Blue = 1,
    Green = 2,
    Red = 4,
    White = 7,
};
//开始场景选择索引
enum E_SceneIndex
{
    One,
    Two,
};

void beginOrEndScene(int w, int h, E_SceneType& nowSceneType)
{
    //显示不会一直更新变化的内容
    setCursorPosition(nowSceneType == Begin ? w / 2 - 3 : w / 2 - 4, 8);
    setTextColor(White);
    cout << (nowSceneType == Begin ? "飞行棋" : "游戏结束");

    //表示当前选择的是哪个选项
    E_SceneIndex nowSelIndex = One;
    //是否退出开始场景循环
    bool isQuitBegin = false;
    //开始场景的逻辑循环
    while (true)
    {
        //开始游戏
        setCursorPosition(nowSceneType == Begin ? w / 2 - 4 : w / 2 - 5, 13);
        setTextColor(nowSelIndex == One ? Red : White);
        cout << (nowSceneType == Begin ? "开始游戏" : "回到主菜单");
        //退出游戏
        setCursorPosition(w / 2 - 4, 15);
        setTextColor(nowSelIndex == Two ? Red : White);
        cout << "退出游戏";

        //输入信息的askii码
        int input = _getch();
        switch (input)
        {
        case 'W':
        case 'w':
            //由于这里只有两个选项 所以它可以直接设置类型
            //如果超过两个 就应该像C++入门当中一样去进行+-运算
            //判断当前选中的是哪个索引了
            nowSelIndex = One;
            break;
        case 'S':
        case 's':
            nowSelIndex = Two;
            break;
        case 'J':
        case 'j':
            //选中谁 处理不同的逻辑
            switch (nowSelIndex)
            {
            case One:
                //开始游戏 就应该切换场景类型
                //当选择第一个选项 如果是开始场景 就应该前往游戏场景
                //否则前往开始场景
                nowSceneType = nowSceneType == Begin ? Game : Begin;
                isQuitBegin = true;
                break;
            case Two:
                closeConsole();
                break;
            }
            break;
        }
        //和while循环配对的break
        //只有当我们选择开始游戏后 才会跳出开始场景循环 
        //来到主循环当中
        if (isQuitBegin)
            break;
    }
}
#pragma endregion

#pragma region 游戏场景逻辑

#pragma region 4 不变的红墙逻辑相关
void drawWall(int w, int h)
{
    //绘制红墙
    //设置为红色
    setTextColor(Red);
    //横向
    for (int i = 0; i < w; i += 2)
    {
        //第一排红墙
        setCursorPosition(i, 0);
        cout << "■";
        //最底部的红墙
        setCursorPosition(i, h - 1);
        cout << "■";
        //另外两行墙体
        setCursorPosition(i, h - 6);
        cout << "■";
        setCursorPosition(i, h - 11);
        cout << "■";
    }
    //竖向
    for (int i = 0; i < h; i++)
    {
        //最左边
        setCursorPosition(0, i);
        cout << "■";
        //最右边
        setCursorPosition(w - 2, i);
        cout << "■";
    }

    //下方提示内容绘制
    setCursorPosition(2, h - 10);
    setTextColor(White);
    cout << "□:普通格子";

    setCursorPosition(2, h - 9);
    setTextColor(Blue);
    cout << "‖:暂停,一回合不动";

    setCursorPosition(26, h - 9);
    setTextColor(Red);
    cout << "●:炸弹,倒退5格";

    setCursorPosition(2, h - 8);
    setTextColor(Red | Green);
    cout << "¤:时空隧道,随机倒退,暂停,换位置";

    setCursorPosition(2, h - 7);
    setTextColor(Red | Blue);
    cout << "★:玩家";
    setCursorPosition(10, h - 7);
    setTextColor(Green | Blue);
    cout << "▲:电脑";
    setCursorPosition(20, h - 7);
    setTextColor(White);
    cout << "◎:玩家和电脑重合";

    setCursorPosition(2, h - 5);
    setTextColor(White);
    cout << "按任意键开始扔色子";
}
#pragma endregion

#pragma region 5 格子枚举和格子结构体相关
//格子的类型
enum E_Grid_Type
{
    Normal,//普通格子
    Boom,//炸弹
    Pause,//暂停
    Tunnel,//时空隧道
};

//位置结构体
struct Vector2
{
    int x;
    int y;

    Vector2()
    {
        x = 0;
        y = 0;
    }

    Vector2(int x, int y) : x(x), y(y) {}
};

//格子结构体
struct Grid
{
    E_Grid_Type type;//格子的类型
    Vector2 pos;//格子的位置

    Grid()
    {
        type = Normal;
        pos = {};
    }

    Grid(int x, int y, E_Grid_Type type)
    {
        pos = { x, y };
        this->type = type;
    }

    //根据格子类型和位置 绘制格子
    void draw()
    {
        //设置格子所在的位置 去进行颜色设置会图像绘制
        setCursorPosition(pos.x, pos.y);
        //根据格子的位置 去绘制对应类型的颜色的图像
        switch (type)
        {
        case Normal:
            setTextColor(White);
            cout << "□";
            break;
        case Boom:
            setTextColor(Red);
            cout << "●";
            break;
        case Pause:
            setTextColor(Blue);
            cout << "‖";
            break;
        case Tunnel:
            setTextColor(Red | Green);
            cout << "¤";
            break;
        }
    }
};
#pragma endregion

#pragma region 6 地图结构体相关
struct Map
{
    Grid* grids = nullptr;//格子数组首地址
    int gridNum;//格子数组数量

    //传入 地图的起始位置 以及 有多少个格子
    Map(int x, int y, int num)
    {
        gridNum = num;//记录格子数量
        grids = new Grid[gridNum];//动态在堆上分配格子数组

        //表示x变化的次数
        int indexX = 0;
        //表示y变化的次数
        int indexY = 0;
        //x的步长
        int stepNum = 2;

        //对地图信息进行初始化
        int randomNum;
        for (int i = 0; i < gridNum; i++)
        {
            //初始化每一个格子信息
            //初始化格子类型
            randomNum = getRandom(0, 100);
            //普通格子 85% 或者 这个格子是第一个或者最后一个格子 那么就必须是普通格子
            if (randomNum < 85 || i == 0 || i == gridNum - 1)
                grids[i].type = Normal;
            //炸弹 5%
            else if (randomNum >= 85 && randomNum < 90)
                grids[i].type = Boom;
            //暂停 5%
            else if (randomNum >= 90 && randomNum < 95)
                grids[i].type = Pause;
            //时空隧道 5%
            else
                grids[i].type = Tunnel;

            //初始化格子位置
            grids[i].pos = Vector2(x, y);

            if (indexX == 10)
            {
                //加Y
                y += 1;
                ++indexY;
                //Y变化结束
                if (indexY == 2)
                {
                    //y加了2次过后 就应该进行下一轮变化了
                    indexX = 0;
                    indexY = 0;
                    //步长反向
                    stepNum = -stepNum;
                }
            }
            else
            {
                //X变化
                x += stepNum;
                ++indexX;
            }

        }
    }

    ~Map()
    {
        if (grids != nullptr)
        {
            delete[] grids;
            grids = nullptr;
        }
    }

    //绘制地图
    void draw()
    {
        //通过循环命令自己管理的格子们去执行一个绘制的行为
        for (int i = 0; i < gridNum; i++)
        {
            //调用格子数组中每一个格子的绘制方法
            grids[i].draw();
        }
    }
};
#pragma endregion

#pragma region 7 玩家枚举和玩家结构体相关
//玩家电脑枚举
enum class E_PlayerType
{
    Player,//玩家
    Computer,//电脑
};

struct Player
{
    //玩家类型
    E_PlayerType type;
    //当前所在格子位置
    int nowIndex;
    //是否暂停
    bool isPause;

    Player(int index, E_PlayerType type) : nowIndex(index), type(type)
    {
        isPause = false;
    }

    void draw(Map& mapInfo)
    {
        //所在的位置
        //获取到地图信息
        Grid grid = mapInfo.grids[nowIndex];
        setCursorPosition(grid.pos.x, grid.pos.y);
        //根据类型去绘制图像和颜色
        switch (type)
        {
        case E_PlayerType::Player:
            setTextColor(Red | Blue);
            cout << "★";
            break;
        case E_PlayerType::Computer:
            setTextColor(Green | Blue);
            cout << "▲";
            break;
        }
    }
};
#pragma endregion

#pragma region 7 玩家和电脑绘制相关
void drawPlayer(Player& player, Player& computer, Map& map)
{
    //重合时
    if (player.nowIndex == computer.nowIndex)
    {
        //绘制重合时的图像
        //得到位置
        Grid grid = map.grids[player.nowIndex];
        setCursorPosition(grid.pos.x, grid.pos.y);
        setTextColor(White);
        cout << "◎";
    }
    //不重合时
    else
    {
        player.draw(map);
        computer.draw(map);
    }
}
#pragma endregion

#pragma region 8 扔骰子相关逻辑
//擦除提示位置的信息
void clearInfo(int w, int h)
{
    //擦除之前显示的提示信息
    for (int j = 5; j >= 2; j--)
    {
        for (int i = 2; i < w - 2; i++)
        {
            setCursorPosition(i, h - j);
            cout << " ";
        }
    }
}

bool randomMove(int w, int h, Player& player, Player& otherPlayer, Map& map)
{
    //擦除之前显示的提示信息
    clearInfo(w, h);

    //根据扔色子的对象决定提示信息的文本颜色
    setTextColor(player.type == E_PlayerType::Player ? Red | Blue : Green | Blue);

    //暂停相关逻辑
    if (player.isPause)
    {
        //提示暂停相关
        setCursorPosition(2, h - 5);
        cout << "处于暂停状态," << (player.type == E_PlayerType::Player ? "你" : "电脑") << "需要暂停过一回合";
        setCursorPosition(2, h - 4);
        cout << "请按任意键,让" << (player.type == E_PlayerType::Player ? "电脑" : "你") << "开始扔骰子";

        //直接不扔色子 直接返回 
        //提示暂停
        player.isPause = false;
        return false;
    }

    //随机扔骰子
    int randomNum = getRandom(1, 6);
    //让对象移动对应随机扔出的步数
    player.nowIndex += randomNum;

    //提示扔了多少点数
    setCursorPosition(2, h - 5);
    cout << (player.type == E_PlayerType::Player ? "你" : "电脑") << "扔出的点数为:" << randomNum;

    //移动 判断所在格子是什么 处理对应逻辑
    if (player.nowIndex >= map.gridNum - 1)
    {
        //如果超过了终点 就应该拉回来
        player.nowIndex = map.gridNum - 1;
        setCursorPosition(2, h - 4);
        cout << (player.type == E_PlayerType::Player ? "恭喜你,你率先到达了终点" : "很遗憾,电脑率先到达了终点");
        setCursorPosition(2, h - 3);
        cout << "请按任意键结束游戏";
        //应该结束游戏了 到达了终点
        return true;
    }
    else
    {
        //没有到终点,就判断当前对象所在格子是什么格子
        //处理对应逻辑
        Grid grid = map.grids[player.nowIndex];
        switch (grid.type)
        {
        case Normal:
            //不处理任何逻辑
            setCursorPosition(2, h - 4);
            cout << (player.type == E_PlayerType::Player ? "你" : "电脑") << "到达了一个安全位置";
            setCursorPosition(2, h - 3);
            cout << "请按任意键,让" << (player.type == E_PlayerType::Player ? "电脑" : "你") << "开始扔骰子";
            break;
        case Boom:
            player.nowIndex -= 5;
            //遇到炸弹 最多退掉起点 不能比起点还小
            if (player.nowIndex < 0)
                player.nowIndex = 0;
            setCursorPosition(2, h - 4);
            cout << (player.type == E_PlayerType::Player ? "你" : "电脑") << "踩到了炸弹,退后5格";
            setCursorPosition(2, h - 3);
            cout << "请按任意键,让" << (player.type == E_PlayerType::Player ? "电脑" : "你") << "开始扔骰子";

            break;
        case Pause:
            //暂停一回合
            //必须添加一个暂停标识 才能知道下一回合需要暂停
            //不能扔色子
            player.isPause = true;
            setCursorPosition(2, h - 4);
            cout << (player.type == E_PlayerType::Player ? "你" : "电脑") << "到了暂停点,你需要暂停一回合";
            setCursorPosition(2, h - 3);
            cout << "请按任意键,让" << (player.type == E_PlayerType::Player ? "电脑" : "你") << "开始扔骰子";
            break;
        case Tunnel:
            //随机 退5格   暂停一回合  交换位置
            randomNum = getRandom(1, 3);
            setCursorPosition(2, h - 4);
            cout << (player.type == E_PlayerType::Player ? "你" : "电脑") << "到达了时空隧道";
            setCursorPosition(2, h - 3);
            //退5格
            if (randomNum == 1)
            {
                player.nowIndex -= 5;
                if (player.nowIndex < 0)
                    player.nowIndex = 0;
                cout << "触发了退5格";
            }
            //暂停
            else if (randomNum == 2)
            {
                player.isPause = true;
                cout << "触发了暂停一回合";
            }
            //交换位置
            else
            {
                int temp = player.nowIndex;
                player.nowIndex = otherPlayer.nowIndex;
                otherPlayer.nowIndex = temp;
                cout << "触发了交换位置";
            }
            setCursorPosition(2, h - 2);
            cout << "请按任意键,让" << (player.type == E_PlayerType::Player ? "电脑" : "你") << "开始扔骰子";
            break;
        }
    }
    //默认就没有到达终点
    return false;
}


bool playerRandomMove(int w, int h, Player& player, Player& otherPlayer, Map& map, E_SceneType& nowSceneType)
{
    //玩家扔骰子逻辑
    //检测输入
    _getch();
    //扔骰子的逻辑
    bool isGameOver = randomMove(w, h, player, otherPlayer, map);
    //绘制地图
    map.draw();
    //绘制玩家
    drawPlayer(player, otherPlayer, map);
    //判断是否要结束游戏
    if (isGameOver)
    {
        nowSceneType = End;
        _getch();
    }

    return isGameOver;
}

#pragma endregion

//游戏场景逻辑处理
void gameScene(int w, int h, E_SceneType& nowSceneType)
{
    //绘制不变的内容
    //红墙 文字提示 等等
    drawWall(w, h);

    //绘制地图信息
    Map map = Map(14, 3, 80);
    map.draw();

    //绘制玩家和电脑
    Player player = Player(0, E_PlayerType::Player);
    Player computer = Player(0, E_PlayerType::Computer);
    drawPlayer(player, computer, map);

    while (true)
    {
        //玩家扔骰子逻辑
        if (playerRandomMove(w, h, player, computer, map, nowSceneType))
            break;

        //电脑扔骰子逻辑
        if (playerRandomMove(w, h, computer, player, map, nowSceneType))
            break;
    }
}
#pragma endregion

int main()
{
    #pragma region 1 控制台初始化
    int w = 50;
    int h = 30;
    consoleInit(w, h);
    #pragma endregion

    #pragma region 2 场景选择相关
    E_SceneType nowSceneType = Begin;
    while (true)
    {
        //根据当前场景ID来决定更新哪一个场景的逻辑
        switch (nowSceneType)
        {
        case Begin:
            //开始场景逻辑
            //先把之前的内容清理掉
            system("cls");
            //我们将开始场景逻辑封装到了函数当中
            beginOrEndScene(w, h, nowSceneType);
            break;
        case Game:
            //游戏场景逻辑
            //先把之前的内容清理掉
            system("cls");
            gameScene(w, h, nowSceneType);
            break;
        case End:
            //结束场景逻辑
            //先把之前的内容清理掉
            system("cls");
            beginOrEndScene(w, h, nowSceneType);
            break;
        }
    }
    #pragma endregion

}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏