【基本操作】タマあんどヒシガタ~2/3 オブジェクト作成【パズル】

●床作成

タマやヒシガタが動き回る「地面」を作成します。

▷床作成

平面を作成してTransformの位置を(4.5, -0.5, 4.5)とし、グレーのチェッカー模様に設定したマテリアルを割り当てます。

床作成

・平面作成

  1. ヒエラルキー左上の「+」をクリック
  2. 「3Dオブジェクト>平面」をクリック
  3. 「Floor」と入力し、ENTERキーを押す

・位置設定

  1. インスペクターのTransformにある位置を(4.5, -0.5, 4.5)とする

・マテリアル作成

  1. プロジェクトの「Assets」を右クリックし、「作成>マテリアル」をクリック
  2. 「matFloor」と入力し、ENTERキーを押す

・マテリアルテクスチャ設定

  1. インスペクターの「Main Maps>アルベド」左の◎をクリックし、「Default-Checker-Gray」をダブルクリック

・マテリアル色設定

  1. インスペクターの「Main Maps>アルベド>カラーバー」をクリックし、色ダイアログの16進数を「CCCCCC」に設定
  2. 色ダイアログの右上にある「×」をクリック

・マテリアルタイリング設定

  1. インスペクターの「Main Maps>タイリング」を(5, 5)に設定

・マテリアル割り当て

  1. プロジェクトの「matFloor」をヒエラルキーの「Floor」にドラッグ&ドロップ

・プロジェクト保存

  1. CTRLを押しながらSキーを押す

●壁作成

タマとヒシガタの移動を停止させる壁を作成します。
移動先に壁がある場合、推進力を失って停止します。

▷壁作成

箱を作成してTransformの位置を(0, 0, 5)とし、オレンジ色の「Background」に設定したマテリアルを割り当てます。

壁作成

・箱作成

  1. ヒエラルキー左上の「+」をクリック
  2. 「3Dオブジェクト>キューブ」をクリック
  3. 「Block」と入力し、ENTERキーを押す

位置、回転、スケール設定

  1. インスペクターのTransformにある位置を(0, 0, 5)とする
  2. インスペクターのTransformにある回転を(0, 0, 0)とする
  3. インスペクターのTransformにあるスケールを(1, 1, 1)とする

・マテリアル作成

  1. プロジェクトの「Assets」を右クリックし、「作成>マテリアル」をクリック
  2. 「matBlock」と入力し、ENTERキーを押す

・マテリアルテクスチャ設定

  1. インスペクターの「Main Maps>アルベド」左の◎をクリックし、「Background」をダブルクリック

・マテリアル色設定

  1. インスペクターの「Main Maps>アルベド>カラーバー」をクリックし、色ダイアログの16進数を「FF8000」に設定
  2. 色ダイアログの右上にある「×」をクリック

・マテリアル割り当て

  1. プロジェクトの「matBlock」をヒエラルキーの「Block」にドラッグ&ドロップ

▷壁認識

タグ「Block」を作成し、壁に割り当てます。

壁認識

・タグ作成

  1. ヒエラルキーの「Block」をクリック
  2. インスペクター上部のタグドロップダウンをクリックし、「タグを追加…」をクリック
  3. インスペクター「Tag & Layers」にあるタグ欄右下の「+」をクリック
  4. 「Block」と入力してENTERを押す

・タグ割り当て

  1. ヒエラルキーの「Block」をクリック
  2. インスペクターのタグドロップダウンをクリックし、「Block」をクリック

▷壁衝突

移動先に壁かジャンプ台があったら移動停止するようにCharCtrlを修正します。

壁衝突

スクリプト編集開始

  1. プロジェクトの「Assets>CharCtrl」をダブルクリック

・スクリプト関数修正

  1. FixedUpdate関数内の「経過フレーム数を0初期化する」の後に以下のスクリプトを追加
        // 移動先に壁かジャンプ台があったら移動終了
        GameObject moveObj = GetObject(transform.position + transform.forward);
        if (moveObj != null && (moveObj.tag == "Block" || moveObj.tag == "Jump"))
        {
            return;
        }

・スクリプト関数修正

  1. GetObject関数内の「処理対象とするタグの一覧を作成」を以下のスクリプトのように修正
        // 処理対象とするタグの一覧を作成
        string[] tags = new string[] { "Toge", "Block" };

・スクリプト保存

  1. CTRLを押しながらSキーを押す

・Unityに戻ってスクリプトをビルド

  1. Unityエディタのタイトルをクリック

・プロジェクト保存

  1. CTRLを押しながらSキーを押す

▷動作確認

タマが壁に衝突したら止まるかどうか、プレビューして確認します。

動作確認

・プレビュー開始

  1. エディタ上中央の「▶」をクリック

・動作確認

  1. タマが壁に衝突したら止まることを確認

・プレビュー終了

  1. エディタ上中央の「▶」をクリック

●ジャンプ台作成

画面をクリックするとジャンプ台の隣にあるタマとヒシガタを押すようにして移動を開始させます。
また、壁と同じように移動先にジャンプ台があると移動を停止します。

▷ジャンプ台作成

箱を作成してTransformの位置を(0, 0, -1)とし、緑色の「Knob」に設定したマテリアルを割り当てます。

ジャンプ台作成

・箱作成

  1. ヒエラルキー左上の「+」をクリック
  2. 「3Dオブジェクト>キューブ」をクリック
  3. 「Jump」と入力し、ENTERキーを押す

位置、回転、スケール設定

  1. インスペクターのTransformにある位置を(0, 0, -1)とする
  2. インスペクターのTransformにある回転を(0, 0, 0)とする
  3. インスペクターのTransformにあるスケールを(1, 1, 1)とする

・マテリアル作成

  1. プロジェクトの「Assets」を右クリックし、「作成>マテリアル」をクリック
  2. 「matJump」と入力し、ENTERキーを押す

・マテリアルテクスチャ設定

  1. インスペクターの「Main Maps>アルベド」左の◎をクリックし、「Knob」をダブルクリック

・マテリアル色設定

  1. インスペクターの「Main Maps>アルベド>カラーバー」をクリックし、色ダイアログの16進数を「008000」に設定
  2. 色ダイアログの右上にある「×」をクリック

・マテリアル割り当て

  1. プロジェクトの「matJump」をヒエラルキーの「Jump」にドラッグ&ドロップ

▷ジャンプ台認識

タグ「Jump」を作成し、ジャンプ台に割り当てます。

ジャンプ台認識

・タグ作成

  1. ヒエラルキーの「Jump」をクリック
  2. インスペクターのタグドロップダウンをクリックし、「タグを追加…」をクリック
  3. インスペクター「Tag & Layers」にあるタグ欄右下の「+」をクリック
  4. 「Jump」と入力してENTERを押す

・タグ割り当て

  1. ヒエラルキーの「Jump」をクリック
  2. インスペクターのタグドロップダウンをクリックし、「Jump」をクリック

▷ジャンプ台制御

ゲーム画面をクリックした時にタマかヒシガタが隣にあったら移動を開始するようCharCtrlを修正します。

ジャンプ台制御

・スクリプト編集開始

  1. プロジェクトの「Assets>CharCtrl」をダブルクリック

・スクリプト変数追加

  1. 「1歩に何フレームかけるか」の変数の前に以下の変数を追加
    bool moveFlag = false;      // 移動中かどうか

・スクリプト関数修正

  1. Update関数内に以下のスクリプトを記述
        // マウスクリックされた場合
        if (Input.GetMouseButtonUp(0) == true)
        {
        }

・スクリプト関数修正

  1. Update関数内の「マウスクリックされた場合」の条件内に以下のスクリプトを記述
            // 現在の回転値を保存
            Quaternion rot = transform.rotation;
            // 上下左右のオブジェクトを取得
            transform.rotation = Quaternion.Euler(0, 0, 0);
            GameObject objU = GetObject(transform.position + transform.forward);
            GameObject objD = GetObject(transform.position - transform.forward);
            GameObject objL = GetObject(transform.position - transform.right);
            GameObject objR = GetObject(transform.position + transform.right);

・スクリプト関数修正

  1. Update関数内の「上下左右のオブジェクトを取得」処理の後に以下のスクリプトを記述
            // 指定方向がジャンプ台かつ逆側がなにも無いかトゲの場合、オブジェクトをジャンプ台の逆側に向ける
            float dir = -1;
            if (objU != null && objU.tag == "Jump" && (objD == null || objD.tag == "Toge")) dir = 180;
            if (objD != null && objD.tag == "Jump" && (objU == null || objU.tag == "Toge")) dir = 0;
            if (objL != null && objL.tag == "Jump" && (objR == null || objR.tag == "Toge")) dir = 90;
            if (objR != null && objR.tag == "Jump" && (objL == null || objL.tag == "Toge")) dir = 270;
            if (dir != -1)
            {
                moveFlag = true;
                rot = Quaternion.Euler(0, dir, 0);
            }
            // 回転値を設定
            transform.rotation = rot;

・スクリプト関数修正

  1. FixedUpdate関数内の「経過フレーム数を0初期化する」処理の後に以下のスクリプトを記述
        // 移動していない場合は終了
        if (moveFlag == false) return;

・スクリプト関数修正

  1. FixedUpdate関数内の「移動先に壁かジャンプ台があったら移動終了」条件内のreturn前に以下のスクリプトを記述
            moveFlag = false;

・スクリプト関数修正

  1. GetObject関数内の「処理対象とするタグの一覧を作成」を以下のスクリプトのように修正
        // 処理対象とするタグの一覧を作成
        string[] tags = new string[] { "Toge", "Block", "Jump" };

・スクリプト保存

  1. CTRLを押しながらSキーを押す

・Unityに戻ってスクリプトをビルド

  1. Unityエディタのタイトルをクリック

・プロジェクト保存

  1. CTRLを押しながらSキーを押す

▷動作確認

クリックしたらタマが動き出すかどうか、プレビューして確認します。

動作確認

・プレビュー開始

  1. エディタ上中央の「▶」をクリック

・動作確認

  1. タマとヒシガタが動いていないことを確認
  2. ゲームビューをクリックしたらジャンプ台と隣接しているタマが動き出すことを確認

・プレビュー終了

  1. エディタ上中央の「▶」をクリック

▷スクリプト記述例

「クリックしたらタマが動き出す」スクリプトの記述例です。

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

public class CharCtrl : MonoBehaviour
{
    bool moveFlag = false;      // 移動中かどうか
    float moveFrame = 30;       // 1歩に何フレームかけるか
    float frameCount = 0;       // 経過フレーム数

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        // マウスクリックされた場合
        if (Input.GetMouseButtonUp(0) == true)
        {
            // 現在の回転値を保存
            Quaternion rot = transform.rotation;
            // 上下左右のオブジェクトを取得
            transform.rotation = Quaternion.Euler(0, 0, 0);
            GameObject objU = GetObject(transform.position + transform.forward);
            GameObject objD = GetObject(transform.position - transform.forward);
            GameObject objL = GetObject(transform.position - transform.right);
            GameObject objR = GetObject(transform.position + transform.right);
            // 指定方向がジャンプ台かつ逆側がなにも無いかトゲの場合、オブジェクトをジャンプ台の逆側に向ける
            float dir = -1;
            if (objU != null && objU.tag == "Jump" && (objD == null || objD.tag == "Toge")) dir = 180;
            if (objD != null && objD.tag == "Jump" && (objU == null || objU.tag == "Toge")) dir = 0;
            if (objL != null && objL.tag == "Jump" && (objR == null || objR.tag == "Toge")) dir = 90;
            if (objR != null && objR.tag == "Jump" && (objL == null || objL.tag == "Toge")) dir = 270;
            if (dir != -1)
            {
                moveFlag = true;
                rot = Quaternion.Euler(0, dir, 0);
            }
            // 回転値を設定
            transform.rotation = rot;
        }
    }

    // 物理フレーム更新時に処理
    void FixedUpdate()
    {
        // タグ検索でタマとヒシガタの一覧を取得する
        GameObject[] tamas = GameObject.FindGameObjectsWithTag("Player");
        GameObject[] hisis = GameObject.FindGameObjectsWithTag("Enemy");

        // 1歩分のフレームが経過するまで何もしない
        frameCount++;
        if (frameCount < moveFrame) return;

        // 経過フレーム数を0初期化する
        frameCount = 0;

        // 移動していない場合は終了
        if (moveFlag == false) return;

        // 移動先に壁かジャンプ台があったら移動終了
        GameObject moveObj = GetObject(transform.position + transform.forward);
        if (moveObj != null && (moveObj.tag == "Block" || moveObj.tag == "Jump"))
        {
            moveFlag = false;
            return;
        }

        // 向いている方向に1メートル進む
        transform.position += transform.forward;

        // ヒシガタとタマが衝突したら両方削除
        if (tamas.Length != 0 && hisis.Length != 0)
        {
            // タマとヒシガタの位置が同じだった場合
            if (tamas[0].transform.position == hisis[0].transform.position)
            {
                // タマとヒシガタを削除
                Destroy(tamas[0]);
                Destroy(hisis[0]);
            }
        }

        // 重なったオブジェクトを取得
        GameObject obj = GetObject(transform.position);
        // トゲと重なったら自分とトゲを削除
        if (obj != null && obj.tag == "Toge")
        {
            Destroy(gameObject);
            Destroy(obj);
        }
    }

    // 指定位置にあるオブジェクトを取得
    GameObject GetObject(Vector3 pos)
    {
        // 小数点誤差を補正
        float posX = Mathf.Floor(pos.x + 0.5f);
        float posZ = Mathf.Floor(pos.z + 0.5f);
        // 処理対象とするタグの一覧を作成
        string[] tags = new string[] { "Toge", "Block", "Jump" };
        // タグを1つ1つ処理
        for (int i = 0; i < tags.Length; i++)
        {
            // 指定タグのオブジェクト一覧を取得
            GameObject[] objs = GameObject.FindGameObjectsWithTag(tags[i]);
            // オブジェクトを1つ1つ処理
            for (int j = 0; j < objs.Length; j++)
            {
                // 指定位置に存在するオブジェクトを返す
                Vector3 objPos = objs[j].transform.position;
                if (objPos.x == posX && objPos.z == posZ) return objs[j];
            }
        }

        // 指定位置にオブジェクトが無いのでNULLを返す
        return null;
    }
}

●ゴール作成

タマと衝突するとクリアになる、「ゴール」を作成します。

▷ゴール作成

パーティクルシステムを作成して位置を(0, 0, 3)とし、その場に留まって揺らめくエフェクトとします。

ゴール作成

・パーティクルシステム作成

  1. ヒエラルキー左上の「+」をクリック
  2. 「エフェクト>パーティクルシステム」をクリック
  3. 「Goal」と入力し、ENTERキーを押す

位置、回転、スケール設定

  1. インスペクターのTransformにある位置を(0, 0, 3)とする
  2. インスペクターのTransformにある回転を(-90, 0, 0)とする
  3. インスペクターのTransformにあるスケールを(1, 1, 1)とする

・メインモジュール設定

メインモジュールを以下のように設定します。

  1. インスペクターにあるParticle Systemの「メインモジュール>継続時間」を(1)に設定
  2. インスペクターにあるParticle Systemの「メインモジュール>開始時の生存期間」を(1)に設定
  3. インスペクターにあるParticle Systemの「メインモジュール>開始時の速度」を(0)に設定
  4. インスペクターにあるParticle Systemの「メインモジュール>開始時の色」右にあるカラーバーをクリックし、色ダイアログの16進数を(FFFF00)に設定
  5. 色ダイアログの右上にある「×」をクリック
  6. インスペクターにあるParticle Systemの「メインモジュール>重力モディファイア」を(-0.1)に設定

・形状モジュール設定

形状モジュールを以下のように設定します。

  1. 形状モジュールの「形状」を(スフィア)に設定
  2. 形状モジュールの「半径」を(0.3)に設定
  3. 形状モジュールの「半径の厚さ」を(0)に設定

▷ゴール認識

タグ「Goal」を作成し、ゴールに割り当てます。

ゴール認識

・タグ作成

  1. ヒエラルキーの「Goal」をクリック
  2. インスペクター上部のタグドロップダウンをクリックし、「タグを追加…」をクリック
  3. インスペクター「Tag & Layers」にあるタグ欄右下の「+」をクリック
  4. 「Goal」と入力してENTERを押す

・タグ割り当て

  1. ヒエラルキーの「Goal」をクリック
  2. インスペクター上部のタグドロップダウンをクリックし、「Goal」をクリック

▷ゴール制御

タマとゴールが衝突した時にタマとヒシガタの動きが止まるようにCharCtrlを修正します。

ゴール制御

・スクリプト編集開始

  1. プロジェクトの「Assets>CharCtrl」をダブルクリック

・スクリプト変数追加

  1. 「移動中かどうか」の変数の前に「プレイ中かどうか」の変数を作成
    bool playFlag = true;       // プレイ中かどうか

・スクリプト関数修正

  1. FixedUpdate関数の先頭に以下の処理を追加
        // プレイ中でない場合は即関数を終了
        if (playFlag == false) return;

・スクリプト関数修正

  1. FixedUpdate関数の最後に以下の処理を追加
        // 自分がタマで、かつゴールと重なった場合
        else if (tag == "Player" && obj != null && obj.tag == "Goal")
        {
            // タマのplayFlagをfalseに設定
            playFlag = false;
        }

・スクリプト関数修正

  1. GetObject関数内の「処理対象とするタグの一覧を作成」を以下のスクリプトのように修正
        // 処理対象とするタグの一覧を作成
        string[] tags = new string[] { "Toge", "Block", "Jump", "Goal" };

・スクリプト保存

  1. CTRLを押しながらSキーを押す

・Unityに戻ってスクリプトをビルド

  1. Unityエディタのタイトルをクリック

・プロジェクト保存

  1. CTRLを押しながらSキーを押す

▷動作確認

タマとゴールが衝突したらタマの動きが止まるかどうか、プレビューして確認します。

動作確認

・プレビュー開始

  1. エディタ上中央の「▶」をクリック

・動作確認

  1. タマとゴールが衝突したらタマの動きが止まることを確認

・プレビュー終了

  1. エディタ上中央の「▶」をクリック

▷スクリプト記述例

「タマとゴールが衝突したらタマの動きが止まる」スクリプトの記述例です。

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

public class CharCtrl : MonoBehaviour
{
    bool playFlag = true;       // プレイ中かどうか
    bool moveFlag = false;      // 移動中かどうか
    float moveFrame = 30;       // 1歩に何フレームかけるか
    float frameCount = 0;       // 経過フレーム数

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        // マウスクリックされた場合
        if (Input.GetMouseButtonUp(0) == true)
        {
            // 現在の回転値を保存
            Quaternion rot = transform.rotation;
            // 上下左右のオブジェクトを取得
            transform.rotation = Quaternion.Euler(0, 0, 0);
            GameObject objU = GetObject(transform.position + transform.forward);
            GameObject objD = GetObject(transform.position - transform.forward);
            GameObject objL = GetObject(transform.position - transform.right);
            GameObject objR = GetObject(transform.position + transform.right);

            // 指定方向がジャンプ台かつ逆側がなにも無いかトゲの場合、オブジェクトをジャンプ台の逆側に向ける
            float dir = -1;
            if (objU != null && objU.tag == "Jump" && (objD == null || objD.tag == "Toge")) dir = 180;
            if (objD != null && objD.tag == "Jump" && (objU == null || objU.tag == "Toge")) dir = 0;
            if (objL != null && objL.tag == "Jump" && (objR == null || objR.tag == "Toge")) dir = 90;
            if (objR != null && objR.tag == "Jump" && (objL == null || objL.tag == "Toge")) dir = 270;
            if (dir != -1)
            {
                moveFlag = true;
                rot = Quaternion.Euler(0, dir, 0);
            }
            // 回転値を設定
            transform.rotation = rot;
        }
    }

    // 物理フレーム更新時に処理
    void FixedUpdate()
    {
        // プレイ中でない場合は即関数を終了
        if (playFlag == false) return;

        // タグ検索でタマとヒシガタの一覧を取得する
        GameObject[] tamas = GameObject.FindGameObjectsWithTag("Player");
        GameObject[] hisis = GameObject.FindGameObjectsWithTag("Enemy");

        // 1歩分のフレームが経過するまで何もしない
        frameCount++;
        if (frameCount < moveFrame) return;

        // 経過フレーム数を0初期化する
        frameCount = 0;

        // 移動していない場合は終了
        if (moveFlag == false) return;

        // 移動先に壁かジャンプ台があったら移動終了
        GameObject moveObj = GetObject(transform.position + transform.forward);
        if (moveObj != null && (moveObj.tag == "Block" || moveObj.tag == "Jump"))
        {
            moveFlag = false;
            return;
        }

        // 向いている方向に1メートル進む
        transform.position += transform.forward;

        // ヒシガタとタマが衝突したら両方削除
        if (tamas.Length != 0 && hisis.Length != 0)
        {
            // タマとヒシガタの位置が同じだった場合
            if (tamas[0].transform.position == hisis[0].transform.position)
            {
                // タマとヒシガタを削除
                Destroy(tamas[0]);
                Destroy(hisis[0]);
            }
        }

        // 重なったオブジェクトを取得
        GameObject obj = GetObject(transform.position);
        // トゲと重なったら自分とトゲを削除
        if (obj != null && obj.tag == "Toge")
        {
            Destroy(gameObject);
            Destroy(obj);
        }
        // 自分がタマで、かつゴールと重なった場合
        else if (tag == "Player" && obj != null && obj.tag == "Goal")
        {
            // タマのplayFlagをfalseに設定
            playFlag = false;
        }
    }

    // 指定位置にあるオブジェクトを取得
    GameObject GetObject(Vector3 pos)
    {
        // 小数点誤差を補正
        float posX = Mathf.Floor(pos.x + 0.5f);
        float posZ = Mathf.Floor(pos.z + 0.5f);
        // 処理対象とするタグの一覧を作成
        string[] tags = new string[] { "Toge", "Block", "Jump", "Goal" };
        // タグを1つ1つ処理
        for (int i = 0; i < tags.Length; i++)
        {
            // 指定タグのオブジェクト一覧を取得
            GameObject[] objs = GameObject.FindGameObjectsWithTag(tags[i]);
            // オブジェクトを1つ1つ処理
            for (int j = 0; j < objs.Length; j++)
            {
                // 指定位置に存在するオブジェクトを返す
                Vector3 objPos = objs[j].transform.position;
                if (objPos.x == posX && objPos.z == posZ) return objs[j];
            }
        }

        // 指定位置にオブジェクトが無いのでNULLを返す
        return null;
    }
}

作成手順を書いた記事です。

   →紹介
   →1/3 キャラクター作成
今▷ →2/3 オブジェクト作成
次▷ →3/3 ゲーム仕上げ

完全手順
※紹介と1~3の手順を1ページにまとめています。
 読み込みに時間がかかるかも知れません。

コメント

タイトルとURLをコピーしました