iOSエンジニアがUnityに入門してみた
Unityとは?
UnityはUnity Technologiesが提供するゲームエンジンで、開発環境や実行環境もあり、2Dや3Dゲームを手軽に開発することができる。アセット(素材)ストアが充実していて、ドキュメントや情報も豊富で、初心者がゲーム開発を始めやすいと言われている。League Of Legendsや原神、ポケモンGoなどの有名タイトルで採用されている。
ゲームエンジンというと、Epic GamesのUnreal Engineも非常に有名で、グラフィックが高品質でPCやコンシューマゲーム開発で人気が高く、FortniteやPUBGなどのタイトルで採用されている。
WWDC 2023で発表されたApple Vision Proでは、Unityと連携してUnityでVision Pro対応のアプリを開発できるだけでなく、パススルーや高解像度レンダリングなどのvisionOS機能が使えると発表されている(プレスリリース)。前からUnityには興味があったが、この発表でUnityに入門してみようと思った。
Unityの開発環境
iOS開発ではApple IDとXcodeが必要だが、Unity開発ではUnity IDを登録して、Unity HubとUnity Editorをインストールする必要がある。
Unity Hub
Unity Editorのバージョンを管理するツール。xcodesと似てそう。
Unity Editor
開発で使うIDE。Unity Editorのバージョンによって使える機能が異なったり、破壊的変更もありうるので、開発するプロジェクトの対応バージョンが大事。この辺は、Xcodeのバージョンアップ対応と似ていて苦労しそう。
GameObjectとは?
Unity開発を始めるとまずGameObjectというものが出てくるが、これはUnity内で作られるあらゆるオブジェクトのことで、GameObjectなしにUnity開発は成り立たない。また、GameObjectと同じくらい大事なのがコンポーネントで、GameObjectという箱に機能を追加していく部品と言える。
例えば、上のスクリーンショットに球体のオブジェクトがあり、Editorの左側のHierarchyにはSphereというGameObjectが、右側のInspectorにはTransformやMesh Renderer、Colliderなどのコンポーネントがある。これらのコンポーネントによって、球体のオブジェクトに座標や回転、大きさ、レンダリング、当たり判定などの機能が使えるようになる。
GameObjectにスクリプトをアタッチする
UnityではGameObjectにスクリプトを追加(アタッチ)することで、コードでGameObjectの振る舞いを制御することができる。また、スクリプトもコンポーネントの1つで、Assets > Create > C# Scriptからスクリプトを作成でき、Hierarchyにある対象のGameObjectまたはGameObjectのInspectorにドラッグ&ドロップすると、スクリプトをアタッチできる。
作成した直後のスクリプトファイルはこのような形で、MonoBehaviourというUnityにおける基底クラスを継承していて、StartやUpdateなどのライフサイクルのイベント関数が自動的に呼ばれる。
Start:オブジェクトの初期化時に一度だけ呼び出され、オブジェクトが有効になった時に実行される。オブジェクトで必要な初期化処理を行う。
Update:ゲーム実行中にフレームごとに呼び出される。ゲームオブジェクトの動作を制御するために利用される。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Sample : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Prefabとは?
Prefab(プレハブ)とは、GameObjectを再利用可能にしたテンプレートのようなもので、同じものを複数配置するなどの場合に便利。PrefabからGameObjectを生成したり、Prefabを編集してその設定を一括でGameObjectに反映したりすることができる。
Prefabを作成するのは簡単で、HierarchyにあるGameObjectをProject内にドラッグ&ドラッグすればよい。逆にProjectにあるPrefabをHierarchyにドラッグ&ドラッグすればGameObjectを追加することができる。例えば、SphereをPrefab化して、Materialを青色に変更してからHierarchyに追加すれば、青い球体を複数追加できる。
PrefabからGameObjectを生成するには、スクリプトでInstantiateメソッドを使ってもでき、ゲームで敵を生成したりするときに使える。また、Instantiateで生成するPrefabを設定するには、[SerializeField]でシリアライズ属性を変数に付与して、Inspectorから生成したいPrefabを設定すればよい。
※Unityではシリアライズ可能なフィールドはInspectorからアクセスできるため
public class Sample : MonoBehaviour
{
// Prefabを設定する変数
[SerializeField] public GameObject enemyPrefab;
void CreateEnemy()
{
// InstantiateメソッドでGameObjectを生成
GameObject enemy = Instantiate(enemyPrefab);
}
}
簡単なゲームを作ってみる
ゲームと言えるほどのものでは全くないが、ここまでの内容+αで「プレイヤー(Cube)から弾(Sphere)を発射させて、敵(Cube)に当たったら消滅させる」ものを作ってみる。
Player
3D ObjectのCubeを作成してスクリプトもアタッチしておく。スペースキーを押したときに弾を発射させたり、矢印キーで左右に移動させたいので、Input.GetKeyDownやInput.GetKeyを使って処理を行う。また、[SerializeField]で弾のPrefabを変数に持ってInspectorから設定する。
public class Player : MonoBehaviour
{
[SerializeField] public GameObject bulletPrefab;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space)) {
// スペースキーを押したら弾を生成する
Instantiate(bulletPrefab, transform.position + Vector3.forward, Quaternion.identity);
}
if (Input.GetKey(KeyCode.LeftArrow)) {
// 左キーを押している間は左に移動する
transform.Translate(Vector3.left * 0.005f);
} else if (Input.GetKey(KeyCode.RightArrow)) {
// 右キーを押している間は右に移動する
transform.Translate(Vector3.right * 0.005f);
}
}
}
Bullet
弾は3D ObjectのSphereで作成してPrefab化しておく。衝突判定を行いたいので、ColliderとRigidbodyコンポーネントを追加する。Rigidbodyは重力や物理演算を扱うときに必要なコンポーネントで、今回は重力は無視するので、Use Gravityのチェックを外しておく。
Playerでスペースキーの押下時にBulletを生成し、BulletのUpdateで前方向に進ませる。弾が他のオブジェクトに衝突した時OnCollisionEnterが呼ばれるので、自身のgameObjectを破棄する。
public class Bullet : MonoBehaviour
{
void Update()
{
// 弾を前進させる
transform.Translate(Vector3.forward * 0.1f);
if (transform.position.z > 100) {
// 弾が一定距離以上進んだら破棄する
Destroy(gameObject);
}
}
void OnCollisionEnter(Collision collision) {
// 衝突したら破棄する
Destroy(gameObject);
}
}
Enemy
敵は3D ObjectのCubeで作成してPrefab化する。弾が敵に当たったら敵を消滅させたいのでOnCollisionEnterを使うが、念の為衝突した相手をcollision.gameObjectから取得して、そのタグが"Bullet"なら自身を破棄させる(敵が消滅する)。なお、Enemyの方には予め"Bullet"というタグを追加する必要がある。
public class Enemy : MonoBehaviour
{
void OnCollisionEnter(Collision collision) {
if (collision.gameObject.tag == "Bullet") {
// 衝突した相手が弾だったら破棄する
Destroy(gameObject);
}
}
}
また、ゲーム開始時に敵を複数出現させたいので、HierarchyにEnemyを追加してもいいが、ステージや進行度などによって敵の出現場所が変わることはよくあるので、今回はEnemyManagerという空のGameObjectを用意して、Start時にenemyPrefabから敵を初期化して横並びに配置してみた。
public class EnemyManager : MonoBehaviour
{
[SerializeField] public GameObject enemyPrefab;
void Start()
{
for (int i = -4; i <= 4; i+=2) {
var position = new Vector3(i, 0, 10f);
Instantiate(enemyPrefab, position, Quaternion.identity);
}
}
}
ここまでの実装したものを実行すると、上のGIFにあるような簡単なシューティングゲームができる。
リポジトリ
おわりに
いつもiOS開発をしているが、今回Unityに入門してみて、GameObjectやコンポーネント、Prefabなどアプリ開発とは異なる概念ややり方がとても新鮮だった!まだUnityの入り口でしかないが、もっと複雑なゲーム開発や機能開発、ワールド製作などもやっていくぞ!
この記事が気に入ったらサポートをしてみませんか?