Unreal Engine 4.24 で正確に往復移動するオブジェクト（アクター）を作る

あちこち検索すると GetActorLocation に Timeline (float) の値を加えて SetActorLocation にセットすることでアクターを動かす方法が出てくるけど、その方法だとしばらく往復運動させていると位置がずれてしまう。ここでは、ずれないで往復運動する方法をメモしておく。

# 環境

- Windows 10 Home
- Unreal Engine 4.24

# 準備

とりあえず ThirdPerson テンプレートで作成したプロジェクトを使って説明してみる。ThirdPerson テンプレートでなくても、同じことはできる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/0788cdfd-1fd7-6eae-c5e1-ec31538db477.png)

設定はデフォルトのままで、プロジェクト名は MotionSample としてみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/6ec354de-f717-af9b-3d4d-6d9e29b2a2c6.png)

最初に配置されている CubeMesh を上下に動くように改造することにする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/bafce386-45ae-513b-0e9e-f235cdff4c79.png)

# 方針

- CubeMesh の初期位置を Vector 変数に保存する。
- TimeLine で往復運動をする物体の位置情報を作る。
- 初期位置に Timeline で生成した数値を加えることで、物体の現在位置を算出する。

初期位置に、Timeline で位置の差分を加えて現在位置を算出することで、Timeline の値が 0 のときには正確に初期位置に戻るようになる。

# 手順

CubeMesh を選んだ状態で、右側にある「ブループリント/スクリプト編集」を押す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/fe455ed0-c3b3-3874-5fac-e0f8331cdbbf.png)

「ブループリントを作成」を押す。ブループリント名はデフォルトのままでいい。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/74afb569-789e-c669-b2fd-5d635d92a05e.png" width="60%"/>

ブループリントのエディタの「イベントグラフ」のタブを開く。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/d351c148-a1b7-9324-beee-ce2ea3477092.png)

右クリックして、Timeline と入力する。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/d6ff2c9f-0501-4de3-17b3-b9c785b30eee.png" width="80%"/>

メニューの一番下に出てくる「タイムラインを追加する」を選ぶ。公式サイトとかの説明だと、英語のメニューになっていて Add Timeline を選べと書いてあったりするので、つい Timeline という文字列を探してしまうかもしれないけど、ここだけ微妙に日本語化されてるのよね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/f1e39a2b-9f8a-8b9e-6a97-e6346dbf6053.png)

タイムラインが配置されたら、ダブルクリックしてタイムラインエディタを出す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/d9e4b47b-6661-bb50-8311-a56d5b67420f.png)

「フロートトラックを追加」を押す。ベクタートラックを追加するのでもできるけど、ここでフロートトラックでやっておく。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/7c3269f2-34ae-70ba-40a0-a2d45c3d2e90.png)

エディタの画面で、Shift を押しながら左クリックすると、グラフの点を追加できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/4d89d7d7-8f21-13c2-ee48-37e9eb960448.png)

こんな感じに、0の点から始まって0に戻るような線を描く。点を選択した状態で、上にある「時間」「値」のところに数値を直接入力することもできる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/fe9710f2-5d21-aeea-63a9-0294c22078ae.png)

上にある「ループ」のところにチェックを入れておく。これにチェックが入ってないと、一往復しただけで動かなくなる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/314496e3-fad3-2464-14d6-a122d26f255a.png)

グラフを描画してからイベントグラフに戻ると、タイムラインの右下に「新規Track0」という緑色のポチが増えているはず（名前は多少違うかもしれない）。こうなってなければ何か操作をまちがっている（かも）。あとは、最初の方針に従ってグラフを作る。

## 初期位置を変数に保存する

左下あたりにある「変数」の横の「＋」を押して、初期位置を入れる Vector 型の変数を作る。名前は DefaultPosition としてみた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/381cf066-9715-bded-8043-cfa12dcfd462.png)


右側の「詳細」の欄で、変数の型を Vector にしておく。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/7b8de76c-fe2c-db77-2ffd-703ff1c11626.png)

変数をイベントグラフ内にドラッグドロップする。選択肢が出るので、Set DefaultPosition のほうを選ぶ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/6eee533d-4d13-e4d4-3c6f-86189ea15eab.png)

イベントグラフで右クリックして、GetAct くらい入力して GetActorLocation を探して選ぶ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/9be762f2-f610-42b8-6dc9-d75b004ccd72.png)

イベントグラフ中に配置したら、下の図のように接続していく。BeginPlay に「セット」をつなぎ、GetActorLocation を値の入力とすることで、アプリ開始時点でのアクターの初期位置を DefaultLocation の変数にセットすることができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/f1abb677-7d14-88a6-2163-7066373ebb81.png)

タイムラインより右側のイベントグラフも、下の図のように作る。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/a97973c4-618c-104b-b58c-9550335fe402.png)

- Default Position の黄色い箱は、変数の Default Position をイベントグラフにドラッグドロップして "Get DefaultPosition" を選ぶことで置ける。
- "+" の黄色い箱は、右クリックメニューから "Add" と入力して "vector + vector" という項目を探して置く。
- Speed の緑色の箱は、先に左側の「変数」のところで＋を押して Speed という名前で作成しておく。右側の「詳細」で 「変数の型」を float にしておく。変数を作ったら、イベントグラフにドラッグドロップして Get Speed を選ぶことで置ける。
- 右下の Make Vector は、右クリックメニューで "Make Vector" と入力して探して置く。
- 中央下の "X" は、右クリックメニューで "mul" と入力して、Multiply の float * float を選ぶ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/e55d1b3b-13c4-5282-e8b5-e564532abece.png)

最後に、メインのウィンドウで CubeMesh を選んで「トランスフォーム」の下にある「可動性」のところを「ムーバブル」に変更しておく。この変更をしないと、プログラムを書いてもアクターが動いてくれない。

# これで動くはず

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/79c31670-30d4-2fbd-1bd9-6b9e10848c11.png)

オブジェクトの初期位置をベースにして、時間が経過したときに Timeline のその時刻の値を、オブジェクトの位置の z 座標にセットするようにしている。初期位置をベースにしているので、Timeline の値が 0 になれば、必ず初期位置に戻る。これによって、往復運動をするごとに位置がずれていくことは防げる。

# 位置がだんだんずれる方法

検索でよく見つかる、位置がだんだんずれていく方法も参考までに載せておく。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/c303c548-4050-8216-f5df-0e5812c4f910.png)

初期位置を保存する変数を使わず、GetActorLocation で取得した位置ベクトルに、直接タイムラインの値を加える。つまり、タイムラインの値を z 軸の移動速度のように使う。加算による誤差が蓄積いしていくため、時間が経過するに従って、オブジェクトが動く z の範囲がだんだんずれていく。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/7c09d121-387e-cced-207a-76bf7cdd1055.png)

動き方は、ずれない方法とは同じにならず、速度 0 の状態から次第に加速していって、途中からは次第に減速して 0 になるという、見た目には自然な動きになる。これは、タイムラインの値が z 軸の位置ではなく、速度として使われるためである。

ずれない方法でも同じような動きにするためには、タイムラインの線をサインカーブ状にすればよい。