【Day7】蛇足:UIフレームワーク作ります

昨日さんざん作ってわかったんですが、ちゃんとフレームワークを作ったほうが良さそう。
今後のためにも。

GodotのFocusのシステムは結構曲者で、マウスを基準として結構自由にフォーカスができるし、状態管理もしっかりしてるとは思う。
でも、キー操作(コントローラー操作)においては、表示されてるフォーカスできる要素を容赦なく際限なくフォーカスしていってしまう。
例えば、ウィンドウが2個重なっていたとして、裏のウィンドウ内の要素もフォーカスしに行ってしまう。

この辺の制御をするのが面倒で、内部にカウンターを持たせて作るパターンもあるんだけど、今度はマウスとの整合性を取るのに少し手間がある。

なので、Godot Meetup Tokyoで公開用にVellum UIというコンポーネントライブラリ&フレームワークを作ります。

今着手してるプロジェクトにそのまま入れて作っていくので、進捗的にも大きくは問題はないし、正直今後プロジェクトが大きくなった時に、確実にUIを作ったりシーンの遷移だけで大幅に時間を使うであろうことが予測されるので、今のうちに片付けておこうねってこと。

作り始めて、昨日まで作ったUIのシステムを剥がしていってるけど、アドオンとしての手応えがあるんで、4月のMeetupに向け公開していくぜ。

【Day5-6】大変だった‥‥‥

UIのシステムを組んでました。
これ毎回作ってるからいい加減モジュール化というかライブラリ化というかしたい‥‥‥

タイトルも丸々出てますが。
背景画像はダミーだけど、いい感じだね。

ゲーム的には2Dだけど、結構ふんだんに3Dを使ってます。
実際どれくらい負荷がかかるもんなのかとか、ワークフローをちゃんと組んでみたいとか、楽しいね。

さて、あらかたUIはシステム化できたと思うので、いい加減アクション部分を作りこんでいこう。

絵を作らないと正直モチベがキープできないんだけど、絵を作ると時間がかかるので、また数日跨ぎの更新になりそう。

【Day4】掛け合いができるゾ

わるくないスライムを置いて掛け合いをしてみた。
うんうん、簡単であるな。

少しだけStateMachineの整理をした。

というのも、おそらく強制的にキャラをポーズさせるより、イベント用のステートに移行させたほうが都合がよいと思われる。
今後タイムライン上からキャラのステートを切り替えたり、値を渡すことで、タイムライン上から人形劇ができるようになるはず。
そのためにはイベント用の汎用的なステートを用意するのが良さそう。

表情差分なんかはDialogic2のイベントを入れるようにしたほうがいいかなぁ。
まぁでも、一旦会話劇の仕組みはこんなもんにしておこう。

【Day3】結局吹き出しは独自スタイルに。

Day2で発見した仕様を独自のものに変えようと思ったけど複雑になりそうだったので、独自のスタイルを作成した。
結構シンプルにできたし十分かな。

イベントエリアの接触でキャラクターのステートを強制で止めたりして無理やりなところはあるんだけど、かなりシンプルだしカスタマイズ可能な状態にしてあるし良い感じ。

今日は出社だったので遅い時間から開始したし、あんまり進まなかったけど仕方ないね。

明日は攻撃ステートでも作ろうかな。
ついでにステート周りを綺麗にするのもありだな‥‥‥ちょっと冗長でしんどいからな。

ではまた。

GeminiとGitHub Copilotとわたし

コラム 第一回です。雑記に近いですが、自分なりの考えをアウトプットしますので、よかったらお読みください。

さて、2016年はVR元年なんて言われた日から、もう10年が経過した2026年。AIの浸透期に入ったと言えますね。

僕自身、AIをバリバリ活用してる人よりは少ないかもしれませんが、一般人としては比較的AIを活用してると思っています。 仕事でも結構活用する機会が増えてますね。

個人としては、

  • GeminiはGoogle OneのAIプラン
  • Claude Codeの従量課金
  • GitHub CopilotはGitHubの有料プラン+従量課金
  • Mureka(サウンド関連)

って感じで、割と使い倒してます。なので、今回は各種AIをどんな活用をしてるかを、未来の自分に向けたメモのつもりで書いていこうと思います。

Gemini 3

Geminiはコードも書けて便利ですが、あんまりコードを書かせる方向で利用することは少ないかもしれません。個人的にはチャッピーよりもGemini派です。嘘が多いと言うか時々情報が古かったり情報が混ざって混乱してる内容を、堂々と伝えてくるのでギョッとする感じですね。

計画を立てたり分析や企画の壁打ち、小さなことの知識補完みたいな使い方が多いです。

Geminiは過去のチャット履歴からうっすら長期記憶を残す設定があるので、回答にバイアスがかかります。しかし、変な思い込みも発生するので、僕はその記憶を残す設定を切ってます。

僕にとって企画を立てることにおいて、Geminiは最適だなと常々思っています。これは個人の特性もあるんでしょうが、僕はもともと「会話をしながらひらめきを得る」タイプです。机に向かってじっくり考えたり、散歩をして考えたりするよりも、言葉で発することのほうがよほどひらめきやすいのです。

つまり、会話をベースとするGemini(や他のAI に関しては、自分との相性が良いなと感じます。

Geminiの良いところは、変に思考を引き出させせる誘導と言うか、雑な対応が少ないところだなと思います。

「なるほど、〇〇に困ってるんですね、そんなときあなたはどう考えますか?」みたいな、事実と深層の思考の引き出しみたいな、カウンセリングみたいな返答が少ない印象。というのも、これを返されるなら正直AIと会話する必要はない。

壁打ちとしてちゃんと一般的な事実や知識を交えながら、僕自身の考えを強めていく動きをしてくれるのを感じるのがいいところだなと思いました。ただAIの出した答えに乗っかるって感じの使い方は正直やめておいたほうがいいと思いました。理由はシンプルに、企画として面白くないからですね。

AIの進化はすごいですが、いつまで経っても洒落やお笑いを理解できてないですね。企画もそうです。「面白さ」というのは、人間の非合理や矛盾した部分が醸し出してるところなのかもしれませんね。

Github Copilot

コードを書いてるときはかなり頼っていますが、バイブコーティングは正直どうかと思ってます。

先述したGeminiの使い方のように、そもそもAIの考え方に乗っかるとあんまり良いものができない気がしてます。

たしかに、要望や設計通りに書いてくれるんですが、細かな意図や未来(拡張)が見えていない印象です。

たとえばゲームであれば、かなり叩きまくったプロトタイプを作ると、本開発にはほぼ使い物にならないですし、プロトタイプのコードを解析させても細い意図は汲み取ってはくれません。

実行結果もログなどから読み取ってはくれますし、動画や画像も解析はしてくれます。しかし、何が良くて何が悪いのか、AIは読み取ってくれません。

たとえば、人間からすればバグも面白い仕様になる みたいなものはAIとしてはただの不具合でしかないので、バグから生まれる新たな仕様みたいなものは完全に消されます。

まあ、バグから仕様というのは少し極端で狭い例かもしれませんが、バイブコーティングはゲームのようなエンタメにはまだまだ向かないなって感じです。

一方で、特定の要素や機能を小さく作るサポートとしては超優秀です。他の機能との連携も考えながらよく実装してくれます。注意点は言葉が足らないと変に補完してコードが冗長になったり、不必要に過去のものを残されたりするので、長くその機能とAIと付き合うのであれば、定期的にしっかりと言葉にしてリファクタリングをするのが良い付き合い方かなと思いました。

ちなみに。

WordPressのテーマやプラグインを作るのは爆速でできます。バイブコーティングとしてできるかは微妙なところはありますが、既存システムの上乗せをするだけならAIはめちゃくちゃ爆速で出来上がりますね。とてもよい。

というわけで、特にオチもないですが、AIとの「今の僕の付き合い方」でした。

読み込み中...

ゲーム開発を応援していただけたら幸いです

Buy Me a Coffee

【Day2】Dialogic2導入はできたが‥‥‥

Dialogic2には組み込みのスタイルがいくつか用意されていて、バブルスタイル(つまり吹き出し)がある。
これは2Dか3Dのノードに、Dialogicのキャラクターを設定しておくことによって、そのノードから吹き出しが出る仕組みっぽい。

ただ、微妙に使いづらい。

キャラクターリソース(dchファイル)を、Playerに設定することはできた。これはまぁ普通にロードして設定しておくだけなので。

で、現在のレイアウトを読み込もうとするとNullが返ってくる。
これはまぁ、Dialogicの仕組み上、毎回会話を表示するレイヤーのノードを消すため、会話が始まらない限りレイアウトは存在しないってのが正しい。

なので、Dialogicの設定としてLayout Node behaviourの中にOn Timeline Endって項目のがあるので、これをHide Layout Nodeに変更。
これによって一度レイアウトが作られたら以降は表示・非表示を切り替えるだけになるはず。

で、コードとして‥‥‥

GDScript
func _setup_dialogic_character() -> void:
	if dialogic_character_resource_path == "":
		return

	var character_resource: Resource = load(dialogic_character_resource_path)

	if character_resource and character_resource is DialogicCharacter:
		var dialogic_character: DialogicCharacter = character_resource as DialogicCharacter
		var layout := Dialogic.Styles.get_layout_node()
		if layout == null:
			layout = Dialogic.Styles.load_style("default_layout") # バブルスタイル
		if layout and layout.has_method("register_character"):
			layout.register_character(dialogic_character, self)

ログでわかる通り、まずCurrent LayoutはNullが返る。先ほどと同じだが、1度でもセリフを表示しないと(つまりタイムラインが実行されないと)レイアウトが生成されない。

なので、強制的にdefault_layoutをロード‥‥‥あ。

俺が作ったスタイルの名前は「default_style」なのに、ロードしようとしてるのは「default_layout」になってしまってる。
なるほど、フォールバックして別のスタイルが作られる理由がわかった。

記事を書いてよかった。

だが、新たな問題が‥‥‥

どうやら、Dialogicの組み込みのBubbleのパーツ(吹き出し自体)の位置情報を計算するロジックが結構限定的っぽい。
実際のコードを抜き出してみる。

GDScript
func get_speaker_canvas_position() -> Vector2:
	if is_instance_valid(node_to_point_at):
		if node_to_point_at is Node3D:
			base_position = get_viewport().get_camera_3d().unproject_position(
				(node_to_point_at as Node3D).global_position)
		if node_to_point_at is CanvasItem:
			base_position = (node_to_point_at as CanvasItem).get_global_transform_with_canvas().origin
	return base_position

base_positionが読みに行ってるのはget_vieport()になってる。
つまり、現在この吹き出しが存在するViewportの中の3Dカメラを見に行ってる。

逆に言うと、専用で別のViewport内で動かしてるカメラの取得に失敗する。

これは改善の余地ありですな。
おそらくカスタムスタイルでレイアウトを作成し、このパーツをインスタンス化している根っこのtext_bubble_layerを継承してパーツも継承したものを一部改変・オーバーライドして使う感じになりそうですな。

いやいや、Saitos氏、なかなかやりますな。
方針が決まっただけだいぶ前進でござるよ。Aheadでござるよ。

さ、深夜のテンションになってきたんで、明日やります。

キャラクターを指定しない状態であれば、座標を見に行かないでフォールバックしてダミーシーンが表示されるので、ポジションさえ指定できるようにオーバーライドすればいけるだろう。

そしたらまた明日。しーゆー。