【UE5】初心者向け ドアに触れると開閉する機構を実装してみる
1.初めに
今回は、ドアに触れると開閉する機構を実装していこうと思います。筆者自身が開発を進める中で躓いたポイントや各ノードの役割について筆者なりの解釈を交えながら説明を行っているため少し長い記事となっていますが、最後までご覧いただけるとありがたいです。なお、説明ではUE5のテンプレートの一つであるFirstPersonテンプレートを使っています。
2.実装イメージ
実装イメージは以下の動画の通りです。
動画のように触れるだけで開くドアを実装する事ができます。3.下準備
まず、ドアのメッシュを用意します。こちらはUnreal Engineが提供するスターターコンテンツの中に入っているので、それをプロジェクトに読み込ませます。コンテンツブラウザで右クリックを押して、「コンテンツを管理」の中にある「機能またはコンテンツパックを追加...」をクリックします。
クリックすると、以下のようなウィンドウが開くので、赤枠で囲った「コンテンツ」を選択します。
選択すると以下の画面になるので、「プロジェクトに追加」をクリックします。
これで、ドアのスタティックメッシュがプロジェクトに追加されました。
次の処理のために、「コンテンツ」フォルダーに移動しておきます。
4.ドアの作成
続いて、持ち運ぶオブジェクトを作成しましょう。コンテンツブラウザで右クリックを押して、
ブループリント → Actor
を選択します。
分かりやすいように名前を「BP_door」としておきましょう。
次に、作成した「BP_door」をダブルクリックで開きます。そして、左側の「コンポーネント」タブの「+追加」から「キューブ」を選択してCubeを追加します。
分かりやすいように名前を「door_frame」としておきます。
同様に、もう一つCubeを追加して名前を「door」としておきます。
この時、「door_frame」の子に「door」がなってしまっている場合は、「door」を「DefaultSceneRoot」にドラッグして「親子付け」を選択し、「door_frame」との親子関係を解除します。
続いて、「door_frame」を選択した状態で右側の「詳細」タブから
スタティックメッシュ → Static Mesh
にある「Cube」の右側の下矢印をクリックして、検索バーに「door」と入力して「SM_DoorFrame」を選択します。
そして、「トランスフォーム」からオブジェクトのサイズを元の2倍にして、可動性を「スタティック」に変更しておきます。
この時、〇で囲んだ鍵をロックのマークにしておくとスケールの比率が固定されて便利です。
大きさを2倍にする理由は、プレイヤーの当たり判定が想定よりも大きく、等倍だとスムーズに通れなかったからです。
位置も変更しておきます。同じく「トランスフォーム」からY座標の位置を「-85」に変更しておきます。
これは、座標を「door」と合わせるための変更です。
続いて、「詳細」タブから
物理 → Simulate Physics
を見つけ、チェックボックスにチェックを入れます。この時、「詳細」タブの上部にある検索バーから探すと簡単に見つけられます。
それでは、次に「door」の設定に移ります。
「door_frame」の時と同じように、右側の「詳細」タブから
スタティックメッシュ → Static Mesh
にある「Cube」の右側の下矢印をクリックして検索バーに「door」と入力し、今度は「SM_Door」を選択します。
そして、「トランスフォーム」から拡大・縮小をx:1.9、y:1.9、z:2にします。
このサイズはお好みで大丈夫です。筆者はドアとフレームの接触を気にして少し小さくしていますが、特に気にしなくても良いと思います。ただし、doorの位置は変えないでください。doorの位置を変えてしまうと回転軸がズレてしまって、ドアの挙動が変わってしまいます。
続いて、「door」にコリジョンを付けていきます。初期状態ではdoorにコリジョンの設定がされていないので、これを修正します。
先ほど変更した「SM_Door」をダブルクリックして編集画面を開きます。
そして、画面の上部にある
コリジョン → 自動凸型コリジョン
を選択します。
すると、「凸型分解」というタブが右側に新しく追加されます。追加されたタブの中身を見ると「適応」というボタンがあり、これを押す事で自動的にコリジョンを作成してくれます。
コリジョンが作成されると、下の画像のようにドアの周りに緑色の線が表示されると思います。
コリジョンが作成できたら「BP_Door」の編集画面に戻ります。
次は、ドアの動きを制限するために「Physics Constraint」を追加していきます。「+追加」から「Physics Constraint」を選択して追加します。
そして右側の「詳細」タブの中にある「Angular Limits」の「Swing 2 Motion」と「Twist Motion」を「Locked」に設定し、「Swing 1 Motion」をLimitedに設定し、その下にある「Swing 1 Limit」を「90」に設定します。
また、同じく詳細タブにある「コンストレイント」の「Component Name 1」に「DefaultSceneRoot」を、「Component Name 2」に「door」を指定します。
初期状態だと入力する場所が隠れているので、「Component Name 1」「Component Name 2」それぞれの場所をクリックすると入力場所が現れます。
設定できていれば、ビューポート上でドアの周りに青い線が現れます。もし現れない場合は、入力文字が正しいか確認してみてください。
これによりdoorの動きを制限して、蝶番の機能を再現出来ました。
それでは次に、プレイヤーがドアの前にいるかどうかを判定するためにボックスコリジョンを追加していきます。
左側の「+追加」から「BoxCollision」を選択して追加します。
続いて、「PhysicsConstraint」>「door」>「Box(コリジョン)」の順番に親子関係を設定します。
特に重要なのがドアとコリジョンの親子関係です。ドアの子にコリジョンを設定しないと、コリジョンがドアに追従せずにその場に残ったままになります。
そして、ボックスコリジョンが「door」を覆うようにトランスフォームを設定します。筆者は以下のように設定しました。
最後に、「BoxCollision」の「詳細」タブの一番下にある「イベント」の中から「On Component Begin Overlap」と「On Component End Overlap」を探して、その右側にある「+」ボタンをクリックします。
これらは、それぞれコリジョン内に侵入した時、コリジョン内から出た時にイベントが呼び出されるBPになります。これでドアの前に立っているかどうかを判定していきます。
5.BPの作成(タイマー)
続いてBPを作成していきます。先ほどの「+」ボタンを押すと下画像のように、2つのブロックが配置されると思います。まずは上のブロックから触っていきます。
「実行」からドラッグして「Cast To BP_FirstPersonCharacter」を追加します。検索バーに「cast to bp」と入れると上位に来るはずです。
そして「Cast to BP_FirstPersonCharacter」ノードの「Object」ピンと「On Component Begin Overlap」ノードの「Other Actor」ピンを繋げます。
ここでは、コリジョン内に侵入してきたアクターがプレイヤー(BP_FirstPersonCharacter)かどうかを確かめ、プレイヤーなら上、プレイヤーでなければ下(CastFailed)に繋いだ処理が実行されます。
続いて使用する変数を作成していきます。左側の「マイブループリント」内にある「変数」の左側の+ボタンを押して変数を追加し、名前を「set_timer」で型を「Timer Handle」にします。
この変数は、後に登場するタイマーをリセットするために用います。
変数を作成した時点では型が「Boolean」になっているので、クリックして変えていきます。検索バーに「Timer」と入力するとすぐに見つけられて便利です。
同じように、今度はActor型の変数を作成し、名前を「actor_name」とします。この変数は、キャストされたアクターの名前を保存するための変数です。
こちらも先ほどと同じように検索するとすぐに見つかります。なお、参照は「オブジェクト参照」に設定して下さい。
次に、先ほど作成した変数「actor_name」を「Cast To...」ノードの一番上の上のピンに接続し、青色の入力ピンと「Cast To...」ノードの「As BP...」ピンを接続します。
これで変数にアクター名が保存されます。
続いて、その先に「Set Timer by Event」ノードを追加して、「time」に0.01と入力しLoopingにチェックを入れます。
このノードは繰り返し処理を行うために使っており、その挙動は「event tick」ノードと似ていますが、event tickノードとは違ってタイマーがセットされている時にのみ繰り返し処理を行うため、システムを軽量化できます。
そして、その後に先ほど作成した変数「set_timer」を繋いで、入力ピンと「Set Timer by Event」ノードの「Return Value」ピンを繋ぎます
6.BPの作成(ドアの開閉)
続いて、ドアを開閉する部分を作っていきます。「Set Timer by Event」ノードの「Event」ピンから線を伸ばして、「Custom Event」ノードを作成します。
これにより、Timerがついている間ドアの開閉機能が動くようになります。
Custom Eventの名前は、分かりやすいように「door_open」としました。
続いて、Custom Event(door_open)ノードから線を伸ばして「Set Relative Rotation(door)」ノードを追加します。
このノードはTargetピンに繋がったオブジェクトの回転を「New Rotation」に設定された値に変化させます。
次に、「NewRotation」ピンから線を伸ばして「RInterp To」ノードを追加します。
このノードはCurrentピンに繋がった回転角からTargetピンに繋がった回転角まで滑らかに変化させるノードで、Delta Timeピンの値ずつ更新していき、Interp Speedピンの値の速さでオブジェクトを回転させます。
では各ピンの設定をしていきます。まず、「Interp Speed」は1.0のままで大丈夫です。次に、「Delta Time」ピンには「Get World Delta Seconds」ノードを接続します。これは1フレームあたりの秒数を表しており、これを繋ぐことで滑らかなドアの動きを実現します。
その次に、Currentから線を伸ばして「Get Relative Rotation(Scene Component)」ノードを接続します。この時、右上の「状況に合わせた表示」のチェックが付いたままだと選択肢に表示されないので外しておきます。
「Get Relative Rotation」ノードの「Target」ピンには「door」を接続します。左側の「コンポーネント」タブの中にある「door」をドラッグしてくる事でTargetピンに繋げられます。
次に、「RInterp To」ノードの「Target」ピンに「Select Rotator」ノードを接続します。
このとき、AのZ軸回転を―90、BのZ軸回転を90としておきます。
このノードはPick Aピンの値がTrueならA、FalseならBの回転角を選択するノードです。
「Pick A」ピンには「Less(<)」ノードを接続します。このノードは上の値より下の値の方が小さければTrue、そうでなければFalseを返します。
次に、何もない場所で右クリックを押して、「Get Actor Forward Vector」ノードを追加します。
このノードは、Targetピンに繋がったアクターの向いている方向(単位ベクトル)を取得します。
「Target」ピンには作成した変数「actor_name」をドラッグして接続します。
続いて、もう一度何もない場所で右クリックを押して、今度は「Get Forward Vector(door)」ノードを追加します。このノードはドアが向いている方向を取得します。
次に、「Get Actor Forward Vector」ノードから線を伸ばして「Dot Product」ノードを追加します。下のピンには「Get Forward Vector」ノードを接続します。
このノードは2つのベクトルのドット積(内積)を返します。
そして、「Dot Product」ノードの出力ピンを「less」ノードの上の入力ピント接続します。下のピンは0.0のままで大丈夫です。
これにより、プレイヤーがドアのどちら側に立っているのかを判定し、その結果に応じて開く向きを変えます。
これで「On Component Begin Overlap」の処理は完成です。
7.BPの作成(変数のリセット)
続いて、プレイヤーがドアから離れた時に変数をリセットするようにします。
「On Component End Overlap」から線を伸ばして、「Cast to BP_FirstPersonCharacter」ノードを接続します。「Object」ピンには「Other Actor」ピンを接続します。
次に「Cast To...」ノードから線を伸ばして、「Clear and Invalidate Timer by Handle」ノードを接続し、「Handle」ピンに作成した変数「set_timer」を繋ぎます。
そして、変数「actor_name」ノードをその後ろに追加します。
これで、全ての処理が完成しました。
最後に、編集画面を閉じてオブジェクトをワールドに配置して動かしてみます。
注意点ですが、今回実装したドアはこの向きに置かないと上手くいきません(上手くいきました 2025/10/09)。また、プレイヤーの前方のベクトルを計算に用いているので、後ろ向きにドアを押しても開ける事ができません。
この辺りは今後の改良点ですね。
今回はここまでです。少し長い説明になりましたが、最後までご覧いただきありがとうございました。よい開発ライフを!
Discussion