iT邦幫忙

2025 iThome 鐵人賽

DAY 3
1
自我挑戰組

用 C++ 實作簡易第一人稱視角遊戲:從入門到理解 Ray Casting系列 第 3

Day 3 | 用 wchar_t* 畫畫?Console 也能做遊戲畫面!Part 2

  • 分享至 

  • xImage
  •  

嗨大家好,這裡是鐵匠史密斯!

昨天我們建立了一塊畫布(wchar_t* screen),今天我要正式把這塊畫布「貼」到 Console 畫面上!

說真的,我對 windows.h 和 Win32 API 其實還不太熟 😂
不過在研究 javidx9 的程式碼時,我慢慢搞懂了幾個重要函式的作用,像是:

  • 怎麼建立一塊畫面緩衝區(CreateConsoleScreenBuffer
  • 怎麼把畫布設定成主控台畫面(SetConsoleActiveScreenBuffer
  • 怎麼把畫布貼上去(WriteConsoleOutputCharacter

下面我就用我的版本示範一次!

如何建立畫面緩衝區(CreateConsoleScreenBuffer

目前已經有80 * 30 = 2400 elements 的 1-D wchar_t* 矩陣 screen (畫布)
那我們要把這個畫布變成 Console 主控台的自定義畫面緩衝區

// Create console handler 
HANDLE hConsole { CreateConsoleScreenBuffer(
    GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL) };

該行意思就是: 請Windows幫我們建立一個自定義畫面緩衝區
第一個參數很重要,我們需要把畫布寫到畫面緩衝區,所以一定要 GENERIC_WRITE !! GENERIC_READ 可加可不加,但這算是慣用寫法: GENERIC_READ | GENERIC_WRITE = 兩個權限都給!
其餘參數均是默認參數,詳情可以詳見 Reference 的 微軟主控台緩衝區資訊

怎麼把畫布設定成主控台畫面(SetConsoleActiveScreenBuffer

既然我們有屬於自己的自定義緩衝區 hConsole 了,接下來,就是:
將自定義畫面緩衝區設定為 「作用中的畫面緩衝區」

SetConsoleActiveScreenBuffer(hConsole);

簡單來說,原本Windows默認的Console 「作用中的畫面緩衝區」 長得像這樣:
https://ithelp.ithome.com.tw/upload/images/20250803/20157653hhxz1k6MqQ.png
既然我們已經有了自定義的畫面緩衝區 hConsole 了,會什麼不讓它變成 「作用中的畫面緩衝區」= 顯示在畫面的緩衝區 呢? 這樣我們 Console 顯示的結果就會替換成我們的畫布了,就像這樣:
https://ithelp.ithome.com.tw/upload/images/20250803/20157653YwVRieJGXy.png

怎麼把畫布貼上去(WriteConsoleOutputCharacter

接下來,就是要把畫布 screen 寫在已成為「作用中」的畫面緩衝區 hConsole
程式執行時可以將畫布順利地顯示在主控台畫面上!!

while (true) {
    // Write cavas into console to show screen 
    WriteConsoleOutputCharacter(hConsole, screen, nScreenWidth * nScreenHeight, { 0,0 }, &dwBytesWritten);
    }

Windows( Windows.h )提供的函式 WriteConsoleOutputCharacter所表達的意思是:

將我的1-D矩陣(畫布) screen 寫入到我的自訂義作用畫面緩衝區 hConsole 內, 共有 nScreenWidth x nScreenHeight 個字元,從緩衝區(畫面)的第一個位置 { 0, 0 } 開始寫入字元, 並用 dwBytesWritten 來紀錄共寫入了幾個字元
關於參數的詳細資訊,可以參照 微軟的WriteConsoleOutputCharacter

如此,就可以在while迴圈內一直顯示我們的畫面了(前1000個字元是 ' ',後面1400個字元是 '%'),結果如下:
https://ithelp.ithome.com.tw/upload/images/20250803/201576537opu9SadEN.png

對了,之所以會設定寬 nScreenWidth 、高 nScreenHeight,就是為了讓我們可以調整Console主控台的大小,現在我們的主控台大小是80(寬) * 30(高):

const int nScreenWidth { 80 };
const int nScreenHeight { 30 };

所以呢,主控台大小需要調整,可以從主控台按右鍵 -> 啟動大小進行調整,如圖:
https://ithelp.ithome.com.tw/upload/images/20250803/20157653z5LNJeXWbJ.png

今日總結 :

  • 怎麼建立一塊畫面緩衝區(CreateConsoleScreenBuffer
  • 怎麼把畫布設定成主控台畫面(SetConsoleActiveScreenBuffer
  • 怎麼把畫布貼上去(WriteConsoleOutputCharacter

目前進度的程式碼 main.cpp 如下:

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

int main()
{
    const int nScreenWidth { 80 };
    const int nScreenHeight { 30 };

    // Create canva
    wchar_t* screen = new wchar_t[nScreenWidth * nScreenHeight];
    for (int i = 0; i < nScreenWidth * nScreenHeight; i++)
    {
        if (i < 1200)
            screen[i] = L' ';  
        else
            screen[i] = L'%';  
    }
    screen[nScreenWidth * nScreenHeight - 1] = '\0'; // null terminator


    // Create console handler (custom screen buffer)
    HANDLE hConsole { CreateConsoleScreenBuffer(
        GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL) };
    // Set Active Screen Buffer so that console will show this buffer first
    SetConsoleActiveScreenBuffer(hConsole);
    DWORD dwBytesWritten { 0 };

    while (true) {
        // Write cavas into console to show screen 
        WriteConsoleOutputCharacter(hConsole, screen, nScreenWidth * nScreenHeight, { 0,0 }, &dwBytesWritten);
    }
    return 0;
}

好啦,畫布我們貼上去了,畫面也成功印到 Console 上了!
那接下來要畫什麼呢?
沒錯 —— 我們要開始畫地圖啦!

下一篇我會開始介紹 wstring map,也就是這款遊戲裡的迷宮地圖是怎麼用字串做出來的?
讓我們繼續走下去~

Reference

  1. 微軟主控台緩衝區資訊
  2. 微軟的WriteConsoleOutputCharacter

上一篇
Day 2 | 用 wchar_t* 畫畫?Console 也能做遊戲畫面!Part 1
下一篇
Day 4 | 解析 map:用 wstring 寫迷宮地圖
系列文
用 C++ 實作簡易第一人稱視角遊戲:從入門到理解 Ray Casting30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言