さけのさかなのブログ

同人ゲーム開発やってます。Unity使ったりする。

椅子とモニタを買う

椅子がボロボロになってきたので新しく椅子を買った。

せっかくなのでちょっと奮発して良いやつを。腰はまだ問題ないんだけど、壊れた時には手遅れだし。

で、新しい椅子にすると姿勢も変わる。背筋が伸びることで視点が高くなり…モニタの位置が合わなくなった。低い。見下ろす角度は首がつらい。

まあモニタを適当な足場に乗せれば済む話なんだけど(vesa規格には対応してなかった)、せっかくなのでモニタも新しい奴にしてやれ、ということ購入。出費とは芋づる式に増えるもので。

せっかくなので4kモニタにしたわけだけど、

  • モニタが大きいと画面端を見る時の角度がけっこうきつくなる。曲面モニタは色物かと思ってたけど確かに欲しくなる。
  • windowsのスナップ機能で四分割して使うのが正解。
  • とはいえちょっと字が小さすぎるので結局125%に拡大して表示してる。100%表示で使いたいけどそれにはメガネを替えたい。沼。
  • 綺麗に見えるというよりはアラが見えるようになる。
  • COMIC FUZでまちカドを見開きで読めるようになる。
    • 何せ書き込みが細かい。これまでは縦置きのFHDモニタで1Pずつ表示してたけどこれで見開き表示ができる。

SRPGのボスAI

1. はしがき

 SRPGのボス戦は得てして単調なものになりかねない。それをAIのカスタマイズで解決する。

2. ザコ戦とボス戦

 ザコとボスの違いは何か。数である。とりあえず、ここではそういうものとする。

  • ザコは徒党を組み、プレイヤーになで斬りにされる。
  • ボスは単騎で戦う。プレイヤーは一体のボス相手に対してじっくりと向かい合うことになる。

 バトルシステムを設計する際には、この2種類の敵を置けるように機能を作ることになる。

3. ボスの要件

 プレイヤーに飽きられないよう、バトルには起伏が求められる。ザコ戦はまあいいだろう。ウェーブや倒す順番などによって状況が変化するからだ。問題はボスだ。素朴に作れば、HPバーの残量を少しずつ減らすだけの退屈なボス戦になりかねない。

 ではどうするかというと、ボスユニットが多彩な行動をとれば良い。「毎ターン違うスキルを使ってくる」とか、「戦闘が始まってもしばらく待機して、動き始めたら自分にバフをうち、ピンチになったら奥の手で粘る」といった具合だ。

 だが、これを純粋なAIのロジックで実現するのは難しい。AIはふつう最善手を打つように組むからだ。とりあえず作ってみると、単調に最強技を連発してプレイヤーを全滅させる(もしくは、息切れを起こしてジリ貧になる)味気ないボスができあがるだろう。毎ターンはどうほう連発はファミコン時代の話である。

4. AIをカスタマイズする

 簡単な方法を考える。AIの思考を制限してやればよい。同じ行動を連続してほしくないなら、しないように組めばいいのだ。結果として「強スキルがあるのになぜか使ってこない舐めプAI」となってしまうが、ここは妥協する。

 ポイントとしては、スキルデータなどの共通部分ではなくてユニットごとのプロパティで設定できるようにすること。これは変更時の副作用を小さくする意図がある。悪い実装だと、「ある敵を調整したら別の敵の動きが変になった」といった現象が起こり得る。

 MSTでは以下のような制限を組めるようにした。

  • 起動制限:ターンが回ってきても何回かは行動せずその場で待機するよう設定できる。この設定はダメージを受けると解除して即行動開始する。
  • HP制限:HPが一定以上だとそのスキルを使わないように設定できる。ピンチになった時に使う「奥の手」だ。ついでに条件を満たしたらセリフでも言わせると良い感じになる。
  • 連発制限:そのスキルを使った後、数ターンは使わないように設定できる。強力すぎて連発させられないスキルに適用する。また、とにかくいろんなスキルを(なんならランダムっぽく)使わせたいとき。

 AIに制限を設けると、ひるがえって数値としては強くすることができる。「強力な攻撃がとんできてパーティが半壊したが、次のターンはデレてなんとか立て直した」みたいな九死に一生の展開は、開発者が意図して組めるものである。

5. その他のアプローチ

 正攻法でやるなら、最善手をうっていれば千変万化する派手な動きになるようにゲームのルールが敷かれていれば良い。が、これはとてつもなく難しい。これができたらひょっとすると対人戦に堪えるシステムになっているのではないだろうか。とはいえある程度は意識する価値はある。うまくいけばプレイヤーが動かしたときも楽しい動きになっているだろう。(多分だけどバフ関係がかなり複雑なシステムになるのではと思う。また、単純なMP制ではなく別のリソース方式を考えたほうがよさそうだ。)

 もちろん、そもそもボスの動きなんて単調でも良くない?という考え方はある。たとえば育成パートで思考させるゲームなら、バトルパートはわりと単純でも良かったりするし。

SRPGでのタンク職

1. 前置き

 MagicScrollTacticsのリリースはPC、Switch、PS4とすでに一段落していて、今は次回作をちまちまと作っているところなのだけど、もうひとつやり残していることがある。ゲーム設計の際にどういう意図があったのかをテキストにしておくというものだ。今は頭の中に残っているものの、次回作ができてしまうとおそらく曖昧になるだろうし、そもそも書く動機が薄くなる(最新作のことについて書きたくなるもんだし)。つまり今のうちにやっておくべきことだろう。

 ということで、企画というほどちゃんとした体裁ではないのだけど、つらつらとこんな感じの記事を何回か書くことになると思う。続くかどうかは謎です。

2. MSTのタンク職

2.1 モダンなゲーム

 MSTはジャンルSRPGではあるものの、RPGの文法で作っている部分がある。特にバトルまわり。小さいマップでパーティが少ないのはそういうことである。

 さて、RPGのバトル開発において、どうしたらモダンなものになるか?と考えたときに、MSTでの答えはロールの概念であり、ロールって何かというと、DPSだとかヒーラーだとか、タンクだとかだ。

2.2 SRPGとタンクの相性の悪さ

 タンク。困ったことに、実はSRPGとタンク職の相性はけっこう悪い。というのは、敵ユニットはAIで動くもので、AIというのは基本的にできるだけ賢くつくるものだからだ。固いユニットと柔らかいユニットが並んでいたらどちらを狙うか? 柔らかいほうに決まっている。しかし、タンク職は頑丈でなくてはならない。

 頑丈なタンク職が敵から狙われるにはどうすればいいか。

 ここで、SRPGだからといって位置の概念で解決するのは悪手だと思っている。確かに前線にタンクをおいて後ろに射撃部隊をおけば実現しそうだ。しかしそれをやるにはパラメータ調整にかなりの制限がかかる。敵の移動力は低くする、敵の射程は短くする、乱戦にはできない、味方の近接アタッカーは諦める…。

 ついでに、ユニット位置や射程が重要になると、プレイが重くなるという問題がある。いちいちステータスウィンドウで移動力と射程をチェックして、という作業をプレイヤーが要求されるからだ。(射程計算をシンプルにしておく必要もある。移動後の攻撃射程は単純な足し算で済んでいるか?)

 とまあ、位置調整によるアプローチが全くありえない訳ではないのだけど、かなりストイックなゲームになるだろうとは思う。それは今回やらない。

2.3 挑発スキル

 結局、MSTではAIにバカになってもらうことで解決している。挑発スキルがそれだ。

 挑発を使えば敵はこちらを狙う。理不尽極まりないが、ともかくそれで間違いなくタンク職は仕事をする。あとはスキル効果がどう途切れるかを設計すれば良い。

 反省点として、MSTの挑発スキルはヘイト値とかはなく確実に吸えるのでちょっと極端に強すぎた感じもある。この辺はまあ次の課題なのだけど、わかりやすく強くないと使ってもらえないタイプのスキルではある、という事情もあり…。ちなみにラスボスの二回行動は強すぎる挑発スキルへの対策として(あわてて)組んだ。

 (実はもうひとつ仕掛けはあって、モンスター系のAIは一番近いユニットを狙うようにできている。ただ近接系アタッカーはやっぱり狙われてしまうので、これだけでタンク運用は難しいと思う。ごま塩程度のもん)

2.4 とまあ

 SRPGだからといってユニット位置で解決しようとしたり、AIにただ賢さを求めると、あんまりうまく回らないなあ、という部分。

 こういうところは頭をゆるくして設計するのがよさそう。

2021年のこと

 年が明けてました。おめでとうございます。

去年振り返り

toriden.hatenablog.com

 MSTでやることが残ってる→PS4版でした。無事リリース。PS5では動いてるんですかね?

 裏方→一昨年の仕込みからの回収はうまく回っているので、おかげでさしあたり干からびなくてもよさそう。表には出ない部分だけど我ながら本当にうまくやった。

 去年コミケがなかったというのは実のところ良し悪しあって、スケジュールについてはだいぶ立てやすくてそれで回った部分があった。例えば外注しようとするとイベントの都合で納期が…というのはままある。

今年やること

次作品(SDC)

store.steampowered.com

 延び延び延びとなっていますが、開発が難航しているというより別件で手が止まっていたのが大きく。というわけでトラブルとかではない、はず。…いやいやそうやってフェードアウトしていくのがアカンパターンなんだが。

 関係ないけどなんか小さいゲーム作りたい。

MST

 アクリルスタンドを作ってあるんだけど頒布するイベントがないので宙ぶらりんになってたりする。半分自分用なのでわりと満足してるんだけど。イベントあったら頒布します。

実生活

 正月早々どうもめでたくない方向で節目になりそうなので、ちょっと備えがいるっぽい。あとは覚悟。ゆるくいくぞ。身体を愛え。

 去年は部屋をがっつり掃除して机を大きくしたのが良かった。今度はそろそろ椅子を替えよう。(年の初めに書くことか?という感じだけど、そのレベルでの大掃除だったのだ)

とまあ、

とりあえず死なないようにするというのはもちろんのこと、それに加えて5年後10年後にも馬力を出せるようにしておかないとなあというフェーズみを感じる。タフにならねば。

マウスを買うこと

マウスが壊れた。ので、買い替えることにした。普通なら電気屋に行って手触りを見たりするんだけど、このご時世に展示品に触るのはよろしくない。ということで無謀にも通販で勝負する。

先代

壊れたのはロジクール MX2000 MX Master 。多分4、5年ぐらい前に購入。マウスカーソルがなめらかに動かなくなり処理落ちするような動作になった。寿命。

まずは素直に同シリーズの最新を買う。

MX Master 3

これはなんといってもホイールが良くない。

ホイールの動作にはラチェットモード(カチカチする)とフリースピンモード(なめらかに回る)があるんだけど、ラチェットモードでの回転が認識されないことがある。回転し始めや回転方向の切り替え時に多発。ホイールってこんなもんだったっけ?と思って1000円ぐらいのマウスと比較したけど、1000円のほうはやっぱりちゃんと動いた。ナニコレ?

amazonレビューで同じような感想があった。きっついなー。とりあえずロジクールのサポートに相談はしてみたものの期待はしていない。 www.amazon.co.jp

ちなみにフリースピンモードはちゃんと動くので、そっちしか使わないなら問題ないと思う。

あとMX2000と比べてちょっと高さがある?ので、わりと握った時の感覚が違うのが困った。同じシリーズだといっても安牌にはなんないなあ。

MX Master3は電磁石ホイールを使っていた。次は電磁石ホイールを搭載していない少し古いやつを試す。

MX ANYWHERE 2S

ホイールクリックが無い。そんなことある?

ホイールの手前にボタンがあり、それがセンタークリック扱いなんだけど当然押しにくい。設定でサイドボタンを使うしかないか。

ホイールクリックを使わないなら問題ないと思う。ボタンの感触はかなり良い。

イマイチ煮え切らないので次はホイールクリックがあるやつを試す。

MX Anywhere 3

めげずに電磁石ホイール搭載機。だからかどうかは知らないが、ラチェットモードでやはり反応が悪いときがある。とはいえMX Master3よりはかなりマシ。

なんとか使えそうだけど、あとは贅沢を言うとフルサイズのマウスがいいんだよなあ。

まだ他のを試したい気持ちはあるんだけど、何せこの時点で出費がなあ! ということでしばらく上記のものを使ってみて慣れるかどうか。

【Unity】セーブデータにサムネイルを含める

やること

セーブデータ一覧で、ゲーム画面がサムネで表示されてるやつ。

コード

public async UniTask<byte[]> GenerateThumbnail(MonoBehaviour coroutineRunner)
{
    Texture2D thumbnail = null;
    await TextureUtil.CaptureScreenshot(width, height, _ => thumbnail = _).ToUniTask(coroutineRunner);
    var jpg = thumbnail.EncodeToJPG();
    Destroy(thumbnail);
    return jpg;
}

TextureUtil.CaptureScreenshotの実装は↓ github.com

表示時にはTexture2D.LoadImage(byte[])してRawImageコンポーネントに投げれば良い。

解説1.テクスチャのリサイズ

これが一発でとはいかない。

いかにもできそうなTexture2D.Resizeピクセル情報が初期化されるし、Graphics.ConvertTextureでは変更が反映されないようだ(VRAMが書き換わるだけなのか?よくわからんけど。)

参考

qiita.com

解説2.UniTaskの非対応部分

ScreenCapture.CaptureScreenshotAsTexture();を呼ぶにはyield return new WaitForEndOfFrame();で待つ必要がある。これがUniTaskでは非対応。READMEにもそう書いてある。

Some methods that require coroutine's end of frame(ScreenCapture.CaptureScreenshotAsTexture, CommandBuffer, etc) does not work correctly when replace to async/await. In that case, use a coroutine.

github.com

megabitconvention03 不参加のこと

当日ぎりぎりです。

今朝から体温を計測したところイベントの基準(36.8度)より高く、今回は参加を見送ることにしました。

頒布予定だったアクリルフィギュアについては、通販を検討するかなあというところ。

それではイベント参加の方はお気をつけて。