【ベータ機能】武器でダメージを与えてみよう(アイテム同士のメッセージのやりとり)

本記事はCluster Creator Kitの「ベータ機能」を利用した解説になります。
ベータ機能では正式リリース前の機能を使うことができます。正式リリース前のため、不安定な挙動をしたり将来的に挙動が変わる可能性があります。

利用にはCluster Creator Kitでベータ機能を設定した状態でアップロードを行う必要があります。ワールドクラフトで利用する場合は、ワールドクラフトのワールドを新規作成する際にベータ機能の設定を行う必要があります。設定方法はこちらをご覧ください。

今回は「異なるアイテム同士がメッセージをやりとりできる」機能を使ったアイテムの作成方法を紹介します。
この機能を使って、「当てるとダメージを与えるアイテム」と「ダメージを受けると消えるアイテム」をつくります。

攻撃するアイテム

まず、攻撃側のアイテム(武器)をつくります。
空のGameObjectを作成し、「Grabbable Item」「Scriptable Item」コンポーネントを設定してください。子オブジェクトに3Dモデルとコライダーも配置しましょう。

続いて当たり判定用のコライダーを設定します。
攻撃の判定をつけたい部分に合わせてコライダーを追加し、コライダーのGameObjectに「Overlap Detector Shape」コンポーネントを追加します。
Overlap Detector Shapeを含むShape系コンポーネントは今回のアップデートで新たに追加されたコンポーネントで、そのコライダーがどういった種類のものであるかを示します。
Overlap Detector Shapeが設定されたコライダーはIs Triggerとして振る舞い、後述の「$.getOverlaps()」の判定に利用されます。

最後に、Scriptable Itemコンポーネントに以下のスクリプトを設定してください。

$.onUpdate(deltaTime => {
    // 初期化
    if (!$.state.initialized) {
        $.state.initialized = true;
        $.state.overlapItems = [];
    }


    // 前のフレームで接触していたアイテムIDの一覧
    let previousOverlapItems = $.state.overlapItems;


    // このフレームで接触しているアイテムIDの一覧
    let currentOverlapItems = [];


    // 接触しているオブジェクトをすべて取得
    let overlaps = $.getOverlaps();


    overlaps.forEach(overlap => {
        // 接触しているオブジェクトがアイテムであるかどうかを確認
        let itemHandle = overlap.object.itemHandle;
        if (itemHandle == null) return;


        // 現在接触しているアイテムの一覧に追加
        currentOverlapItems.push(itemHandle.id);


        // 前のフレームで接触していたアイテムは除外
        // メッセージの送信には頻度制限があるためその対策、また接触し続けた場合にダメージが入り続けることを防止
        if (previousOverlapItems.includes(itemHandle.id)) return;


        // メッセージを送信
        itemHandle.send("damage", 10);
    });


    // 接触しているアイテムの一覧を更新
    $.state.overlapItems = currentOverlapItems;
});

攻撃を受けるアイテム

上の武器アイテムから一定以上のダメージを受けると消えるアイテムをつくります。
空のGameObjectにScriptable Itemコンポーネントを追加し、子オブジェクトにモデルやコライダーを配置します。

アイテムが用意できたら、Scriptable Itemに以下のスクリプトを設定します。

$.onReceive((requestName, arg, sender) => {
    // 初期化
    if (!$.state.initialized) {
        $.state.initialized = true;
        $.state.hp = 30;
    }
   
    // 特定のrequestNameのときだけ処理
    if (requestName == "damage") {
        // 引数の値の分だけHPを減らす
        let hp = $.state.hp - arg;


        // HPが0以下になると破壊
        if (hp <= 0) {
            $.destroy();
        }


        $.state.hp = hp;
    }
});

それぞれのアイテムが設定できたら、ベータ機能を有効にしたアイテムとしてアップロードし、確認してみましょう。

今回のサンプルは現時点ではUnityワールドでは正しく動作しないことがあります(後述)。クラフトアイテムとしてアップロード後、ワールドクラフト上で設置して確認してみてください。

新しい機能を利用している部分を中心に、サンプルコードを解説します。

let overlaps = $.getOverlaps();

$.getOverlaps()で、アイテムと重なっている(接触している)すべてのオブジェクトを配列として取得できます。

let itemHandle = overlap.object.itemHandle;

このoverlap$.getOverlaps()で取得した配列の要素で、アイテムと重なっているオブジェクトを示します。
重なっているオブジェクトがアイテムであった場合、overlap.object.itemHandleでそのアイテムのハンドルを取得できます。対象がアイテムでない場合はnullになります。

currentOverlapItems.push(itemHandle.id);

itemHandle.idでそのアイテムを一意に識別するIDが取得できます。接触しているアイテムのIDを$.state.overlapItemsに保存し、接触し続けているかどうかの判定に利用しています。

itemHandle.send("damage", 10);

対象のアイテムにメッセージを送信します。
メッセージの種類を示すための文字列と、メッセージの内容である引数を渡すことができます。

$.onReceive((requestName, arg, sender) => {

メッセージを受け取った際に呼び出される処理を記述します。
requestNameにはメッセージの種類を示すための文字列が、argには引数が格納されます。senderにメッセージの送信元アイテムのItemHandleが格納されているため、結果などを返信することもできます。

$.destroy();

$.destroy()を呼び出すと、そのアイテムを消滅させることができます。
現時点ではUnityワールドで最初から置かれているアイテムでは動作せず、クラフトアイテムとして設置した場合か、Create Item Gimmick$.createItem()メソッドなどによって動的に生成されたアイテムでのみ有効です。

先ほどはメッセージの引数として数値だけを設定しましたが、この引数にはハッシュ形式で複雑な内容を渡すこともできます。
スクリプトを以下のように変えてみましょう。

武器のアイテム
itemHandle.send("damage", 10);

itemHandle.send("damage", { value: 10, type: "fire" });
壊されるアイテム
hp = $.state.hp - arg;

let damage = arg.value;
if (arg.type == "fire") damage *= 2;
let hp = $.state.hp - damage;

ダメージ量に加えて、属性の情報も付加するようにしました。特定の属性だとダメージが2倍になります。
typeの値を「fire」から「water」「electric」などに置き換えたアイテムをいくつか用意して、試してみましょう。

今回実装されたメッセージ機能を使うと、他の人がアップロードしたアイテム同士であっても、requestNameや引数の形式が適切であれば互いにメッセージをやり取りすることができます。