コンストラクションスクリプトを使う
今回はコンストラクションスクリプトを使っていろいろやってみます。
使い道がたっぷりある機能ですので、積極的に使っていきましょう。
コンストラクションスクリプト(Construction Script)はBlueprintのインスタンス(実体)が生成された際に実行されるスクリプトです。
開始時に実行される、という点ではBeginPlayイベントからのスクリプトも似たようなものではありますが、大きく違う点がいくつかあります。
まず、使用できる命令に違いがあります。
わかりやすいところでは時間経過を待つ"Delay"命令はコンストラクションスクリプトでは使用できません。
コンストラクションスクリプトは生成時の1フレームで処理が完了するようです。
もう1つの違いとしてはEditor上に配置した場合にコンストラクションスクリプトは実行されるという点です。
BeginPlayイベントはゲームを開始しなければ発行されないため、例えばBeginPlayイベントでStaticMeshComponentを追加していたとしてもEditor上ではそのメッシュは表示されません。
あくまでもゲーム開始時(Playボタンを押したとき)に初めて表示されるというわけです。
これに対してコンストラクションスクリプトはEditor上に配置した段階で発行されるので、ここで追加したコンポーネントも表示されます。
正確にはActorのパラメータが変化した場合に常にコンストラクションスクリプトは実行されるようですが、これについてはサンプルを見ながら追々解説していきます。
では、いくつかのコンストラクションスクリプトを使用したサンプルを作っていきましょう。
とりあえずいつも通りに適当なプロジェクトを作成してください。
最初のサンプルは以前もやったDynamicMaterialInstanceです。
以前にやったDynamicMaterialInstanceはLevelBPのBeginPlayイベントで実行していました。
このため、動作が正常かどうかはゲームを実行しないと確認できませんでした。
ゲームの挙動と関連するものであればこれも致し方ないのですが、例えばオブジェクトのベースカラーだけを変更したいという場合はゲームを起動しないとわからないというのは不便です。
そこで、コンストラクションスクリプトを利用してEditor上でもマテリアルが変化するのを確認できるようにしましょう。
まず今回のマテリアルは以下のような単純なものを用います。
(クリックで拡大)
BaseColorがパラメータとなっていて、外部から変更することが可能です。
次に適当なBlueprintを作成し、"BaseMesh"という名前のStaticMeshComponentを追加します。
ここに適当なメッシュを設定してください。propsフォルダ内の"MaterialSphere"がわかりやすくていいかと思います。
さて、このBlueprintのコンストラクションスクリプトを以下のように作成しましょう。
(クリックで拡大)
Blueprintに追加した変数はLinearColorの"BaseColor"とMaterialInstanceDynamicの"DynamicMat"の2つです。
”BaseColor"はEditor上で編集が可能なように設定してます(目のアイコンが開いている状態)。
コンストラクションスクリプトではDynamicMaterialInstanceを生成し、これを"DynamicMat"に格納しています。
まあ、格納する意味は今回についてはあまりありませんが、ゲーム中に何らかの理由でBaseColorが変化する場合などはこの変数を利用してBaseColorを再び変化させることができます。
このDynamicMaterialInstanceはその後、BaseMeshに登録され、BPの変数である"BaseColor"をマテリアルのパラメータ"BaseColor"に設定しています。
では、このBlueprintをEditorに配置し、"BaseColor"変数を変更してみましょう。
変更するとそれに合わせてオブジェクトの色が変わるはずです。
インスタンスのパラメータが変化するとコンストラクションスクリプトが実行されている一例といえるでしょう。
もっとわかりやすいサンプルを作成していきましょう。
配置位置を中心に指定半径範囲内にランダムに草を配置するBlueprintを作成してみます。
Blueprintを新規作成したら、変数としてintの"NumBush"とfloatの"Radius"を追加しましょう。
どちらもEditorで編集可能にしておきます。
次にコンストラクションスクリプトを以下のように設定します。
(クリックで拡大)
"StaticMesh SM_Bushを追加する"というノードがありますが、このノードを作成するには一手間必要です。
コンテンツブラウザ上でpropsフォルダにある"SM_Bush"を選択しておきます。
この状態でスクリプトエディタで右クリックし、"SM_Bush"と入力しましょう。
すると"Add SM_Bush (as StaticMeshComponent)"というのが見つかるので、これを選択します。
これ以外で特定のStaticMeshを追加したい場合は、"Add StaticMeshComponent"ノードを追加し、このReturnValueの命令である"Set Static Mesh"で設定する方法もあります。
ただ、この場合はノードが2つになってしまうので、前述の方法をとった方がスクリプトがすっきりします。
では、このBlueprintをEditor上に配置しましょう。
配置したらEditor上で平行移動させてみてください。平行移動するたびにSM_Bushがランダムに配置されるのがわかるでしょう。
もちろん、ゲーム開始時にもランダムに配置され、Editor上での配置とは変わってしまいますしゲームの始めるたびに配置が変わります。
それでも問題ないゲームもあるでしょうが、ゲームのたびに変化してしまうのは困る場合がほとんどでしょう。
そこでランダムの種を固定することでランダムの配置を固定する方法をとってみましょう。
コンピュータにおけるランダム(乱数)は疑似乱数と言われるもので完全な無作為のランダムではありません。
ある種の計算式を利用して生成されているため、初期値を同一にすると全く同じ乱数を常に生成することが可能です。
UE4でもランダムの種を指定することができる"Random Stream"というものが存在しているので、こちらを利用します。
コンストラクションスクリプトは以下のようになります。
(クリックで拡大)
追加されている変数はintの"RandomSeed"です。
最初に作成したRandom Streamに”Set Random Stream Seed"命令を利用してランダムの種を設定しています。
ここで注意が必要なのは"RandomStreamを作成する"命令のInitial Seed引数です。
初期のランダムの種をここで設定するのですが、"RandomSeed"変数をここに入力してもいいのではないかと考えられると思います。
しかし、ここでランダムの種を設定してもパラメータ変更で変化が起こりませんでした。
あくまでもコンストラクションスクリプトとして実行されている命令だけが影響を受けるようです。
このBlueprintを配置し、平行移動しても草の配置は変化しません。
しかし、ランダムの種を変化させるとそれに合わせて変化します。
また、ランダムの種を同じ数値にすると同じ配置が現れますし、"NumBush"変数を増やすと今の配置に追加する形で配置されます。
これならレベルデザイナが納得のいく配置を作り出し、これを変化させずに済むでしょう。
最後にちょっと複雑なコンストラクションスクリプトを公開しておきます。
以前のUE4勉強会で講演してくださった湊さんが作成中のゲームで、屏風を長さに応じて変化させている、ということを言っていました。
その際にもコンストラクションスクリプトを使用して、と言っていましたので、せっかくだから作ってみました。
全く同じ計算式で生成されているわけではないでしょうけど、似たような感じで作成しているものと思われます。
まず長さの指定ですが、floatの変数を利用する手もあります。
しかし今回はVectorで終了位置を指定する形で実装してみようと思います。
"EndPoint"というVectorの変数を追加します。
この際、Editorで終了位置を指定できるようにした方が何かと有利ですので、"3Dウィジェットを~"の項目にチェックを入れておきます。
こうすると"EndPoint"がEditor上で移動することが可能になります。
なお、この変数ですが、数値的には配置したActorの相対位置となる点に注意してください。
コンストラクションスクリプトはちょっと複雑ですが以下のように設定します。
(クリックで拡大)
複雑なのでノードのつながりがちょっとわかりにくいかもしれません。
ここで使われている"Place Byoubu"というのは追加した関数です。
この関数は以下のように設定されています。
(クリックで拡大)
このBlueprintをEditor上に配置すると以下のようになります。
Arrowオブジェクトが表示されている部分がActor Locationです。わかりやすくするためにRootComponentとしてArrowを置いています。
右の方にEndPointと書かれたウィジェットが存在します。これを移動するとそれに合わせて屏風が変化します。
屏風の長さがある程度になると屏風の枚数が増えたり減ったりします。
増えたり減ったりしない範囲では畳まれたり広がったりするようになっています。
こういう作りになっていると、各レベルに配置する屏風の長さによって別々のメッシュを作成する必要がなくなりますよね。
屏風のわりにサイバーな見た目だって?
虎を捕まえてくれ、とか言われないのでいいじゃないですか。