召喚士になりたい!(妖精召喚編)

子供の頃から

式神、妖精、精霊、悪魔、使い魔、AI

そういう存在にすごく憧れていた。

 

黒い砂漠でも召喚士:リトルサマナー

 

中二病な時に「これを勉強したら召喚できるよ」って

召喚に必要な儀式や魔術があったら、どれだけ難しくても本気で勉強したと思う。

実際中学の頃、市立図書館でまで行って「悪魔の召喚方法」みたいな分厚い本とか借りたよ!

 

いま技術も進んで、Unityというツールもあって それを使う知識もあって

VRヘッドセットも持ってて、そういう存在を作ることができる環境がある。

 

 

 

じゃあ、やれよ!!

 

なるしかないよな!憧れの召喚士に!!

 

 

 

 

ということで、手始めに妖精を召喚する儀式の準備をする。

 

 

 

●妖精の仕様


・キラキラ光る

・プレイヤーの周りを浮遊、周回する。

・一定距離 離れたらプレイヤーの後を追う


とりあえず妖精となる最低限の仕様はこれだけ。

 

 

 

 

①妖精の特有のキラキラ出るアレを表現

VR空間は以前ミクさんがダンスしてくれる空間つくったものを流用。

球体を出現させる。

 

球体にパーティクルを追加する。

球体を選択してadd Component>Effects>ParticleSystem

 

本来はこれでUnityデフォルトのキラキラが出るんだけど

なんかピンクのドットが溢れ出した・・・

 

調べてみるとパーティクルにマテリアルが設定されていないことが原因らしいので設定していく

 

球体のInspectorのParticleSystemから

Rendererをクリック

Materialがはじめ「None」になっているので、右にある〇をクリック

Default-Particleを検索してマテリアルを選択する。

 

デフォルトのキラキラになってくれた。

 

 

キラキラが出る方向がプレイヤーを向いているので下向きに出るように調整する

 

Shape>Rotation Xを90で下向きに。

キラキラが広がる角度や高さはマウスで調節が可能

 

キラキラのサイズが大きくて、多い、長いので調整

Sphere> StartLifetime 1.5秒  MaxParticles 50

StartColorで色も変えられる

 

 

これで、まずはキラキラの出る妖精の卵が完成

 

 

 

②プレイヤーの周りを回らせる

ここからはスクリプトを書いていく

回転には Transform.RotateAround というunityの関数を使う

 

「ターゲットの周りを周回」とか「オブジェクトの周りをまわる」とかで検索していくつかを参考にした。

[Unity] MovePosition, position, RotateAround 円運動させる

https://gist.github.com/hiroyukihonda/8552618

 

 

ただ一定速度で回り続けるのではなく

数秒進んだら止まる、またしばらくしたら進むという動きをつけたい

 

動く間隔は不定期にしたいので Random.Range 関数を使う。

数秒後に動く という処理はどうしようか色々調べた結果

Invoke関数 というものがあることを知る

 

プレイヤーを中心に時計回りで進んだり止まったりするスクリプト

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

public class ScriptFairy : MonoBehaviour {


    //妖精にアタッチするスクリプト
    //プレイヤーを中心にして周りを飛ぶ
    
    //指定するオブジェクト名
    const string Player_ObjName = "Camera";


    int Fairy_State ;                   //状態
    public float angle = 30f;           //一秒当たりの回転角度

    private Transform PlayerCam;        //プレイヤーの位置情報
    private Vector3 PlayerPos;          //プレイヤーの位置情報を一時的に保存しておく変数



	// Use this for initialization
	void Start () {
		PlayerCam = GameObject.Find(Player_ObjName).transform;      //オブジェクトを取得
        PlayerPos = PlayerCam.position;                             //プレイヤーの位置を取得

        Fairy_State = 0;                                            //状態:待機

        Invoke("State_Move", 3);                                    //3秒後に「State_move」メソッドを呼び出す
        transform.Rotate(new Vector3(0, 10, 0), Space.World);       //自分をZ軸を中心に0~360でランダムに回転させる
	}
	
	 //1フレームごとに繰り返される処理
	void Update () {
		PlayerPos = PlayerCam.position;                                     //プレイヤーの位置を取得

         //状態による処理分岐
        switch (Fairy_State){
            
            case 1: //待機移動(プレイヤーを軸にして周回)
                
                Vector3 FiyTrns = this.transform.TransformDirection(Vector3.up);        //妖精のY軸をローカルからワールドへ変換してFiyTrnsに入れる
                transform.RotateAround(PlayerPos , FiyTrns , angle * Time.deltaTime);   //プレイヤーを中心にY軸で回転 Plus_or_Minusで回転方向を決める

                if (!IsInvoking("State_Wait")) {                                        //「Invoke」処理でState_Waitを呼び出しているものが無い場合
                    Debug.Log("状態:処理をするよ");
                    Invoke("State_Wait", Random.Range(1,3));                            //1~3秒後に「State_Wait」メソッドを呼び出す
                }
                break;

            default://待機

                break;
        }

	}



    void State_Wait() {//状態:待機の処理
        Debug.Log("状態:待機");
        Fairy_State = 0;
        Invoke("State_Move", Random.Range(3,5));//5~10秒後に「State_Wait」メソッドを呼び出す
    }

    void State_Move() {//状態:移動の処理
        Debug.Log("状態:移動");
        Fairy_State = 1;

    }



}

 

 

で、そのスクリプトを球体につけて、妖精の卵が完成!

 

 

 

③3Dモデルで妖精っぽくする

3Dがフリーで手に入るサイトをいくつか探して、一番気に入ったのがココ

https://sketchfab.com/feed

 

 

びっくりしたのがコレ↓

クオリティやばすぎwww

 

 

このサイトに登録して、妖精のモデルをDLして置き換える。

 

 

パタパタうごくアニメーションが最初から付属してるのでアニメーションの設定をする

 

モデル置き換えてアニメーションがあるだけでそれっぽく見える。

 

 

 

 

④プレイヤー追跡機能をつける

プレイヤーが移動して、一定距離 離れるとプレイヤーの後を追うようにする。

 

主に以下の関数を使う

Mathf.Clamp

Vector3.Distance

Vector3.MoveTowards

 

update()処理に追加

 //1フレームごとに繰り返される処理
    void Update () {

        PlayerPos = PlayerCam.position;                                     //プレイヤーの位置を取得

        if (Vector3.Distance(PlayerPos, this.transform.position) > 2f) {    //距離が2以上離れた場合
            Fairy_State = 2;                                                //状態:追従
            Debug.Log("距離が離れた");
        }

        //状態による処理分岐
        switch (Fairy_State){
            
            case 1: //待機移動(プレイヤーを軸にして周回)
                 FiyPtcl.startColor = new Color(1f,0.5f,0.1f,1);//オレンジ
                Vector3 FiyTrns = this.transform.TransformDirection(Vector3.up);        //妖精のY軸をローカルからワールドへ変換してFiyTrnsに入れる
                transform.RotateAround(PlayerPos , FiyTrns , angle * Time.deltaTime );   //プレイヤーを中心にY軸で回転 Plus_or_Minusで回転方向を決める
               

                if (!IsInvoking("State_Wait")) {                                        //「Invoke」処理でState_Waitを呼び出しているものが無い場合
                    Debug.Log("状態:処理をするよ");
                    Invoke("State_Wait", Random.Range(1,3));                            //1~3秒後に「State_Wait」メソッドを呼び出す
                }
                break;

            case 2://追従(プレイヤーを追跡)
                CancelInvoke();                     //「Invoke」処理をすべてキャンセル
                this.transform.LookAt(PlayerCam);   //プレイヤーを向く
                
                var FiyDst = Mathf.Clamp(Vector3.Distance(PlayerPos, this.transform.position) - 1f, 0f, 0.01f); //プレイヤーと妖精の距離-1の値をFiyDstに入れる
                this.transform.position = Vector3.MoveTowards(this.transform.position, PlayerPos, FiyDst);      //妖精の位置からプレイヤーまで移動する


                if (Vector3.Distance(PlayerPos, this.transform.position) < 1f) {  //追いついた場合
                    Debug.Log("追いついた");
                    Fairy_State = 0;
                    State_Wait();
                }

                break;

            default://待機
                //その場で停止  
                
                this.transform.LookAt(PlayerCam);   //プレイヤーを向く
                break;
        }

    }

 

これで離れると追従するようになる。

 

 

 

周回中なのか追跡中なのかわかりづらかったので、

状態によってパーティクルの色を変化するようにした

 

最後に、時計回りだけだったので

左右ランダムに移動するようにして妖精のベースが完成!!

 

 

最終的なスクリプトは以下

(コメントや処理が間違ってた場合は優しく教えてさい)

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

public class ScriptFairy : MonoBehaviour {

    //妖精にアタッチするスクリプト
    //プレイヤーを中心にして周りを飛ぶ
    //状態が分かるようにマテリアルの色を変更する!
    
    //指定するオブジェクト名
    const string Fairy_ObjName= "Navi";
    const string Player_ObjName = "Camera";


    int Fairy_State ;                   //状態
    public float angle = 30f;           //一秒当たりの回転角度

    private Transform PlayerCam;        //プレイヤーの位置情報
    private Vector3 PlayerPos;          //プレイヤーの位置情報を一時的に保存しておく変数

    private int Plus_or_Minus;
    public ParticleSystem.MainModule FiyPtcl;//パーティクルの情報を入れる



    //スクリプトが開始された時だけ処理
    void Start () {
        
        PlayerCam = GameObject.Find(Player_ObjName).transform;      //オブジェクトを取得
        PlayerPos = PlayerCam.position;                             //プレイヤーの位置を取得

        FiyPtcl = GetComponent<ParticleSystem>().main;              //パーティクル情報取得
        Debug.Log(FiyPtcl.startColor.color);                        //カラーコード確認用

        Fairy_State = 0;                                            //状態:待機
        this.transform.LookAt(PlayerCam);                           //プレイヤーを向く


        Invoke("State_Move", 3);                                    //3秒後に「State_move」メソッドを呼び出す

        transform.Rotate(new Vector3(0, Random.Range(0, 360),0), Space.World);//自分をZ軸を中心に0~360でランダムに回転させる

    }

    //1フレームごとに繰り返される処理
    void Update () {

        PlayerPos = PlayerCam.position;                                     //プレイヤーの位置を取得


        if (Vector3.Distance(PlayerPos, this.transform.position) > 2f) {    //距離が2以上離れた場合
            Fairy_State = 2;                                                //状態:追従
            Debug.Log("距離が離れた");
        }



        //状態による処理分岐
        switch (Fairy_State){
            
            case 1: //待機移動(プレイヤーを軸にして周回)
                 FiyPtcl.startColor = new Color(1f,0.5f,0.1f,1);//オレンジ
                Vector3 FiyTrns = this.transform.TransformDirection(Vector3.up);        //妖精のY軸をローカルからワールドへ変換してFiyTrnsに入れる
                transform.RotateAround(PlayerPos , FiyTrns , angle * Time.deltaTime * Plus_or_Minus);   //プレイヤーを中心にY軸で回転 Plus_or_Minusで回転方向を決める
                

                if (!IsInvoking("State_Wait")) {                                        //「Invoke」処理でState_Waitを呼び出しているものが無い場合
                    Debug.Log("状態:処理をするよ");
                    Invoke("State_Wait", Random.Range(1,3));                            //1~3秒後に「State_Wait」メソッドを呼び出す
                }
                break;

            case 2://追従(プレイヤーを追跡)
                FiyPtcl.startColor = new Color(0.4f,0.9f,0.9f,1);//ライム色
                CancelInvoke();                     //「Invoke」処理をすべてキャンセル
                this.transform.LookAt(PlayerCam);   //プレイヤーを向く
                
                var FiyDst = Mathf.Clamp(Vector3.Distance(PlayerPos, this.transform.position) - 1f, 0f, 0.01f); //プレイヤーと妖精の距離-1の値をFiyDstに入れる
                this.transform.position = Vector3.MoveTowards(this.transform.position, PlayerPos, FiyDst);      //妖精の位置からプレイヤーまで移動する


                
                if (Vector3.Distance(PlayerPos, this.transform.position) < 1f) {  //追いついた場合
                    Debug.Log("追いついた");
                    Fairy_State = 0;
                    State_Wait();
                }
               
                break;

            default://待機
                //その場で停止  
                
                this.transform.LookAt(PlayerCam);   //プレイヤーを向く
                FiyPtcl.startColor = Color.yellow;//パーティクルの色
                break;
        }



        

    }

    void State_Wait() {//状態:待機の処理
        Debug.Log("状態:待機");
        Fairy_State = 0;
        Invoke("State_Move", Random.Range(3,5));//5~10秒後に「State_Wait」メソッドを呼び出す
    }

    void State_Move() {//状態:移動の処理
        Debug.Log("状態:移動");
        Fairy_State = 1;

        //回転方向に使用
        Plus_or_Minus = Random.Range(-1,1); //-1~1の乱数
        if (Plus_or_Minus==0){              //0なら
            Plus_or_Minus=1;                //1にする
        }


    }




}

 

 

このスクリプトつけるだけでプレイヤーの周りをふわふわ飛んでくれるので

ARでも使えるんじゃないかなーと思う。

 

最終的にはAI搭載して、ホロレンズとかMRで自分のそばに居てくれる存在にする。

 

 

 

凄いよね本当に夢が叶いそう。

 

 

 

関連記事




コメントを残す

※コメントは承認後に表示されます。
 コメントを公開されたくない場合、名前の後に「:非公開」とつけてください。