rn.log

備忘録など

【UE4】アニメーションブループリントをちょっと触ってみた。

アニメーションBP(アニメーションブループリント)を触ってみました。

UE4のバージョン : UE4.19

アニメーションBPとは

キャラクターのアニメーションを組む機能です。
UE4に標準で入っています。

とりあえずアニメーションBPを組んでみる

STEP1: 必要なデータを用意する

アニメーションBPを使用してアニメーションを組むためには3Dモデルとモーションデータが必要です。

そういえば最近、Paragonのアセット無料化のニュースを見かけました。

www.unrealengine.com

今回はありがたくこちらを使わせてもらうことにします。

マーケットプレイスでParagonアセットを見つける

EpicGamesランチャーのマーケットプレイスで"Paragon"と検索欄に入れます。

f:id:r-ngtm:20180409171843p:plain

Paragonのアセットがいろいろ出てくるので好きなものを選びます。

f:id:r-ngtm:20180409172412p:plain

Minionsというキャラがメカメカしくてなんかカッコイイ。 これにしよ。

ちなみに、Minionsのデータサイズは5GBあります。

DLしたアセットをプロジェクトへ追加する

DLしたデータはLibraryタブから追加できます。
f:id:r-ngtm:20180409173435p:plain

プロジェクト名一覧が表示されるので、データを追加したいプロジェクトを選びます。

以上でデータの用意は完了です。


今回の記事ではParagon: Minionsというアセットを使用していきます。
f:id:r-ngtm:20180409195357p:plain:w400

STEP2 : アニメーションBPの作成

モデルデータとモーションの用意が終わったらアニメーションBPの作成に移ります。

コンテンツブラウザ上で右クリックして、「アニメーション」 -> 「アニメーションBP」を選択します
f:id:r-ngtm:20180409174300p:plain


クラスとスケルトンを選択してOKを押します。
f:id:r-ngtm:20180409174500p:plain

"AnimBP_Buff_Black"という名前を付けることにしました。
f:id:r-ngtm:20180409174911p:plain

STEP3: ステートマシーンを追加する

上記で作成したアニメーションBP "AnimBP_Buff_Black"を開くと以下のような画面が出てきます。 f:id:r-ngtm:20180409175523p:plain

ここで画面を右クリックして"State Machine"と入力して、
"新規のステートマシーンを追加"を選んでStateMachineノードを作成します。
f:id:r-ngtm:20180409175704p:plain

ノードをつなぎます
f:id:r-ngtm:20180409182226g:plain


f:id:r-ngtm:20180409175402p:plain

STEP3: Idleモーションを再生してキャラクターを動かす

次にステートマシーンを編集していきます。

先ほど作成したステートマシーンのノードをダブルクリックして開きます。

f:id:r-ngtm:20180409182510p:plain


そして、アセットブラウザタブの中からIdleモーションをドラッグ&ドロップし、ノードを接続します。

f:id:r-ngtm:20180409183255g:plain

ノードが接続された状態でコンパイルボタンをクリックします。
うまくいけばIdleモーションが再生されます。

f:id:r-ngtm:20180409183708p:plain

f:id:r-ngtm:20180409183835g:plain:w240

アニメーションBPに関する説明はいったん終わりとします。

マップ上にキャラクターを出してアニメーションさせてみる

アニメーションするキャラクターをマップ上に出してみます

STEP1: キャラクターBPの作成

コンテンツブラウザの右クリックメニューからブループリントを選択
f:id:r-ngtm:20180409202212p:plain

キャラクターを選択
f:id:r-ngtm:20180409202337p:plain

ブループリントの名前は"BP_Buff_Black"としておきます。
f:id:r-ngtm:20180409202443p:plain

STEP2: キャラクターBPにアニメーションBPを設定する

Meshコンポーネントを選択し、
Anim Classへ "AnimBP_Buff_Black"を設定、Skeltal Meshへ "Buff_Black"を設定します。
f:id:r-ngtm:20180409202758p:plain

STEP3: マップにキャラクターを配置してゲームを再生

BP_Buff_Blackをマップへドラッグ&ドロップしてゲーム再生するとキャラクターが動きます。
f:id:r-ngtm:20180409203233g:plain

おまけ: 2つのモーションをブレンドしてみる

移動速度が速いときはRunモーションを、移動していないときはIdleモーションを再生。
中間の場合は速さに応じてRun/Idleモーションをブレンドする、というのをやってみました。

モーションを混ぜるにはブレンドスペース1Dを使う

2つのモーションをブレンドする(混ぜる)には ブレンドスペース1D を使用します。
BlendSpace1Dを利用することで、実数を使ってモーションを混ぜることができます。

例えば、「IdleモーションとRunモーションと1:2の割合で混ぜる」といった事ができます。


似た名前のもので "ブレンドスペース" (名前の末尾に1Dが付いていない) がありますが、今回はこれは使用しません。

STEP1: ブレンドスペース1Dの作成

コンテンツブラウザを右クリックして、「アニメーション」 -> 「ブレンドスペース1D」を選択します。

f:id:r-ngtm:20180409185639p:plain


今回は Buff_Blackのスケルトンから使用するブレンドスペース1Dを作りたいので、Buff_Black_Skeleton を選択します
f:id:r-ngtm:20180409185919p:plain


名前ですが、RunIdleを合成するブレンドスペース1Dを作りたいので、名前を「BlendSpace1D_RunIdle」としました。
f:id:r-ngtm:20180409190141p:plain

ブレンドスペース1Dの編集

次にブレンドスペース1Dを編集していきます。

ブレンドスペース1Dをダブルクリックして開くと以下のような画面が表示されます。
f:id:r-ngtm:20180409190949p:plain:w500


今回は赤い字で示した部分を触ります。
f:id:r-ngtm:20180409190911p:plain:w500

STEP2: モーションの登録

アセットブラウザからIdle(待機モーション)をドラッグ&ドロップし、Blend Space Editor(左隣にあるウィンドウ)の端に配置します。
f:id:r-ngtm:20180409191723g:plain

アセットブラウザからRun_FWD(前方へ歩くモーション)をドラッグ&ドロップし、Blend Space Editor(左隣にあるウィンドウ)の端に配置します。
f:id:r-ngtm:20180409192125g:plain

以上を済ませると左側にIdleモーション、右端にはRun_FWDモーションが登録された状態となります。
f:id:r-ngtm:20180409192513p:plain:w350 f:id:r-ngtm:20180409192519p:plain:w350

補足: 下にある数字の意味

ここで、ウィンドウの下を見てみると数字が書かれていることがわかります。
これは
「Axis(軸入力) = 0.0のときにはIdleモーションを再生し、Axis = 100.0の時にはRun_FWDモーションを再生する」
という意味を表しています。

f:id:r-ngtm:20180409193021p:plain

ここで補足ですが、アニメーションBPからブレンドスペース1Dに対してfloat値を引数として渡すことができます。
この入力のことをAxis(軸入力)と呼びます。
f:id:r-ngtm:20180409194617p:plain

STEP3: ステートの作成

まずは最初に作成したアニメーションBP "AnimBP_Buff_Black"を開きます。 f:id:r-ngtm:20180409193553p:plain

次に、右クリックメニューからステートを作成してEntryノードと接続します。
f:id:r-ngtm:20180409193741g:plain
今回はIdleとRunをブレンドしたいので、ノード名を"Idle/Run"としました。

STEP4: ブレンドスペース1Dを使う

Idle/Runノードをダブルクリックして開き、右クリックメニューから"BlendSpace1D_RunIdle"を追加して最終アニメーションポーズへ接続します。
f:id:r-ngtm:20180409200144g:plain

ブレンドスペース1Dの挙動を確認してみる

Axis = 0.0の場合
f:id:r-ngtm:20180409201313g:plain
Idleモーションが再生されます。


Axis = 100.0の場合
f:id:r-ngtm:20180409201450g:plain
Runモーションが再生されます.

Axis = 50.0の場合
f:id:r-ngtm:20180409201734g:plain
IdleモーションとRunモーションが1:1の割合でブレンドされて再生されます。

STEP5: キャラクターの移動スピードをアニメーションに反映する

実際にキャラクターを走らせるため、もうひと手間加えます。

AnimBP_Buff_Blackのイベントグラフタブを選択し、以下のようなノードを組みます。
f:id:r-ngtm:20180409204532p:plain

キャラクターの移動速度ベクトルの長さを計算して、変数Speedに代入しています。


次に、Idle/Runノードを開いてブレンドスペース1Dに変数Speedを接続します。
f:id:r-ngtm:20180409205108p:plain

補足: 挙動について軽く説明

キャラクターが速く移動しているときはSpeedが100に近づくので、BlendSpaceのAxis=100.0に登録されたRunモーションの比重が大きくなります。
移動が遅くなるとSpeedが0に近づくのでBlendSpaceのAxis=0.0に登録されたIdleモーションの比重が大きくなります。

遅く移動しているときはIdleモーションが再生され、速く移動しているときはRunモーションが再生されます。

STEP6: キャラクターを移動させてみる

実際にキャラクターを移動させてアニメーションを確認してみます。

まずは BP_Buff_Blackをダブルクリックして開き、「イベントグラフ」タブを開きます。
f:id:r-ngtm:20180409203551p:plain

Tickノードに"Add Movement Input"ノードを接続します。 WorldDirectionには(1,0,0)を入れておきます
f:id:r-ngtm:20180409203732p:plain

この状態で再生を押すと以下のようなRun_FWDモーションが大きくブレンドされた動きになります。
f:id:r-ngtm:20180409210006g:plain


移動速度を下げるとIdleモーションが大きくブレンドされた動きになります
f:id:r-ngtm:20180409210407p:plain
f:id:r-ngtm:20180409210240g:plain

【Unity 設計の話】初心者がやりがちな設計ミスにマサカリを投げてみる

はじめに

Unityでコードを書くとき、自分が普段気を付けていることを自戒の念を込めて記事としてまとめたいと思います。

かなり初歩的な話になるのでご容赦ください。

題材

今回は、1秒ごとに弾を発射する固定砲台を作ることを考えます。
シューティングゲームを作るときになどにおなじみかと思います。

f:id:r-ngtm:20180222124032g:plain:h280

これを実現するためにはいろんな方法が考えられますが、

今回はC#スクリプトを書いて弾を一定間隔で生成させることにします。

悪いコードの例

何も考えずにスクリプトを書くとこんなコードになるでしょうか。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletGenerator : MonoBehaviour
{
     // 弾のPrefabを指定
    [SerializeField] private GameObject bulletPrefab;

    // 弾の発射ロジック
    private IEnumerator Start()
    {
        while(true)
        {
            var bullet = Instantiate(this.bulletPrefab); // 弾を生成

            var rigidbody = bullet.GetComponent<Rigidbody>();
            rigidbody.velocity = new Vector3(0f, 10f, 0f); // 弾を上に飛ばす

            yield return new WaitForSeconds(1f); // 1秒待つ
        }
    }
}


昔の自分はこのようなコードをよく書いていましたが、これは悪いコードです。

なぜ悪いコードなのか。マサカリを投げつつ理由を書き連ねていきたいと思います。

マサカリその1 : PrefabのシリアライズにGameObjectを使うのはやめよう

フィールドのタイプにGameObjectを使えばPrefabを設定することができますが、これは色々な問題を招くのでやめましょう。

問題その1: Prefabの指定ミスが起きるリスク

PrefabのタイプをGameObjectにすると、あらゆるPrefabが指定できてしまいます。

本来は弾のPrefabが入る想定なのに間違えてエネミーのPrefabを指定しまう、といったヒューマンエラーを招きます。

問題その2: コンポーネントへのアクセスが汚い

弾のPrefabにRigidbodyがアタッチされていることを想定し、「弾のRigidbodyコンポーネントにアクセスしたい」という状況を考えます。

PrefabのタイプはGameObjectなので、RigidbodyにアクセスするにはGetComponentすることになります。

var rigidbody = bullet.GetComponent<Rigidbody>();

GetComponentは重いとされているので処理負荷的にこれは良くないコードです。

問題その3:依存関係が強い

このコードでは、弾の速度を設定する際にRigidbodyを直接参照しています。

つまり、「弾の速度を設定したい」という目的に対してコンポーネントのRigidbodyが密に結合しているという関係になっています。
これは設計上良くないです。

改善案: GameObjectの代わりにクラスを指定する。

上記を踏まえて修正したものがこちらです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletGenerator : MonoBehaviour
{
    // 弾のPrefabを指定
    [SerializeField] private Bullet bulletPrefab;

    // Use this for initialization
    private IEnumerator Start()
    {
        while(true)
        {
            var bullet = Instantiate(this.bulletPrefab); // 弾を生成
            bullet.SetVelocity(new Vector3(0f, 10f, 0f)); // 上に飛ばす

            yield return new WaitForSeconds(1f); // 1秒待つ
        }
    }
}


GameObjectの代わりに自作クラスを指定しています。
そして、rigidbodyのvelocityに速度を代入する代わりにメソッドSetVelocityを実行して速度を指定しています。

※ 補足: Bulletクラス内部には速度を指定するメソッドpublic SetVelocity(Vector3 v) が定義されているものとします。

改善点: コードの見通しが良くなった。

速度を設定するメソッドを呼び出していることにより、「速度を設定しているんだな」ということがコードから読み取れるようになっています。

さらに、BulletGeneratorから見てBullet内部のRigidbodyの有無を意識しなくて済むようになりました。

BulletGeneratorでは「弾の速度の設定をしたい」という関心、Bulletでは「速度をどのように設定するか」という関心。
これら二つの関心をきれいに分離することができています。

これをプログラムの言葉で、「関心の分離」 と呼びます。

まとめ

・Prefabのタイプ指定にGameObjectを使うのはやめよう
・メソッドを利用して関心の分離を意識しよう

マサカリその2:数をメソッドの中に直接書くのはやめよう

コードの該当箇所を抜粋しました。

bullet.SetVelocity(new Vector3(0f, 10f, 0f)); // 上に飛ばす

yield return new WaitForSeconds(1f); // 1秒待つ


一般に、数をコードに直接指定するのは良くないとされています。(マジックナンバー)

問題その1: 数での使いまわしが難しい

例えば、以下のような状況に対処するのが難しくなってしまいます。
・弾の移動速度new Vector3(0f, 10f, 0f)を別の場所でも使いたい
・待機時間1fを別の場所でも使いたい

同じ目的の数を2か所以上に書いてしまうと、片方を変更したときにもう片方の変更を忘れてしまう、といったヒューマンエラーが発生します。

ある意味、数と目的が密に結合していると言えます。

問題その2:数の意味が分かりにくい

ソースコードを読んだ人は以下のような疑問が出てくるかと思います。
「new Vector3(0f, 10f, 0f)ってどういう値なの?」
「1fって何?」

数の直接指定は意味が分かりにくく、コードの明瞭性が落ちてしまいます。
これはヒューマンエラーを招きます。

改善案 : 定数を定義する

上記を踏まえて以下のようにコードを修正します。
読み取り専用な定数を定義し、ロジック内では定義した定数を利用しています。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletGenerator : MonoBehaviour
{
    // 弾の移動速度
    static readonly Vector3 BULLET_VELOCITY = new Vector3(0f, 10f, 0f);

    // 弾の発射の間隔
    const float BULLET_INTERVAL = 1f;

    // 弾のPrefabを指定
    [SerializeField] private Bullet bulletPrefab;

    // 弾の発射ロジック
    private IEnumerator Start()
    {
        while(true)
        {
            var bullet = Instantiate(this.bulletPrefab); // 弾を生成

            bullet.SetVelocity(BULLET_VELOCITY); // 速度設定

            yield return new WaitForSeconds(BULLET_INTERVAL); // 待つ
        }
    }
}

定数を利用することで、その数が何を意味するのかが見えやすくなりました。
また、定数の使いまわしもできるようになりました。

まとめ

・コード中に数を直接記述するのは避けて定数を定義しよう。

その他の懸念事項

ほかにも以下のようなことが考えられると思います。

・BulletGeneratorには弾の発射ロジックだけ持たせておいて、弾の発射の実行は別のクラスにさせるべきでは?(単一責任原則
・弾のPrefabはBulletGeneratorに持たせて良いのか? Prefabを管理するクラスを作ってそこで管理するべきなのでは?
・定数はBulletGeneratorに直接持たせて良いのか? 定数を管理するクラスを作ってそこで管理するべきなのでは?
・弾の一定間隔の発射はコルーチンを使わずにUniRxを使うべきなのでは?

ここらへんはプロジェクトによって正解が変わるかと思います。
銀の弾丸は無い

補足: 環境について

Windows 10
Unity2018.1.0b7

UE4未経験者が5日間でインベーダーゲームを作った話

はじめに

UE4未経験な私が5日間で単純なゲームを完成させることができたので、まとめたいと思います。

作ったゲーム

上から迫ってくるエネミーたちを撃ってKILLするゲームです。 いわゆるインベーダーゲーム
f:id:r-ngtm:20180208103213g:plain

全てのエネミーを倒すとクリア
f:id:r-ngtm:20180208103347g:plain

エネミーが撃つ弾に触ってしまうとゲームオーバーです。
[f:id:r-ngtm:20180208103507g:plain

エネミーが下まで来てしまってもゲームオーバーです。
f:id:r-ngtm:20180208104237g:plain

開発環境

Windows 10
UE4.18.3

学習開始時のUE4スキル

UE4の学習を始めた時点での私のUE4スキルを挙げると以下の通りです。
・2年前にUE4インストールしたことがある
・2年前にレベルブループリントを使って画面に"Hello"と表示させたことがある。
UE4ゲームを作ったことは無い

これはUE4未経験といっても差し支えないでしょう。
そんなUE4未経験な私が5日間でインベーダーゲーム完成させるまでの過程を紹介したいと思います。

学習の流れ

以下の流れで学習とゲーム作成を行いました。
・初日~3日目 : UE4の基本を覚える
・4~5日目 : インベーダーゲームを作る

初日~3日目 : UE4の使い方を覚える

最初にやった事はUE4の基本を覚えること。人から勧められた下記の書籍を読みました。

www.borndigital.co.jp

全ての章を読んだ訳ではなく、6章~12章だけを読みました。
また、この書籍にはハンズオン動画が付いており、これを観ながらUE4の学習を進めました。

書籍だけを読むよりハンズオン動画を観たほうが速いです。ハンズオン動画オススメ

覚えたことは全部メモする

今回、一度覚えたことは忘れないようにGoogleスプレッドシート全てメモする、という事をやっていました。

f:id:r-ngtm:20180208105257p:plain

覚えたことを忘れてしまった場合でも、メモを作ってるおかげですぐに思い出すことができます。
調べ直す手間を省略することができます。

メモしたもの

GoogleSpreadシートにメモしたものは主に以下のものです。
・分からないことを調べていた時に見つけたWebサイトのURL
・ショートカットキー
・エディタの操作方法
・ブループリントで使用したノード
・マテリアルで使用したノード
コンポーネント

1回調べた物は2度調べなくていいように全てをメモします。
こうすることで時間を節約することができます。

4~5日目 : インベーダーゲームの開発

6~12章を読み終えたところで基礎をおさえることができました。
4日目からインベーダーゲームの作成に取り掛かります。

プロジェクトの新規作成

テンプレート; "TwinStickExample" を使用して新規プロジェクトを作成しました。

プレイヤーキャラの実装

TwinStickExampleではキーボード操作の上下左右移動が実装されています。
f:id:r-ngtm:20180208112702g:plain

今回はインベーダーゲームを作りたいのでこれを改造し、左右だけに移動するようにしました。
f:id:r-ngtm:20180208113056g:plain

また、弾の上下左右発射が実装されていたのでこれを改造し、マウスボタンを押しているときにだけ上方向に弾を発射するようにしました。

Tips: マウスの入力をとる方法

プロジェクト設定のAxisMappingを利用してマウス入力をとっています。
f:id:r-ngtm:20180208114345p:plain:w300

AxisMappingで設定した入力はブループリント側から取得することができます。
f:id:r-ngtm:20180208114459p:plain:w300

弾を生成するFactoryクラスを用意

弾を生成部分のロジックがかなり複雑なので、ロジックを見やすくするために専用のFactoryクラスを用意することにしました。
f:id:r-ngtm:20180208120706p:plain

中身はこんな感じです。
f:id:r-ngtm:20180208121005p:plain:w400

このFactoryクラスは位置と向きを指定して弾を生成するFireShot関数を持っています。
FireShot関数はTwinStickPawnが持つFireShot関数をベースとして作成しました。

エネミーキャラの用意

TwinStickExampleではプレイヤーが操作する水色の機体が用意されています。
f:id:r-ngtm:20180208112328p:plain

これを複製してマテリアルを差し替え、エネミー用の赤い機体を用意しました。
f:id:r-ngtm:20180208120359p:plain

また、一定時間ごとに弾を下方向に発射するようにロジックを改造しました。
f:id:r-ngtm:20180208121445p:plain

エネミーのFireShow関数の中身はこんな感じ。
f:id:r-ngtm:20180208121610p:plain

Factoryクラスを作ったことおかげで、エネミーの弾の発射ロジックを簡潔に組むことができました。

エネミーのジグザグ移動の実装

エネミーのジグザグ移動の実装にはスプライン曲線を利用しました。

下記URLを参考に作成しました
[UE4] スプライン上を動く足場の作り方|株式会社ヒストリア

壁の用意

インベーダーゲームでは弾を防ぐ壁があるので、これを実装しました。
f:id:r-ngtm:20180208122028p:plain

壁の仕様は以下の通りです
・壁に弾が当たったら弾を消す
・壁にはHPがある
・弾が当たると壁そのもののHPが削れ、サイズも小さくなる
・壁のHPがゼロになったら壁は消滅

詳細は割愛しますが、壁のブループリントのイベントグラフだけ載せておきます。
f:id:r-ngtm:20180208122941p:plain:w400

弾の改造

今回は「弾はプレイヤーのものなのか、エネミーのものなのか」という情報を持たせたいので、TwinStickProjectileには列挙型PawnType型の変数を持たせます。
f:id:r-ngtm:20180208124032p:plain
f:id:r-ngtm:20180208123909p:plain

列挙型PawnTypeにはPLAYERとENEMYの二つを持たせています。
f:id:r-ngtm:20180208124503p:plain:w300
f:id:r-ngtm:20180208124304p:plain:w300

ちなみに、列挙型は自分で作る必要があります。
f:id:r-ngtm:20180208124556p:plain:w300

エネミー死亡判定

エネミーにプレイヤーの弾が当たったときにエネミーが死ぬようにします。
f:id:r-ngtm:20180208123344p:plain

判定時に弾のPawnTypeを見て、弾がプレイヤーのものだった場合にエネミーを破棄するということをやっています。

ゲームオーバー・ゲームクリア表示の用意

ゲームクリア表示・ゲームオーバー表示はWidgetを使っています。
f:id:r-ngtm:20180208125021p:plain

ゲームオーバー時に表示を出す

プレイヤーに弾が当たったときにプレイヤーが死んでGameOverが表示されるようにします。
f:id:r-ngtm:20180208125341p:plain
f:id:r-ngtm:20180208125234p:plain

完成

以上でインベーダーゲームの完成です。

f:id:r-ngtm:20180208103213g:plain

【UE4】経過時間を表示するテキストを画面に出してみる

はじめに

UE4を触っていた時に出てきた疑問。
UIを画面に表示させるにはどうすればいいんだろう? ゲーム内のスコアとか画面に表示させたいな....」

そこで今回はUIを作り方の勉強の一環として、ゲーム開始からの経過時間を表示するテキストを画面に出す方法をまとめてみました。

作ったもの

ゲーム再生からの経過時間を表示するテキストを画面に出してみました。
f:id:r-ngtm:20180207113751g:plain

作成手順

STEP1. Widgetを作成する
STEP2. WidgetにTextを設置する
STEP3. ブループリントを使ってゲーム開始時にWidgetを画面に出す
STEP4. 経過時間をTextへ反映するイベントグラフを組む

STEP1. Widgetを作成する

コンテンツブラウザの右クリックメニューからWidgetを作成します
f:id:r-ngtm:20180207110020p:plain

名前は UI_HUDとしておきます。

f:id:r-ngtm:20180207110107p:plain

STEP2. WidgetにTextを設置する

作成したWidgeアセットを開き、ドラッグ&ドロップでTextを配置します。

f:id:r-ngtm:20180207110756g:plain

Textの名前は HogeText としておきます。

STEP3. ブループリントを使ってゲーム開始時にWidgetを画面に出す

レベルブループリントを開き、以下のイベントグラフを組みます。

f:id:r-ngtm:20180207110337p:plain

ウィジェットを作成ノード」Widgetを作成し、「Add to Viewportノード」を使ってWidgetを画面に追加しています。

確認してみる

この状態でゲームを再生すると、以下のような画面になります。

f:id:r-ngtm:20180207111207p:plain

Widgetが画面に出ていることが確認できます。

STEP4. 経過時間をTextを反映するイベントグラフを組む

次に、経過時間をTextに反映させるようにします。

WidgetHogeTextを選択し、詳細パネルでContent/Textのバインドボタンをクリックして「バインディングを作成」を選択します。
f:id:r-ngtm:20180207111934p:plain

イベントグラフが作成されるので、リターンノードにGet Game Time in Secondsノードを接続します。

確認してみる

この状態でゲームを再生すると、テキストに経過時間が反映されるようになります。

f:id:r-ngtm:20180207112749g:plain

ゲーム内経過時間を表示するテキストを作ることができました。

まとめ

・UIを作るにはWidgetアセットを作成すると良い

Widgetアセットを画面に表示させるにはウィジェットを作成ノード」Widgetを作成し、「Add to Viewportノード」を使ってWidgetを画面に追加する。

Widget表示を動的に変更するにはバインディングを作成すると良い

バインディングで作成した関数のリターンノードに渡したデータはUIに反映される

参考

Unreal Engine | プロパティのバインディング



豆知識: バインディングには変数も設定できる

バインディングにはWidgetのイベントグラフで定義した変数も使用できます。
f:id:r-ngtm:20180207115832p:plain

f:id:r-ngtm:20180207115908p:plain

【UE4】マテリアルのUVスケールをランダム変更するブループリントを組んでみた

最近、UE4をまじめに勉強し始めました。
ぷちこん提出したい。

マテリアルをコンパイルせずに変えたい

ここからが本題ですが、マテリアルを触っていた時の疑問
「マテリアルをコンパイルせずにテクスチャUVスケールを変えるにはどうすればいいんだろう?」

これを解決するために調べながら作ったものをまとめてみます。

今回作ったもの

今回は勉強がてら、マテリアルのテクスチャUVスケールをランダムに変えるブループリントを組んでみました。

f:id:r-ngtm:20180206113424g:plain

環境

UE4.18.3
テンプレート : Twin Stick Shooter

作成手順

STEP1. UVスケールのfloat値を持ったマテリアルコレクションを用意

STEP2. マテリアル内部でマテリアルコレクションのUVスケールを取得してテクスチャに適用

STEP3. ブループリント側からマテリアルコレクションのUVスケール値をランダムに変更

STEP1. マテリアルコレクションの用意

マテリアルに外部からパラメータを渡すにはマテリアルコレクションを使います。

まずは右クリックからマテリアルコレクションを作成。
f:id:r-ngtm:20180206111744p:plain

マテリアルコレクションの名前はMC_Floorとしておきます。
f:id:r-ngtm:20180206111820p:plain

マテリアルコレクションの設定

マテリアルコレクションにはUVスケール値として2つのfloat値 ScaleXScaleYを追加しておきます。

f:id:r-ngtm:20180206112202p:plain

STEP2. マテリアルの作成

右クリックメニューからマテリアルを作成します。
f:id:r-ngtm:20180206121209p:plain

以下のようなマテリアルを組みました。
f:id:r-ngtm:20180206112322p:plain

マテリアルコレクションのScaleXとScaleYを使ってテクスチャをUVスケールしています。

マテリアル解説

左端にある緑色のノードはCollectionParameterノードです。
STEP1で作成したマテリアルコレクションからfloat値を取得しています。

f:id:r-ngtm:20180206121828p:plain

MakeFloat2ノードを使い、取得したScaleXとScaleYからfloat2を作成しています。
f:id:r-ngtm:20180206112607p:plain

テクスチャのスケールを変更するにはTextureCoordinateノードを使用します。
f:id:r-ngtm:20180206114649p:plain

今回は、マテリアルコレクションを使ってスケール値を変更したいので、ScaleUVsByCenterノードを使って 乗算したものをTextureSampleノードのUVsに入力しています。

STEP3. ブループリントを組む

以下のようなイベントグラフを組みました。
f:id:r-ngtm:20180206115409p:plain
1秒ごとにマテリアルコレクションのScaleXとScaleYを 1~2間のランダムなfloatに設定するイベントグラフです。

ブループリント解説

イベントTickノードにDelayノードをつなげることで1秒ごとにイベントが発火するようにしています。
f:id:r-ngtm:20180206115539p:plain

RandomノードにLerpノードをつなげることで、1-2の間のランダムなfloat値を取得しています。
f:id:r-ngtm:20180206115841p:plain

SetScalarParameterValueノードを使い、マテリアルコレクションMC_Floor内部の変数の値を変更しています。
f:id:r-ngtm:20180206120029p:plain

まとめ

・マテリアルをコンパイルせずにパラメータを変更するにはマテリアルコレクションを使う。
マテリアル内部でマテリアルコレクションにアクセスするにはCollectionParameterノードを使う
・TextureCoordinateノードの出力をパラメータで乗算するにはScaleUVsByCenterノードを使う
ブループリントからマテリアルコレクションを変更するときはSetScalarParameterValueノードを使う。

【プレイ感想】モンハンワールドはストレスが少ない

モンハンワールドを買いました。

まだ3時間しかプレイしていませんが感想を。

良かった点

・テンポ感が良くなった
・マップの単調さが減った

悪かった点

・拠点で迷う
・文字が小さすぎて読みづらい

良かった点

今までのモンハン = 採集のテンポが悪い

個人的に今までのモンハンで一番不満だった点は、採集モーションが長く退屈だということ。

私はPSPのモンハン2ndGから入り、NintendoSwitchのモンハンダブルクロスまでプレイしましたが、

その間採集モーションが全く変わっていません。 


おっさんがしゃがんで素材をかき集める採集モーションは数千、数万回は眺めているんじゃないでしょうか。

同じモーションを眺めるのは正直飽きました

おまけに、モーション完了までの時間が地味に長い


採集モーションを眺めるのは退屈で、苦痛だというのが正直なところです。

今回のモンハン = 採集のテンポが良い 

この退屈な採集モーション、モンハンワールドでは短くなります。

そして素材回収も簡略化され、短いモーションを1回(特殊な素材の場合は数回)見るだけで素材の回収が完了します。

テンポ感が格段に良くなりました。


例えば、今までのモンハンではハチミツ5個集める時に採集を5回繰り返す必要がありました。

モンハンワールドの場合、1回のモーションで全てのハチミツを回収します。

素材をサクサク集めることができるので快適です。

今までのモンハン = マップ移動時にロードが入る

今までのモンハンだとマップ移動時にローディングが入っていました。

マップ1からマップ2へ移動するときにロード、 マップ2からマップ3へ移動するときにもロード。

このロード処理、ロード完了を待つのがなかなかの苦痛です。 

今回のモンハン = マップ移動のロードが無い

モンハンワールドを2時間ほどプレイしてようやく気が付いたんですが、なんとこのモンハンワールド、

ロード処理が皆無です。ゼロ回です。

おそらく、クエスト開始時にマップをメモリに展開するか、ゲームプレイ中に裏で動的にマップをロードしているのでしょうか。

ロード処理がなくなったことによってゲームプレイの没入感が上がったように思います。

ストレスが減って素晴らしい。

今までのモンハン = 移動が退屈

これまでのモンハンシリーズでは、マップの高低差が少ない平たいマップであることが多かったです。

つまり、プレイしていても代わり映えがあまり無いマップになっています。


移動時にプレイヤーがやることは、目的地への最短経路をただ走るだけという単純作業。

モンハンを始めたころはまだ良いですが、繰り返しプレイしていると正直飽きます

移動が退屈で、苦痛で仕方がない。

今回のモンハン = 移動が刺激的

今までのモンハンだと高低差のあまり無い、平たい地形だったので地図を見ていれば道に迷うことはまずありませんでした。

迷わないので、退屈


モンハンワールドの場合、高低差が激しく、かなり複雑な地形になっています。

私は地図を見ていても迷いました

地形が複雑になったことにより、冒険をしている感があって楽しい。

マップの要素が増えた

おまけに、モンハンワールドでは天井には仕掛けがあったり、空中にモンスターがいたり、地面では痕跡があったり、といろんな要素がマップのあちらこちらに散りばめられています。


今までのモンハンと比べてマップ移動時にやることが爆発的に増え飽きさせない作りになっていると感じました。

素晴らしい。

悪かった点

拠点で迷う

初回プレイ時、拠点でクエストの受付嬢がどこにいるのかが分からなくなり、1時間ほど迷ってしまいました。

地図を見ても、受付嬢の居場所が載っていない。

ここがちょっと不親切に感じました。

文字が小さい

プレイに支障をきたすレベルで画面の文字が小さいと思いました。
読めない。

設定メニューから文字サイズを大きき変更できるようにしてほしかったと思いました。

目が悪い人には不親切かも。

「メガネを買え」ということなのかもしれません。

【Unity2018.1】Particle Systemを軽く覗いてみた

Unity2018.1のParticleSystemを軽く覗いてみました。

ParticleSystemの機能のうち、気になったものを紹介していきたいと思います。

環境

Unity2018.1.0b2

WIndows 10

機能その1: パーティクル再生終了時に処理を実行する Stop Action

パーティクルの再生終了時処理を実行する機能です

f:id:r-ngtm:20180115190330p:plain

Stop Actionを使うにはMainモジュールのLoopingのチェックを外しておく必要があります。
(チェックが入っていると再生終了しないため)

詳細

Stop Actionの選択項目は4種類あります。

f:id:r-ngtm:20180115190521p:plain

None = 何もしない
Disable = 再生終了時に自身のGameObjectを非アクティブ化
Destroy = 再生終了時に自身のGameObjectを破棄
Callback = 再生終了時に自身のコールバックを呼び出す

コールバックの使い方

下記スクリプトをParticleSystemにアタッチすると再生終了時にOnParticleSystemStopped()が実行されます。(Stop Action = "Callback"の場合のみ)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ParticleTest : MonoBehaviour
{
    void OnParticleSystemStopped()
    {
        Debug.Log("OnParticleSystemStopped");
    }
}

機能その2:パーティクルの初期色にテクスチャを使う

パーティクルのテクスチャを使用して設定できるようになりました。

f:id:r-ngtm:20180115191218p:plain

これを使うとテクスチャから湧き出るようなエフェクトが作れます。

f:id:r-ngtm:20180115184600g:plain


魔法陣エフェクトとかも簡単に作れそう。いろいろ応用がききそうな機能です。

f:id:r-ngtm:20180115185013g:plain