よく使うVisualEffectGraphのノード/ブロック

cistLT Advent Calendar 2021(https://adventar.org/calendars/6520) 三日目の記事です。
UnityでVFXを少し触ったのですが、どこにあるのかわからない機能/効果がよくわからないノードやブロックが多くありました。毎回使ってみたりリファレンス読むのが面倒なので使用頻度が高かったものを置いておきます。

1.時間に関係するノード

Delta Time

()内の前フレームからの経過時間
f:id:NajaNaja:20211203182935p:plain

Total Time

()内のものが生成されてからの経過時間
Total Time(Game)なら0から始まらないためVelocityやPositionに入力すると画面外に飛んでいってしまう。乱数の種や三角関数の時間tとして使う
Total Time(VFX)やTotal Time(Per-Particle)はエフェクト再生からの時間であるため、徐々に加速、変化する表現に利用できる
f:id:NajaNaja:20211203182927p:plain

2.定数、乱数、三角関数、四則演算

float

ほとんどのノードが入力に対応しているので定数を扱うならおすすめ
数値以外の固定値を使いたいときはOparater Inlineから探すとよい
f:id:NajaNaja:20211203182938p:plain

Random Number

設定した範囲の乱数を出力する
f:id:NajaNaja:20211203182921p:plain

Sine/Cosine/Tangent

float型で入力した値を三角関数で計算して返す
波が作りたいならTotalTimeと組み合わせ
球が作りたいならRundom Numberと組み合わせ
f:id:NajaNaja:20211203182924p:plain

Add

入力した数値の和を出力する
f:id:NajaNaja:20211203182932p:plain

Multiply

入力した数値の積を出力する
f:id:NajaNaja:20211203182919p:plain

3.とりあえずつけると見た目が良くなるブロック

Turbulence

Update内に配置するブロック
パーティクルに揺らぎを加える
煙などに使える
f:id:NajaNaja:20211203182930p:plain

Gravity

Updateに配置するブロック
パーティクルに重力を加える
火花や何かの破片などの落ちるものに使える
f:id:NajaNaja:20211203182942p:plain

Liner Drag

Update内に配置するブロック
パーティクルに空気抵抗を加える
減速表現に使える
f:id:NajaNaja:20211203182916p:plain

Force

上記の力の加え方と違うことをしたいときはこれを使う
f:id:NajaNaja:20211203182940p:plain

あとがき

VFXGraphはまだ日本語記事が少なく、初心者には情報が得にくいように感じました。自分はこの記事で紹介したような内容を初心者の時に知りたかったので記事を書いてみました。誰かの助けになれば幸いです。

Unityで2Dシューティングゲームを作るときに考えたこと

大学が一週間休みになったのでその期間で2DSTGを作りました。その時、詰まったところや便利だったものをまとめます。

自機移動

チュートリアルで学んだ通りにVectorによって力を与えて動かしました。…が、動かしてみると、違和感。入力が少し遅れて反映されているように感じます。数分の間、違和感と格闘して気が付きました。Vectorによる加速は物理演算を用いているため、慣性がかかっていることが原因でした。つまり、初速が遅く、徐々に加速していくような挙動を取るのです。3Dゲームを作るのが得意なツールであるUnityでは簡単に物理演算が実装できることが大きな強みですが、市販の"2D"ゲームにおいて、移動に慣性の概念を持つゲームが少なく、それが違和感を生んでいました。

f:id:NajaNaja:20201211203404g:plain
Vector加算の移動とposition加算の移動

Unityにおける移動の実装をいくつか調べましたが、最終的にUnityで位置情報を扱うtransformコンポーネントのpositionの値を直接書き換えることにしました。Unity公式では「非現実的な挙動になる」ために推奨されていませんが…しかし時として非現実的な挙動の方が気持ちいいのがゲームなのでこれでいいんじゃないでしょうか。もっといい方法あれば教えてください。

また、この処理だと、フレーム毎に移動距離が決まっているので実行環境によって移動速度に差ができてしまいます。この問題を解決するにはFixedUpdate関数内に処理を書くか、Time.deltaTimeと定数の積を移動距離とする必要があります。ただし、FixedUpdate関数は液晶のリフレッシュレートによっては誤差が発生し、時折コマ落ちが発生することがあるようです。これは処理の工夫やUnityの物理演算速度をフレームレートよりも高くすることで対処できます。

今回制作するゲームにおいて厳密な物理演算処理は必要ないと考えているのでTime.deltaTimeを使う方法で実装しました。

コルーチン

弾の発射周期をUpdateで管理すると環境によってFPSの違いがあるため弾の発射周期も環境に依存してしまいます。

ここでもTime.deltaTimeを用いて実装する方法を考えていましたが、毎フレーム処理をしなければならない移動と違い、弾の発射は0.2秒に一度などの(フレームレートに比べて)低頻度でしか行いません。60FPSの動作環境だとUpdate関数は約0.017秒毎に呼ばれるため、Update関数に処理を記述すると大部分の処理は時間経過をカウントするだけの処理になってしまいます。そもそもUpdateは重い関数なので必要がないなら呼ばない方がいい関数です。

そこで指定秒数毎に処理を実行できるコルーチンを用いた非同期処理で記述しました。

using System.Collections;
using UnityEngine;

public class weapon1 : MonoBehaviour
{
    [SerializeField] GameObject bullet;
    public bool fire = false;
    void Start()
    {
        StartCoroutine("shot");
    }
    IEnumerator shot()
    {
        //無限ループで入力待ち
        while (true)
        {
            while (fire == true)
            {
                //弾のインスタンスを生成
                Instantiate(bullet, this.gameObject.transform.position, transform.localRotation);
                //発射間隔の時間だけ待機する
                yield return new WaitForSeconds(0.5f);
            }
            yield return null;
        }
    }
}

 

dotween

 友人がすごく便利だといっていたので前々から興味があったdotweenというアセットを導入。下記のような短いコードで敵機のループ移動が書けます。

using UnityEngine;
using DG.Tweening;

public class enemymove : MonoBehaviour
{
    Vector2 move;
    void Start()
    {
        move = new Vector2(0, -5);
        transform.DOLocalMove(move, 4f).SetRelative().SetEase(Ease.InOutFlash, 2).SetLoops(-1).SetLink(gameObject);
    }
}

 
メインはtransform.DOLocalMove(move, 4f)の部分でtransformを4秒かけてmoveのように動かす命令になっています。そこにいろいろなオプションを付けることで理想の動きを実現する感じです。

今回の場合は「相対座標で(SetRelative())」、「移動はinとoutを滑らかに行って戻っての二回行う(SetEase(Ease.InOutFlash,2))」、「無限回行う(SetLoops(-1))」、「ゲームオブジェクトを削除したときは自動でtweenを破棄する(SetLink(gameObject))」といった感じです。

これはtransformによる移動だけでなく、colorやscaleの変更にも使えるため、UIに使うと簡単に見栄えが良くなっていいと思います。処理も軽いらしいのでUnityで動きのあるものを作るときは使い得かもしれません。

感想

チュートリアルや小さい機能を作っているだけでは気にならないような問題が多く勉強になりました。

一週間でどこまでいけるんだろう?という興味と勢いだけで突貫で作ったため設計や画像には目も当てられないようなところも多いです。しかし、課題が明らかになると何を勉強すればいいのかわかっていいですね。

今後はUniRxとか触ってみたいかなぁ…とか思っています。