【Unity】曲がる弾をつくる(2D)
曲がる弾をつくります。
上と下から
— 相生葵 (@pirorirori_n712) October 28, 2018
可愛いキャラクターは【Rド】様(https://t.co/80g5E20XwZ …) pic.twitter.com/G0DeaVj1Ux
初めはSlerpでつくろうと思ったのですが、
複数の方向へ飛ばしたかったので二次ベジェ曲線を使って作りました。
ベジェ曲線に辿り着くまでに、
LerpとSlerpもかじったので
この二つについても書いていきたいと思います。
二次ベジェ曲線のところだけ知りたいという方は
こちらから飛んでください。
(二次ベジェ曲線ではLerpを使います)
LerpとSlerpについて
*Lerp
Vector3.Lerp
Lerpは線形補間です。
字面の通り、与えられた二点を線で補間します。
・使い方
Vector3.Lerp(posA, posB, ratio)
第3引数のratioはposAからposBまでを結んだ直線の上を進んだ割合です。
0.0f~1.0fで指定します。
つまり、
ratioが0.0fのときLerpはposAの位置を返し
ratioが0.5fのときLerpはposAとposBの中間地点を返し
ratioが1.0fのときLerpはposBの位置を返します。
Lerp()
— 相生葵 (@pirorirori_n712) October 21, 2018
よく分からないけど、全然動きが違う!
可愛いキャラクターは【Rド】様(https://t.co/80g5E20XwZ … … … … …) pic.twitter.com/o1AxbINKGo
*Slerp
Vector3.Slerp
Slerpは球面補間です。
字面の通り、与えられた二点を弧で補間します。
・使い方
Vector3.Slerp(posA, posB, ratio)
第3引数のratioはposAからposBまでを結んだ直線の上を進んだ割合です。
0.0f~1.0fで指定します。
つまり、
ratioが0.0fのときLerpはposAの位置を返し
ratioが0.5fのときLerpはposAとposBの中間地点を返し
ratioが1.0fのときLerpはposBの位置を返します。
Slerp()
— 相生葵 (@pirorirori_n712) October 21, 2018
結構きれいに曲がる
可愛いキャラクターは【Rド】様(https://t.co/80g5E20XwZ … … … …) pic.twitter.com/S4r3QpDBmZ
単に曲げるだけならSlerpでも良かったのですが、
自由に曲げて弾を出したかったので
二次ベジェ曲線を使ってみました。
*二次ベジェ曲線
※上のLerpとSlerpの動画内では追尾弾を実装していますが、
以下のベジェ曲線では追尾機能は実装していません。
二次ベジェ曲線については、
動画もついている以下の記事がとても分かり易いです。
setchi.hatenablog.com
二次ベジェ曲線の作り方を、
ざっくりまとめると以下のようになります。
(上の記事がとても分かり易いので、ぜひ上の記事を読んでください)
*材料
・スタート地点の座標
・中継地点の座標(弾自身が中継するわけではない)
・ターゲットの座標
*作り方
①スタート地点から中継地点までのベクトル上、
そして中継地点からターゲットまでのベクトル上を
それぞれ点(ピンクの丸と水色の丸)に走らせます。
②先ほどベクトル上を走らせていた点を、ベクトルで結びます。
③先ほど結んで作ったベクトル上に点を走らせます。
→この点が弾の軌道になります
ざっくり作り方を説明すると以上になります。
ベクトル上を走る点はLerpで走らせます。
弾の軌道を考察していくと、以下のようになります。
(全ての弾が自身の走るベクトルを、同じ割合で進んでいく場合)
①弾の進行が0%のとき
スタート地点から中継地点を走る点はスタート地点、
中継地点からターゲットまでを走る点は中継地点から始まります。
このとき、この二点を結んだベクトルは
スタート地点から中継地点を結ぶベクトルとなり、
進行は0%なので弾の位置はちょうどスタート地点となります。
②弾の進行が50%のとき
スタート地点から中継地点を走る点は、
自身が走るベクトルのちょうど中間地点にいます。
同じく、中継地点からターゲットまでを走る点は、
自身が走るベクトルのちょうど中間地点にいます。
上の二点を結んだベクトル上を走る点も、
自身が走るベクトル(上の二点を結んだベクトル)のちょうど中間地点にいます。
③弾の進行が100%のとき
スタート地点から中継地点を走る点は中継地点、
中継地点からターゲットまでを走る点はターゲットに到着します。
このとき、二点を結んだベクトルは
中継地点からターゲットを結ぶベクトルとなり、
この上を走る点も
ゴールであるターゲットに到着します。
まとめると
二つのベクトル上に点を走らせ、
その点をつないだベクトル上に点を走らせることで
曲がった線の軌道を描いています。
(図では分かりにくいと思うので、ぜひ上の分かり易い記事を読んでください)
上の動きをコードにすると以下のようになります。
弾のオブジェクトにつけています。
void Update () { //弾の進行具合(Lerpの第三引数に入れる) time += Time.deltaTime; //二次ベジェ曲線を使う //スタートから中継地点をつなぐベクトル上を走る点の位置 var firstVec= Vector3.Lerp(startPos, relayPos, time); //中継地点からターゲットまでをつなぐベクトル上を走る点の位置 var SecondVec = Vector3.Lerp(relayPos, targetPos, time); //上の二点をつなぐベクトル上を走る点(弾)の位置 var vec = Vector3.Lerp(firstVec, SecondVec, time); //弾の位置を代入する this.transform.position = vec; }
2次ベジェ曲線
— 相生葵 (@pirorirori_n712) October 28, 2018
スタート地点→上の緑の点
上の緑の点→ターゲット
可愛いキャラクターは【Rド】様(https://t.co/80g5E20XwZ) pic.twitter.com/iLc7yxynm8
relayPosを複数にする(弾ごとに変える)ことで
弾に複数の弧を描かせることができます。
(上と下から、左と右から、など)
一番上にある
上と下から弾を出している動画のコードは
以下のように書いています。
・弾を複製するコード
右のキャラクターにつけています(スタート地点)
using System.Collections; using System.Collections.Generic; using UnityEngine; public class TamaToPlayerFollow : MonoBehaviour { //プレイヤー(ターゲット) public GameObject player; //弾のプレハブ public GameObject tama; //弾を0.1秒ごとに打ち出す private float targetTime = 0.1f; private float currentTime = 0; //中継地点1 public GameObject greenPoint; //中継地点2 public GameObject greenPoint1; //中継地点を割り振るための変数 int count = 0; // Update is called once per frame void Update() { //弾を0.1秒ごとに打ち出すためのもの currentTime += Time.deltaTime; if(targetTime < currentTime) { currentTime = 0; //敵の位置を保存 var pos = this.gameObject.transform.position; //弾のプレハブを作成 var t = Instantiate(tama) as GameObject; //弾の初期位置を敵の位置にする t.transform.position = pos; //弾につけているスクリプト、TamaTobasuコンポネントを保存する var cash=t.GetComponent<TamaTobasu>(); //スタート地点を弾のスクリプトに渡す cash.CharaPos = this.transform.position; //弾を一つ打ち出すたびに中継地点を変える count++; //中継地点を弾のスクリプトに渡す if(count%2==1) cash.GreenPos=greenPoint.transform.position; else cash.GreenPos = greenPoint1.transform.position; //プレイヤー(ターゲット)の位置を弾のスクリプトに渡す cash.PlayerPos = player.transform.position; }
・弾を動かすコード
弾につけています
using System.Collections; using System.Collections.Generic; using UnityEngine; public class TamaTobasu : MonoBehaviour { //それぞれの位置を保存する変数 //スタート地点 private Vector2 charaPos; public Vector2 CharaPos { set { charaPos = value; } } //ゴール地点 private Vector2 playerPos; public Vector2 PlayerPos { set { playerPos = value; } } //中継地点 private Vector2 greenPos; public Vector2 GreenPos { set { greenPos = value; } } //進む割合を管理する変数 float time; // Update is called once per frame void Update () { //弾の進む割合をTime.deltaTimeで決める time += Time.deltaTime; //二次ベジェ曲線 //スタート地点から中継地点までのベクトル上を通る点の現在の位置 var a = Vector3.Lerp(charaPos, greenPos, time); //中継地点からターゲットまでのベクトル上を通る点の現在の位置 var b = Vector3.Lerp(greenPos, playerPos, time); //上の二つの点を結んだベクトル上を通る点の現在の位置(弾の位置) this.transform.position = Vector3.Lerp(a, b, time); } }