子供の頃から
式神、妖精、精霊、悪魔、使い魔、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の関数を使う
「ターゲットの周りを周回」とか「オブジェクトの周りをまわる」とかで検索していくつかを参考にした。
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; } }
で、そのスクリプトを球体につけて、妖精の卵が完成!
妖精のたまご
動きが単調すぎて試行錯誤中 pic.twitter.com/QHxuRQEifC
— みたかシロ (@siro_mitaka) 2018年5月25日
③3Dモデルで妖精っぽくする
3Dがフリーで手に入るサイトをいくつか探して、一番気に入ったのがココ
びっくりしたのがコレ↓
クオリティやばすぎwww
このサイトに登録して、妖精のモデルをDLして置き換える。
パタパタうごくアニメーションが最初から付属してるのでアニメーションの設定をする
モデル置き換えてアニメーションがあるだけでそれっぽく見える。
とりあえず簡易モデルとアニメーションつけて
マシになったのでいったん寝よう pic.twitter.com/xc31w65OAP— みたかシロ (@siro_mitaka) 2018年5月25日
④プレイヤー追跡機能をつける
プレイヤーが移動して、一定距離 離れるとプレイヤーの後を追うようにする。
主に以下の関数を使う
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; } }
これで離れると追従するようになる。
離れたら近づいてくるようにした。
可愛い。 pic.twitter.com/Ghmw2TArLT
— みたかシロ (@siro_mitaka) 2018年5月26日
周回中なのか追跡中なのかわかりづらかったので、
状態によってパーティクルの色を変化するようにした。
最後に、時計回りだけだったので
左右ランダムに移動するようにして妖精のベースが完成!!
左右ランダム移動と
パーティクルの色で待機、移動、追跡の状態が分かるようにした。ひと通りのやりたい動きは完成。 pic.twitter.com/E8ScJ8OhAH
— みたかシロ (@siro_mitaka) 2018年5月26日
最終的なスクリプトは以下
(コメントや処理が間違ってた場合は優しく教えてさい)
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で自分のそばに居てくれる存在にする。
凄いよね本当に夢が叶いそう。
※コメントは承認後に表示されます。
コメントを公開されたくない場合、名前の後に「:非公開」とつけてください。