うさぎの惑星

兎パンダが作ったものをあげていくよ

UdonSharpを使って銃を作った

こんにちは、お久しぶりです。兎パンダです。

 

概要

VRChatのUdonSharpを使って銃を使ったゲームワールドを作ろうと考え、C#の技術がほぼ流用できるのでめちゃくちゃ参考にしながら作りました。
その結果、銃による敵の判定とその距離を示す画面が作成できるようになりました。やったー。

まだまだ拙いですが、もしこれを見ていたUdon強い系の方がいらっしゃいましたら、問題点ご指摘いただけると幸いです。

 

初めに

VRChatというものには結構前からUdonをこねるという文化があるように、UdonSharpやgraphを用いてより多彩なワールドを作成することが可能となりました。

そんなこともあって、UnityやC#などを用いてゲームを作ったことがある人は結構有利な状況になっていたのですが、私はなんとなくめんどくさがって作っていませんでした。

始めるの大変そうだし。

 

そんな中、最近うちの弟がGANTZという漫画にハマり始めました。
https://ja.wikipedia.org/wiki/GANTZ

要は未知の武器を使って宇宙人を討伐していくという漫画なのですが、その漫画の代表的な武器として「Xガン」というものがあります。

この銃のディスプレイに敵の情報や距離などが載っており、打つとギミックが発動して、数秒のタイムラグののち対象物が爆発する銃です。射程距離10m。

この武器の設定を見たときにふと脳内で「これUnityで作れんじゃん」とよぎりました。

Xガンの要件

①銃の背面にディスプレイがあり、対象のレントゲン図、距離などが記載されている。
②トリガーの一つはロックオン機能、回数は明記されていない(数十体は可能?)
③二つのトリガーを同時に引くとギミックが作動
④特徴的な音と主に発光する
⑤数秒のタイムラグののちに対象物が破裂する。

多分①くらいなら簡単にできそうと思い立ち、Udonsharpの導入方法を調べ、

真時代傾向璋:U# 入門 ①
https://hatuxes.hatenablog.jp/entry/2020/04/05/013310

ここを参考にしてやってみたらめちゃ簡単にできたので、調子に乗って作ってみました。

 

作戦

Unityには「Ray」と呼ばれる不可視の光線が存在しており、こいつはC#で作った命令文(スクリプト)で自由に飛ばすことができる。

そんでもって、ものをこいつに当てると当たったものの座標などの情報が手に入る優れもの。もちろん距離もわかる。

こいつを使って
①Rayを飛ばす
②当たったものの情報(距離と名前)を取得
ってやれば解決するだろうと作戦を立てました。

早速実験してみましょう。

 

②についての実験-1

【Unity(C#)】Rayの判定(Tag、Layerを使って衝突させたいオブジェクトの選定)
https://qiita.com/OKsaiyowa/items/5bf32ec513bcd987ab8e

ここのサイトを参考にしてrayを飛ばすスクリプトを組みました。
(間違っているので混乱防止のために載せていません)

Update関数で常にRayを飛ばし、接触したオブジェクトのレイヤーと距離を常にTextに変換、ディスプレイに表示するというものです。

 

オブジェクト

親:銃のトリガー(PickUpとUseができる)

子:銃の本体、銃口にある透明なBox(ここからRayを飛ばす)

としました。

持ち手を親オブジェクト にして、あとは全部子オブジェクトでいいよねって考え方です。

 

方法

適当な床と立体物を用意、その中に敵として黒いBoxを用意しました。

床と立体物のレイヤーはdefaultを使用。敵のレイヤーは新規で23:Enemyを作成し使用しました。

 

結果

defaultのオブジェクト、Enemyのオブジェクトともに距離の識別に成功しました。

しかし、レイヤーの判定はdefaultのオブジェクトは成功したが、Enemyオブジェクトは失敗した。このとき、Enemyオブジェクトに銃を向けたときの表示は”user1”というものだった。

この結果とは別に、この銃を自分に向けるとバグが発生してしまい、Udonが機能しなくなってしまうということがわかった。

 

感想と考察

識別できてた。嬉しいですね。

どうやらVRChatにアップロードした時点で新規のレイヤーの名称はuser1、user2……と変更されてしまうみたいです。悲しいですね。
レイヤーの判定を名称で行っていたのが裏目になっていたみたいです。

これはレイヤーの名前をuser1に変更すれば解決すると予想。

 

もう一つ、銃を自分に向けた際にバグが発生してしまい、立ち行かなくなる問題は判定処理に問題が発生していたためであった。

坪倉輝明さんのツイート
https://twitter.com/kohack_v/status/1372175115513860100?s=20

 

これによればVRChatでのプレイヤーの情報を参照し書き換えるという悪質なプログラムが出回らないように、プレイヤーのレイヤー情報など(どこまで取れないのかは不明、接触一なら取れる?)は取得できず”null”(空っぽ)として返される。
それによって、nullを想定していなかった私のスクリプトは止まったのだと考えられる。

これはTwitterの中でも触れられていますがnullのときの処理を作成すれば解決すると予想。

これらをもとに新しいスクリプトを組んだ。

 

②についての実験-2

オブジェクトと方法は実験-2と同一のものとした。

スクリプトを直し、下記のようにした。

(注意:この記事を書いている間に新しい挙動などが加わっているため、なるべく当時に近いものにしていますがちょっと違うかもしれません。各々で試してみよう!)

using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using VRC.Udon;

public class XGun : UdonSharpBehaviour
{
    public GameObject gunray;  //発射口の情報を取得

    Ray ray;
    RaycastHit hit;

    public Text DisplayDataText;
    public Text DisplayDataText_l;
 
    void Update()
    {
        ray = new Ray(gunray.transform.position, gunray.transform.forward);

        DisplayDataText_l.text = "None";
        DisplayDataText_l.color = new Color(0.0f, 0.0f, 0.0f, 1.0f);
        DisplayDataText.text = " ";

       if (Physics.Raycast(ray, out hit))
            {
                if (hit.collider == null)
                {
                    DisplayDataText_l.text = "Player";
                }
                else
                {
                    //接触地点との距離をTextとして出力する
                    DisplayDataText.text = hit.distance.ToString("f1") + "m";

                    if (hit.collider.gameObject.layer == LayerMask.NameToLayer("user1"))
                    {
                        DisplayDataText_l.text = "Enemy";

                        DisplayDataText_l.color = new Color(1.0f, 0.0f, 0.0f, 1.0f);
                    }
                    else
                    {
                        DisplayDataText_l.text = "Default";

                        DisplayDataText_l.color = new Color(0.0f, 0.0f, 0.0f, 1.0f);
                    }
                }

            }
    }

    public override void OnPickup()
    {
        //手に持ったときにテキストを表示
        DisplayDataText.enabled = true;
        DisplayDataText_l.enabled = true;
    }

    public override void OnDrop()
    {
        //離したときにテキストを非表示
        DisplayDataText.enabled = false;
        DisplayDataText_l.enabled = false;
    }

 

結果

先ほどと同様に、defaultのオブジェクト、Enemyのオブジェクトともに距離の識別に成功した。

レイヤーの識別もdefaultオブジェクト、Enemyオブジェクトともに成功し、プレイヤーに向けた際もUdonは作動していた。

 

考察と感想

できていなかったことができるようになりました。嬉し~~~。

ここではサラッと書いていますけど、原因の判明と改善にめっちゃ時間かかりました。

やっぱり一人で作っちゃダメですね。

 

とりあえず距離の判定と物体の判明はできることが判明したので、次回は要件の②以降ができるようにしていきます。

 

参考

真時代傾向璋:U# 入門 ①<。
https://hatuxes.hatenablog.jp/entry/2020/04/05/013310

 

【Unity(C#)】Rayの判定(Tag、Layerを使って衝突させたいオブジェクトの選定)。
https://qiita.com/OKsaiyowa/items/5bf32ec513bcd987ab8e