8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C++で作るゲームエンジン自作シリーズ

Part1 設計編 Part2 ウィンドウ Part3 OpenGL Part4 シェーダー Part5 テクスチャ Part6 3Dモデル Part7 当たり判定
- 👈 Now - - - - -

はじめに

ゲームエンジン作りの第一歩はウィンドウを開くこと。

「え、ウィンドウ開くだけ?簡単じゃん」

...と思うでしょ?Win32 APIで書くとこうなる:

// Win32 APIでウィンドウを開く(抜粋)
WNDCLASSEX wc = {};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.lpszClassName = L"GameWindow";
RegisterClassEx(&wc);

HWND hwnd = CreateWindowEx(
    0, L"GameWindow", L"Game",
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
    nullptr, nullptr, hInstance, nullptr
);
// ... まだ続く

しかもこれWindowsだけ。macOSならCocoa、LinuxならX11を書く必要がある。

GLFWを使えば一発。

GLFWとは

GLFW(Graphics Library Framework) は、ウィンドウ作成とOpenGLコンテキスト管理を抽象化するライブラリ。

できること:

  • クロスプラットフォームなウィンドウ作成
  • OpenGLコンテキストの初期化
  • キーボード・マウス入力の取得
  • ジョイスティック対応

開発環境のセットアップ

vcpkgでインストール

# vcpkgをインストール(まだの場合)
git clone https://github.com/microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg integrate install

# GLFWをインストール
.\vcpkg install glfw3:x64-windows

GLADのダウンロード

OpenGLの関数は実行時にロードする必要がある。GLADがこれを自動化してくれる。

  1. https://glad.dav1d.de/ にアクセス
  2. 以下を選択:
    • Language: C/C++
    • Specification: OpenGL
    • API gl: Version 3.3
    • Profile: Core
  3. 「Generate」をクリック
  4. zipをダウンロードして external/glad/ に展開

プロジェクト構成

cpp-game-engine/
├── CMakeLists.txt
├── src/
│   └── main.cpp
└── external/
    └── glad/
        ├── include/
        │   ├── glad/
        │   │   └── glad.h
        │   └── KHR/
        │       └── khrplatform.h
        └── src/
            └── glad.c

CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(GameEngine VERSION 1.0)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# GLFWを探す
find_package(glfw3 CONFIG REQUIRED)
find_package(OpenGL REQUIRED)

# GLADをライブラリとして追加
add_library(glad STATIC external/glad/src/glad.c)
target_include_directories(glad PUBLIC external/glad/include)

# メインの実行ファイル
add_executable(GameEngine src/main.cpp)

target_link_libraries(GameEngine PRIVATE
    glfw
    glad
    OpenGL::GL
)

target_include_directories(GameEngine PRIVATE
    ${CMAKE_SOURCE_DIR}/external/glad/include
)

main.cpp

いよいよコードを書く!

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>

// ウィンドウサイズ変更時のコールバック
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
    glViewport(0, 0, width, height);
}

// キー入力のコールバック
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
        glfwSetWindowShouldClose(window, true);
    }
}

int main() {
    // GLFWの初期化
    if (!glfwInit()) {
        std::cerr << "Failed to initialize GLFW" << std::endl;
        return -1;
    }

    // OpenGLバージョンを指定(3.3 Core Profile)
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // ウィンドウを作成
    GLFWwindow* window = glfwCreateWindow(800, 600, "Game Engine", nullptr, nullptr);
    if (!window) {
        std::cerr << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }

    // OpenGLコンテキストを現在のスレッドに設定
    glfwMakeContextCurrent(window);

    // GLADでOpenGL関数をロード
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cerr << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // ビューポートを設定
    glViewport(0, 0, 800, 600);

    // コールバックを登録
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    glfwSetKeyCallback(window, key_callback);

    // OpenGL情報を表示
    std::cout << "OpenGL Version: " << glGetString(GL_VERSION) << std::endl;
    std::cout << "GLSL Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
    std::cout << "Vendor: " << glGetString(GL_VENDOR) << std::endl;
    std::cout << "Renderer: " << glGetString(GL_RENDERER) << std::endl;

    // メインループ
    while (!glfwWindowShouldClose(window)) {
        // 背景色をクリア(紺色)
        glClearColor(0.1f, 0.1f, 0.2f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // ダブルバッファリング:描画バッファを交換
        glfwSwapBuffers(window);
        
        // イベントを処理
        glfwPollEvents();
    }

    // 終了処理
    glfwDestroyWindow(window);
    glfwTerminate();
    
    return 0;
}

コードの解説

1. GLFWの初期化

if (!glfwInit()) {
    std::cerr << "Failed to initialize GLFW" << std::endl;
    return -1;
}

GLFWを使う前に必ず glfwInit() を呼ぶ。

2. OpenGLバージョンの指定

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

OpenGL 3.3 Core Profileを使う。

  • Core Profile: 古い機能(固定機能パイプライン)を削除した新しいAPI
  • Compatibility Profile: 古いコードとの互換性あり(非推奨)

Core Profileを使う理由:

  • 明示的で理解しやすい
  • パフォーマンスが良い
  • シェーダー必須(=モダンな書き方を学べる)

3. ウィンドウの作成

GLFWwindow* window = glfwCreateWindow(800, 600, "Game Engine", nullptr, nullptr);

引数:

  • 幅: 800
  • 高さ: 600
  • タイトル: "Game Engine"
  • モニター: nullptr(ウィンドウモード)
  • 共有コンテキスト: nullptr(なし)

4. OpenGLコンテキストの設定

glfwMakeContextCurrent(window);

OpenGLはコンテキストという概念がある。描画命令は「現在のコンテキスト」に対して実行される。

複数ウィンドウがある場合、どのウィンドウに描画するか切り替える必要がある。

5. GLADの初期化

if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
    std::cerr << "Failed to initialize GLAD" << std::endl;
    return -1;
}

OpenGLの関数ポインタをロード。これをやらないと glClearColor などが使えない。

注意: glfwMakeContextCurrentに呼ぶこと!

6. メインループ

while (!glfwWindowShouldClose(window)) {
    glClearColor(0.1f, 0.1f, 0.2f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    
    glfwSwapBuffers(window);
    glfwPollEvents();
}

ゲームループの基本形:

  1. 描画: glClear で画面をクリア
  2. バッファ交換: glfwSwapBuffers でダブルバッファを交換
  3. イベント処理: glfwPollEvents でキーボード・マウスイベントを処理

ダブルバッファリングとは

画面のチラつきを防ぐ技術。

┌─────────┐    ┌─────────┐
│ Front   │    │ Back    │
│ Buffer  │ ←→ │ Buffer  │
│ (表示)  │    │ (描画)  │
└─────────┘    └─────────┘
  1. Backバッファに描画
  2. 描画完了したらFrontと交換
  3. 交換は一瞬なのでチラつかない

ビルドと実行

# ビルド
cmake -B build -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
cmake --build build --config Release

# 実行
.\build\Release\GameEngine.exe

実行結果

ウィンドウが開いて、紺色の画面が表示される!

コンソールにはOpenGLのバージョン情報とかが出る。環境によって違うよ。

ESCキーで終了できる。

トラブルシューティング

GLFWが見つからない

Could not find a package configuration file provided by "glfw3"

vcpkgのツールチェーンを指定しているか確認:

cmake -B build -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake

gladLoadGLLoaderが失敗

glfwMakeContextCurrent を先に呼んでいるか確認。

ウィンドウが真っ黒

glClearColorglClear が呼ばれているか確認。

まとめ

今回やったこと:

  1. GLFWでウィンドウを作成
  2. GLADでOpenGL関数をロード
  3. メインループで画面をクリア

たった50行程度で、クロスプラットフォームなウィンドウが開けた!

Win32 APIだけで書いたら300行は超える。GLFWに感謝。

次回予告

Part3: OpenGLで三角形を描く

いよいよ何かを描く!三角形を画面に表示するよ。

8
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?