バグバグあんぱんグルメレースデス
はじめに
概要
デスゲームジャムに向けてワールド制作を行った過程を記録しました。特にアイテム毎に役割を持たせてゲーム機能を持つアイテムの複製を簡単にするためにsend機能について記述します。(ベストプラクティスがあれば教えてほしいです、、、)
ワールドを作るきっかけ
Xにてデスゲームジャムの告知を受信しました。
告知を受けても、やる・やらないという選択がありますが、このような告知を受けてやる・やらない理由を考えるのはあまりいい時間の使い方ではないと思っています。
例えば、時間が足りない、自分が期待した結果を実現するほどの技術が足りない、作業が面倒くさい、といった感想を持つことがあるでしょう。しかしこれらの感想は参加した後の過程や結果に対する感想であって、やる・やらないの判断材料になっていないです。むしろ、「面倒くさい」という感想は新しいことに取り組むときの脳の正常な化学反応であり、私という思考はどうにか取り組もうと脳に相談しているわけです。ですから「面倒くさい」という脳の感想は、深層心理の私はやる気満々であることの結果であって、私が取り組むべき判断材料になると思っています。
告知が目に入って、面倒くさいとおもちゃったもんはしょうがない。取り組みましょう、そうしましょう。(はー、面倒くさいってすばらしい!)
開発環境(ワールド作り)
- Unity 2021.3.4f1
- Windows10
- Cluster Creator Kit 2.30.0
- 特に決めていない、最新であればよい
- 特に決めていない、最新であればよい
ワールドづくり
ゲームコンセプト
タイムアタックのレースは見かけたことがあるのですが、ゴールを競うレースがあるとちょっと新鮮かもなぁと思い、レースゲームを作ろうと思っていました。(レースゲームにならなかったですけど)
ClusterではLinxさんが配布していただいている、cluster向けドライブワールド・レースゲーム制作キット「LinxCarSystem」 + オリジナルカーモデル「Felis」があれば基本的なレースのメカニクスやオブジェクトがあり、構築する敷居は低くなっているようでした。
メカニクス検討
このゴールを競うレースとなるとやはり道中でのランキングが気になります。ロジック的にはいくつか方法があるようでした。
下記の絵ではGoal(G)とStart(S)があり、その道中にnode1, node2...と区切ります。
Goalに近いnodeのほうに存在すれば順位は高く、また同じnodeに存在する場合は次のnodeに近いほうが順位が高い、とする方法を検討していました。
他にも、レースランキングロジックとしてレースゲームで順位をリアルタイムに判定するアルゴリズムにある通り、区切りを面で用意しておくこともできるようでした。このロジックを当初とらなかった理由としては、立体的なコースを作れないと思い、採用していません。(例えばコースが平行だったり、交わったり、ねじれを持つとつらい)(結局コースは直線になったんですけどねぇ)
実装検討(ランキング)
アイテム間のやり取り
ランキングの方針は↑でよいとして、どう実現するかにとても時間がかかりました。
各nodeは①Nodeに含まれるプレイヤー情報と②Nodeの境界や中心点くらいしか持っていません。
「次のNodeの中心点」や「Node全体を考慮したときのプレイヤーのランキング」はNodeが持っていないので、Nodeを取りまとめるいわゆるNodeManagerがあると便利になります。(ほかの実現方法があるかもしれません)
このように、NodeとNodeManager間で情報のやり取りうにはsendという機能を使うのがよい、、、と思っています。このsend機能はアイテムにメッセージを送ることができ、受信側は事前に決めた取り決めに従ったメッセージを受けたら特定の処理をさせることができます。この取り決めのことをプロトコルと呼びます。
直近のプロトコルの例
第2回「ゆるゲームジャム」では「damage」という文字列をプロトコルとしていました。この取り決めに従って各アイテム製作者は「damage」を受信したら、「HPが減る」「吹っ飛ぶ」「音が鳴る」「アニメーションする」といった機能を実装していました。これはアイテム製作者や実装時期に寄らず「damage」を送信するアイテムを作れば、「damage」を受信するアイテムの機能を実行できることになります。
send周りの設定
sendする側の設定です。getItemsNearやgetOverlapsでアイテムを取得するには、onReceiveで受信する側のアイテムに「検知可能なコライダー」をつけないといけないようでした。つらい。getItemsNearで得られるハンドルが意図したハンドルであることを確認する術は2025/01/10時点で無いはずです。。。
$.onStart(() => {
let itemHandles = $.getItemsNear(new Vector3(), Infinity);
for (let itemHandle of itemHandles) {
const arg = {};
arg.itemHandle = $.itemHandle;
itemHandle.send("initialize ranking manager", arg);
}
});
### Send対象の取得と(あてずっぽうの)パフォーマンス改善
NodeとNodeManagerを用意し、それらのプロトコルを決めてsendすればいい、まではわかりました。
ではNodeがNodeManagerにsendするにはどうしたらよいでしょうか。
僕がAPIの使い方に疎いのもありますが、2025年1月の時点で[getItemsNear](https://docs.cluster.mu/script/interfaces/ClusterScript.html#getItemsNear)を使えばアイテムハンドルを取得できそうです。(ほかにいい取得方法があれば教えてください)
このgetItemsNearはそのアイテムから距離空間で近いアイテムのハンドルを取得します。いまNodeManagerとNode間の距離はわからないので、ワールド内のアイテムすべてを取得することになります。
いま想定しているメカニクスのために、このsendを送る処理はNodeManager→NodeとNode→NodeManagerの両方必要になります。
``` js
? return ($) => {
? let itemHandles = $.getItemsNear(new Vector3(), Infinity);
? for (let itemHandle of itemHandles) {
? const arg = {};
? arg.itemHandle = $.itemHandle;
? itemHandle.send(key, {});
? }
? }
この状態を毎フレーム実行するとなると、ワールドアップロードして確認するまでもなくsendの頻度上限に引っ掛かるでしょう。(送受信できるアイテムをマイフレームsendするとき、send頻度上限に達するアイテム数はおよそ50個)
このような解決方法としては
一括系send()はリレー式もアリといった回避方法もありますが、ClusterScript で send と戦ったお話しのように、プロトコルに関連するアイテム群を一度だけ覚えておいて、あとはそのアイテム群だけにsendすればよさそうです。
onReceiveで受信する側
```js
$.onReceive((messageType, arg, sender) => {
$.log(`marker onReceive`);
$.log(`marker messageType: ${messageType}`);
$.log(`marker arg: ${arg}`);
}, {item:true, player:true});
実装検討(スコア)
子曰 前期限一週間而惑 計画未立 心尚乱
実装検討(ランキング)の通りに実装すればいいのですが、ランキングって個人の技術で決まりますよね。
それってデスゲームといえるゲームでしょうか(ここでその疑問を持つのは遅すぎる。。。)
1位以外は死?3位以下は死?
また、ランキングにするなら逆転できる仕組みが必要です。(最初から1位だった人がずっと1位で最後ゴールしました、ってゲームとして楽しいですか?)
そのような仕組みですが、例えば敵プレイヤーをぶっ飛ばしたり、敵プレイヤーの移動速度を落としたり、自分をぶっ飛ばしたり、、、しかし2025年時点でプレイヤーをぶっ飛ばすAPIは正式採用されていません。速度の上下で逆転劇を作る自信がありませんでした。
結局スコア制にし、一応ポイントの上げ下げもできるようにしました。きっとプレイヤー間でポイントのやり取りできると楽しいでしょう、、、涙
スコアシステム
ポイントの上げ下げや、コースアウトといった判定を行うシステムを考えました。
この機能の役割をアイテム毎割り振るために、これまで見ていたsendを使います。
このようにsendを使って、ポイントの上限や、ポイント一覧表示、プレイヤーの状態管理の役割を分けてアイテムに付与しました。
これらを組み合わせて今回のレースゲームをつくっています。参考になるかわかりませんが現状のコードをScoringSystemで配置しています。できればprehubを置いたり、UnityPackageManagerの仕組みの載せたいですが、、、、
おわりに
ワールド制作振り返り
アスレチックではないゲームワールドではおそらくsendは一般的に使われている技術なんだと思います。
また、クラフトアイテムと配置済ワールドアイテムの相互作用にも使われていると思います。
今回sendによるアイテム間の連携をまともに使ってゲームメカニクスを作成し、ようやく複雑なゲーム作りができる技術ができたと思います。
(追記)デスゲームジャム企画への感謝
デスゲームジャムにつつましく応募させて頂きました。
すでに投稿されているほかの方のワールドの出来が高く、なんだか恥ずかしくなってきました。完全に恥ずかしくなる前に投稿しちゃいました。(狂気が必要)
これまでハロクラ公式企画に投稿したことがありますが、一般の方の企画参加は初めてでした。
公式企画の場合、投稿してそれで終わりです。一応ハロクラでサムネイルの展示と、お題企画の枠の奥底に1か月程度エントリを配置いただけます。(公式運営さんも準備頑張っていらっしゃるとは思うのですけど、その過程をつぶやくといったアピール/コミュニケーション不足で損してる気がします。。。)
デスゲームジャム運営の方は、まず審査のために全力で楽しんでくれたそうです。(申し訳ねぇぇぇ)
さらに協賛の方々も企画をアピールしたり、ハブワールドを用意いただいたり、、、(申し訳ねぇぇぇぇぇぇ)
このすばらしい企画に参加できてなんだかうれしい気持ちでいっぱいになりました。運営大変だったと思いますが、企画いただきありがとうございました。
Discussion