ゲーム実装で身に付くプログラミング 10

色ブロックをクリックしたら背景色を反映させる

色ブロックをクリックしたら
背景色を反映させる

色ブロックをクリックしたら消して、その色を背景色に混ぜます(図5)。背景色はクリックした色ブロックの赤緑青のうち1色が加算され、より明るくなります。色ブロックをクリックして消していき、1個もなくなったらゲームオーバーです。

図5:背景色を反映

・サンプルコード「ColorBlock.h」ファイル

#ifndef COLORBLOCK_H
#define COLORBLOCK_H

#include "raylib.h"

typedef struct {
    float x;
    float y;
    float vx;
    float vy;
    int width;
    int height;
    int color;
} ColorBlock;

void ColorBlock_Init(ColorBlock* block, int c, int w, int h);
int ColorBlock_Draw(ColorBlock* block, Texture2D img[]);
bool ColorBlock_IsClicked(ColorBlock* block);

#endif

【サンプルコードの解説】
「ColorBlock」構造体を引数に「ColorBlock_IsClicked」関数を追加します。

・サンプルコード「ColorBlock.c」ファイル

#include "ColorBlock.h"
#include <stdlib.h>

void ColorBlock_Init(ColorBlock* block, int c, int w, int h) {
(中略)
}

int ColorBlock_Draw(ColorBlock* block, Texture2D img[]) {
(中略)
}

bool ColorBlock_IsClicked(ColorBlock* block) {
    Vector2 mouse = GetMousePosition();
    Rectangle rect = {
        block->x,
        block->y,
        (float)block->width,
        (float)block->height
    };
    if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) &&
        CheckCollisionPointRec(mouse, rect)) {
        return true;
    }
    return false;
}

【サンプルコードの解説】
ColorBlock_IsClicked関数でマウス座標を「mouse」変数に、色ブロックの正方形のサイズを「rect」変数にそれぞれ取得します。マウス左ボタンが押された時にmouseがrectの範囲に入っていればクリック成功で、戻り値としてtrueを返します。それ以外はfalseを返します。

・サンプルコード「main.c」ファイル

#include "ColorBlock.h"
#include "resource_dir.h"
#include <stdlib.h>

#define MAX_TEXTURES 6
#define MAX_SOUNDS 4
#define MAX_BLOCKS 300

typedef enum { TITLE,MAIN_LOOP,GAME_OVER,STAGE_CLEAR } GameState;
GameState game_state = TITLE;
Texture2D img[MAX_TEXTURES];
Sound snd[MAX_SOUNDS];
ColorBlock colors[MAX_BLOCKS];
int colorCount = 0;
int stage = 2;
float rgb[3];
Color bg = { 255,255,255,255 };

void init(void) {
    colorCount = stage * 3;
    for (int i = 0; i < colorCount; i++)
        ColorBlock_Init(&colors[i], i % 3, img[0].width, img[0].height);
    rgb[0] = rgb[1] = rgb[2] = 0;
    while (rgb[0] == rgb[1]) {
        rgb[0] = rand() % stage;
        rgb[1] = rand() % stage;
        rgb[2] = rand() % stage;
    }
}

void title(void) {
(中略)
}

void main_loop(void) {
    int flg = 0;
    for (int i = 0; i < colorCount; i++)
        flg += ColorBlock_Draw(&colors[i], img);
    for (int i = 0; i < colorCount; i++) {
        if (ColorBlock_IsClicked(&colors[i])) {
            PlaySound(snd[0]);
            rgb[colors[i].color]++;
            /* erase */
            for (int j = i; j < colorCount - 1; j++)
                colors[j] = colors[j + 1];
            colorCount--;
            break;
        }
    }
    if (flg == 0) {
        game_state = GAME_OVER;
        PlaySound(snd[2]);
    }
}

void bg_color(void) {
    int max;
    if (rgb[0] < rgb[1])
        max = (rgb[1] < rgb[2]) ? 2 : 1;
    else
        max = (rgb[0] < rgb[2]) ? 2 : 0;
    float ratio = 255.0f / rgb[max];
    unsigned char r = (unsigned char)(rgb[0] * ratio);
    unsigned char g = (unsigned char)(rgb[1] * ratio);
    unsigned char b = (unsigned char)(rgb[2] * ratio);
    bg = (Color){ r,g,b,255 };
    if (r == g && g == b) {
        game_state = STAGE_CLEAR;
        PlaySound(snd[3]);
    }
}

void game_over(void) {
(中略)
}

int main(void) {
(中略)
    while (!WindowShouldClose()) {
        BeginDrawing();
        ClearBackground(bg);
        switch (game_state) {
        case TITLE:
            title();
            break;
        case MAIN_LOOP:
            main_loop();
            bg_color();
            break;
        case GAME_OVER:
            game_over();
            break;
        }
        EndDrawing();
    }
(中略)
}

【サンプルコードの解説】
「rgb」配列を宣言します。
「init」関数でrgbの3要素に<stdlib.h>を使ってランダムな値を代入します。
「main_loop」関数で色ブロックがクリックされたら"Mix.ogg"サウンドを再生し、rgb配列のクリックした色に対応する要素をインクリメントし、色ブロックを消します。
「bg_color」関数で、例えば(赤,緑,青)=(3,5,1)なら(r,g,b)=(255*3/5, 255*5/5, 255*1/5)のように背景色を計算します。背景色が真っ白になったらgame_stateをステージクリアにし、"StageClear.ogg"サウンドを再生します。
「main」関数のメインループ内のゲームループ中で、bg_color関数を呼び出します。

ステージクリア画面を表示する

最後にステージクリア画面(図6)を実装すれば終わりです。既にゲームステートをステージクリアにする処理は実装済みです。

図6:ステージクリア画面を表示

・サンプルコード「main.c」ファイル

(前略)

void init(void) {
(中略)
}

void title(void) {
(中略)
}

void main_loop(void) {
(中略)
}

void bg_color(void) {
(中略)
}

void game_over(void) {
(中略)
}

void stage_clear(void) {
    if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
        game_state = MAIN_LOOP;
        stage++;
        init();
        PlaySound(snd[1]);
    }
    DrawTexture(img[5],
        (1800 - img[5].width) / 2,
        (900 - img[5].height) / 2,
        WHITE);
}

int main() {
(中略)
    while (!WindowShouldClose()) {
        BeginDrawing();
        ClearBackground(bg);
        switch (game_state) {
        case TITLE:
            title();
            break;
        case MAIN_LOOP:
            main_loop();
            bg_color();
            break;
        case GAME_OVER:
            game_over();
            break;
        case STAGE_CLEAR:
            stage_clear();
            break;
        }
        EndDrawing();
    }
(中略)
}

【サンプルコードの解説】
「stage_clear」関数で"StageClear.png"画像を描画します。マウスの左ボタンが押されたらgame_state変数をゲームループにセットし、「stage」変数をインクリメントし、ステージ開始処理をし、"GameStart.ogg"サウンドを再生します。
「main」関数で、メインループ中のステージクリアの際にstage_clear関数を呼び出します。

【コラム】解決策を考えること

ネガティブなことがあったら、その解決策を考えますよね。そのプロセスでポジティブな思考になれたら良いですね。

おわりに

今回はプログラミング言語「C」の2Dグラフィックスライブラリ「RayLib」でコンソールゲーム「Color」を開発しました。「C/C++」では「クラス」や「STL」の「vector」が使えて便利でしたが、C言語でも「構造体」があったり、メモリを動的に確保せず前もってメモリを静的に確保したりしました。

人気記事トップ10

人気記事ランキングをもっと見る

企画広告も役立つ情報バッチリ! Sponsored