#この記事は、
ウォーSLGが好きな筆者が、ヘックス戦略SLGを自分で作る記録です。
この記事のまとめ
- マスターデータを作成して読み込みまで実装した
- 共通定義ファイルを作成して参照できるようにした
- マップデータを作成して実装した
ゲームの基本データや共通定義ファイルの必要性
ゲームにはいろいろなデータがあります。RPGであれば、敵のパラメータや武器のパラメータ。今回のようなSLGであれば、マップの地形データや各ユニットのパラメータなどです。それらのデータを作成して、プログラムに組み込みます。
CocosCreatorのコーディング環境はVSCodeなので、開発効率のため、intellisenseのコード補完や定義のジャンプが機能する構造を重視します。
ゲーム用マスターデータの作成
メンテナンス性を考えてGoogleスプレッドシートで作成します。こんな感じ。
この元データから、コピペでコードに貼れるような文字列を別シートに生成します。
地形データと、移動タイプデータのデータは、それぞれこのようなシートを作成して、
このような形でコードに設定します。
const CONSTANT = {
// 地形ID
CAPITAL: 0,
FACTORY: 1,
AIRPORT: 2,
HARBOR: 3,
CITY: 4,
BIG_CITY: 5,
FORT: 6,
LOAD: 7,
BRIDGE: 8,
PLAIN: 9,
WASTELAND: 10,
WOODS: 11,
FOREST: 12,
HILL: 13,
MOUNTAIN: 14,
MOUNTAIN_L: 15,
DESERT: 16,
SNOW: 17,
WETLAND: 18,
RIVER: 19,
SHOAL: 20,
OCEAN: 21,
// 地形名
TerrainName: [
'CAPITAL', // 0 : 首都
'FACTORY', // 1 : 工場
'AIRPORT', // 2 : 空港
'HARBOR', // 3 : 港
'CITY', // 4 : 都市
'BIG_CITY', // 5 : 大都市
'FORT', // 6 : 要塞
'LOAD', // 7 : 道路
'BRIDGE', // 8 : 橋
'PLAIN', // 9 : 平地
'WASTELAND', // 10 : 荒れ地
'WOODS', // 11 : 林
'FOREST', // 12 : 森
'HILL', // 13 : 丘陵
'MOUNTAIN', // 14 : 山
'MOUNTAIN_L', // 15 : 山岳
'DESERT', // 16 : 砂地
'SNOW', // 17 : 雪原
'WETLAND', // 18 : 湿原
'RIVER', // 19 : 川
'SHOAL', // 20 : 浅瀬
'OCEAN', // 21 : 海
],
TerrainNameJ: [
'首都', // 0
'工場', // 1
'空港', // 2
'港', // 3
'都市', // 4
'大都市', // 5
'要塞', // 6
'道路', // 7
'橋', // 8
'平地', // 9
'荒れ地', // 10
'林', // 11
'森', // 12
'丘陵', // 13
'山', // 14
'山岳', // 15
'砂地', // 16
'雪原', // 17
'湿原', // 18
'川', // 19
'浅瀬', // 20
'海', // 21
],
WALK: 0,
WHEEL: 1,
CRAWLER: 2,
ROTOR: 3,
WING: 4,
SHIP: 5,
MoveTypeName: [
'WALK', // 0: 歩行
'WHEEL', // 1: 装輪
'CRAWLER', // 2: 装軌
'ROTOR', // 3: ヘリ
'WING', // 4: 航空
'SHIP', // 5: 艦船
],
MoveTypeNameJ: [
'歩行', // 0
'装輪', // 1
'装軌', // 2
'ヘリ', // 3
'航空', // 4
'艦船', // 5
],
};
export default CONSTANT;
移動力・防御力・占領可能かどうか、などのパラメータも同様に作成します。
このような形で実装します。
const TerrainData = [
{ cost: [1, 1, 1, 1, 1, 0], def: 0.40, cap: true },
{ cost: [1, 1, 1, 1, 1, 0], def: 0.10, cap: true },
{ cost: [1, 1, 1, 1, 1, 0], def: 0.10, cap: true },
{ cost: [1, 1, 1, 1, 1, 0], def: 0.10, cap: true },
{ cost: [1, 1, 1, 1, 1, 0], def: 0.30, cap: true },
{ cost: [1, 1, 1, 1, 1, 0], def: 0.35, cap: true },
{ cost: [1, 1, 1, 1, 1, 0], def: 0.50, cap: false },
{ cost: [1, 1, 1, 1, 1, 0], def: 0.00, cap: false },
{ cost: [1, 1, 1, 1, 1, 0], def: 0.00, cap: false },
{ cost: [1, 2, 1, 1, 1, 0], def: 0.05, cap: false },
{ cost: [2, 3, 2, 1, 1, 0], def: 0.05, cap: false },
{ cost: [1, 2, 1, 1, 1, 0], def: 0.15, cap: false },
{ cost: [1, 3, 2, 1, 1, 0], def: 0.25, cap: false },
{ cost: [2, 3, 2, 1, 1, 0], def: 0.10, cap: false },
{ cost: [3, 0, 0, 1, 1, 0], def: 0.30, cap: false },
{ cost: [0, 0, 0, 0, 1, 0], def: 0.00, cap: false },
{ cost: [2, 2, 2, 1, 1, 0], def: 0.00, cap: false },
{ cost: [3, 2, 2, 1, 1, 0], def: 0.00, cap: false },
{ cost: [3, 0, 0, 1, 1, 0], def: 0.00, cap: false },
{ cost: [0, 0, 0, 1, 1, 0], def: 0.00, cap: false },
{ cost: [0, 0, 0, 1, 1, 0], def: 0.00, cap: false },
{ cost: [0, 0, 0, 1, 1, 1], def: 0.00, cap: false },
];
export default TerrainData;
以下のように、Intelienseのコード補完も機能しながら、各データにアクセスできます。良い感じですね。
// 移動タイプ:装軌の砂漠地形での移動力
TerrainData[CONST.DESERT].cost[CONST.CRAWLER];
// 首都の防御力
TerrainData[CONST.CAPITAL].def;
// 要塞が占領可能かどうか
TerrainData[CONST.FORT].cap;
ノードの共通参照ファイルの作成
CocosはNodeTreeでゲームシーンが構築されているため、あちこちでノード(やコンポーネント)を参照して動きます。
ノードの参照は、node.getChildByName('NodeName') のような形で取得するのですが、ノードやコンポーネントの取得のメソッドが結構高コストです。結構プログラムの各所で参照するノードが出てくるので、まとめて一気に参照を作成しておきます。
// eslint-disable-next-line private-props/no-use-outside
const { ccclass } = cc._decorator;
// *********************************
// Node Reference クラス
// 各所でノードを参照するために使用
// ********************************
@ccclass
// eslint-disable-next-line no-unused-vars
class NR_INIT extends cc.Component {
// *********************************
// ベースノード群のリファレンスを設定
// ********************************
initBaseNodesRef() {
NR.SCENE = cc.director.getScene(); // シーン
NR.CANVAS = NR.SCENE.getChildByName('Canvas'); // キャンバス
NR.MAIN_CAMERA = NR.CANVAS.getChildByName('Main Camera'); // カメラノード
}
// *********************************
// ヘックスノード群のリファレンスを設定
// ********************************
initHexNodesRef() {
NR.MAP_MANAGER = NR.CANVAS.getChildByName('MapManager'); // マップマネージャ
NR.HEX_BASE_LAYER = NR.MAP_MANAGER.getChildByName('HexBaseLayer'); // ヘックスのスプライトを置くレイヤー
NR.HEX_LINE_LAYER = NR.MAP_MANAGER.getChildByName('HexLineLayer'); // ヘックスのラインを置くレイヤー
NR.HEX_EFFECT_LAYER = NR.MAP_MANAGER.getChildByName('HexEffectLayer'); // ヘックスのエフェクトを置くレイヤー
}
// *********************************
// cc.ComponemtのライフサイクルonLoad
// 初期化時に一番最初に呼ばれる
// ********************************
onLoad() {
console.time('NodeReference OnLoad');
this.initBaseNodesRef(); // ベースノード群のリファレンスを設定
this.initHexNodesRef(); // ヘックスノード群のリファレンスを設定
if (CC_DEBUG) {
for (let node in NR) {
cc.log(' ' + node + ': ' + NR[node]);
}
}
console.timeEnd('NodeReference OnLoad');
}
}
// 以下は、上のNR_INITコンポーネントでonLoad時に初期化される
// コード補完が機能するようにこのような形で定義している
const NR = {
// Base
SCENE: null, // シーン
CANVAS: null, // キャンバス
MAIN_CAMERA: null, // メインカメラ
// Hex
MAP_MANAGER: null,
HEX_BASE_LAYER: null,
HEX_LINE_LAYER: null,
HEX_EFFECT_LAYER: null,
};
export default NR;
コンポーネントは、onLoadで参照用の変数を初期化するためです。一番最初に処理してもらうために、NodeTree上の一番上の初期化用nodeに貼り付けておきます。必要な場所でimportして使用します。Intelienseのコード補完が機能します。
マップデータの作成
マップデータもGoogleスプレッドシートで作成します。色はみやすさの為につけています。
このような形のシートを作成して、
コピペでコードに貼れるようにしました。
const MapData = [
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21],
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21, 21],
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21, 21],
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21],
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21, 21],
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21],
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21],
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21, 21],
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21, 21],
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21],
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21, 21],
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21, 21, 21],
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21, 21, 21],
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 7, 7, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 7, 21, 21, 21, 21, 21],
[21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 7, 21, 21, 21, 21, 21],
[21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 21, 21, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 9, 9, 21, 21, 21],
[21, 21, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7, 9, 9, 21, 21, 21],
[21, 21, 21, 9, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 9, 21, 21, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, 7, 9, 9, 21, 21],
[21, 21, 9, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 9, 21, 21, 9, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 9, 9, 21, 21, 21],
[21, 21, 21, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 9, 9, 21, 21, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 9, 21, 21, 21],
[21, 21, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 9, 21, 21, 21, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 9, 9, 21, 21, 21],
[21, 21, 9, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 9, 21, 21, 21],
[21, 9, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 9, 21, 21, 21, 21, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 9, 9, 21, 21, 21],
[21, 21, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7, 9, 21, 21, 21, 21, 9, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, 9, 9, 21, 21],
[21, 9, 9, 7, 9, 9, 9, 9, 9, 9, 7, 7, 7, 9, 9, 9, 21, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21, 21, 21],
[21, 21, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 9, 9, 9, 9, 21, 21, 21, 21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21, 21, 21, 21],
[21, 21, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21],
[21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21],
];
export default MapData;
自体は左下原点の座標系なので、配列が逆になっていますが、プログラムで対応します。
次は、ヘックスに前後関係を意識した上でスプライトを貼っていこうと思います。