直線上のターゲットに当たる銃をつくる(レイキャスト)

今回は「直線上にあるオブジェクトを取得する『レイキャスト』」機能を使ったアイテムの作成方法を紹介します。
この機能を使うと、例えば目に見えない速度で当たる銃や、その銃の着弾地点を示すマーカーなどをつくることができます。今回は、こちらの記事の機能に加えて、銃の着弾地点を示すマーカーをつくります。

的のアイテム

まずは、ダメージを受けて消える的を用意します。
新しい空のGameObjectにScriptable Itemコンポーネントを追加し、以下のスクリプトを設定します。
子オブジェクトにモデルやコライダーも配置しておきましょう。

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


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


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

銃のアイテム

新規GameObjectにGrabbable ItemおよびScriptable Itemコンポーネント、またモデルやコライダーなどを設定し、Scriptable Itemに以下のスクリプトを設定してください。

const maxDistance = 30;


$.onUse(isDown => {
    // ボタンを離したときには発動しない
    if (!isDown) return;


    // アイテムの現在位置からZ軸の向いている方向を見る
    let position = $.getPosition();
    let direction = new Vector3(0, 0, 1).applyQuaternion($.getRotation());
    let raycastResult = $.raycast(position, direction, maxDistance);


    // 直線上に何も見つからなかった場合
    if (raycastResult == null) return;


    // 直線上にあるオブジェクトがアイテムであるかどうか確認
    let handle = raycastResult.handle;
    if (handle == null || handle.type !== "item") return;


    // 対象アイテムにメッセージ送信
    handle.send("damage", { value: 50 });
});

それぞれのアイテムが設定できたら、ベータ機能を有効にしたアイテムとしてアップロードし、確認してみましょう。
的を狙って銃を使うと、ダメージを与えて消すことができます。

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

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

let raycastResult = $.raycast(position, direction, maxDistance);

$.raycast()メソッドは、ある点から特定の方向に向かう「レイ」が最初に衝突するオブジェクトを取得します。
戻り値のRaycastResult型には対象のオブジェクトのハンドルと、レイが衝突する点の座標や法線の情報が含まれます。

let handle = raycastResult.handle;
if (handle == null || handle.type !== "item") return;

raycastResult.handleで、レイが衝突したオブジェクトのハンドルを取得できます。ハンドルはPlayerHandleまたはItemHandleのいずれかです。handle.typeが”item”であればアイテム、”player”であればプレイヤーです。対象がアイテムでもプレイヤーでもない場合(地面など)はnullになります。

レイキャストを利用すると、オブジェクトの情報だけでなく、レイが衝突した点についての情報も取得することができます。
これを利用して、銃が狙っている点を示すターゲットマーカーをつくってみましょう。

銃のアイテムの子に「Marker」という子オブジェクトを作成し、Markerの子にマーカーの見た目となるQuadなどを追加します。

Markerやその子などにコライダーを含めないよう注意してください。

銃のスクリプトを、以下のものに差し替えます。

const maxDistance = 30;
const marker = $.subNode("Marker");


$.onUpdate(deltaTime => {
    // アイテムの現在位置からZ軸の向いている方向を見る
    let position = $.getPosition();
    let direction = new Vector3(0, 0, 1).applyQuaternion($.getRotation());
    let raycastResult = $.raycast(position, direction, maxDistance);


    // 直線上に何も見つからなかった場合
    if (raycastResult == null) {
        $.state.targetItem = null;
        marker.setEnabled(false);
        return;
    }


    // マーカーの位置を更新
    let raycastPoint = raycastResult.hit.point;
    let markerPosition = raycastPoint.clone().sub(position).applyQuaternion($.getRotation().clone().invert());
    marker.setPosition(markerPosition);


    marker.setEnabled(true);


    // アイテム情報を取得・保存
    let handle = raycastResult.handle;
    if (handle != null && handle.type === "item") {
        $.state.targetItem = handle;
    } else {
        $.state.targetItem = null;
    }
});


$.onUse(isDown => {
    // ボタンを離したときには発動しない
    if (!isDown) return;


    // 狙っているアイテムを取得
    itemHandle = $.state.targetItem;


    // itemHandleが有効かどうか確認
    if (itemHandle == null || !itemHandle.exists()) return;


    // 対象アイテムにメッセージ送信
    itemHandle.send("damage", { value: 50 });
});
let raycastPoint = raycastResult.hit.point;
let markerPosition = raycastPoint.clone().sub(position).applyQuaternion($.getRotation().clone().invert());
marker.setPosition(markerPosition);

raycastResult.hitでレイが衝突した点のグローバル座標や法線情報を取得できます。
ここでは衝突した点の座標を取得し、アイテムのローカル座標に変換してマーカーの位置に設定しています。

if (itemHandle == null || !itemHandle.exists()) return;

itemHandleが示しているアイテムが現時点で存在するかどうかをitemHandle.exists()で確認することができます。
今回はハンドルの取得と参照を別の箇所でおこなっており、実行時点でアイテムが破棄されている可能性もあるためこのチェックをしています。

記事をシェアしてワールド制作を盛り上げよう!

Cluster Creators Guide|バーチャル空間での創作を学ぶならをもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む