2024春休み開発日記(1)俺は休みではないが

2021冬休み開発日記(1)2021冬休み開発日記(3)から時は流れ、ずっと気にかけていた続きに着手することにした。

当時Pythonで作っていたものを、Unityで作り直してパワーアップさせつつ、Unityの勉強もできるという素晴らしい計画です!

設計

大枠

  • 楽をして、とにかくプログラミングを進めて達成感を味わいモチベーションを上げること!
  • ChatGPTは活用してどんどん調べ学習をする
  • 調べたことは定期的にNotionにまとめていく
  • まとめ作業にこだわりすぎないこと
  • 素材はPython版のものを再利用
  • あとAIを使ってカット絵ぐらいは作りたい

必要クラス(機能)

  • Sprite…基底クラス
  • Emo…クレーのエモ(Spriteの継承クラス)
  • Charactor…クレーや敵(Spriteの継承クラス)
  • Skill…クレーや敵の技(Spriteの継承クラス)
  • Bgm…ミュージックプレイヤー(効果音は別)
  • Scence…シーンの管理&背景管理
  • Enum…設定保存管理用
  • TextBox…ドラクエ風文字表示
  • Ui…メイン画面以外のメニューなど管理
  • Theater…キャラを自動で動かす演劇管理(Scenceの子分)

3月18日(月)

とりあえず日記開始。開発の方は、1ヶ月ほど前から再開していて、Pythonで作ってたときの半分くらいは移植できた。Unityを使えば物理エンジンやゲームの基礎的なクラスは用意されているので、どんどん開発できる!と思いきや、色々なことが出来すぎたり、概念が沢山あったりとで、なかなか進まない。ここで苦労して一通りのことがわかってくれば、開発速度も上がる!だろう…

3月19日(火)

アイテムを拾ったときの処理をジュエリーハンターの宝石を取るときの処理を参考に作り始めたが、OnTriggerEnter2DとOnCollisionEnter2Dの違いでトラブった。

ジュエリーハンターの宝石はRigidbody2Dはついておらず空中に浮いているためOnTriggerEnter2Dが使われていた。自分の作った牛乳瓶は物理動作させたかったのでRigidbody2Dをつけていた。ここでIs TrriggerをTrueにすると、物理動作が停止してその代わりOnTriggerEnter2Dでの衝突判定が可能となる。しかし、物理動作が停止するので牛乳瓶は地面に当たらず奈落の底へ…。

思い込みでIs TrriggerをTrueにしてOnTriggerEnter2Dを使うものだと作ってしまったが、物理動作させているものについてはIs Trriggerは構わず、OnCollisionEnter2Dを使えばよいだけだったのだった…

まとめ

  • Riggidbody2Dを使って物理動作しているものの衝突判定はOnCollisionEnter2Dに記述
  • 物理動作の必要のないものは、Is TrriggerをTrueにしてOnTriggerEnter2Dに記述

3月20日(水)

やること

  • 画面左側のステータス画面を複数に分割
  • メイン画面の周囲に枠を作ってみる

3月21日(木)

今日は無理だ〜。やることを書き出すくらいはやっておこう

  • カットインのサイズを枠外まで広げる
  • 画像を新しい解像度で再作成する
  • カットインやエモのオブジェクトの操作をプレイヤーに持たせるか?検討

3月22日(金)

カットインのサイズを多くして枠外まで広げてみたけど、一日経って比べてみると、前の枠内のほうが良い気がする…。とりあえず、簡単に変更はできるので小さいまま作り込もう。

せっかくカットインの絵を作ったので、今日はカットインの部分を作り込もう。

フェードインやフェードアウトなどの演出効果の作り方を考えてみた。エフェクトとかトランジションというキーワードで検索すると色々出てくる。教科書で学んだ「Sprite Renderer」コンポーネントに便利なメソッドがあると思いきや、どうもそうではないらしい。「Image」コンポーネントというものがあって、そちらのほうが簡易的だがトランジションの機能が搭載されているようだ。

  • Sprite Renderer
    プレイヤーやNPCなどゲーム内の動くGameObject向けに作られている。
  • Image
    Canvas内のGameObject向けに作られている。つまりUI向け。

Canvas内に作る。つまりCanvas内のUnitサイズを32pxにしたので、画像のサイズも32pxの倍数が望ましい。ということで、4×6=128×192で作ってみよう!

Asepriteにはマスクレイヤーの機能が無いため、手動でエクスポート作業をした。ちなみに次回大型アップデートのv1.4ではマスクレイヤーが実装されるらしい。

3月23日(土)

エモやカットイン、プレイヤー、NPC、クレーの大冒険では様々なGameObjectを演劇のように自動で演じさせていくことになる。これらの「演じる」メソッドを、スーパークラスやインターフェースで定義して、それぞれのクラスで異なる動作の実装を作るのか、そんな尊大なことはせず、それぞれのクラスで普通のメソッドとして実装するのか、悩んだ。

  • スーパークラスやインターフェースで共通のメソッドを定義して、各クラスで実装
  • 各クラスで、普通のメソッドとして実装(ただし自動実行用のメソッドとわかりやすくするためにプレフィクスでActをつける)
    例)ActMoveTo、ActShowEmo…etc

オブジェクト指向であれば前述のスーパークラスやインターフェースを使うべきだが、今回は楽をしてということなので、後者のプレフィックスを付けるだけのメソッド実装とすることにした。

3月26日(火)

キャンプから帰ってきた~
カットインの画像生成について忘備録。

  1. StableDiffusionで画像生成
    512x512pxで出力される
  2. NeSpriteProでドット絵化
    縮小率2.0で変換。パレットは元画像から抽出。
    256x256pxで出力される
  3. Asepriteで切り出す。
    32の倍数になるサイズを選定。
    128x192pxで切り抜く

↓目元に変な線が入っているのは、目の位置を調整するため。

3月27日(水)

やること

  • CutInクラスを完成させる。
  • Act系のメソッドのリファクタリングをする。

ActMoveIn、ActMoveOut、ActFillIn、ActFillOutを作ってみた。DOTweenでとても簡単に実装できた。リファクタリングは進まず。

3月28日(木)

今日は無理だ~。とはいえ、色々考えたことを書き残す。Player、Emo、CutIn、それぞれのクラスは個別に動作させるように作ってきたが、EmoにしてもCutInにしてもPlayerやNPCに大きく依存している。EmoやCutInはPlayerやNPCオブジェクトの子オブジェクトとして動作するようにすべきか?

現状

// SceneManagerのメンバ変数がゴチャゴチャしてしまう
[SerializeField] private Player player;
[SerializeField] private Emo emo;
[SerializeField] private CutIn cutinLeft;
[SerializeField] private CutIn cutinRight;
[SerializeField] private MessageWindow messageWindowObj;
[SerializeField] private SelectWindow selectWindowObj;
[SerializeField] private Text sideTextComponent;

// CutInは左が誰で、右が誰かあとからだと分かりづらい
this.player.ActMoveTo("Stone01", 3f);
this.emo.ActPlayEmo(1, 3f);
this.cutinLeft.ActMoveIn();
this.messageWindowObj.SetMessage("わっ!狐おばっ!");
this.messageWindowObj.SetMessage("…");
this.cutinRight.ActFillOut();

改善案

// SceneManagerは親オブジェクトのplayerとnpcだけでOKとなる
[SerializeField] private Player player;
[SerializeField] private Npc npc;
[SerializeField] private MessageWindow messageWindowObj;
[SerializeField] private SelectWindow selectWindowObj;
[SerializeField] private Text sideTextComponent;

// PlayerかNpcかがハッキリわかる。メッセージ系オブジェクトはどうしよう…
this.player.ActMoveTo("Stone01", 3f);
this.player.emo.ActPlayEmo(1, 3f);
this.player.cutin.ActMoveIn();
this.messageWindowObj.SetMessage("わっ!狐おばっ!");
this.messageWindowObj.SetMessage("…");
this.npc.cutin.ActFillOut();

3月29日(金)

関係ないけど、良さげな素材を見つけたのでアドレスを残しておく。
https://rvros.itch.io/animated-pixel-hero

次すること

  • プレイヤーにステータスアップのメソッドを作る
    アイテムと接触した場合や、メソッドを呼ばれた場合、メッセージウィンドウになにのステータスがアップしたかを表示して、エモーションも表示されるようにする。

3月30日(土)

木曜日にPlayerの子要素にEmoとCutInを入れる構想を練っていたが、CutInについてはCanvas下に置く必要があるので、ヒエラルキーでの親子関係はPlayerとEmoだけにして、PlayerからEmoやCutInを操作するためにPlayerのフィールドにEmoやCutInのオブジェクトへの参照をもたせ、それを使ってEmoやCutInのメソッドを呼び出すことに変更した。

3月31日(日)

朝からガッツリ開発をしようと思っていたが体調不良。花粉症と昨日の暴飲暴食が原因か?しばらく健康的な生活を心がけよう。

今日はゆる~く設計や調べ物に注力することにした。

Coroutine(コルーチン)

Unityでイベントドリブンな処理(非同期処理)を行うにはコルーチンを用いるのが一般的。コルーチンは本体であるジェネレーター関数・メソッドと呼び出し側のメソッドの2つで1組な構成となる。例えば移動処理のコルーチンであれば、呼び出し関数(ActToMove)とジェネレーター関数(ToMoveCoroutine)が必要になる。前述の例のようにジェネレーター関数にはサフィックスに「Coroutine」をつけるのがUnityでは一般的だそうだ。Pythonなどではジェネレーター関数にgenなどのプレフィックスを付けることもあり、それぞれの言語やフレームワークの風習に従うのがベスト。

4月1日(月)

ステータスアップのメソッドが完成。Playerクラス内にコルーチンを作らざるを得なくなり、こんなにコルーチンばかりになる設計で良いのか、不安になる。SceneManagerがシナリオを担当してコルーチンを回すイメージで開発していたので、そこから呼ばれたPlayerクラスのメソッドが更にコルーチンを呼ばざるを得ない状況が良いのかどうか、ChatGPTなどに聞いてみると、Unityの開発においては至極一般的だという。ただし、階層が深くなってくると比例して見通しが悪くなったりバグが混在する恐れがあるので注意しましょうとのこと。とりあえず、納得したのでOKとした。

4月2日(火)

最近モチベーションが低めな原因を考えてみた。

  • いくつか思いつくクラスやメソッドを実装してきたが、新しく取り組まなければいけないことがパッと思いつかなくなった。
  • また、全体として何をするのかしっかりとしたイメージを作らなければならないが、もう少し揺らしておきたい気持ちがあり(特に理由はない)方向性が見えていない。

ということで、取り組みたい機能の一覧と全体の方向性について書き出すことにした。

取り組みたい機能

  • スケジュールクラス…1ヶ月のスケジュールを組み、そのスケジュールに従った行動をさせる。
  • シーンの生成…上記スケジュールのネタ(アルバイト、魔法学校での勉強、冒険)などを実装
  • ワールド情報の管理…スケジュール実行にあたり日時の要素を管理する仕組みを実装

全体の方向性

  • あくまで育成シミュレーションゲーム
  • あわよくばタイピング練習要素を取り入れる

4月3日(水)

昨日まとめた、取り組みたい機能の中から「ワールド情報の管理」を始めに実装することにした。

どのクラスが担当するか?

年、月、日、週、スケジュール程度の内容なので、プレイヤー情報の一部ということでPlayerStatsに持たせることに。

「王歴1021年、1月、1日、水曜日、第1週」

4月4日(木)

流石に木曜日は無理だー

4月5日(金)

ワールド情報を追加し始めた。Player情報の一部ということでPlayerStatsの中に実装。PlayerステータスもHP、MP、LV、経験値、所持金をMainStatsとし、腕力、素早さなどはBaseStatsと分けることにした。HPは4桁になるし経験値や所持金はもっと大きな桁になる。またカンマ区切りの表記をしたい関係もあり分けて考える必要がある。BaseStatsについては最大2桁として、項目数も多いため、1行に2項目ずつ表示しないと長ったらしくなるので、これもまた分けて実装する必要がある。今後アビリティなどはExtraStatsやAbilityStatsなどと付けて実装するのかな??

ちなみに各種ステータスは桁数やカンマの付与など出力時には分ける必要があるが、データ管理(保持、変更、ファイル出力、ファイル入力)に関してはどれも共通になるわけで、、、「画面表示周りだけ種類によって異なるよ」ということを忘れないようにしておこう。

4月6日(土)

BaseStatsについては値の上限に応じてエモを表示するようにしてあるが、HPとMPについてはダメージ文字の表示にすべきだよね。ということで早速実装しよう!クラス名が思いつかないのでChatGPTに聞いてみた。回答は以下の通り

StatusEffectDisplay

https://chat.openai.com/share/7c51b953-1571-4a30-b714-20ee3eddde1c

あまり良くないのでClaudeにも聞いてみたら

DamagePopup

https://claude.ai/

Popupという良いフレーズがあったのでPopupTextという名前にすることにした。

と思ったら、サンプルコードでBoundTextというのがあったので、BoundTextに決定!

文字の見た目を凝りたいのでTextMeshProの勉強も兼ねて、TextMeshProを使ってみようと思う。

バウンドするテキストのサンプルコードはこちらからいただきました

https://qiita.com/ktrnet/items/76522182e9fba86e40c2

TextMeshProへのスプライトフォントの登録方法はこちら。

https://nekojara.city/unity-textmesh-pro-sprite-number

4月7日(日)

BoundText完成!

4月8日(月)

そろそろシーンの変更を実装するべきか。ジュエリーハンターのステージのごとく背景ごとにシーンを作るべきか、ある程度まとめるべきか…

  • 背景ごとシーンを作成(部屋、街、店、etc..)
  • シチュエーションごとシーンを作成(ストーリーフェーズ、スケジュールフェーズ、戦闘フェーズなど)

背景が1枚絵じゃなくて、多少のオブジェクトを表示したくなる気もするが、とりあえず、シチュエーション毎にシーンを用意することにした。ChatGPTもそっちを薦めた。

どのようなシチュエーションがあるか?

  • スケジュール入力
  • アルバイト・学習(クレーのアクティビティ)
  • 特別な戦闘
  • オープニング
  • イベント

4月9日(火~水)

久しぶりの大学の講義だったので、流石にこちらは休業

4月11日(木)

こういうところで更新が止まってしまうので、とりあえず再開できるように、書き出しておく…

現在の作業は月曜日に考えたシチュエーションからスケジュールとアクティビティを作ろうと考え、下準備として、ゲーム内の年月日や曜日などのデータ管理や保存の扱いを整備することにした。それに伴い、既に作成してあったプレイヤーステータスに関してもリファクタリングをかけようと検討中。プレイヤーステータスだけ保存すればいいわけではなくなってきたので、データ管理(ゲーム内での保持管理・データロードセーブ)を行う構成を再検討する必要がある。

4月12日(金)

ああああ

4月13日(土)

だんだん崩れていくうううううーーー

開発ではないけど、日記を書いておく。今期の大学の講義人数は299名。残念ながら300名には届かなかった。レポートの処理が大変だなぁ。

最近Windows11の調子が悪いので、クリーンナップを考えなければならなくなってきた。MacはHomeBrewという便利なパッケージ管理ソフトがあって、それを使って使っているアプリの一括インストールが行えるが、Windowsには対応していない。ということで代替ソフトを調べてみると、Chocolateyというものがあるらしい。Qiitaなどでも記事になっているので安全性も高そうだが、一応、ChatGPTに確認してみたところ、Chocolateyはオープンソースで開発され、コミュニティでしっかり監視されており、アプリの定義ファイルなどはマルチシグネチャで信頼のあるメンバーが管理しているそうなので安全だろうという見解だった。

4月14日(日)

現状を整理するためにクラス図のたたき台を作る。

4月15日(月)

今期の受講生300名突破!自分の場合はリアルで講義をするわけだけど、YouTubeなどで生配信する場合は、視聴者さんが300名集まった!ということか。そう考えると有名なYoutuberの生放送に数万人集まるということはすごいことだ。とはいえ、受講生の子たちの生の反応が見えるのと、視聴者さんからチャットで返信が来るのと、雰囲気は全く別物であるけど。う~ん。

2件のコメント

コメントする