Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9738

パスに沿ってなめらかに一定速度でオブジェクトを移動させる実装の解説

$
0
0

my.gif

まえがき

問題

ゲームだと、パスに沿ってキャラを移動させたい時があります。
ただ、単純に実装すると速度がバラバラになってしまいます。

目的

今回は曲線でもだいたい一定の速度で動かせるようにする実装を解説します。
UnityならDoTweenのDoPathやCinemachineのCinemachinePathを使用すれば実装しなくても可能ですが、ベジェ曲線のみで、他の曲線を使うことはできません。
自分で実装すれば、好きな曲線を使えます。
ただ、今回は解説のため、ベジェ曲線を使います。

https://ja.wikipedia.org/wiki/ベジェ曲線

Vector3Bezier3(floatt,Vector3p1,Vector3p2,Vector3p3,Vector3p4){vard=1-t;returnd*d*d*p1+3*d*d*t*p2+3*d*t*t*p3+t*t*t*p4;}

参考

この記事はCinemachineのCinemachinePathBaseを参考に作っています。
github

解説

単純な実装

first.gif

これはベジェ曲線の引数tに$Time.t$を渡すだけの実装です。
明らかに速度がおかしいです。
0-0.1と0.5-0.6の移動距離が違うのと、
スタート位置から1つ目のパスまで1秒、そこからゴールまで1秒かけているのが原因です。

voidUpdate(){vart=Time.time;varindexA=Mathf.FloorToInt(Mathf.Min(Paths.Length-1,t));varindexB=Mathf.FloorToInt(Mathf.Min(Paths.Length-1,indexA+1));if(indexA==indexB)return;transform.position=CalcPos(t);}Vector3CalcPos(floatt){varindexA=Mathf.FloorToInt(Mathf.Min(Paths.Length-1,t));varindexB=Mathf.FloorToInt(Mathf.Min(Paths.Length-1,indexA+1));returnBezier3(t-indexA,Paths[indexA].Pos,Paths[indexA].Pos+Paths[indexA].Tangent,Paths[indexB].Pos-Paths[indexB].Tangent,Paths[indexB].Pos);}

改善した物

my.gif

この問題はtを0-ベジェ曲線の長さで扱えれば速度をだいたい一定にすることができます。

やっていることは、tを少しずつ動かして、進んだ距離を測る。
進んだ距離からtを返すテーブルを作るという事です。

以上です。
こうすると、距離からtに変換する関数を作成できるので、だいたい一定の速度で移動できるようになります。

コード
[SerializeField]intSegment;[SerializeField]PathContainerPaths;floatPathLength;//セグメントの総数intNumKeys;float[]DistanceToTArray;floatDistanceStepSize;voidStart(){Build();}voidBuild(){PathLength=0;NumKeys=(Paths.Length-1)*Segment+1;vartToDistance=CalcTToDistance();DistanceToTArray=CalcDistanceToT(tToDistance);}voidUpdate(){transform.position=CalcPos(DistanceToT(Time.time*PathLength/2));}//距離からtに変換floatDistanceToT(floatdistance){floatd=distance/DistanceStepSize;intindex=Mathf.FloorToInt(d);if(index>=DistanceToTArray.Length-1)returnDistanceToTArray[DistanceToTArray.Length-1];floatt=d-index;returnMathf.Lerp(DistanceToTArray[index],DistanceToTArray[index+1],t);}//tをSegmentに分割して進んだ距離を配列に入れて返すfloat[]CalcTToDistance(){vartToDistance=newfloat[NumKeys];varpp=Paths[0].Pos;floatt=0;for(intn=1;n<NumKeys;n++){t+=1f/Segment;Vector3p=CalcPos(t);floatd=Vector3.Distance(pp,p);PathLength+=d;pp=p;tToDistance[n]=PathLength;}returntToDistance;}//距離をSegmentに分割してその位置のtを配列に入れて返すfloat[]CalcDistanceToT(float[]tToDistance){vardistanceToT=newfloat[NumKeys];distanceToT[0]=0;DistanceStepSize=PathLength/(NumKeys-1);floatdistance=0;inttIndex=1;for(inti=1;i<NumKeys;i++){distance+=DistanceStepSize;vard=tToDistance[tIndex];while(d<distance&&tIndex<NumKeys-1){tIndex++;d=tToDistance[tIndex];}varprevD=tToDistance[tIndex-1];floatdelta=d-prevD;floatt=(distance-prevD)/delta;distanceToT[i]=(1f/Segment)*(t+tIndex-1);}returndistanceToT;}Vector3CalcPos(floatt){varindexA=Mathf.FloorToInt(Mathf.Min(Paths.Length-1,t));varindexB=Mathf.FloorToInt(Mathf.Min(Paths.Length-1,indexA+1));returnBezier3(t-indexA,Paths[indexA].Pos,Paths[indexA].Pos+Paths[indexA].Tangent,Paths[indexB].Pos-Paths[indexB].Tangent,Paths[indexB].Pos);}


あとがき

以上です。
動作するコードはこちらです。
https://github.com/nakajimakotaro/PathSmoothMove


Viewing all articles
Browse latest Browse all 9738

Trending Articles