YKpages

ロボット関連で勉強したことのまとめ

Unity ゲーム開発日記:タイトル画とBGM

はじめに

ゲームのシステムは完成しつつあります

システム以外でゲームに必要なものと言えばタイトル画とBGMですね

イラストを描く

何かしらイラストを描くソフトを用意します

私の場合

  • PC : Clip Studio
  • iPad : Procreate

サイズや解像度を気にしてイラストを描きます (最適なサイズや解像度は各々が選択)

また、背景を透過させる場合のファイル形式は、PNGが良いらしいです

f:id:kato_robotics:20181113024602p:plain

画像を挿入する場合、 UIのCanvasの子としてImageを作成します

このとき、Canvasの表示サイズの設定を「Scale With Screen Size」とすると スクリーンのサイズ(スマホの画面サイズなど)に合わせてUIの大きさを自動で調節してくれます

dojican-lab.blogspot.com

BGMの作成

曲が作れるソフトを用意します

PCの場合は「StudioOne」などがあります

今回は、iOSMacOSに標準で入っているGarageBandを使用します

簡単に曲が作成できるので暇つぶしにもちょうどよいです

Unityにおいて音を鳴らす方法は以下を参考にします

unity3d.com

他にも調べればたくさん出てくるのでここでは何もかかないでおきます

おわりに

ゲームにおけるタイトル画とBGMの作成と導入方法をまとめました

ゲーム完成に向けて作りこんでいきましょう

Unity + Vuforia マーカトラッキングメモ

はじめに

Unity + Vuforia でマーカトラッキングをするための自分用メモ

対象は iPad (iOS12)

環境

  • MacOS
  • Unity 2018.2.8f1
  • Vuforia 7.2.23
  • iPad6 (iOS12)

マーカトラッキング

Vuforia は Unity に統合されたので初めから入っている

「MainCamera」を削除して GameObject > Vuforia > ARCamera を選択

このとき、Vuforia のパッケージがインポートされる

  • Assets
    • Editor
    • Vuforia

次に、GameObject > Vuforia > Image を選択して「ImageTarget」を作成

このとき、Vuforia のパッケージがさらにインポートされる

  • Assets
    • StreamingAssets
    • Resources

最後に、BuildSttings > PlayerSettings > XR Settings > VuforiaAugmentedReality にチェックを入れる

これでマーカ(デフォルトではAstronaut)が認識できる

VuforiaConfiguration

Assets > Resources > VuforiaConfiguration で Vuforia の設定とかできる

またここで Vuforia のバージョンも確認できる

「Device Tracker」にチェックを入れる

「Tracking mode」は「POSITIONAL」

kato-robotics.hatenablog.com

「ARCamera」の「World Center Mode」が「DEVICE」になっている

kato-robotics.hatenablog.com

おまけ1

マーカを認識して何かするスクリプトは、Assets > Vuforia > Scritps > DefaultTrackableEventHandler

このスクリプトをいじるといろいろできる

おまけ2

Blender で作成した fbsファイル(例:Arrows.fbs)のオブジェクト

Unity にインポートすると

  • Assets
    • Camera
    • Cone
    • Cylinder
    • Lamp

となっている

オブジェクト一つひとつに「Camera」と「Lamp」が付いていると処理がとても重くなるので 削除するか無効にしておく

おわりに

Unity + Vuforia でマーカをトラッキングする方法をまとめた

Unity ゲーム開発日記:実機デバッグ

はじめに

これまでシューティングゲーム「オシャレなロケット:Oshare-Na-Rocket」のシステムを作ってきました

このゲームはスマートフォン向けに作っているので実機でちゃんと動作するか確認する必要があります

よって今回は実機デバッグを行っていきます(本当はもっと早くやるべきだった)

環境

  • Windows10
  • Unity 2018.2.14f1
  • Android 7

参考

uni.gas.mixh.jp

Android SDK の入手

まだAndroid SDKを持っていない人はAndroid Studioをインストールします

また、すでにAndroid Studioをインストールしている場合でもアップデートが必要かもしれません

SDKのアップデートはAndroid StudioのConfigから行えます

developer.android.com

Android SDK & JDK のパス設定

Edit > Preferences > External Tools に「Android SDK」と「JDK」のパスを設定するところがあります

Android SDKのパスはAndroid StudioのConfig画面を開くとパスが書いてあります

JDKは「C:/Program Files/Android/Android Studio/jre」としました(要確認)

Android 実機デバッグ

File > Build Setting を選択してビルド設定画面を開きます

そして以下の図のように設定します

f:id:kato_robotics:20181104150526p:plain

まず、「Add Open Scene」ボタンを押してシーンを登録します

次に"Platform"で"Android"をクリックして「Switch Platform」ボタンを押します

その後、「Player Setting」ボタンを押すとインスペクタウインドウに設定項目が出てきます

一番上の「Company Name」と「Product Name」を入力(ハイフンは使えない?)

f:id:kato_robotics:20181104151924p:plain

「Other Setting」の「Package Name」に「com.CompanyName.ProductName」となるように入力します

f:id:kato_robotics:20181104151942p:plain

ビルド

これで最低限の設定が終わりました

Android端末のUSBデバッグを有効にします

これを忘れるとUnityがAndroid端末を認識できません

「Build and Run」ボタンを押して、ビルドが成功するとAndroid端末でアプリが開きます

f:id:kato_robotics:20181104153159p:plain

おわりに

無事、実機でもちゃんと動作することが確認できました

次はシステムを遊べるように作っていきます

Unity ゲーム開発日記:当たり判定を作る

はじめに

今回は当たり判定を作っていきます

これができれば最低限のシューティングゲームのシステムができるはずです

Colliderの設定

衝突判定のためにオブジェクトの形状を定義します

Playerには「Box Collider」、Enemyには「Sphere Collider」にしました

いい感じに大きさを調節します

次にオブジェクトのレイヤーを分けます

何も設定していない場合、オブジェクトは全て衝突してしまいます

オブジェクトがどのオブジェクトと衝突するかは、レイヤーを設定することで簡単に決められるようです

Edit > Project Settings > Tags and Layers で以下のように設定

f:id:kato_robotics:20181104054416p:plain

Edit > Project Settings > Physics で以下のように設定

f:id:kato_robotics:20181104071912p:plain

Bullet どうしも衝突するようにしました

Particle System 作成

PlayerまたはEnemyが相手のBulletに衝突して消滅するときのエフェクトとしてParticle Systemを作成します

Create > Effects > Particle System を選択、名前は「enemyParticle」とします

設定は以下のように(好きなように)

f:id:kato_robotics:20181104055856p:plain

f:id:kato_robotics:20181104062043p:plain

f:id:kato_robotics:20181104055907p:plain

新しくマテリアルを作成してenemy用の画像を設定

Shader を Particle > Alpha Blended にする

作成したマテリアルをParticle Systemに設定

こんな感じになります

f:id:kato_robotics:20181104062139p:plain

これをEnemyが消滅したときに発生させます

同じようにPlayer用のParticle Systemも作ります

f:id:kato_robotics:20181104062523p:plain

Particle System のパーティクルに画像を設定

vivi.dyndns.org

衝突時のイベントを書く

衝突判定には方法が二つあるようです

qiita.com

レイヤーを設定するとTriggerを使えますが、 今回はOnCollisionEnter関数を使ってみます

各オブジェクトに「Tag」を設定します

「Tag」を利用して衝突したオブジェクトを判別します

f:id:kato_robotics:20181104081157p:plain

Particle System は Prefab 化してから Instantiate 関数を用いて発生させます

// PlayerDriver.csに追加
public GameObject playerParticleSystem;

// 衝突時のイベント
    void OnCollisionEnter(Collision col)
    {
        // 衝突したオブジェクトのTagが敵の弾かどうか
        if (col.gameObject.tag != "EnemyBullet") return;
        Debug.Log(col.gameObject.tag);
        Debug.Log(gameObject.name);

        // Particle System を起動
        GameObject pps = Instantiate(playerParticleSystem, transform.position, transform.rotation);
        Destroy(pps, 2f);
        // 弾を削除
        Destroy(col.gameObject);
        // Enemyオブジェクトを削除
        Destroy(gameObject);
    }
// EnemyDriver.csに追加
public GameObject enemyParticleSystem;

   // 衝突時のイベント
    void OnCollisionEnter(Collision col)
    {
        // 衝突したオブジェクトのTagがプレイヤーの弾かどうか
        if (col.gameObject.tag != "PlayerBullet") return;
        Debug.Log(col.gameObject.tag);
        Debug.Log(gameObject.name);

        // Particle System を起動
        GameObject eps = Instantiate(enemyParticleSystem, transform.position, transform.rotation);
        Destroy(eps, 2f);
        // 弾を削除
        Destroy(col.gameObject);
        // Enemyオブジェクトを削除
        Destroy(gameObject);
    }

当たり判定を取り入れたゲームはこんな感じになりました

f:id:kato_robotics:20181104081826p:plain

画像では分かりにくいですが、当たり判定がうまく働いています

弾が敵に当たるとParticleSystemを発生させた後、敵が消滅します

おわりに

無事に当たり判定を取り入れることができました

これでシューティングゲームの最低限のシステムができました

次はクリア条件などゲームとして遊べるようにしていきます

Unity ゲーム開発日記:敵を作る

はじめに

今回は敵を作っていきます

まずイメージを作成

プレイヤーは四角形にしたので、 敵は丸にします

f:id:kato_robotics:20181103222143p:plain

ゲームウインドウではこんな感じです

f:id:kato_robotics:20181103222246p:plain

敵(Enemy)を作成

Create > 2D Objects > Sprite を選択して、名前は「Enemy」とします

Sprite のイメージには先ほど作成した Enemy 用のイメージを選択

Playerと同じようにRigidbodyをアタッチします

EnemyManagerを作成

まず、空のオブジェクトを作成して名前を「EnemyManager」とします

新規のC#スクリプトを作成して、「EnemyManager」とします

     void Start () 
    {
        for(int i = 0; i < 10; i++)
        {
            Instantiate(enemy, new Vector3(10f, 10f, 0f), Quaternion.identity);
        }
    }

とりあえずStart関数では、Enemyを10体作成するようにしました

原点に置くとPlayerとかぶってしまうので遠くに置いています

f:id:kato_robotics:20181104015229p:plain

Enemyを動かす

EnemyDriver.csというスクリプトを作成します

public class EnemyDriver : MonoBehaviour {

    public float enemySpeed = 1.0f;
    Rigidbody enemyRigidbody;

    // スクリーンの大きさ
    Vector3 min;
    Vector3 max;

    // 進行方向
    Vector3 enemyDirection;

    void Start () {
        enemyRigidbody = GetComponent<Rigidbody>();
        // スクリーンの大きさを取得
        min = Camera.main.ScreenToWorldPoint(new Vector3(0, 0, 10f));
        max = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 10f));
        // ランダムに座標を決める
        Vector3 enemyPosition = new Vector3(0, 0, 0);
        enemyPosition.x = Random.Range(min.x, max.x);
        enemyPosition.y = Random.Range(min.y, max.y);
        if(enemyPosition.x > -2f && enemyPosition.x < 2f && enemyPosition.y > -2f && enemyPosition.y < 2f)
        {
            enemyPosition.x += Random.Range(-5f, 5f);
            enemyPosition.y += Random.Range(-5f, 5f);
        }
        transform.position = enemyPosition;
        // ランダムに進行方向を定める
        Vector3 angle = new Vector3(0, 0, Random.Range(0, 360));
        enemyDirection = Quaternion.Euler(angle) * Vector3.up;
        enemyRigidbody.velocity = enemyDirection * enemySpeed;
    }
    
    void Update () {
        WallCollision();
        EnemyLookAt();
    }

    void EnemyLookAt()
    {
        // 進行方向に向かせる(2Dであることに気をつける)
        float angle = GetAngle(enemyDirection);
        transform.eulerAngles = new Vector3(0, 0, angle - 90f);
    }

    float GetAngle(Vector3 v)
    {
        float rad = Mathf.Atan2(v.y, v.x);
        return rad * Mathf.Rad2Deg;
    }

    void WallCollision()
    {
        if (transform.position.x < min.x)
        {
            Rebound(1);
        }
        if (transform.position.x > max.x)
        {
            Rebound(2);
        }
        if (transform.position.y < min.y)
        {
            Rebound(3);
        }
        if (transform.position.y > max.y)
        {
            Rebound(4);
        }
    }

    // enemyが壁に当たって跳ね返る
    void Rebound(int flag)
    {
        // 壁に当たったら位置を戻す
        Vector3 pos = transform.position;
        if (flag == 1) pos.x = min.x;
        else if (flag == 2) pos.x = max.x;
        else if (flag == 3) pos.y = min.y;
        else if (flag == 4) pos.y = max.y;
        transform.position = pos;
        // XまたはY方向の速度を逆にする
        if(flag == 1 || flag == 2) enemyDirection.x = -enemyDirection.x;
        else if(flag == 3 || flag == 4) enemyDirection.y = -enemyDirection.y;
        // 速度を更新
        enemyRigidbody.velocity = enemyDirection * enemySpeed;
    }
}

スクリーン内をランダムに動き回るようにしました

かなり力技になってしまいましたがこれで良しとしておきます(勉強します)

参考

Enemyの進行方向を決定する処理のために参考にしました

qiita.com

Enemyから弾を撃つ

以前に作ったPlayerが弾を撃つ関数を改良しました

PlayerとEnemyで共通の関数を利用することにします

public class ShotBullet : MonoBehaviour {

    public bool isShoot = true;
    public GameObject shotBullet;
    public GameObject bullet;

    IEnumerator Start()
    {
        while (isShoot)
        {
            // 弾をプレイヤーと同じ姿勢でインスタンス化
            GameObject sb = Instantiate(shotBullet, transform.position, transform.rotation);
            GameObject b = Instantiate(bullet, sb.transform);
            b.transform.position = sb.transform.position;
            b.transform.rotation = sb.transform.rotation;
            // 0.3待つ
            yield return new WaitForSeconds(0.3f);
        }
    }
}
public class Bullet : MonoBehaviour {

    public float bulletSpeed = 4f;

    void Start () {
        GetComponent<Rigidbody>().velocity = transform.up.normalized * bulletSpeed;
    }
    
    // Update is called once per frame
    void Update () {
        
    }
}

まず、空のオブジェクト「ShotBullet」をPlayerまたはEnemyの姿勢に合わせて作成

次に「Bullet」オブジェクトを作成して「ShotBullet」の"子"とします

「Bullet」は「ShotBullet」の座標系に従って真っすぐにとんでいきます

f:id:kato_robotics:20181104042530p:plain

おわりに

敵を作成しました

次は当たり判定を付けていきます

これができればシューティングゲームにおける最低限のシステムを作ったことになります

Unity ゲーム開発日記:弾を発射

はじめに

今回はプレイヤーから弾を発射します

その前に

以下のようなプレイヤーの画像を作成しました

f:id:kato_robotics:20181103194947p:plain

ゲーム画面ではこんな感じです(まだ全然ロケットぽくない)

f:id:kato_robotics:20181103195154p:plain

弾のオブジェクトを作成

まず空のオブジェクトを作成して名前を「PlayerBullet」とします

この空のオブジェクトにはスクリプトなどは一切アタッチしません(後で付けます)

次に 2D Sprite を作成して名前を「Bullet」とします

そして「Bullet」を「PlayerBullet」の子にします

  • 親:PlayerBullet(空のオブジェクト)
  • 子:Bullet(2D Sprite)

オブジェクトを親子関係にすると、子のオブジェクトは親のオブジェクトの座標系に従います

これにより「Bullet」の動きを簡単に書くことができます

Bullet.cs を書く

    void Start () 
    {
        GetComponent<Rigidbody>().velocity = transform.up.normalized * bulletSpeed;
    }

このコードでは上方向へ速度を与えます

先ほど書いたとおり、「Bullet」は「PlayerBullet」の座標系に従います

以下のコードのように「PlayerBullet」をPlayerの姿勢に合わせてインスタンス化すれば「Bullet」はPlayerが向いている方向へ発射されます

    IEnumerator Start()
    {
        while(isShoot)
        {
            // 弾をプレイヤーと同じ姿勢でインスタンス化
            Instantiate(playerBullet, transform.position, transform.rotation);
            // 0.3待つ
            yield return new WaitForSeconds(0.3f);
        }
    }

弾を撃っているときのゲーム画面はこのようになりました(Playerがどれか分かりづらいですね)

f:id:kato_robotics:20181103210125p:plain

おわりに

今回はPlayerから弾を発射しました

次は敵を作成したいと思います

Unity ゲーム開発日記:プレイヤーを動かす

はじめに

2Dシューティングゲーム「オシャレなロケット:Oshare-Na-Rocket」のシステムを作っていきます。

今回はプレイヤーを動かすシステムを作ります。

参考

主に公式のチュートリアルを参考にします。

チュートリアルでは2Dモードで作成していますが、 私は3Dモードで作成します(ゲーム自体は基本的に2Dです)。

unity3d.com

以下の記事はPlayerを進行方向へ向けるときに参考にさせていただきました

qiita.com

背景を黒に

Main Camera の "Clear Flag" を "Solid Color" にして色を黒にします

背景はとりあえずこれで良しとします

プレイヤーのオブジェクトを作成

Create > 2D Objects > Sprite を選択

名前は "Player" にします

Player の Sprite に "Knob" を選択すると、黒い背景に白い点が現れます

そしてPlayerに "Rigidbody" をアタッチ

Rigidbodyをアタッチすると、そのオブジェクトを物理的に制御できます

docs.unity3d.com

タッチ(マウス)でプレイヤーを操作

タッチ、またはマウスでプレイヤーを動かします

本来はスマートフォンのタッチ操作で動かすことになりますが、 Unityエディタ上ではマウスで動かします

方法

  • タッチを始めた座標(スクリーン座標)を取得
  • 指をスライドさせた後の座標を取得
  • 1と2からプレイヤーが移動する方向を計算
  • プレイヤー移動

タッチ操作のための関数を作成

// タッチを始めた座標
Vector2 TouchPositionFrist;
     void PlayerTouch ()
    {
        // タッチ数をカウント、0より大きい場合(つまりタッチがある場合)
        if (Input.touchCount > 0)
        {
            Touch touch = Input.GetTouch(0);

            // タッチを始めた時
            if (touch.phase == TouchPhase.Began)
            {
                //タッチを始めた座標を取得
                TouchPositionFrist = touch.position;
            }
            // 指をスライドさせている時
            else if (touch.phase == TouchPhase.Moved)
            {
                // プレイヤーを移動させる関数へ座標を渡す
                PlayerMove(TouchPositionFrist, touch.position);
            }
            // 指を離した時
            if (touch.phase == TouchPhase.Ended)
            {
                // プレイヤーの速度を0にする
                PlayerRigidbody.velocity = new Vector3(0, 0, 0);
            }
        }
    }

プレイヤーを動かす関数

    void Start () 
    {
        // プレイヤーのRigidbodyコンポーネントを取得
        PlayerRigidbody = GetComponent<Rigidbody>();
    }
    void PlayerMove (Vector3 v1, Vector3 v2)
    {
        // 二つのベクトルから指をスライドさせた方向を求める
        Vector3 direction = (v2 - v1).normalized;
        // 指をスライドさせた方向とは逆向きにプレイヤーを移動
        PlayerRigidbody.velocity = -direction * PlayerSpeed;
    }

プレイヤーに進行方向を向かせる

PlayerMove関数に追加

    void PlayerMove (Vector3 v1, Vector3 v2)
    {
        Vector3 direction = (v2 - v1).normalized;
        PlayerRigidbody.velocity = direction * PlayerSpeed;
        // 進行方向に向かせる(2Dであることに気をつける)
        float angle = GetAngle(direction);
        transform.eulerAngles = new Vector3(0, 0, angle);
    }

アークタンジェントを使ってZ軸の向きを求める

    float GetAngle(Vector3 v)
    {
        float rad = Mathf.Atan2(v.y, v.x);
        return rad * Mathf.Rad2Deg;
    }

移動制限を設ける

Start関数でスクリーンの大きさを取得します

プレイヤーをスクリーン内で動かすようにするためです

Camera.main.ScreenToWorlePointでカメラがNullだよみたいなエラーが出た場合、 CameraのTagが設定されていない可能性があります。CameraのTagを"MainCamera"にすると直ります。

    void Start () 
    {
        Debug.Log("!!START!!");
        playerRigidbody = GetComponent<Rigidbody>();
        // スクリーンの大きさを取得
        min = Camera.main.ScreenToWorldPoint(new Vector3(0, 0, 10f));
        max = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 10f));
    }

そしてPlayerMove関数を以下のように変更します。

Mathf.Clamp関数は定めた範囲から出た場合、内側に戻してくれます。

marginは余裕を持たせるために設定しました。

    void PlayerMove (Vector3 v1, Vector3 v2)
    {
        // 指をスライドさせた方向に移動
        Vector3 direction = (v2 - v1).normalized;
        playerRigidbody.velocity = direction * playerSpeed;
        // 進行方向に向かせる(2Dであることに気をつける)
        float angle = GetAngle(direction);
        transform.eulerAngles = new Vector3(0, 0, angle - 90f);
        // 移動制限を設ける
        Vector3 playerPosition = transform.position;
        float margin_x = max.x / 20f;
        float margin_y = max.y / 20f;
        playerPosition.x = Mathf.Clamp(playerPosition.x, min.x + margin_x, max.x - margin_x);
        playerPosition.y = Mathf.Clamp(playerPosition.y, min.y + margin_y, max.y - margin_y);
        transform.position = playerPosition;
    }

Camera.main.ScreenToWorlePointのエラーについて

blog.be-style.jpn.com

今回書いたコード

github.com

おわりに

今回はプレイヤーを動かすシステムを作成しました

次は弾を発射するシステムを作っていきます