You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows控制台C++ ASCII 3D引擎Unicode字符颜色修改问题

解决ASCII 3D游戏引擎的控制台字符着色问题

问题原因分析

  • 你创建了自定义控制台缓冲区hConsole,但原Color函数调用GetStdHandle(STD_OUTPUT_HANDLE)获取的是默认缓冲区句柄,修改的不是当前激活的缓冲区,所以颜色不生效。
  • WriteConsoleOutputCharacter仅输出字符数据,不会应用之前设置的文本属性,需要用WriteConsoleOutput同时传递字符和颜色信息。
  • system()函数报错0xc0000142通常是命令执行环境异常,不建议用它控制控制台颜色。

修改方案

  1. 新增颜色缓冲区,存储每个字符对应的颜色属性。
  2. 替换WriteConsoleOutputCharacter为WriteConsoleOutput,同时输出字符和颜色。
  3. 调整移动/旋转速度,提升操作流畅度。

修改后的完整代码

#include <iostream>
#include <Windows.h>

int nScreenWidth = 120;
int nScreenHeight = 40;

float fPlayerX = 8.0f;
float fPlayerY = 7.0f;
float fPlayerView = 0.0f;

int nMapHeight = 16;
int nMapWidth = 16;

float fFOV = 3.14159 / 4.0;
float fDepth = 16.0f;

// 颜色属性定义(前景色,背景默认黑色)
// 前景色值:1=蓝,2=绿,3=青,4=红,5=紫,6=深黄,7=白,8=灰,9=亮蓝,10=亮绿,11=亮青,12=亮红,13=粉,14=黄,15=亮白
void SetColor(WORD* colorBuffer, int x, int y, int color) {
    colorBuffer[y * nScreenWidth + x] = color;
}

int main() {
    wchar_t* screen = new wchar_t[nScreenWidth * nScreenHeight];
    WORD* colorBuffer = new WORD[nScreenWidth * nScreenHeight];
    HANDLE hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
    SetConsoleActiveScreenBuffer(hConsole);

    std::wstring map;
    map += L"#########.......";
    map += L"#...............";
    map += L"#.......########";
    map += L"#..............#";
    map += L"#......##......#";
    map += L"#......##......#";
    map += L"#..............#";
    map += L"###............#";
    map += L"##.............#";
    map += L"#......####..###";
    map += L"#......#.......#";
    map += L"#......#.......#";
    map += L"#..............#";
    map += L"#......#########";
    map += L"#..............#";
    map += L"################";

    const HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE);

    while (true) {
        bool is_moving = false;

        if (GetAsyncKeyState((unsigned short)'A') & 0x8000) {
            fPlayerView -= 0.05f;
            is_moving = true;
        }
        if (GetAsyncKeyState((unsigned short)'D') & 0x8000) {
            fPlayerView += 0.05f;
            is_moving = true;
        }
        if (GetAsyncKeyState((unsigned short)'W') & 0x8000) {
            fPlayerX += sinf(fPlayerView) * 0.1f;
            fPlayerY += cosf(fPlayerView) * 0.1f;
            is_moving = true;

            if (map[(int)fPlayerY * nMapWidth + (int)fPlayerX] == '#') {
                fPlayerX -= sinf(fPlayerView) * 0.1f;
                fPlayerY -= cosf(fPlayerView) * 0.1f;
            }
        }
        if (GetAsyncKeyState((unsigned short)'S') & 0x8000) {
            fPlayerX -= sinf(fPlayerView) * 0.1f;
            fPlayerY -= cosf(fPlayerView) * 0.1f;
            is_moving = true;

            if (map[(int)fPlayerY * nMapWidth + (int)fPlayerX] == '#') {
                fPlayerX += sinf(fPlayerView) * 0.1f;
                fPlayerY += cosf(fPlayerView) * 0.1f;
            }
        }

        if (!is_moving) {
            WaitForSingleObject(stdin_handle, 100);
            continue;
        }

        // 初始化屏幕和颜色缓冲区
        for (int y = 0; y < nScreenHeight; y++) {
            for (int x = 0; x < nScreenWidth; x++) {
                screen[y * nScreenWidth + x] = ' ';
                colorBuffer[y * nScreenWidth + x] = 7;
            }
        }

        for (int x = 0; x < nScreenWidth; x++) {
            float fRayAngle = (fPlayerView - fFOV / 2.0f) + ((float)x / (float)nScreenWidth) * fFOV;

            float fDistanceToWall = 0;
            bool bHitWall = false;

            float fEyeX = sinf(fRayAngle);
            float fEyeY = cosf(fRayAngle);

            while (!bHitWall && fDistanceToWall < fDepth) {
                fDistanceToWall += 0.1f;

                int nTestX = (int)(fPlayerX + fEyeX * fDistanceToWall);
                int nTestY = (int)(fPlayerY + fEyeY * fDistanceToWall);

                if (nTestX < 0 || nTestX >= nMapWidth || nTestY < 0 || nTestY >= nMapHeight) {
                    bHitWall = true;
                    fDistanceToWall = fDepth;
                } else if (map[nTestY * nMapWidth + nTestX] == '#') {
                    bHitWall = true;
                }
            }

            int nCeiling = (float)(nScreenHeight / 2.0) - nScreenHeight / ((float)fDistanceToWall);
            int nFloor = nScreenHeight - nCeiling;

            short nShade = ' ';
            int wallColor = 7;
            if (fDistanceToWall <= fDepth / 4.0f) { 
                nShade = 0x2588; 
                wallColor = 14;
            } else if (fDistanceToWall < fDepth / 3.0f) { 
                nShade = 0x2593; 
                wallColor = 6;
            } else if (fDistanceToWall < fDepth / 2.0f) { 
                nShade = 0x2592; 
                wallColor = 8;
            } else if (fDistanceToWall < fDepth) { 
                nShade = 0x2591; 
                wallColor = 2;
            }

            for (int y = 0; y < nScreenHeight; y++) {
                if (y < nCeiling) {
                    screen[y * nScreenWidth + x] = ' ';
                    SetColor(colorBuffer, x, y, 0);
                } else if (y > nCeiling && y <= nFloor) {
                    screen[y * nScreenWidth + x] = nShade;
                    SetColor(colorBuffer, x, y, wallColor);
                } else {
                    float b = 1.0f - (((float)y - nScreenHeight / 2.0f) / ((float)nScreenHeight / 2.0f));
                    short fShade = ' ';
                    int floorColor = 7;
                    if (b < 0.25) { 
                        fShade = 0x2588; 
                        floorColor = 9;
                    } else if (b < 0.5) { 
                        fShade = 0x2593; 
                        floorColor = 1;
                    } else if (b < 0.75) { 
                        fShade = 0x2592; 
                        floorColor = 1;
                    } else if (b < 0.9){  
                        fShade = 0x2591; 
                        floorColor = 5;
                    } else { 
                        fShade = ' '; 
                        floorColor = 0;
                    }
                    screen[y * nScreenWidth + x] = fShade;
                    SetColor(colorBuffer, x, y, floorColor);
                }
            }
        }

        // 准备输出结构
        CHAR_INFO* charInfo = new CHAR_INFO[nScreenWidth * nScreenHeight];
        for (int i = 0; i < nScreenWidth * nScreenHeight; i++) {
            charInfo[i].Char.UnicodeChar = screen[i];
            charInfo[i].Attributes = colorBuffer[i];
        }

        // 输出到控制台
        SMALL_RECT writeRegion = {0, 0, (SHORT)(nScreenWidth-1), (SHORT)(nScreenHeight-1)};
        COORD bufferSize = {(SHORT)nScreenWidth, (SHORT)nScreenHeight};
        COORD bufferCoord = {0, 0};
        WriteConsoleOutput(hConsole, charInfo, bufferSize, bufferCoord, &writeRegion);

        delete[] charInfo;
    }

    delete[] screen;
    delete[] colorBuffer;
    return 0;
}

关键修改说明

  • 用colorBuffer数组存储每个字符的颜色属性,替代原即时设置方式。
  • 使用WriteConsoleOutput同时输出字符和颜色属性,确保颜色生效。
  • 提升移动/旋转速度,解决原操作过慢的问题。
  • 给墙壁添加随距离变化的颜色,增强3D视觉层次感。

内容的提问来源于stack exchange,提问作者RED91

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 08:38:12