第1章: コンピュータグラフィックスの歴史とMiniLibX

1.1 コンピュータグラフィックスの誕生

1.1.1 Ivan SutherlandとSketchpad(1963年)

コンピュータグラフィックスの歴史は、1963年にMITのIvan Sutherlandが開発したSketchpadに始まる。Sutherlandの博士論文「Sketchpad: A man-machine graphical communication system」は、インタラクティブコンピュータグラフィックスの基礎を確立した。

Sketchpadの革新的な概念

> "Sketchpad pioneered the concepts of object-oriented programming, graphical user interfaces, and computer-aided design." > > — ACM A.M. Turing Award citation for Ivan Sutherland, 1988

Sketchpadが導入した概念:

| 概念 | 説明 | 現代での影響 | |------|------|-------------| | ライトペン | 直接画面操作 | タッチスクリーン、スタイラス | | オブジェクト指向 | グラフィカル要素の階層 | OOP、シーングラフ | | 制約充足 | 幾何学的関係の維持 | CAD、レイアウトエンジン | | インスタンス化 | マスターからの複製 | コンポーネント、プレハブ |

Sketchpadの基本構造(概念図):

┌─────────────────────────────────────┐
│           Display File              │
│  ┌─────────────────────────────┐   │
│  │  オブジェクトリスト         │   │
│  │  ├── 線分1 (x1,y1)→(x2,y2)  │   │
│  │  ├── 円1 (cx,cy,r)          │   │
│  │  └── 制約 (線分1 ⊥ 線分2)  │   │
│  └─────────────────────────────┘   │
└─────────────────────────────────────┘
           ↓ 変換
┌─────────────────────────────────────┐
│         CRTディスプレイ             │
│         (ベクタースキャン)          │
└─────────────────────────────────────┘

1.1.2 ベクターグラフィックスからラスターグラフィックスへ

ベクターディスプレイの時代(1960年代)

初期のコンピュータディスプレイは、電子ビームを直接制御して図形を描画するベクタースキャン方式を採用していた。

ベクタースキャンの動作:

電子ビームが直接図形を描画:
    開始点 → 終点へビームを移動

利点:
- 高解像度の線描画
- メモリ効率が良い(座標のみ保存)
- スケーリングが容易

欠点:
- 複雑な画像でちらつき
- 塗りつぶしが困難
- 画像表示に不適

ラスターディスプレイの登場(1970年代)

1970年代、フレームバッファの概念が登場し、ラスタースキャン方式のディスプレイが実用化された。

ラスタースキャンの動作:

画面をピクセルのグリッドとして走査:

    ←←←←←←←←←←←←←←←←
    →→→→→→→→→→→→→→→→
    ←←←←←←←←←←←←←←←←
    →→→→→→→→→→→→→→→→
    ...

フレームバッファ:
┌───┬───┬───┬───┬───┬───┐
│RGB│RGB│RGB│RGB│RGB│RGB│ 行0
├───┼───┼───┼───┼───┼───┤
│RGB│RGB│RGB│RGB│RGB│RGB│ 行1
├───┼───┼───┼───┼───┼───┤
│RGB│RGB│RGB│RGB│RGB│RGB│ 行2
└───┴───┴───┴───┴───┴───┘

フレームバッファの数学

定義(フレームバッファ)

フレームバッファ F は2次元配列:
F : [0, W) × [0, H) → C

where:
  W : 画面の幅(ピクセル)
  H : 画面の高さ(ピクセル)
  C : カラースペース(例:RGB24 = [0,255]³)

メモリ使用量:
  M = W × H × bpp / 8 bytes

where:
  bpp : bits per pixel

例:1920×1080、24bit色
  M = 1920 × 1080 × 24 / 8 = 6,220,800 bytes ≈ 6MB

1.1.3 カラー表現の理論

三原色理論(Young-Helmholtz, 1802/1850年代)

Thomas YoungとHermann von Helmholtzは、人間の色知覚が3種類の錐体細胞(赤、緑、青に対応)に基づくことを発見した。

RGBカラーモデル

RGB色空間:

    G(緑)
     ↑
     │    ・ 白 (255,255,255)
     │   /│
     │  / │
     │ /  │
     │/   │
     ├────┼────→ R(赤)
    /│    │
   / │    │
  /  │    │
 ↙   │    │
B(青)・ 黒 (0,0,0)

色の表現:
  赤   = (255, 0, 0)
  緑   = (0, 255, 0)
  青   = (0, 0, 255)
  黄   = (255, 255, 0)    // 赤 + 緑
  シアン = (0, 255, 255)    // 緑 + 青
  マゼンタ = (255, 0, 255)   // 赤 + 青
  白   = (255, 255, 255)  // 赤 + 緑 + 青
  黒   = (0, 0, 0)        // なし

ピクセルフォーマット

// 24ビットRGB(真色)
typedef struct {
    unsigned char r;  // 0-255
    unsigned char g;  // 0-255
    unsigned char b;  // 0-255
} t_rgb24;

// 32ビットRGBA(アルファチャンネル付き)
typedef struct {
    unsigned char r;
    unsigned char g;
    unsigned char b;
    unsigned char a;  // 0=透明, 255=不透明
} t_rgba32;

// パックされた32ビット整数として
// 0xAARRGGBB or 0xRRGGBBAA(エンディアン依存)
unsigned int color = 0xFF00FF00;  // 不透明な緑

1.2 ウィンドウシステムの発展

1.2.1 Xerox Altoと最初のGUI(1973年)

1973年、Xerox PARCでAltoコンピュータが開発された。Altoは最初のビットマップディスプレイを持つパーソナルコンピュータであり、WIMP(Windows, Icons, Menus, Pointer)インターフェースの原型を確立した。

Alan Kayの貢献

> "The best way to predict the future is to invent it." > > — Alan Kay, Xerox PARC, 1971

Alan KayはDynabook構想を提唱し、Smalltalkプログラミング言語とともに、現代のGUIの基礎を築いた。

1.2.2 X Window System(1984年)

MITプロジェクトAthena

1984年、MITのProject AthenaでX Window Systemが開発された。XはUnixワークステーション向けの標準的なウィンドウシステムとなった。

Xのアーキテクチャ

X Window Systemの構造:

┌─────────────────────────────────────────────┐
│              クライアント                    │
│     ┌─────────────┐ ┌─────────────┐         │
│     │ アプリケーション1│ │ アプリケーション2│         │
│     └──────┬──────┘ └──────┬──────┘         │
│            │              │                │
│            └──────┬───────┘                │
│                   ↓                        │
│         ┌─────────────────┐                │
│         │   Xlib / XCB    │ Xプロトコル    │
│         └────────┬────────┘                │
└──────────────────┼──────────────────────────┘
                   │ ネットワーク(TCP/IP または Unix Socket)
                   ↓
┌──────────────────┴──────────────────────────┐
│              Xサーバー                       │
│     ┌─────────────────────────────────┐     │
│     │    ウィンドウマネージャ          │     │
│     │    ├── ウィンドウ配置            │     │
│     │    ├── 装飾(タイトルバー等)    │     │
│     │    └── フォーカス管理            │     │
│     └─────────────────────────────────┘     │
│                     ↓                       │
│     ┌─────────────────────────────────┐     │
│     │    グラフィックスハードウェア    │     │
│     └─────────────────────────────────┘     │
└─────────────────────────────────────────────┘

Xプロトコルの特徴

  • ネットワーク透過性:クライアントとサーバーは別のマシンで実行可能
  • 非同期通信:リクエストとレスポンスが独立
  • 拡張性:拡張機能をプロトコルに追加可能

1.2.3 ダブルバッファリング

ティアリングとちらつき問題

シングルバッファの問題:

時刻t1: フレームバッファに描画開始
        ┌──────────────┐
        │ 古いフレーム │← 画面表示中
        │     +       │
        │ 新しい描画  │← 描画中
        └──────────────┘

時刻t2: 垂直同期信号(VSync)
        画面更新 → ティアリング発生!
        ┌──────────────┐
        │ 新しい部分  │
        ├──────────────┤
        │ 古い部分    │← 描画が間に合わなかった
        └──────────────┘

ダブルバッファリングの解決策

ダブルバッファリング:

バックバッファ          フロントバッファ
(描画先)              (表示中)
┌──────────────┐       ┌──────────────┐
│              │       │              │
│  新しい描画  │       │ 現在の表示   │
│              │       │              │
└──────────────┘       └──────────────┘
        ↓ 描画完了後、VSync時にスワップ
┌──────────────┐       ┌──────────────┐
│              │       │              │
│ 現在の表示   │       │  新しい描画  │
│ (次のフレーム用)     │              │
└──────────────┘       └──────────────┘

1.3 イベント駆動プログラミング

1.3.1 ハリウッドの原則

イベント駆動の起源

1980年代、GUIアプリケーションの普及とともに、イベント駆動プログラミングが標準となった。

> "Don't call us, we'll call you." > > — Hollywood Principle(ハリウッドの原則)

従来の逐次プログラミングとの比較:

逐次プログラミング:
┌───────────────────────────────┐
│ main() {                      │
│     input = get_input();      │ ← プログラムが入力を要求
│     process(input);           │
│     output(result);           │
│ }                             │
└───────────────────────────────┘

イベント駆動プログラミング:
┌───────────────────────────────┐
│ main() {                      │
│     register_handler(KEY, f); │ ← ハンドラを登録
│     register_handler(MOUSE, g);
│     event_loop();             │ ← システムがイベントを通知
│ }                             │
│                               │
│ void f(Event e) {             │ ← システムから呼び出される
│     // キー処理               │
│ }                             │
└───────────────────────────────┘

1.3.2 コールバックパターン

コールバック関数の理論

定義(コールバック)

コールバックは、他のコードに引数として渡される
実行可能なコード(関数)であり、適切な時点で
呼び出し元によって実行される。

形式的には:
  register : Event × (Event → Action) → void
  dispatch : Event → Action

コールバックの呼び出し:
  ∀ e ∈ Event: dispatch(e) = f(e)
  where f = registered_handler(event_type(e))

Xのイベントモデル

// Xイベントの構造
typedef union _XEvent {
    int type;                    // イベントタイプ
    XKeyEvent xkey;             // キーイベント
    XButtonEvent xbutton;       // マウスボタン
    XMotionEvent xmotion;       // マウス移動
    XExposeEvent xexpose;       // 再描画要求
    XConfigureEvent xconfigure; // ウィンドウ設定変更
    // ...
} XEvent;

// イベントループの基本パターン
void event_loop(Display *dpy) {
    XEvent event;

    while (1) {
        XNextEvent(dpy, &event);  // イベントを待機

        switch (event.type) {
            case KeyPress:
                handle_key(&event.xkey);
                break;
            case ButtonPress:
                handle_button(&event.xbutton);
                break;
            case Expose:
                handle_expose(&event.xexpose);
                break;
            // ...
        }
    }
}

1.3.3 MiniLibXのイベントシステム

MiniLibXのフック関数

MiniLibXは、Xイベントを簡略化したインターフェースを提供する:

// MiniLibXのフック登録
int mlx_key_hook(void *win, int (*f)(), void *param);
int mlx_mouse_hook(void *win, int (*f)(), void *param);
int mlx_expose_hook(void *win, int (*f)(), void *param);
int mlx_loop_hook(void *mlx, int (*f)(), void *param);
int mlx_hook(void *win, int event, int mask, int (*f)(), void *param);

// 関係性
┌──────────────────────────────────────────────┐
│              mlx_hook (汎用)                 │
│  ┌────────────────┐ ┌────────────────┐      │
│  │ mlx_key_hook   │ │ mlx_mouse_hook │ ...  │
│  │ (event=2,3)    │ │ (event=4,5)    │      │
│  └────────────────┘ └────────────────┘      │
└──────────────────────────────────────────────┘
                      ↓
┌──────────────────────────────────────────────┐
│              X Window Events                 │
│  KeyPress, KeyRelease, ButtonPress, ...     │
└──────────────────────────────────────────────┘

Xイベントコード

// X11のイベントタイプ
#define KeyPress        2
#define KeyRelease      3
#define ButtonPress     4
#define ButtonRelease   5
#define MotionNotify    6
#define EnterNotify     7
#define LeaveNotify     8
#define FocusIn         9
#define FocusOut        10
#define Expose          12
#define DestroyNotify   17

// MiniLibXでの使用例
mlx_hook(win, 2, 1L<<0, key_press, &game);    // KeyPress
mlx_hook(win, 17, 0, close_window, &game);    // DestroyNotify

1.4 ゲームループの理論

1.4.1 ゲームループの歴史

初期のゲームループ(1970年代)

最初のビデオゲーム(Pong, 1972年)から、ゲームはループ構造を採用してきた。

基本的なゲームループ:

while (game_running) {
    process_input();      // 入力処理
    update_game_state();  // ゲーム状態更新
    render();             // 描画
}

1.4.2 固定タイムステップ vs 可変タイムステップ

固定タイムステップ

固定タイムステップ(例:60 FPS):

while (running) {
    start_time = get_time();

    process_input();
    update(FIXED_DT);  // DT = 1/60秒
    render();

    elapsed = get_time() - start_time;
    if (elapsed < FRAME_TIME) {
        sleep(FRAME_TIME - elapsed);
    }
}

利点:
- 物理シミュレーションが安定
- 再現可能な動作
- デバッグが容易

欠点:
- 処理が間に合わないとスローダウン
- ハードウェア依存

可変タイムステップ

可変タイムステップ:

last_time = get_time();

while (running) {
    current_time = get_time();
    dt = current_time - last_time;
    last_time = current_time;

    process_input();
    update(dt);  // 経過時間に基づく更新
    render();
}

利点:
- スムーズな動作
- ハードウェア適応

欠点:
- 物理シミュレーションが不安定になりうる
- 浮動小数点誤差の蓄積

1.4.3 So_longでのゲームループ

// MiniLibXでのゲームループ
// mlx_loop()が内部的にイベントループを実行

// ループフックで毎フレーム処理
int game_loop(t_game *game)
{
    // ゲーム状態の更新(必要に応じて)
    // So_longはイベント駆動なので通常は不要

    // 描画は入力時に行うか、ここで行う
    if (game->needs_redraw)
    {
        render_game(game);
        game->needs_redraw = 0;
    }

    return (0);
}

// mlx_loop_hook(mlx, game_loop, &game);
// mlx_loop(mlx);

1.5 タイルベースグラフィックス

1.5.1 タイルマップの歴史

ファミコン/NES時代(1983年)

メモリとハードウェアの制約から、タイルベースのグラフィックスが発展した。

タイルマップの概念:

画面サイズ: 256×240 ピクセル
タイルサイズ: 8×8 ピクセル
タイルマップ: 32×30 = 960 タイル

メモリ効率:
- 直接ビットマップ: 256 × 240 × 2 = 122,880 bytes
- タイルマップ: 960 タイル番号 + 256 タイル × 64 = 16,384 bytes
  → 約87%のメモリ節約

タイルマップの形式的定義

定義(タイルマップ)

タイルマップ M は:
  M = (T, G, W, H) where:
    T : タイルセット(パターンの集合)
    G : [0, W) × [0, H) → T(グリッド関数)
    W : マップの幅(タイル単位)
    H : マップの高さ(タイル単位)

描画関数:
  render(M, x, y) = T[G[x÷tile_size, y÷tile_size]]

where:
  tile_size : タイルのピクセルサイズ

1.5.2 So_longのマップ形式

So_longマップ (.ber ファイル):

記号の意味:
  '1' : 壁(通行不可)
  '0' : 床(通行可能)
  'P' : プレイヤー開始位置
  'C' : 収集物(コレクタブル)
  'E' : 出口

例:
1111111111111
10010000000C1
1000011111001
1P0011E000001
1111111111111

これは次のタイルマップに変換される:
G(x, y) = map[y][x]

where:
  G(0, 0) = '1' (壁)
  G(1, 3) = 'P' (プレイヤー)
  G(11, 1) = 'C' (収集物)
  G(6, 3) = 'E' (出口)

1.6 MiniLibXライブラリ

1.6.1 MiniLibXの設計思想

MiniLibXは42 Schoolが教育目的で開発した軽量グラフィックスライブラリである。

設計原則

  • シンプルさ:最小限のAPIで基本的なグラフィックス操作
  • クロスプラットフォーム:X11(Linux)、AppKit(macOS)
  • 教育目的:低レベルの概念を学習可能
  • C言語互換:純粋なC言語で使用可能
  • 1.6.2 MiniLibXの基本関数

    初期化とウィンドウ管理

    // MLXインスタンスの初期化
    void *mlx_init(void);
    
    // 戻り値:MLX接続ハンドル(失敗時はNULL)
    // 内部的にX11/Cocoaに接続
    
    // ウィンドウの作成
    void *mlx_new_window(void *mlx, int width, int height, char *title);
    
    // パラメータ:
    //   mlx    : MLXインスタンス
    //   width  : 幅(ピクセル)
    //   height : 高さ(ピクセル)
    //   title  : ウィンドウタイトル
    // 戻り値:ウィンドウハンドル
    
    // ウィンドウの破棄
    int mlx_destroy_window(void *mlx, void *win);
    

    画像処理

    // 新しい画像の作成
    void *mlx_new_image(void *mlx, int width, int height);
    
    // XPMファイルから画像を読み込み
    void *mlx_xpm_file_to_image(void *mlx, char *filename, int *w, int *h);
    
    // 画像データへのアクセス
    char *mlx_get_data_addr(void *img, int *bpp, int *line_len, int *endian);
    
    // パラメータ:
    //   img      : 画像ポインタ
    //   bpp      : bits per pixel(出力)
    //   line_len : 1行のバイト数(出力)
    //   endian   : バイトオーダー(出力)
    
    // ウィンドウへの画像描画
    int mlx_put_image_to_window(void *mlx, void *win, void *img, int x, int y);
    
    // 画像の破棄
    int mlx_destroy_image(void *mlx, void *img);
    

    イベント処理

    // キーフック
    int mlx_key_hook(void *win, int (*f)(), void *param);
    
    // 汎用フック
    int mlx_hook(void *win, int event, int mask, int (*f)(), void *param);
    
    // ループフック(毎フレーム呼び出し)
    int mlx_loop_hook(void *mlx, int (*f)(), void *param);
    
    // イベントループの開始
    int mlx_loop(void *mlx);
    

    1.6.3 最初のMiniLibXプログラム

    #include <mlx.h>
    #include <stdlib.h>
    
    // プラットフォーム別キーコード
    #ifdef __APPLE__
        #define KEY_ESC 53
    #else
        #define KEY_ESC 65307
    #endif
    
    typedef struct s_app
    {
        void *mlx;
        void *win;
    }   t_app;
    
    // ウィンドウクローズハンドラ
    int close_handler(t_app *app)
    {
        mlx_destroy_window(app->mlx, app->win);
        exit(0);
        return (0);
    }
    
    // キー押下ハンドラ
    int key_handler(int keycode, t_app *app)
    {
        if (keycode == KEY_ESC)
            close_handler(app);
        return (0);
    }
    
    int main(void)
    {
        t_app app;
    
        // MLX初期化
        app.mlx = mlx_init();
        if (!app.mlx)
            return (1);
    
        // ウィンドウ作成
        app.win = mlx_new_window(app.mlx, 800, 600, "Hello MiniLibX");
        if (!app.win)
            return (1);
    
        // イベントフック登録
        mlx_key_hook(app.win, key_handler, &app);
        mlx_hook(app.win, 17, 0, close_handler, &app);  // DestroyNotify
    
        // イベントループ開始
        mlx_loop(app.mlx);
    
        return (0);
    }
    

    1.7 So_longプロジェクトの概要

    1.7.1 プロジェクト要件

    必須機能

  • マップ読み込み
- .ber拡張子のテキストファイル - 長方形で壁(1)に囲まれている - プレイヤー(P)、収集物(C)、出口(E)を含む

  • プレイヤー移動
- W/A/S/Dまたは矢印キーで移動 - 壁は通過不可 - 移動回数をターミナルに表示

  • ゲームロジック
- すべての収集物を集める - 出口から脱出でクリア

  • ウィンドウ管理
- ESCまたは×ボタンで終了 - スムーズな描画

1.7.2 データ構造設計

/* 位置を表す構造体 */
typedef struct s_pos
{
    int x;
    int y;
}   t_pos;

/* マップデータ */
typedef struct s_map
{
    char    **grid;        // 2次元配列
    int     width;         // 幅(タイル数)
    int     height;        // 高さ(タイル数)
    int     collectibles;  // 収集物の総数
    int     collected;     // 収集済み数
    t_pos   player;        // プレイヤー位置
    t_pos   exit;          // 出口位置
}   t_map;

/* テクスチャ */
typedef struct s_textures
{
    void *wall;
    void *floor;
    void *player;
    void *collectible;
    void *exit_closed;
    void *exit_open;
    int  size;             // タイルサイズ
}   t_textures;

/* ゲーム状態 */
typedef struct s_game
{
    void        *mlx;
    void        *win;
    t_map       map;
    t_textures  tex;
    int         moves;
    int         game_over;
}   t_game;

1.7.3 プロジェクト構成

so_long/
├── Makefile
├── includes/
│   └── so_long.h
├── srcs/
│   ├── main.c            // エントリポイント
│   ├── init.c            // 初期化
│   ├── map_parser.c      // マップ読み込み
│   ├── map_validator.c   // マップ検証
│   ├── path_checker.c    // パス到達性検証(BFS/DFS)
│   ├── render.c          // 描画
│   ├── events.c          // イベント処理
│   ├── movement.c        // 移動処理
│   ├── utils.c           // ユーティリティ
│   └── cleanup.c         // リソース解放
├── textures/
│   ├── wall.xpm
│   ├── floor.xpm
│   ├── player.xpm
│   ├── collectible.xpm
│   ├── exit_closed.xpm
│   └── exit_open.xpm
└── maps/
    ├── valid.ber
    └── test.ber

1.8 XPMファイル形式

1.8.1 XPMの歴史

XPM(X PixMap)は、1989年にDaniel Dardaillerが開発したテキストベースの画像フォーマットである。Cソースコードとして直接組み込める点が特徴。

1.8.2 XPMの構造

/* XPM */
static char *image_name[] = {
/* 幅 高さ 色数 文字/ピクセル */
"32 32 4 1",
/* カラーテーブル */
". c None",      /* 透明 */
"# c #000000",   /* 黒 */
"@ c #FF0000",   /* 赤 */
"  c #FFFFFF",   /* 白(スペース)*/
/* ピクセルデータ */
"................................",
"..............##................",
".............####...............",
/* ... 32行 ... */
};

1.8.3 So_long用テクスチャ例

/* wall.xpm - 32x32ピクセルの壁テクスチャ */
static char *wall_xpm[] = {
"32 32 3 1",
". c #2C2C2C",
"# c #1A1A1A",
"@ c #3F3F3F",
"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
"@##############################@",
"@#............................#@",
"@#............................#@",
/* ... */
};

1.9 参考文献

  • Sutherland, I. E. (1963). "Sketchpad: A man-machine graphical communication system". MIT PhD Thesis
  • Foley, J. D. et al. (1990). "Computer Graphics: Principles and Practice". Addison-Wesley
  • Kay, A. (1993). "The Early History of Smalltalk". ACM SIGPLAN Notices, 28(3)
  • Scheifler, R. W. & Gettys, J. (1986). "The X Window System". ACM Transactions on Graphics, 5(2)
  • Gamma, E. et al. (1994). "Design Patterns: Elements of Reusable Object-Oriented Software". Addison-Wesley (Observer Pattern)
  • Gregory, J. (2018). "Game Engine Architecture, Third Edition". CRC Press
  • Nystrom, R. (2014). "Game Programming Patterns". Genever Benning
  • 1.10 まとめ

    この章では、So_longプロジェクトの理論的基盤を学んだ:

  • コンピュータグラフィックスの歴史:Sutherland、ベクター/ラスター、フレームバッファ
  • 色彩理論:RGB、ピクセルフォーマット、アルファチャンネル
  • ウィンドウシステム:X Window、ダブルバッファリング
  • イベント駆動プログラミング:コールバック、フック、イベントループ
  • ゲームループ:固定/可変タイムステップ
  • タイルベースグラフィックス:タイルマップ、メモリ効率
  • MiniLibX:API、初期化、イベント処理

次の章では、ウィンドウ管理とイベント処理について詳しく学び、キーボード入力とプレイヤー移動を実装する。