Windows控制台C++ ASCII 3D引擎Unicode字符颜色修改问题
解决ASCII 3D游戏引擎的控制台字符着色问题
问题原因分析
- 你创建了自定义控制台缓冲区
hConsole,但原Color函数调用GetStdHandle(STD_OUTPUT_HANDLE)获取的是默认缓冲区句柄,修改的不是当前激活的缓冲区,所以颜色不生效。 WriteConsoleOutputCharacter仅输出字符数据,不会应用之前设置的文本属性,需要用WriteConsoleOutput同时传递字符和颜色信息。system()函数报错0xc0000142通常是命令执行环境异常,不建议用它控制控制台颜色。
修改方案
- 新增颜色缓冲区,存储每个字符对应的颜色属性。
- 替换
WriteConsoleOutputCharacter为WriteConsoleOutput,同时输出字符和颜色。 - 调整移动/旋转速度,提升操作流畅度。
修改后的完整代码
#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
相关产品推荐
相关产品推荐

