前提・実現したいこと
位置ベクトル間で、PingPongメソッドを使って往復運動を実装させるにはどのようにすればよいでしょうか?
そもそもPingPongメソッドで、位置ベクトル間の往復運動が実装可能かどうかということがわかっていないです。
やりたいこととしては、キャラクタの往復運動です。
PingPongメソッドで実装可能であれば、おそらく1番手軽な実装になると思うのでPingPongメソッドで
実装したいと考えています。
試したこと
Vector3にPingPongメソッドはありませんでした。
Mathf.PingPongメソッドしかなさそうです。
y軸座標に関しては、現状のゲームオブジェクトの位置を維持という意図で下記のように組んでみました。
C#
1 float startTime; 2 float speed = 20; 3 Vector3 dir; 4 5 void Start() 6 { 7 startTime = Time.time; 8 dir = endPoint.transform.position - startPoint.transform.position; 9 } 10 11 void Update() 12 { 13 float dx = Mathf.PingPong((Time.time-startTime)*speed, Mathf.Abs(dir.x)); 14 float dz = Mathf.PingPong((Time.time-startTime)*speed, Mathf.Abs(dir.z)); 15 if (dir.x < 0) dx = -dx; 16 if (dir.z < 0) dz = -dz; 17 transform.position = new Vector3(startPoint.transform.position.x+dx, 18 transform.position.y, startPoint.transform.position.z+dz); 19 }
上記でゲームを実行したところ、往復運動はするものの、直線上の動きではなく、
カクカクとした動きとなってしまいました。
この不具合の原因は、x座標とz座標とで、dirの最大値に到達するタイミングが合わないためと考えています。
そうすると、PingPongメソッドで、位置ベクトル間での往復運動は実装不可能なのでしょうか?
ちなみに、dx=-dxのような処理をしているのは、
PingPongメソッドの第2引数が負の値に対応していないみたいだからです。
以前の質問は、「負の値も振り幅にする」であって、
片道方向ならば、もしかしたら、第2引数で負の値に対応しているのかもしれないと期待したのですが、
ダメそうでした。下記のようなログを取ってみました。
負の値に対応してなさそうというのはわかるのですが、なぜ一律に-4の出力なるのかは不明です。
C#
1 Debug.Log(Mathf.PingPong(0, -2)); // -4 2 Debug.Log(Mathf.PingPong(1, -2)); // -4 3 Debug.Log(Mathf.PingPong(2, -2)); // -4 4 Debug.Log(Mathf.PingPong(3, -2)); // -4
また、位置ベクトル間の往復運動が実装できたとして、
ゲームオブジェクトをY軸周りで、進行方向に向かせたいのですが、どのように実装すればよいでしょうか?
Y軸周りで向きを設定することに関しては、進行方向のベクトルをdirとすると、
C#
1this.transform.rotaion = Quaternion.Euler(0, dir.y, 0);
で設定できると考えていて、
進行方向は、startPointかendPointのどちらかから、求めることができることはわかっていますが、
現在、startPointとendPointのどちらに向かっているかを取得する方法は、
まず、PingPongメソッドの往復運動が実装できてからでないと、考えることができないという状態です。
追記
・length - Mathf.Repeat(t, length * 2.0f)
C#
1 int length = 3; 2 for (int t=0; t<13; t++){ 3 Debug.Log($"Mathf.PingPong({t},{length}):{Mathf.PingPong(t,length)}, 4 length - Mathf.Repeat(t, length * 2.0f):{length - Mathf.Repeat(t, length * 2.0f)}"); 5 }
出力結果。
Mathf.PingPong(0,3):0, length - Mathf.Repeat(t, length * 2.0f):3 Mathf.PingPong(1,3):1, length - Mathf.Repeat(t, length * 2.0f):2 Mathf.PingPong(2,3):2, length - Mathf.Repeat(t, length * 2.0f):1 Mathf.PingPong(3,3):3, length - Mathf.Repeat(t, length * 2.0f):0 Mathf.PingPong(4,3):2, length - Mathf.Repeat(t, length * 2.0f):-1 Mathf.PingPong(5,3):1, length - Mathf.Repeat(t, length * 2.0f):-2 Mathf.PingPong(6,3):0, length - Mathf.Repeat(t, length * 2.0f):3 Mathf.PingPong(7,3):1, length - Mathf.Repeat(t, length * 2.0f):2 Mathf.PingPong(8,3):2, length - Mathf.Repeat(t, length * 2.0f):1
追記②
レイの変更箇所だけ記載します。
C#
1 if (Physics.Raycast(new Ray(new Vector3(newPosition.x, 100.0f, newPosition.z), Vector3.down), out var hitInfo)) 2 { 3 if(!(hitInfo.collider.CompareTag("Player") || hitInfo.collider.CompareTag("Ground"))){ 4 newPosition = hitInfo.point; 5 } 6 Debug.Log(hitInfo.collider); 7 }
追記③
C#
1 IEnumerator Turn(Transform trf){ 2 Vector3 baseForward = trf.forward; 3 for (float time = 0.0f; time < this.animationLength; time += Time.deltaTime) 4 { 5 float t = time / this.animationLength; 6 Quaternion q = Quaternion.Euler(0.0f, Mathf.Lerp(0.0f, 180.0f, t), 0.0f); 7 trf.forward = q * baseForward; 8 yield return null; 9 } 10 // 補間値t=1の調整処理 11 trf.forward = Quaternion.Euler(0.0f, 180.0f, 0.0f) * baseForward; 12 }
追記④
C#
1 IEnumerator YTurnToTarget(Transform trf, Transform target){ 2 Vector3 dir = (target.position - trf.position).normalized; 3 Debug.Log(dir.y); 4 Vector3 baseForward = trf.forward; 5 for (float time = 0.0f; time < this.animationLength; time += Time.deltaTime) 6 { 7 float t = time / this.animationLength; 8 Quaternion q = Quaternion.Euler(0.0f, Mathf.Lerp(0.0f, dir.y, t), 0.0f); 9 trf.forward = q * baseForward; 10 yield return null; 11 } 12 // 補間値t=1の調整処理 13 trf.forward = Quaternion.Euler(0.0f, dir.y, 0.0f) * baseForward; 14 }
追記⑤
C#
1 float startTime; 2 float speed = 2.0f; 3 4 void Update(){ 5 if (Input.GetMouseButtonDown(0)){ 6 // TimetimeCoroutineが動いていたら何もしないという処理を可能であれば書きたい 7 startTime = Time.time; 8 StartCoroutine(TimeCoroutine(startTime)); 9 }else if(Input.GetMouseButtonDown(1)){ 10 Suspension(TimeCoroutine, DoSomethingCoroutine); 11 } 12 } 13 14 // Time.timeを使ったコルーチン 15 // 今回はPingPongの処理に該当。コードが長くなり、他でエラーが発生しているため、暫定的な内容となりました。 16 IEnumerator TimeCoroutine(float startTime){ 17 float t = (Time.time - startTime) * speed; 18 while(true){ 19 Debug.Log("$TimeCoroutine:{t}"); 20 } 21 } 22 23 IEnumerator DoSomethingCoroutine(float animationLength){ 24 for (float time = 0.0f; time < animationLength; time += Time.deltaTime) 25 { 26 float t = time / this.animationLength; 27 // 補間値tを使った何かしらの処理 28 Debug.Log($"t:{t}"); 29 yield return null; 30 } 31 // 補間値t=1の調整処理 32 Debug.Log("t:1"); 33 } 34 35 void Suspension(Coroutine TimeCoroutine, Coroutine SuspensionCoroutine){ 36 // 第1引数のTimetimeCoroutineが動いていなければ何もしない 37 // if文で判定してreturn 38 // ...というような処理を組みたいと思ったのですが、コルーチンの生存確認をする方法が 39 // 見つかりませんでした。 40 // フィールドにフラグを持たせて、TimetimeCoroutine内で切り替えるという手法以外にありますか? 41 StopCoroutine(TimeCoroutine(0f)); 42 Debug.Log("中断"); 43 float pauseTime = Time.time; 44 yield return SuspensionCoroutine(); 45 startTime += Time.time - pauseTime; 46 StartCoroutine(TimeCoroutine(startTime)); 47 Debug.Log("再開"); 48 }
追記⑥
C#
1public class MyTime : MonoBehaviour 2{ 3 private static bool m_isActive = false; 4 private static float m_time; 5 private static float m_timeScale = 1.0f; 6 private static IEnumerator mytimeCoroutine; 7 public static float time{ 8 get { return m_time; } 9 } 10 public static bool isActive{ 11 get { return m_isActive; } 12 } 13 14 public static float timeSale{ 15 get { return m_timeScale; } 16 set { 17 if (m_timeScale == 0 && m_isActive){ 18 StopCoroutine(mytimeCoroutine); 19 m_timeScale = 0; 20 }else{ 21 m_timeScale = value; 22 } 23 } 24 } 25 26 private static IEnumerator TimeCoroutine(){ 27 // 本当はメソッドのようにreturnで処理を中止したかったのですが、できませんか? 28 // if (m_isActive) return; 29 if(!m_isActive){ 30 m_isActive = true; 31 while(true){ 32 m_time += Time.deltaTime; 33 m_time *= m_timeScale; 34 yield return null; 35 } 36 } 37 } 38 39 public static void StartTime(){ 40 StartCoroutine(TimeCoroutine()); 41 } 42 43 public static void StopTime(){ 44 if (!m_isActive) return; 45 m_isActive = false; 46 StopCoroutine(mytimeCoroutine); 47 } 48 49 public static void RestartTime(){ 50 if (mytimeCoroutine == null) return; 51 StartCoroutine(mytimeCoroutine); 52 } 53 54 public static void ClearTime(){ 55 StopTime(); 56 mytimeCoroutine = null; 57 m_time = 0; 58 } 59}
追記⑦
C#
1public class TestCoroutine : MonoBehaviour 2{ 3 4 UnityEngine.Coroutine RunningMyCoroutine; 5 6 void Start() 7 { 8 RunningMyCoroutine = StartCoroutine(MyCoroutine()); 9 } 10 11 void Update() 12 { 13 if(Input.GetMouseButtonDown(0)){ 14 RunningMyCoroutine = StartCoroutine(MyCoroutine()); 15 } 16 } 17 18 IEnumerator MyCoroutine(){ 19 if (RunningMyCoroutine != null) yield break; 20 int i = 0; 21 while(i<5){ 22 Debug.Log($"MyCoroutine:{i}"); 23 i++; 24 yield return new WaitForSeconds(2.0f); 25 } 26 RunningMyCoroutine = null; 27 } 28}
追記⑧
C#
1 [SerializeField] GameObject startPoint; 2 [SerializeField] GameObject endPoint; 3 [SerializeField] float animationLength = 1.0f; 4 5 readonly float speed = 4; 6 7 void Start() 8 { 9 Vector3 startPosition = startPoint.transform.position; 10 Vector3 endPosition = endPoint.transform.position; 11 startPosition.y = 0.0f; 12 endPosition.y = 0.0f; 13 StartCoroutine(Move(this.transform, endPosition)); 14 } 15 16 IEnumerator Move(Transform trf, Vector3 toPosition) 17 { 18 WaitForInterruption interrupter = new WaitForInterruption(); 19 while (true) 20 { 21 Vector3 currentPosition = trf.position; 22 Vector3 nextPosition = Vector3.MoveTowards(currentPosition, toPosition, speed * Time.deltaTime); 23 if (currentPosition == nextPosition) 24 { 25 break; 26 } 27 trf.position = nextPosition; 28 // yield return new WaitUntil(()=>true); 29 // yield return new WaitWhile(()=>false); 30 // yield return false; 31 // yield return true; 32 yield return interrupter; 33 } 34 } 35 36 class WaitForInterruption : CustomYieldInstruction 37 { 38 // public override bool keepWaiting => this.runningCoroutineCount > 0; 39 public override bool keepWaiting => false; 40 }
回答3件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。