テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】スプライトアニメーションの再生速度がやたらと早い時の対処

f:id:tsubaki_t1:20191112185918g:plain

 スプライトアニメーションを今まで通りドラッグ&ドロップで作成した所、やたらとアニメーション再生が早いという事になりました。例えばトップ絵の左のように、速歩きのような速度でアニメーションを再生してしまいます。出来れば右のような速度に設定したい所です。

サンプルレート

 これは単純にアニメーションのサンプルレートが想定より早い事が原因です。Unityの初期設定でスプライトアニメーションを作成する場合サンプルレートは12となっています*1。これは 1秒間に12回キーを打てる という事です。

 例えば2Dスプライトを登録した場合、毎フレーム変化するスプライトが登録されるので、4スプライトで構成されるアニメーションの場合は1スプライト0.3秒で、1秒に3回サイクルが回るアニメーションが再生されます。

f:id:tsubaki_t1:20191112190708j:plain

 これでは速度が早すぎるので、1サイクル1秒で再生したいというのが、今回の趣旨です。

微妙な解決策

 最初に出てくるアイディアは1秒に合わせてアニメーションするようにAnimationClipを調整するというものです。これはサンプルレート的に少しもったいないような気がしなくもないです。またアニメーションをループさせる場合、「最終に登録したスプライトは1サンプル分しか表示されない」問題があるので、秒数側を操作すると少し面倒くさい話になります。

f:id:tsubaki_t1:20191112191314p:plain

 またAnimator側でアニメーション速度を調整するというアイディアがあるかもしれません。速度的に0.3をかければ1秒間にだいたい1回の再生です。これは他のアニメーションとの折り合いを考えたときに少しだけ面倒くさくなります。

f:id:tsubaki_t1:20191112191736j:plain

そうだ、サンプルレートを変えよう

 この問題をスマートに解決するのは、サンプルレートを変更することです。たぶんコレが一番はやいとおもいます。

 問題は、以前にあったサンプルレートの項目が表示されないことです。以前は現在のフレーム数の下あたりにありましたが、現在は表示されなくなっています。サンプルレートは何故か現在は非表示になっており、オプションから表示に切り替える必要があります。

 コンテキストメニュー > Show Sample rate でサンプルレートが表示されます。

f:id:tsubaki_t1:20191112192000j:plain

f:id:tsubaki_t1:20191112192141j:plain

f:id:tsubaki_t1:20191112192407g:plain

~ HAPPY END ~

感想

 なぜ非表示にしたし。コレ見つける前に一瞬Debugモードで編集を考えました。

関連

ぴぽや https://pipoya.net/

*1:普通に作ると60

【Unity】NavMeshComponentsに2D対応ブランチが追加されていたので試してみた

https://user-images.githubusercontent.com/1644563/68397953-e4cd2700-016b-11ea-913b-f062f47bc505.gif

NavMeshComponents

 NavMeshComponentsはNavMeshの機能を拡張するクラス郡です。NavMeshを動的に構築したりアセットに書き出したりといった事を非常に簡単に実現してくれます。エディターからリンクを参照される程度には重要な機能の割にパッケージ化されない謎機能でもあります。

2D ブランチ

 なんとなくブランチ一覧を見た時「2019.3-2D」という妙なブランチを見かけたので早速入れて試した所、Tilemapで作った地形をNavMeshで走らせることが出来ました。追加されているのはNavMeshBuilder2DNavMeshSourceTag2D というクラスで、この機能でナビゲーションを実現します。

f:id:tsubaki_t1:20191107234235j:plain
ブランチ

f:id:tsubaki_t1:20191107234203j:plain
含まれるスクリプト

タイルマップでNavMeshを使用しよう

 タイルマップベースでパスの取得を試してみました。

f:id:tsubaki_t1:20191110171319j:plain

ステージを作成しよう

 まずはタイルマップベースのステージを作成します。使用したのは2D UFO Tutorialです。この中のBackGround.pngに用が合ったので使用しています。現実的な話で言えば、タイルマップなら別に何でも良いです。取得した画像ファイルはタイルのグリッドに合わせて分割します。今回の場合は3x3で9分割です。
 なおスプライトのサイズが大きすぎるかもしれないので、Pixel Per Unitを1000辺りにしました(1000ピクセルが1m)

assetstore.unity.com

f:id:tsubaki_t1:20191110172017j:plain
Background.pngを9分割

 次にCustom Physics Shapeを選択して、タイルのコライダーの形状を変更します。このコライダーは通れる場所に設定するという点に注意する必要があります。通常だと逆(コライダーが無い場所を通れる)ですが、今回の場合はそうなっています。

f:id:tsubaki_t1:20191110172449g:plain
コライダーの設定

 あとはタイルマップを作成していきます。

  1. パレットを作成
  2. パレットにタイルを登録
  3. タイルを作成していく

あとは作成したタイルマップにはTilemap Collider2Dを設定します。これで事前準備は完了です。

https://user-images.githubusercontent.com/1644563/68541238-d6158880-0394-11ea-838f-cc700df166c2.gif

f:id:tsubaki_t1:20191110173535j:plain

NavMeshをベイクしよう!

 NavMeshをベイクします。

 最初に先程作成したTilemap Collider 2Dが付いているグリッドに NavMesh Surface Tag 2D を設定します。 GameObjectを新しく作成し、NavMeshBuilder2Dコンポーネントを追加します。 NavMeshBuilder2DのBakeボタンを押します。

f:id:tsubaki_t1:20191110174103j:plain

f:id:tsubaki_t1:20191110174143j:plain

 実際にベイクできたかは Window > AI > Navigation ウィンドウを開いて確認します。ベイクで来ている場合は移動範囲を青く表現されます。
 ベイクで来たらBake On Enableを設定しておきます。

f:id:tsubaki_t1:20191110174210j:plain

移動範囲を、もっとタイルに沿って配置したい場合は、Agent TypeのHumanoidのRadiusを小さな値にします。

NavMeshによる移動経路を取得しよう

 ナビゲーションの移動経路を取得してみます。これは特に特殊なことはせず、今まで通りの形で取得できます。例えばパスを LineRenderer で表現するコードを考えてみます。

using UnityEngine;
using UnityEngine.AI;

[DefaultExecutionOrder(10)]
public class DrawPath : MonoBehaviour
{
    [SerializeField] LineRenderer line;

    [SerializeField] Transform startPos, endPos;

    private NavMeshPath path;

    void Awake()
    {
        path = new NavMeshPath();
    }

    void OnEnable()
    {
        var result = NavMesh.CalculatePath(startPos.position, endPos.position, NavMesh.AllAreas, path);
        enabled = line.enabled = result;

        if( result )
        {
            var corners = path.corners;
            line.positionCount = corners.Length;
            line.SetPositions(corners);
        }
    }
}

f:id:tsubaki_t1:20191110180406j:plain

f:id:tsubaki_t1:20191110180417j:plain

NavMeshAgentを使用する場合

 NavMeshAgentはパスを3Dの物と認識しているので注意が必要です。NavMeshAgentを使用したい場合、座標だけ同期して実際の描画は別のGameObjectで行う等が楽で良いです。

関連

 トップ絵のキャラクターの表現はコレを使用。移動方向を渡せば良いだけの簡単仕様

tsubakit1.hateblo.jp

 この機能はすごく単純に、NavMeshをベイクする際のポリゴン情報をNavMeshから取得しているだけです。

tsubakit1.hateblo.jp

【Unity】LWRPでポストプロセスを使用しつつUIの手前にパーティクルを表示する

f:id:tsubaki_t1:20191009221926g:plain

PCを新しくしてからの初更新です。

環境:Unity 2019.2f8、LightWeight RP 6.9.1

LWRPはカメラのスタックができない

 いろいろと便利なLWRPですが、一つだけよく言われる致命的な問題があります。 カメラを複数使用して描画する事ができない という点です。 RenderTexture に描画結果を出力して後でまとめるといった事は可能なのですが、今まで使ってきた 複数のカメラを使って描画する事が出来ないのは、非常にもどかしいです。

 特に問題になりやすいのはUIとパーティクルの描画です。 よくある演出として UIの手前にエフェクトを表示する があります。これは大抵の場合「ゲームを描画するカメラ」「UIだけを表示するカメラ」の二つに分けて描画し、「UIだけを表示するカメラ」にパーティクルを描画させることで実現していました。  ただしLWRPになりカメラのスタックが出来なくなったので、専用カメラが使用できません。よって異なる方法を考える必要があります。

 難しいのが、まず ScreenSpaceOverlay は使えません。UIの手前にパーティクルを描画出来ない為です。   また ScreenSpace-Camera でも単純には上手くいきません。単純に処理すればUIがポストプロセスの影響を受けるので、特にカラーコレクションを使用すると盛大に色合いが変わってしまいます。またBloomやDOFの影響を受けるのはひどいです。同様にPlaneDistanceの設置でUIがステージにめり込む事もあり得ます。この設定をいくら小さくしても、手前で爆発するなど何らかの理由でUIよりステージオブジェクトが手前に描画される事があるのは問題でしょう。

f:id:tsubaki_t1:20191009223546p:plain
UIがポストプロセスの影響を受けてしまっているケース

f:id:tsubaki_t1:20191009223652p:plain
UIがステージにめり込んでしまっているケース。ScreenSpace-CameraだとPlaneDistance設定で起こりえる

PostProcessingの後にUIとパーティクルを描画する

 一番手っ取り早いアプローチは Custom Forward Render を使用して、UIとパーティクルを描画する事です。 このアプローチではUIをポストプロセスの影響を受けないようにしつつパーティクルをUIの手前に描画出来ます。また Screen Space Camera を使いつつもステージやキャラクターの後ろにUIが表示されるといった事も回避できます。
 この手順のアイディアは非常に単純で 任意のレイヤーに所属するオブジェクトを、ポストプロセスの後に描画しよう というものです。

 実際の手順はこんな感じです。

 まず最初にUIは ScreenSpace-Camera を使用する必要があります。 CanvasのRender ModeをScreen space-Cameraに設定します。またCanvas GameObjectのレイヤーはUIに設定します。パーティクルのGameObjectもUIに設定します。
 パーティクルをUIの手前に描画したいので、Order in Layerを使用してUIの手前にパーティクルを描画するように設定します。 UIの場合はCanvasに、パーティクルの場合はRendererにあります。

f:id:tsubaki_t1:20191009225200j:plain
Canvasの設定

f:id:tsubaki_t1:20191009225813p:plain
ParticleのOrder In layerを設定する場所

 レンダラー側の設定を行います。まずは Create > Rendering > ***Pipeline > Forward Renderer でCustom Forward Rendererアセットを作ります。その後、Lightweight Renderer Pipeline AssetのRender TypeをCustomに設定し、Custom Forward Rendererアセットを登録します。

f:id:tsubaki_t1:20191009230101p:plain
Custom Forward Rendererを作る

f:id:tsubaki_t1:20191009230506j:plain
Custom Forward Rendererを使用する設定

 次にCustom Forward Rendererの設定を行います。ここは少し手順が多いので、箇条書きします。

  1. Default Layer Mask から UI を外す。
  2. Render Features+ を押す。追加するパスは Rendere Object を選択
  3. NameUIに変更(任意)
  4. EventAfter Rendering に変更
  5. Filters > QueueTransparent に変更
  6. Filters > Layer MaskUI を選択
  7. Override > Depth にチェックを入れ、Depth TestDisabledに変更

f:id:tsubaki_t1:20191009231723g:plain
手順

これでUIの手前にパーティクルを描画出来ました。

UIの間にパーティクルを挟む

 今回の手順では描画順は完全にOrder In Layer順になっています。なのでパーティクルの所属するOrder In LayerをCanvasの間に挟んでやれば、UIとUIの間にパーティクルを挟む事も可能です。

f:id:tsubaki_t1:20191009232807j:plain
UIとUIの間にパーティクルを挟む

注意点

 これはあくまでもパーティクルがTransparentを使用している前提の使い方なので成立しています(つまりDepth Testを使用していない事が重要)。もしUIの手前に複数のSubMeshを持つオブジェクトを描画するといった場合はRenderTextureをオフスクリーンで描画する方が理にかなっていると言えるかもしれません。

 今回の手順はURPでも使用できました。HDRPは不明です。

 ScreenSpace-Cameraを使用する関係上、編集する場合にはPrefabモードが便利です。ただし、その場合ScreenSpace-Overlayになるので(PrefabからScene上のカメラを参照出来ない為)描画順回りは少し混乱するかもしれません。

 カメラのスタックはロードマップには載っているみたいです。現状は未実装なので、このトリックが必要という感じで。

【Unity】LWRPの2Dパイプラインで、ShaderGraphを使ってみる

f:id:tsubaki_t1:20190710212151g:plain

今回はLWRPの2DRendererでShaderGraphを使用してみます。

2Dパイプライン

コチラを参照

tsubakit1.hateblo.jp

Shader Graph

この2Dパイプラインでは、単純なLit/Unlitのシェーダーだけでなく2D用のマスターノードが用意されています。コレを使用すると、スプライトレンダラー向けの表示にもShaderGraphを使用したシェーダー構築が可能になります。

それ以前にもUnlitのシェーダーを使用すれば簡単な表現はできたんですが、Spite Mask等にちゃんと対応したマスターノードが提供されたという感じです。マスターノード、もうちょっと簡単に構築出来れば良いんですが…

f:id:tsubaki_t1:20190710212013j:plain

使ってみる

Shaderを用意する際には、Create > シェーダー > 2D Renderer から作りたいシェーダーを選択します。Litが光源処理に対応したシェーダー、Unlitが対応していないシェーダーです。

あとは作成したシェーダーをダブルクリックでShader Graphを起動、編集していきます。

f:id:tsubaki_t1:20190710212452j:plain

Shader Graphでスプライトを使用する場合、必ず _MainTexのTexture2Dを要求しますので、コレを追加します。下のシェーダーは今までのシェーダーと同じような動きをするシェーダーの例です。

MainTexが無い場合、「Material does not have a MainTex property. it is required for sprite renderer」と表示されます。

f:id:tsubaki_t1:20190710212846j:plain

他のテクスチャ設定ですが、Secondary Textureで設定したテクスチャの名称と一致します。例えばNormalMapのテクスチャを設定した場合、ShaderGraph側でもNormalMapと設定すれば、シェーダー側で使用できるようになります。

なおノーマルマップは普通にSamplerTexture2Dでとってくれば良いです。

f:id:tsubaki_t1:20190710213235j:plain

f:id:tsubaki_t1:20190710213605j:plain

後は色々と頑張って作れば、簡単なエフェクト程度なら割とサクっと作れます。下は試しに作ったスプライトをチェック柄に消す奴(トップ絵の)

f:id:tsubaki_t1:20190710213753j:plain

注意事項

  • Emissiveが効きません。
  • SpriteAtlasを使用すると絵が破綻します
  • 別に2D用ノード必要なくね?…それな