LoginSignup
0
5

More than 1 year has passed since last update.

UnityでWebVRのシューティングを作成する

Last updated at Posted at 2021-06-24

概要

  • Unity 2020.3でカンタンなVRのシューティングゲームを作成します
  • 最初は普通にマウスやスワイプなどで遊べるシューティングゲームを作成します
  • 最終的にWebGLでデプロイしてブラウザで楽しめるVRのコンテンツを作成します
  • 一日で完成できたモノなので手順を参考にして作ってもらえれば幸いです

使用する素材(Asset)とWebVR用のパッケージ

素材(Asset)

WebVR用のパッケージ

完成物

  • パソコンで遊ぶ方はマウスでドラッグすることで視点を変えることができます
  • 近寄ってくるオブジェクトに対して照準を合わせると破壊できます
  • オブジェクトを破壊すると点数が入ります
  • 制限時間が0になるゲーム終了です
  • オブジェクトを破壊できずカメラにあたるとゲームオーバーです

image.png

作成手順

3Dシューティングゲームの作成

  • MainCameraをプレイヤーとして扱います
  • カメラの視点を動かしながら敵を倒すシューティングゲームです

3Dのプロジェクトを新規作成する

image.png

カメラの視点をマウスで動かせるようにする

  • DragMove.csというスクリプトを作成して以下の内容を記述します
  • 作成したスクリプトをMainCameraにアタッチします
  • MoveObjにカメラのオブジェクトであるMainCameraをアタッチします
  • アタッチ後に実行するとマウスのドラッグでカメラの視点を動かすことができます

image.png

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DragMove : MonoBehaviour
{
    public GameObject moveObj;
    private Vector3 newAngle = new Vector3(0, 0, 0);
    private Vector3 lastMousePosition;

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            // マウスクリック開始(マウスダウン)時にカメラの角度を保持(Z軸には回転させないため).
            newAngle = moveObj.transform.localEulerAngles;
            lastMousePosition = Input.mousePosition;
        }
        else if (Input.GetMouseButton(0))
        {
            // マウスの移動量分カメラを回転させる.
            newAngle.y -= (Input.mousePosition.x - lastMousePosition.x) * 0.1f;
            newAngle.x -= (Input.mousePosition.y - lastMousePosition.y) * 0.1f;
            moveObj.transform.localEulerAngles = newAngle;
            lastMousePosition = Input.mousePosition;
        }
    }

}

敵の設定

  • MainCameraに目掛けて敵が近寄ってくるようにします

敵のオブジェクトの準備

  • AssetLow Poly Space Rocksから敵として使うオブジェクトを選びます
    • 元のAssetとは別のフォルダにコピーしておきます
    • 今回は4つのEmenyのオブジェクトを作成しました

image.png

敵のスクリプトの設定

  • 敵のオブジェクトに対し以下の設定をします
    • Enemy.csというスクリプトを作成して以下の内容を記述してアタッチします
    • プレイヤーとの接触のためにSphereColliderを設定します
    • Enemyというタグを設定します

image.png

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class Enemy : MonoBehaviour
{
    GameObject target;
    float speed = 0.8f;
    // float speed = 0.8f;//PCだと0.8f
    float rotateSpeed = 0.5f;
    void Start()
    {
        target = GameObject.FindGameObjectWithTag("MainCamera");
    }

    void Update()
    {
        //自分の位置、ターゲット、速度
        transform.position = Vector3.MoveTowards(transform.position, target.transform.position, speed);
        transform.Rotate(new Vector3(rotateSpeed, rotateSpeed, rotateSpeed), Space.Self);
    }

}

ゲーム全体を制御する

  • スコアや制限時間の更新
  • ゲームオーバーの演出などを設定します

Canvasに表示するオブジェクトの準備

  • TextMeshProScoreTextTimeTextというオブジェクトを作成します
    • ScoreTextのアンカーを右上に設定します
    • TimeTextはのアンカーを左上に設定します
  • GameOverImageというImageのオブジェクトを作成します
    • GameOverImageはアンカーを中央にしてサイズをx:4000 y:4000に設定します

image.png

真ん中に照準の画像を配置する

  • 以下のアイコンの画像をダウンロードしてプロジェクトに追加します
  • Texture TypeSprite (2D and UI)に設定します
    • SpriteにすることでCanvasのImageから選択できるようになります
  • CanvasにTargetImageというImageのオブジェクトを作成します
    • オブジェクトのアンカーは中央にします
    • Source Imageを先ほど追加したspriteを設定します

image.png

image.png

スクリプトの設定

  • GameController.csというスクリプトを作成して以下の内容を記述します
  • CanvasにあるScoreTextオブジェクトをscoreTextにアタッチします
  • CanvasにあるtimeTextオブジェクトとtimeTextにアタッチします
  • 実際に作成したときは一番最後で作成したスクリプトです
    • 今回の手順ではstaticの変数で他のスクリプトから読み取る都合から先に作成しています

image.png

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class GameController : MonoBehaviour
{
    public TextMeshProUGUI scoreText;
    public TextMeshProUGUI timeText;

    public static float time = 60;
    public static int score = 0;
    const string DAMAGE_FX_OBJ_NAME = "GameOverImage";

    // Update is called once per frame
    void Update()
    {
        GameEndCheck();
        scoreText.text = $"SCORE: {score}";
    }

    void GameEndCheck()
    {
        if (time >= 0)
        {
            time -= Time.deltaTime;
            timeText.text = $"TIME: {Math.Floor(time)}";
        }
        else
        {
            timeText.text = "TIME: 0";
            EnemyAllDestroy();
        }
    }

    public static void EnemyAllDestroy()
    {
        GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
        foreach (GameObject enemy in enemies)
        {
            Destroy(enemy);
        }
    }

    public static void GameOver()
    {
        GameObject damageFx = GameObject.Find(DAMAGE_FX_OBJ_NAME);
        Image damageFxImg = damageFx.GetComponent<Image>();
        damageFxImg.color = new Color(0.5f, 0f, 0f, 0.5f);
        time = 0;
    }
}

敵を定期的に生成

空のオブジェクトの作成

  • EnemyGenerateという空のオブジェクトを作成します
  • EnemyGenerateオブジェクトの位置はX:0, Y:0, Z:80にします

image.png

スクリプトの設定

  • EnemyGenerate.csというスクリプトを作成して以下の内容を記述します
  • EnemyGenerateオブジェクトにPrefabで作成した敵を複数アタッチします

image.png

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyGenerate : MonoBehaviour
{
    public GameObject[] enemies;
    float posRangeX = 40;
    float posRangeY = 40;
    float geneTimeSec = 4;
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine("EnemyGanerate");
    }

    // Update is called once per frame
    void Update()
    {
        if (GameController.time < 0)
            StopCoroutine("EnemyGanerate");
    }

    IEnumerator EnemyGanerate()
    {
        while (true)
        {
            GameLevelChenge();
            int enemyRnd = Random.Range(0, enemies.Length);
            float posXrnd = Random.Range(posRangeX * -1, posRangeX);
            float posYrnd = Random.Range(posRangeY * -1, posRangeY);
            Instantiate(
                enemies[enemyRnd],
                new Vector3(
                    transform.position.x + posXrnd,
                    transform.position.y + posYrnd,
                    transform.position.z
                ),
                Quaternion.identity
            );
            yield return new WaitForSeconds(geneTimeSec);
        }
    }

    void GameLevelChenge()
    {
        if (geneTimeSec > 1.0f) geneTimeSec -= 0.15f;
    }
}

Playerの制御

敵を倒したときのパーティクルを準備する

  • AssetSherbb's Particle Collectionから敵を倒したときのパーティクルを選びます
    • 選んだパーティクルは別のフォルダにコピーしておきます
    • コピーしたパーティクルはStop ActionDestroyを設定します
    • Destroyを設定しないとパーティクルが残り続けるので気を付けます

image.png

スクリプトの設定

  • Player.csというスクリプトを作成して以下の内容を記述します
  • 作成したスクリプトをMainCameraにアタッチします
  • 上記で準備したパーティクルのオブジェクトをスクリプトのshootVFXにアタッチします

image.png

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class Player : MonoBehaviour
{
    float distance = 100f;

    public GameObject shootVFX;

    // Update is called once per frame
    void Update()
    {
        RayShoot();
    }

    void OnCollisionEnter(Collision col)
    {
        if (col.gameObject.tag == "Enemy") GameController.GameOver();
    }

    void RayShoot()
    {
        Ray ray = new Ray(transform.position, transform.forward);
        Debug.DrawRay(ray.origin, ray.direction * distance, Color.red, 0.1f, false);
        RaycastHit hit;

        if (Physics.Raycast(ray, out hit, distance))
        {
            if (hit.collider.gameObject.tag == "Enemy")
            {
                GameController.score++;
                Debug.Log(hit.collider.gameObject.name);
                Instantiate(
                    shootVFX,
                    new Vector3(
                            hit.collider.gameObject.transform.position.x,
                            hit.collider.gameObject.transform.position.y,
                            hit.collider.gameObject.transform.position.z
                    ),
                    hit.collider.gameObject.transform.rotation
                );
                Destroy(hit.collider.gameObject);
            }
        }
    }
}

ここまでの手順で3Dシューティングゲームとして完成

  • 上記の手順を終えればカメラの視点を元にした3Dシューティングゲームとして遊べます
  • PC, Mac & Linux Standaloneでそれぞれのパソコンで遊ぶモノをビルドできます

image.png

WebVR化する

作成したシーンの複製(シーンのバックアップ)

  • すでに3Dシューティングとしては完成しているので別のシーンファイルでWebVR化していきます
  • シーンファイルを選択してCtrl + Dで複製できます
  • 複製したシーンファイルはWebVR用とわかるよう名前を変更しておきます
  • 複製しておけばWebVR化したときに上手くいかなかってもある程度は復元することができます
    • シーン共通で使用しているPrefabなどのオブジェクトを直接編集したりするとダメですが・・・

image.png

WebVRのパッケージをインストールしサンプルのシーンの追加

  • 以下の記事に手順が記載されています
    • パッケージの追加から各種設定
    • サンプルシーンの追加

サンプルシーンからWebXRCameraSetのコピー

  • WebXRCameraSetCtrl + Cでコピーします

image.png

WebVR化するシーンにWebXRCameraSetの貼り付け

  • 先ほど複製したWebVR化のシーンを開きます
  • 開いてHierarchyCtrl + VでコピーしたWebXRCameraSetを貼り付けます
  • 貼り付けた後は元からあるMainCameraは削除します

image.png

WebXRCameraSetの設定

  • 各コンポネントを追加します
    • RigidBody
    • BoxCollider:Sizeを「X:10, Y:10, Z:10」
  • Player.csをアタッチする
  • DragMove.csをアタッチする

image.png

WebVRとしてビルドする

  • Build Settingsで複製したシーンを追加してビルドします
    • 元からあるシーンは削除します
  • ビルドでできたファイルをウェブサーバーにアップロードするとWebVRとして遊べます

ふりかえり

  • 3DゲームをつくってそれをWebVRすることができました
  • Canvas上のオブジェクトはVR化するにあたって工夫は必要そうです
  • 一日で完成できるので難易度的にもそこまで難しくないかもしれません
0
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
5