itch.ioに自作ゲームを投稿したがDLされたのは三日で3件でした

ということで、タイトルに言いたいことは全て書かせてもらった。

現実は甘くない。
どれだけ力を入れてゲームを作ろうが宣伝しなけりゃこのザマである。

英語と日本語で説明文を書いたしプレイ動画もgifで作って掲載している。サムネイルは5枚貼っている。

そもそもDLできるページにアクセスしてきてくれたのが15人ぐらいなものでitch.io自体に集客するという力はないように感じる。新着に載りすぐに流されそこからはもう何もない。

3DLは初日に1回。翌日は0、二日目になぜか2DLされて合計3だ。

そこからはそもそもDLページ自体にアクセスがないという状態で完全に埋もれる。

おそらく、開発ログというのを更新してけばなんとかなるのかもしれないが、なんかもう心は折れていてやる気にならない。

ということで、これからitch.ioに初めてリリースしようという人はこういう現実なので期待しないように。おそらくXとかで宣伝しておかないと全く話にならない。

個人的にはitchは大手ときいているから初日で100回ぐらいページにアクセスし十人ぐらいがDLし、インディー開発者には優しいということで3件ぐらいコメントがくるもんだと思っていた。

しかしそんなことは全くないのである。

とりあえずゲームは完成した

ということで、約100日に渡る開発も終わりを迎えた。(?)

一応、作りたいゲームは作れたと思う。

itch.ioにアカウントを作り、一応アップロードまで済ませた。

あとはいつ公開するかだ。今は非公開にしている。

まだDLページをいじっているところだ。

ゲームの紹介文もAIに清書してもらったりして、やっぱりAIがあると楽だなという感じ。(英語もいけるし)

最初はChatGPTに相談していたが、Geminiも使うようになり、VSCODEでGitHub Copilotも使いだしが、終盤になるとgrok3がかなり性能がよく、一番使うようになった。

全部無料でゲームが作れたことはありがたい。

所詮、アマチュアなんでね。

今回のゲームはgodotで作った初めてのゲームだしAIを活用したのも初めてだった。

そういう意味で初心者だった。次のゲームはしっかり企画を考えてある程度仕様書みたいなものも考えた方がいいだろう。作りながらアイデアも同時に考えると手が止まるというのも怖い。

「今日なにしようか」と考えてから開発すると一瞬エネルギーを使うからね。

最初から結構細かいところまで考えておけば、作るだけとなる。もしかするとそっちのほうが開発が早いんじゃないかな。漫画でいうと全話のネームを考えたからペン入れする感じか。漫画描いたことないけど。

+++

今気になっていることは、ソースコードの暗号化だ。やろうとしたけどできなかった。

エラーが出る。godot4.4 c# macos の環境だと最後の最後で.NETがどうのこうのというエラーがでてエディタが起動しない。まあいいや。今回リリースするのは体験版だしね。

itch.ioでバズったら完成品を出して売ろうと思っているけど、雰囲気をみてみると面白いゲームでも埋もれるっぽい。まだ公開はしていないけれど、なんの実績もない人間が突然リリースしたゲームがバズるとは思えない。Xとか育てておいた方が良かったのかな。まあしょうがないね。

もちろん、このブログも誰も読んでいないと思うし。それもまたしょうがないこと。

とりあえずゲームがヒットしなくても「作り方だけは手元に残る」だからまた挑戦したらいい。

十本ぐらい作りゃなんか1つぐらいヒットするだろう。そこまで計算に入れてコツコツやらないとな。

「初期化は大事だよ」という話

ゲーム開発をしています。(unityでも同じだけどこの記事ではgodotの話をしている。)

タイトル画面を作り、実際にゲーム画面を作る。そして、ゲームオーバー画面を作った。

ゲームオーバーするとタイトルに戻るという一連の流れだ。

このとき初期化が必要となる。シングルトンやstaticの値を初期化しなければ次のゲームで必ずエラーがでることになる。

作っているときは、あまり初期化のことを気にしないかもしれない。特にゲーム画面を作っているときは最低限の初期化はするがもう一度読み込まれるなんてことはあまり想定していない。

ゲーム画面はかなり多くのノードを使うことになるだろうけど、その全てを初期化しなければいけない。基本的にノードを移行するときに初期化されるものは多いが、上で書いたシングルトンとstaticは初期化されない。これでエラーが出るととても焦る。

そもそもゲームオーバーを作る前にゲーム画面を作り込んでいくとコード量が増えていく。

シングルトンもstaticも数が増えていくととても大変だ。

そして、実際ゲームが完成し、ゲームオーバーやエンディングを作ってタイトル画面に戻ってもう一度プレイしようとするとエラーで止まるというわけだ。だから最初から初期化のことを念頭においておかないといけない。というか、プレイ(ゲーム)画面もそこそこにゲームオーバーを作り一旦タイトルに戻る処理を作った方がいいかもしれない。全体の流れがあり、一応動いているということを確認してからゲーム画面を作り込むとエラーもすぐに見つけられる。

ということで、シングルトンやstaticはある意味では手動で初期化が必要となってくるので注意したい。

(Godot4.X) 効果音(SE)を鳴らすstaticメソッド

    // SEを再生するstaticメソッド
    public static void Play_SE(AudioStream SESound, Node parentNode)
    {
        if (SESound != null && parentNode != null)
        {
            var player = new AudioStreamPlayer
            {
                Stream = SESound,
                Bus = "SFX"
            };
            parentNode.AddChild(player);
            player.Play();
            // 再生終了後に削除(オプション)
            player.Finished += () => player.QueueFree();
        }
    }

このコードをstaticクラスに書いておけばどこからでも音を鳴らすことができる。

引数には[Export]などで音楽ファイルを読み込ませておいたものを引数に入れる。

2つ目の引数は、thisにしたらい。

[Export] public AudioStream SESound { get; set; } // SEファイルへの参照

//音を鳴らす

XXStaticClass.Play_SE(SESound,this);

こんな感じ。

他にも便利なやり方はいくらでもあるだろうけど、小規模なゲームなら十分だ。

(Godot)tweenをするときはコピーを作れ

タイトルそのまま。

現在表示しているsprite2Dにtweenをしたくなるときがあるが、このsprite2Dを直接扱うとないかエラーが出るときがある。

だから一旦コピーして、コピーしたものをtweenする。このコピーは移動したり拡大したりするだけでそのまま消滅させることになる。

✳︎ちなみに使う関数は「Duplicate」というものがある。

本体を動かすしかないパターンはもちろん本体を動かすのだが、コピーで良いときはコピーを動かしたほうがいいという話。

こういう細かいことはAIに聞いてもなかなか教えてくれず自力で思いつくしかない。ある意味では盲点である。

ということで、tween周りをいじるとやたらとエラーが出るのだが、コピーを作ってコピーに動いてもらいそして消えてもらうということをやると大体うまくいく。

というメモ。

(godot)ノードを削除する前に知るべきこと

QueueFreeをよく使って消していたが、今になって気づいたことがある。

ノード(シーンというべきか)を移行すると基本的にすべてのノードは自動で削除される。

だからわざわざ「QueueFree」を使う必要はなく、非表示にしておくだけで良い。

QueueFreeを使うと少し重いし、エラーが出る可能性があがる。(チェックしてから消すのも手間)

今までなぜ「QueueFree」を頻繁につかっていたかというと、ゲーム画面ばかり作っていたからだ。

つまり、バトルシステムみたいなものを作り、次のモンスターと戦うために初期化していた。そのとき一回すべてのモンスターを削除する必要があると思っていたので消していたのだ。

しかし、実際のゲームはそういう風にはならない。バトルが終わればフィールドに戻ったり町に戻ったりする。その時はシーンを移行するので、自動で消してくれるのだ。

このことに気づくにはゲームに「シーン移行」のコードを書くまで待つ必要があった。

私が今作っているゲームはバトルゲームなので、バトル画面中心のプログラムを書いていた。(約二ヶ月ほど)この1つのシーンしかなかったので、やたら初期化処理をしていたがシーンを以降するのであれば無駄なコードだった。そして、「非表示にする」というコードの意味があまりわからないでいた。

AIに聞いてみても「あとで使う」とか言ってくるが、どういう時に使うかはピンときてなかった。

つまり、どういうことか? バトルでモンスターを倒したときは、QueueFreeを使うのではなく非表示「Visible = false / Hide()」にしておけばよいということだ。そして全てのモンスターを倒し戦闘が終了したらシーンを以降するので、そのタイミングで自動でgodotが削除してくれる。わざわざすべてのモンスターを非表示にしてから削除するのではなく、非表示にしておくだけでいいというわけだ。

QueueFreeをたくさん使うと思わぬエラーが出ることが多くあった。が、非表示だけにしておけばそれがない。ということで、ノードを消すときは基本非表示(処理が軽いし)にしておけばいい。

もちろん作っているゲームによってこれは違ってくるので注意が必要だ。

分かったこと

1、シーン移行の際、Godotが自動で消してくれること

2、非表示にしておくだけで安全に(?)消せるということ

3、消したくないものはAutoloadなど使えばいいこと

これによって多少コードが少なくなった。嬉しい。

(個人開発)中規模以上のゲームは仕様書がいるかも

さすがに毎日ゲームを開発しているのでコードが増えてきた。

それによって不具合が発生する。

よくあるのは、気づかないところで”new”を2回していて、データが合わなくなること。

今日はシングルトンにしていたクラスを別に読み込み二重になっていた。

最初はシングルトンにするつもりはなかったのだが、途中で「よく使うなあ」と思ってシングルトンにしたのが原因。

二ヶ月も前に書いたコードのことなんかもう覚えているわけもなく、「なんで?」となってしまった。

ゲームはつくりながらコロコロ仕様が変わってくる。

作っている間に脳が働き、新しいアイデアも出てくる。

少しでも面白くしようとするとどんどん元とは違ったシステムになってしまう。

んで、途中でわけがわからなくなるというわけ。

「最低限、重要なデータはどう扱うか」ということは最初に決めておかないと後々問題になる。そうなってしまうと修正するだけで時間が取られていく。あまりにその作業が長いとだるくなり、作るのを諦めるか、1からやり直すかという話になるかもしれない。

ということで、「中規模以上のゲーム開発は仕様書がいるのではないか」という話。

一人の人間の脳に収まる情報なんてあまりない。二ヶ月でこのような感じだからこれから先は本当に気をつけていかないとバグが嫌になって作るのを諦めるかもしれない。

AIが手伝ってくれるからといったって限界はあるし、ちゃんと全て管理しておかないとゲーム一本まともに作れないのではないか。

(Godot4) Tweenが動かないのは、Pausedのせいかもしれないという話

GetTree().Paused = true;

このコードは、プログラムを停止することができるコードだ。

私はバトルが終了時にこれを使っている。

そして、問題がおきた。このコードがあると当たり前だけど他のプログラムは止まるので、何をしても動かない。

ちょっとTweenを使ってバトル終了後にお金でも獲得したことをアピールしてみようと思って動かそうとしたが動かなかった。

「なぜ?なぜ?」

もちろん、「GetTree().Paused = true;」という」コードを書いているから動かないのだ。

しかし、これを書いたことを忘れていると大変なことになる。

「動かないとき=何かエラーがある」と思ってしまうからだ。

いろいろ考えた。というかエディターにエラーの文字はない。だからこそ余計に考えてしまう。

まず、_Readyで動かしてみるとtween自体は動いていた。だからコードはあっている。

なぜ、動かないだ?・・・。 ということで閃いた。先日書いたこいつが悪さをしているのだ。

ということで解決した。

        Tween tween = GetTree().CreateTween();
        tween.SetPauseMode(Tween.TweenPauseMode.Process);

解決するにはこのように書けばいい。たった一行である。

ちなみに私はGodotでの開発はC#しか使っていない。

ChatGPTに聞いても私がPausedをしているという想定はしていないので、全然違う答えを書いてくる。

結局自力で思い出すしかなかった。あぶなかった。一時間近い時間が溶けたと思うが、次からもうはまらないので、よしとしよう。

ということで、エラーなくプログラムが動かないときは、「GetTree().Paused = true;」をしたかどうかを思い出すと幸せになれるかもしれない。

(Godot4.3)Tweenの情報が少ないがマニュアルを見ればいい

https://docs.godotengine.org/en/4.3/classes/class_tween.html

マニュアルはこちら。


今日やろうとしていた処理は、一度画像を動かしてから消すというもの。

ChatGPTに聞いてもそれっぽいことをいうがおそらくGodot3の話をしているのでうまく動かない。

Tween tween = GetTree().CreateTween();
tween.TweenProperty(GetNode("Sprite"), "modulate", Colors.Red, 1.0f);
tween.TweenProperty(GetNode("Sprite"), "scale", Vector2.Zero, 1.0f);
tween.TweenCallback(Callable.From(GetNode("Sprite").QueueFree));

マニュアルからコピペしたが、このようにコールバックで「GetNode(“Sprite”).QueueFree)」をかけば消えてくれる。

GetNode(“Sprite”)は当然だが、動かしている画像ということになる。

私がC#で開発しているから余計に情報が少なそうだが、マニュアルを毛嫌いせずに読めば普通に答えは書いてある。AIで楽はできるが全部教えてくれるわけでもないし100%正しいこというわけでもないので注意したい。

それにしてもブログを書いてくれている他の開発者は動かしたり消したりというサンプルをよく書くが、「消すこと」に関してはほとんど記述していないことも面白い。

データの取得はケチらない方がいい

unityでもGodotでもアプリ開発でも同じ。

データを取得するときはとりあえず全部取得した方がいい。

例えば、オブジェクトがあるとき、位置だけしか使わないとしても、色や大きさも取得していて損はない。

今のハードの性能で考えると無駄にデータを取得したところでそんなに負荷にならない。

困るのは後々になって、新しいことを実装しようとしたとき、データが足りずに実装できなくなること。

最初からすべて決まっていて仕様書や計画書がしっかりしていれば問題ないと思う。

特に個人開発で全部自由にやっている場合は、現状での必要最低限のデータで作っていくより取得できるときは無駄になるかもしれないがたくさん取得しておこうという話。

最終的に、「完成!」となってから後からデータを消して負荷対策した方が簡単だと思う。

ということで、必要最低限のデータよりとりあえず取れるデータはとっておいた方が良さそうだ。

まあこの抽象的な記事を読んだところで、あまり意味がないと思うけど、個人的なメモとして。