概要
-
Unity 2020.3
でカンタンなVRのシューティングゲームを作成します - 最初は普通にマウスやスワイプなどで遊べるシューティングゲームを作成します
- 最終的に
WebGL
でデプロイしてブラウザで楽しめるVRのコンテンツを作成します - 一日で完成できたモノなので手順を参考にして作ってもらえれば幸いです
使用する素材(Asset)とWebVR用のパッケージ
素材(Asset)
WebVR用のパッケージ
完成物
- パソコンで遊ぶ方はマウスでドラッグすることで視点を変えることができます
- 近寄ってくるオブジェクトに対して照準を合わせると破壊できます
- オブジェクトを破壊すると点数が入ります
- 制限時間が0になるゲーム終了です
- オブジェクトを破壊できずカメラにあたるとゲームオーバーです
作成手順
3Dシューティングゲームの作成
-
MainCamera
をプレイヤーとして扱います - カメラの視点を動かしながら敵を倒すシューティングゲームです
3Dのプロジェクトを新規作成する
カメラの視点をマウスで動かせるようにする
-
DragMove.cs
というスクリプトを作成して以下の内容を記述します - 作成したスクリプトを
MainCamera
にアタッチします -
MoveObj
にカメラのオブジェクトであるMainCamera
をアタッチします - アタッチ後に実行するとマウスのドラッグでカメラの視点を動かすことができます
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
に目掛けて敵が近寄ってくるようにします
敵のオブジェクトの準備
-
Asset
のLow Poly Space Rocks
から敵として使うオブジェクトを選びます- 元の
Asset
とは別のフォルダにコピーしておきます - 今回は4つの
Emeny
のオブジェクトを作成しました
- 元の
敵のスクリプトの設定
- 敵のオブジェクトに対し以下の設定をします
-
Enemy.cs
というスクリプトを作成して以下の内容を記述してアタッチします - プレイヤーとの接触のために
SphereCollider
を設定します -
Enemy
というタグを設定します
-
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に表示するオブジェクトの準備
-
TextMeshPro
でScoreText
とTimeText
というオブジェクトを作成します-
ScoreText
のアンカーを右上に設定します -
TimeText
はのアンカーを左上に設定します
-
-
GameOverImage
というImage
のオブジェクトを作成します-
GameOverImage
はアンカーを中央にしてサイズをx:4000 y:4000
に設定します
-
真ん中に照準の画像を配置する
- 以下のアイコンの画像をダウンロードしてプロジェクトに追加します
-
Texture Type
をSprite (2D and UI)
に設定します- SpriteにすることでCanvasのImageから選択できるようになります
- CanvasにTargetImageというImageのオブジェクトを作成します
- オブジェクトのアンカーは中央にします
-
Source Image
を先ほど追加したspriteを設定します
スクリプトの設定
-
GameController.cs
というスクリプトを作成して以下の内容を記述します - Canvasにある
ScoreText
オブジェクトをscoreText
にアタッチします - Canvasにある
timeText
オブジェクトとtimeText
にアタッチします - 実際に作成したときは一番最後で作成したスクリプトです
- 今回の手順では
static
の変数で他のスクリプトから読み取る都合から先に作成しています
- 今回の手順では
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
にします
スクリプトの設定
-
EnemyGenerate.cs
というスクリプトを作成して以下の内容を記述します -
EnemyGenerate
オブジェクトにPrefab
で作成した敵を複数アタッチします
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の制御
敵を倒したときのパーティクルを準備する
-
Asset
のSherbb's Particle Collection
から敵を倒したときのパーティクルを選びます- 選んだパーティクルは別のフォルダにコピーしておきます
- コピーしたパーティクルは
Stop Action
でDestroy
を設定します -
Destroy
を設定しないとパーティクルが残り続けるので気を付けます
スクリプトの設定
-
Player.cs
というスクリプトを作成して以下の内容を記述します - 作成したスクリプトを
MainCamera
にアタッチします - 上記で準備したパーティクルのオブジェクトをスクリプトの
shootVFX
にアタッチします
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
でそれぞれのパソコンで遊ぶモノをビルドできます
WebVR化する
作成したシーンの複製(シーンのバックアップ)
- すでに3Dシューティングとしては完成しているので別のシーンファイルでWebVR化していきます
- シーンファイルを選択して
Ctrl + D
で複製できます - 複製したシーンファイルはWebVR用とわかるよう名前を変更しておきます
- 複製しておけばWebVR化したときに上手くいかなかってもある程度は復元することができます
- シーン共通で使用しているPrefabなどのオブジェクトを直接編集したりするとダメですが・・・
WebVRのパッケージをインストールしサンプルのシーンの追加
- 以下の記事に手順が記載されています
- パッケージの追加から各種設定
- サンプルシーンの追加
サンプルシーンからWebXRCameraSetのコピー
-
WebXRCameraSet
をCtrl + C
でコピーします
WebVR化するシーンにWebXRCameraSetの貼り付け
- 先ほど複製したWebVR化のシーンを開きます
- 開いて
Hierarchy
でCtrl + V
でコピーしたWebXRCameraSet
を貼り付けます - 貼り付けた後は元からある
MainCamera
は削除します
WebXRCameraSetの設定
- 各コンポネントを追加します
- RigidBody
- BoxCollider:Sizeを「X:10, Y:10, Z:10」
- Player.csをアタッチする
- DragMove.csをアタッチする
WebVRとしてビルドする
-
Build Settings
で複製したシーンを追加してビルドします- 元からあるシーンは削除します
- ビルドでできたファイルをウェブサーバーにアップロードするとWebVRとして遊べます
ふりかえり
- 3DゲームをつくってそれをWebVRすることができました
- Canvas上のオブジェクトはVR化するにあたって工夫は必要そうです
- 一日で完成できるので難易度的にもそこまで難しくないかもしれません