自作スネークゲームのメインシーン設計

スネークゲームを独自に実装する計画を立てました。以下はその概要です。 1. メインシーンは配列で表現し、xとy座標から配列のインデックスを計算します。各要素には現在の位置の種類や表示するグラフィックが格納されています。 2. スネークはリンクリストで表現され、各ノードには現在の座標や表示情報が保存されます。ヘッドノードには進行方向などの情報も含まれます。 3. 一定時間ごとに画面を更新します。 4. ターミナル上で動作し、視覚的にはそれほど美しくないことが想定されています。 まずはメインシーンについて説明します。 まず、以下の列挙型と構造体を作成しました:
1 enum FieldStatus {
2     EMPTY = 0,    // 空地
3     BORDER,        // 境界
4     NEW_NODE,      // 新しいノード
5     SNAKE          // スネーク
6 };
7 
8 struct FieldElement {
9     int type;
10    char icon;
11 };
シーンが必要とする機能を考慮すると次のようになります: 1. 指定された座標の情報を取得する。 2. 指定された座標の情報を設定する。 3. シーンのサイズを取得する。 4. 特定の座標の情報をリセットする。 5. センターコordinateを取得する。 6. 画面に出力する。 このため、以下のクラスを設計しました:
1 /// @file field.h
2 #ifndef FIELD_H
3 #define FIELD_H
4 
5 #include "defs.h"
6 
7 namespace game {
8 
9 class Field {
10    public:
11        Field();
12        Field(int width, int height);
13        ~Field();
14    
15        int initialize();
16        int getFieldValue(int x, int y, FieldElement &element);
17        int setFieldValue(int x, int y, FieldElement element);
18        int getWidth();
19        int getHeight();
20        int resetCoordinate(int x, int y);
21        Coordinate getCenterCoordinate();
22        friend std::ostream &operator<<(std::ostream &out, FieldElement &element);
23        int displayField();
24    
25    private:
26        int calculateIndex(int x, int y);
27    
28    private:
29        int _width;
30        int _height;
31        FieldElement *_data;
32        bool _isInitialized;
33 };
34 }
35 
36 #endif
出力ストリーム演算子をオーバーロードすることで、出力を簡略化しました。シーンは一次元配列を使用しており、これによりシーンサイズをカスタマイズ可能にしています。 以下は実装例です:
1 /// @file field.cpp
2 #include "field.h"
3 
4 namespace game {
5 
6 Field::Field() : _width(20), _height(20) {
7     _isInitialized = initialize();
8 }
9 
10 Field::Field(int width, int height) : _width(width), _height(height) {
11     _isInitialized = initialize();
12 }
13 
14 Field::~Field() {
15     delete[] _data;
16 }
17 
18 int Field::initialize() {
19     int size = _width * _height;
20     _data = new FieldElement[size];
21     if (!_data) return -1;
22 
23     for (int i = 0; i < _width; ++i) {
24         for (int j = 0; j < _height; ++j) {
25             int index = calculateIndex(i, j);
26             if ((i == 0 || i == _width - 1) && (j == 0 || j == _height - 1)) {
27                 _data[index].type = BORDER;
28                 _data[index].icon = '+';
29             } else if (i == 0 || i == _width - 1) {
30                 _data[index].type = BORDER;
31                 _data[index].icon = '=';
32             } else if (j == 0 || j == _height - 1) {
33                 _data[index].type = BORDER;
34                 _data[index].icon = '|';
35             } else {
36                 resetCoordinate(i, j);
37             }
38         }
39     }
40     return 0;
41 }
42 
43 int Field::calculateIndex(int x, int y) {
44     if (x >= _width || y >= _height) return -1;
45     return y * _width + x;
46 }
47 
48 int Field::getFieldValue(int x, int y, FieldElement &element) {
49     int index = calculateIndex(x, y);
50     if (index == -1) return -1;
51     element = _data[index];
52     return element.type;
53 }
54 
55 int Field::setFieldValue(int x, int y, FieldElement element) {
56     int index = calculateIndex(x, y);
57     if (index == -1) return -1;
58     _data[index] = element;
59     return element.type;
60 }
61 
62 int Field::getWidth() { return _width; }
63 int Field::getHeight() { return _height; }
64 
65 int Field::resetCoordinate(int x, int y) {
66     int index = calculateIndex(x, y);
67     _data[index].type = EMPTY;
68     _data[index].icon = ' ';
69     return 0;
70 }
71 
72 Coordinate Field::getCenterCoordinate() {
73     Coordinate coord;
74     coord.x = (_width + 1) / 2;
75     coord.y = (_height + 1) / 2;
76     return coord;
77 }
78 
79 std::ostream &operator<<(std::ostream &out, FieldElement &element) {
80     out << element.icon;
81     return out;
82 }
83 
84 int Field::displayField() {
85     for (int i = 0; i < _width; ++i) {
86         for (int j = 0; j < _height; ++j) {
87             FieldElement elem;
88             getFieldValue(i, j, elem);
89             std::cout << elem;
90         }
91         std::cout << std::endl;
92     }
93     return 0;
94 }
95 }
テスト用のGameクラスを作成しました。
1 /// @file game.h
2 #ifndef GAME_H
3 #define GAME_H
4 
5 #include "field.h"
6 
7 namespace game {
8 
9 class Game {
10    public:
11        int start();
12        int showField();
13        int updateField();
14    
15    private:
16        Field _scene;
17 };
18 }
19 
20 #endif
1 /// @file game.cpp
2 #include <iostream>
3 #include "game.h"
4 
5 namespace game {
6 
7 int Game::start() {
8     showField();
9     return 0;
10 }
11 
12 int Game::showField() {
13     int w = _scene.getWidth();
14     int h = _scene.getHeight();
15     for (int i = 0; i < w; ++i) {
16         for (int j = 0; j < h; ++j) {
17             _scene.displayField();
18         }
19         std::cout << std::endl;
20     }
21     return 0;
22 }
23 
24 int Game::updateField() {
25     int h = _scene.getHeight();
26     printf("\033[%dA", h);
27     showField();
28     return 0;
29 }
30 }
1 /// @file main.cpp
2 #include "game.h"
3 using namespace game;
4 
5 int main() {
6     Game game;
7     game.start();
8     for (int i = 0; i < 5; ++i) {
9         game.updateField();
10        sleep(1);
11    }
12    return 0;
13 }
期待される結果は、境界線を持つフレームが表示され、5秒後に終了することです。 実行結果は以下の通りです:
++====================================++
||                                    ||
||                                    ||
||                                    ||
||                                    ||
||                                    ||
||                                    ||
||                                    ||
||                                    ||
||                                    ||
||                                    ||
||                                    ||
||                                    ||
||                                    ||
||                                    ||
||                                    ||
||                                    ||
||                                    ||
++====================================++
予測通りの動作を確認できました。

タグ: C++ ゲーム開発 アルゴリズム

6月8日 21:55 投稿