🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

2回答

1491閲覧

Mathf.Lerpの線形補間。

退会済みユーザー

退会済みユーザー

総合スコア0

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2018/11/14 17:17

編集2018/11/15 16:39

前提・実現したいこと

Mathf.Lerpとコルーチンを使って、指定時間で開始値から終了値まで値を変化させるということができると思うのですが、
どのように書けばよいでしょうか?
ご教示お願い致します。

試したこと

値を1からスタートさせて、3秒かけて5の値まで変化させる、
ということができるようなコルーチンをMathf.Lerpを使って作成したいのですが、
途中でどう書けばよいかわからなくなってしまいました。
tやdurationをどのように変化させればよいのでしょうか?

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class Sample : MonoBehaviour { 6 7 void Start () { 8 StartCoroutine(ChangeCoroutine(1f, 5f, 3f)); 9 } 10 11 IEnumerator ChangeCoroutine(float start, float end, float duration){ 12 13 float t=0; 14 15 while(t<1){ 16 float i = Mathf.Lerp(start, end, t); 17 Debug.Log(i); 18 //tやdurationをどのように書けばよいかわからない。 19 yield return null; 20 } 21 } 22} 23

追記①

C#

1 void Start () { 2 float i = 0; 3 StartCoroutine(ChangeCoroutine(i, 1f, 5f, 3f)); 4 } 5 6 IEnumerator ChangeCoroutine(float value, float start, float end, float duration){ 7 8 float t=0; 9 10 while(t<1){ 11 value = Mathf.Lerp(start, end, t); 12 t += Time.deltaTime/duration; 13 Debug.Log(value); 14 yield return null; 15 } 16 17 //tが1未満でwhile文から抜ける可能性がある為、調整。 18 value = end; 19 Debug.Log(value); 20 } 21}

追記②

追記①のように変化させる値を引数で渡すコードは、実用的でなかったので、次のようなコード例を考えてみました。

C#

1 void Start () { 2 StartCoroutine(ChangeCoroutine(cube, cube.transform.position.x, 5f, 3f)); 3 } 4 5 // Update is called once per frame 6 void Update () { 7 8 } 9 10 IEnumerator ChangeCoroutine(GameObject go, float start, float end, float duration){ 11 12 float t=0; 13 float x=0; 14 while(t<1){ 15 x = Mathf.Lerp(start, end, t); 16 t += Time.deltaTime/duration; 17 go.transform.position = new Vector3(x, go.transform.position.y, go.transform.position.z); 18 yield return null; 19 } 20 21 //調整。 22 go.transform.position = new Vector3(end, go.transform.position.y, go.transform.position.z); 23 24 }

経過時間が格納されているかログで確認しました。

C#

1 float time = 0; 2 //3秒間、毎フレーム行う処理をwhile文の中に記述。 3 while(time < 3){ 4 time += Time.deltaTime; 5 Debug.Log(time); 6 yield return null; 7 }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

最近同じような質問をして解決した者です。
その時のコードを改造して簡単な解説付きで置いておきます。
参考になれば嬉しいです。

ソースコード

このコードはテラシュールブログさんを参考にさせていただいてます。

cs

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class Hoge : MonoBehaviour { 6 public float startValue; //開始時の値 7 public float endValue; //終了時の値 8 public float duration; //開始時から終了時の値になるまでの総合時間 9 10 float startTime; //スタートした時間 11 12 float t; //結果 13 14 private void Start() 15 { 16 //開始時の時間を入れておく 17 startTime = Time.timeSinceLevelLoad; 18 } 19 20 private void Update() 21 { 22 //経過時間を求める 23 float diff = Time.timeSinceLevelLoad - startTime; 24 //求めた差から総合時間との割合を計算 25 float rate = diff / duration; 26 27 //開始と終了の値を計算した割合で線形補間する 28 t = Mathf.Lerp(startValue, endValue, rate); 29 } 30} 31 32

ちょっとした解説(?)

まず、開始した時間を取っているTime.timeSinceLevelLoadですが、これはシーンが読み込まれてからの時間です。

経過時間(diff)を求めている式は現在の時間 - 開始時間という計算です。

経過時間の割合(rate)を求める式は
割合 = 比べられる量 / もとにする量という割合の公式を当てはめて
割合 = 経過時間 / 総合時間という式になっています。

これで指定した時間かけて0から1まで値を足すようになりました。

あとは開始時の値と終了時の値を計算した割合で線形補間するだけです。
Mathf.Lerp(startValue, endValue, rate)

投稿2018/11/15 10:21

Carly7766

総合スコア33

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/11/15 15:49

ご回答ありがとうございます。 こちらもとても勉強になりました。 「割合 = 比べられる量 / もとにする量」という割合の公式、基本的なことでしたが、意識してませんでした。 ありがとうございます。
guest

0

ベストアンサー

コードをあまり変更せずに記述するとこんな感じでしょうか。
tが1未満でwhile文から抜けてしまう可能性が高いため、抜けた後にend値を突っ込んだ方が良いと思います。また、ピッタリdurationの値にはならず、多少誤差が発生します。

using System.Collections; using UnityEngine; public class Sample : MonoBehaviour { void Start() { StartCoroutine(ChangeCoroutine(1f, 5f, 3f)); } IEnumerator ChangeCoroutine(float start, float end, float duration) { float t = 0; while (t < 1) { float i = Mathf.Lerp(start, end, t); t += Time.deltaTime / duration; yield return null; } } }

投稿2018/11/15 04:44

shibuyan

総合スコア17

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/11/15 09:10

ご回答ありがとうございます。 2点質問があるのですが、 ・1点目。 >tが1未満でwhile文から抜けてしまう可能性が高いため、抜けた後にend値を突っ込んだ方が良いと >思います。 質問文に追記しましたが、while文の後に、value = end;とすればよいでしょうか? (使い勝手をよくする為に、valueという引数を増やすことにしました。) ・2点目。 コルーチンでyield return nullを使っているからこそ、Time.deltaTimeの加算で、秒数のカウントができているということでしょうか? 試しに、 //yield return null; yield return new WaitForSeconds(2); と書き換えてみたら、durationの経過時間通りに変化しない様子でした。 「コルーチン内で、Time.deltaTimeを使って秒数をカウントする場合は、yield return nullと合わせて使う。」ということでしょうか? コルーチン内で、下記のようにすると、tに経過時間(秒)が格納されるということでしょうか? float time = 0; while(例えば、経過時間に関する条件){ time += Time.deltaTime; yield return null; }
shibuyan

2018/11/15 10:38

1点目 ChangeCoroutineの引数に入れる意図はわかりませんが、whileを抜けたらendをvalueとして使うという意味は合っています。 > (使い勝手をよくする為に、valueという引数を増やすことにしました。) このままだとvalueの値は関数の呼び出し元には返らないので、Action<float>とかでコールバックしてやる必要があるかなと思います。 2点目 yield return nullの実行周期がフレーム単位なのでTime.deltaTimeで加算しています。 > コルーチン内で、下記のようにすると、tに経過時間(秒)が格納されるということでしょうか? です。 ``` while(例えば、経過時間に関する条件){ time += Time.deltaTime; Debug.Log(time); yield return null; } ``` ログを↑のように仕込めば加算されることが確認できるかと思います。
退会済みユーザー

退会済みユーザー

2018/11/15 16:34

ご回答ありがとうございます。 Action<float>とかでコールバックする方法があるのですね。 Action<float>はまだ完全に理解していないので、勉強して参りたいと思います。 また、質問になってしまってすみませんが、 1点目 今、自分がすぐに作れるコードとして、追記②のようなコードならば、実用的かもしれないと思ったのですが、いかがですか? 2点目 >yield return nullの実行周期がフレーム単位なのでTime.deltaTimeで加算しています。 「コルーチン内でTime.deltaTimeを使う場合、yield return nullもセットで使う」という考え方をして大丈夫でしょうか? 3点目(質問というより感想ですが) 追記②の後半のコードの通り、ログをとって確認致しました。 経過時間が格納されているようでした。 ただ、最後のログが3秒を超えてしまっていたので、 「指定時間以内だけ毎フレーム特定処理をする」ということは無理みたいですね。 (1フレーム分、指定時間をオーバーする可能性があるみたいですね。)
shibuyan

2018/11/19 01:51

通知を見落としていました;; 1点目 パフォーマンスとかあまり考えない場合は、以下のコードでコールバックで値を受け取ってtransformに値を代入する方が他でも流用しやすいかなと。 ``` void Start () { var startValue = cube.transform.position.x; StartCoroutine(ChangeCoroutine( startValue, 5f, 3f, x=> cube.transform.position = new Vector3(x, cube.transform.position.y, cube.transform.position.z)) ); } IEnumerator ChangeCoroutine( float start, float end, float duration, System.Action<float> onUpdate) { float t=0; float x=0; while(t<1) { x = Mathf.Lerp(start, end, t); t += Time.deltaTime/duration; if (onUpdate != null) onUpdate(x); yield return null; } //調整。 if (onUpdate != null) onUpdate(x); } ``` 2点目 >「コルーチン内でTime.deltaTimeを使う場合、yield return nullもセットで使う」という考え方をして大丈夫でしょうか? コルーチン内でUnityのUpdate周期で処理をしたいから、`yield return null`を使用し、Time.deltaTimeを使うという順序でしょうかね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問