はじめに:
記事を読んでくださりありがとうございます。
私は学生で、ゲームのバックエンドプログラマを目指して日々ゲームやアプリの制作をさせていただいています。
今回は、私が制作中のゲームで起きた問題とその解消法について情報を残しておこうと思い記事を書かせていただきました。
実装背景
- ゲーム画面をアクションからバトルに遷移させたい
- PlayerやEnemyの情報は保持させたい
- 各Sceneに情報を置いておくと破棄されてしまう
- 別のManagerを生成して情報を渡してあげる必要がある
生じた問題
まず、Enemyの生成をすべてアクション用のSceneでしていたのを、
- Enemy用のスクリプト
- そのデータを保存するためのスクリプト
- それを管理するManager
の3つを作成しする必要が出てきました。
そこで、一度仮の画像を持ってきて、
アクション用のSceneでは画像と座標Enemy用のID
を持たせてその情報だけ読み込ませてdrawで描画するという方法に変更。
Playerに関してはもともとスクリプトを別にしていたためそのまま続投。
さらに、
この情報をアクションシーンから一度SceneManagerに渡して、
その情報を戦闘用のシーンに渡す必要があるため、情報を持たせられるように変数を設定。
情報を持たせたまま遷移するためのコード
サンプルコード
/コンストラクタで各情報取得と宣言/
GameScene::GameScene(sf::RenderWindow* window, Player* player,bool returnedFromBattle)
: windowRef(window), playerRef(player),justReturnedFromBattle(returnedFromBattle),enemy(enemy){
/この中で各キャラクターの情報やTileMapの情報を呼び出してあげる/
}
GameScene::update{
/ほかの処理も書いておく ex)EnemyData,PlayerDataの更新など/
SceneManager::instance().changeScene<BattleScene>(playerRef, collidedEnemy, windowRef);/GameScene→BatttleSceneの遷移/
}
必ずしもこの手法を取らないといけないわけではないです。
個人的にはもっときれいな手法があると思うので、こういう手法もある程度と思っておいてください。
試行錯誤
最初からうまくはいかなかったため、かなりいろいろと試してみました。
ex)
-
BattleSceneに呼び出すためにアクションシーンのコンストラクタの情報をそのまま取得させる
-
SceneManagerに情報を移行させるときに元情報を最初から持たせてみる
-
データを一括管理できるようにすべてのキャラクターのためのManagerを生成してみる
採用しなかった理由
1.→Sceneを破壊したときに情報まで破棄されるので取得できない
2.→ゲーム画面上の上方に更新されないので、遷移後と遷移前の情報が一致しない
3.→PlayerとEnemyで持たせるべき情報が違うので一括管理はかえって不自由になる
解決方法
生じた問題でも話した通り、ManagerにSceneの情報を一度渡してから破壊し、Managerの情報をもとに別のSceneを構築することで元のデータを破壊したり削除したりすることなく遷移することができるようになりました。
今回学べたこと・まとめ
Scene間で情報のやり取りをするためには仲介してあげる必要がありその理由として、情報の永続性の問題が生じるから。
さらに言うと、Sceneを管理するのであれば、情報が必要であろうとなかろうとManagerを介してあげるとよりコードをきれいかつ簡潔にまとめてあげられる。
簡潔な状況分析まとめ
・GameScene → BattleScene の遷移時に Scene が破棄される仕様
・Player / Enemy の状態を Scene 内に置いていたため、遷移時にデータが消える
・永続化のためには Scene 外にデータを持つ必要がある
・そのために SceneManager を導入した
Sceneは状態であって、Managerは情報のため、そこをはき違えて一緒にしてしまうと、痛い目に合うということですね。(後にさらに痛い目にあう)
特に今回のようなゲームを作ったり、情報を扱うようなアプリを作る際には意識しておく必要があり、なんにでも応用できるいい経験ができたと思います。
長々と呼んでいただきありがとうございました。
もしも、もっといいコードやこう書いた方がいいなどありましたら、是非先駆者ニキネキの皆様にご教授願いたいです。