投稿記事

Unityの記事 (7)

水無川旅館 2024/07/15 06:52

【Unity】ポストプロセスの設定を実行時に変更する方法

 こんにちは! 今日は Unity 技術記事を書いてみたいと思います。

 今回のお題はポストプロセスの設定を実行時に変更することです!

 今回の環境は

 前提です!

ポストプロセスって?

 まず大前提として、ポストプロセスというのは以下のように、画作りの最終工程で見映えを良くするシステムです。

ポストプロセスあり

ポストプロセスなし

 ポストプロセスなしというのも、これはこれでありな気がしてきたな((

 とはいえ基本的には、ありのほうがモダンな感じになると思います。

ポストプロセスのエフェクトはどう定義されているの?

 PostProcessing Stack v2 では、 PostProcessEffectSettings というクラスを継承して、エフェクトは作られています。

 もしエフェクトを自作したい場合、このクラスを継承すればOKです!

エフェクトをスクリプトから取得するには?

 このエフェクトをスクリプトから取得するには、 PostProcessProfile クラスの GetSetting または TryGetSettings メソッドを使います。

 また、 PostProcessProfile のインスタンスは、 PostProcessVolume クラスの profile プロパティから取得することができます。

 そして、 PostProcessVolume クラスのインスタンスは、おなじみ GameObjectMonoBehaviourGetComponent 関数を使えば取得できます!

 そこで、仮に PostProcessEffectSettings を継承したエフェクトのクラスを T とすると、以下の形式で設定を変更することができます!

using UnityEngine.Rendering.PostProcessing;

var volume = GetComponent<PostProcessVolume>();
var effect = volume.profile.GetSetting<T>();

// FloatParameter なので .value が必要
// effect.foo.value = 0.5f;

(注意:パラメタは FloatParameter 等で定義されていることが多いです!)

つまり…どういうことだってばよ?

 仮に Unity 標準の Vignette の Intensity を変更したいとすると、以下のようにすればいいのです!

using UnityEngine.Rendering.PostProcessing;

var volume = GetComponent<PostProcessVolume>();
var vignette = volume.profile.GetSetting<Vignette>();

// FloatParameter なので .value が必要
// vignette.intensity.value = 0.5f;

 注意点としては、予め Vignette の intensity のチェックマークをつけておく必要がある、というものがあります。これをつけないと、数値を変更しても反映されません。


↑!!intensity にチェックしておく!!

他のエフェクトのサンプルコードもほしいんですけどーー!!

 さすがにそこまで列挙するのは無理です(w

 他のエフェクトの場合、Unity 標準のエフェクトなら、PostProcessEffectEffectSettings のドキュメント を読んで、必要なクラスやプロパティを見つけます。

 アセットの場合、ドキュメントやコードがついていることが多いと思いますので、そのドキュメントやコードを読みながらって感じです。

 どっちもなかったら……。さすがにそんなことはないと思いたいですねw

 このあたりは、各々アセットごとに対応するしかなさそうです。

DOTween との合わせ技も可能!

 実際には、ポストプロセスをアニメーションさせたいことも多いんじゃないかと思います。 DOTween との併用ももちろん可能です!

 そういうのを駆使すると、このような表現ができるようになります。

おまけ:DOTweenで設定変更するサンプルコード RPGのエンカウント風表現

 フォロワー限定で、 DOTween でポストプロセスの設定を変更するサンプルコードを公開したいと思います…!

 参考にするもよし、そのまま使うもよしです。そのまま使っても、下記のように、RPGで敵にエンカウントした時のような「ズーム」する感じの表現ができます。


↑車の横あたりにエンカウント判定があります

特典コードの利用規約

 特典コードは Mozilla Public License, version 2.0 (MPL 2.0) で提供されています。 MPL 2.0 の日本語参考訳もあります。

 なお、著作権は放棄していません。MPL 2.0 というライセンスで提供します。使用する場合は、クレジット表記・ライセンス表記はお願い致します。

 また無保証です(OSSでよくある条件です)。このコードを使うことで起きたいかなる事態にも責任は負いかねますので、ご了承ください。

フォロワー以上限定無料

無料プラン限定特典を受け取ることができます

無料

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

水無川旅館 2024/07/08 08:03

【Unity】Static Collider 同士は衝突しない問題

 こんばんは。今日は珍しく(?)技術記事を書いてみたいと思います。

 前提として、今回の記事は Unity の 3D (built-in) 環境です。

Static Collider 同士は衝突しない(OnCollision/TriggerEnterなどのメッセージが呼ばれない)

 Unity の Collider にはいくつか種類があります。

参考:衝突の基本 - Unity マニュアル

  • Static Collider - 静的コライダーと呼ばれる種類のコライダー。 Rigidbody がついていないもので、ステージ等、主に動かない「静止した」物体のコライダーです。つまり、普通にオブジェクトに Box Collider 等を設定するとこの種類のコライダーになります。
  • Dynamic Collider - Rigidbody がついており、 Is Kinematic がオフのコライダー。
  • Kinematic Collider - Rigidbody がついており、 Is Kinematic がオンになっているコライダー。

Dynamic Collider と Kinematic Collider をまとめて Rigidbody Collider と呼びます。

 その上で、上記の Static Collider 同士が重なったり触れあっても、 OnCollisionEnter や OnTriggerEnter といったメッセージが呼ばれません。動かない物体だから必要ないよね、という理屈のようです。

アクションゲームで当たり判定を実装するには……

 アクションゲームでは当たり判定・攻撃判定を実装したいですよね。判定は目に見えませんし、 Rigidbody なしで、 Collider のみでやりたいというのは自然な考え方です。

 なお、「武器」のような目に見えるものに Rigidbody をつけるという考え方もなくはないと思いますが、個人的にはあまりおすすめしません。以下の動画が参考になります。

https://www.youtube.com/watch?v=8-NjP4Kn8GY

 閑話休題。

 そうなると、「攻撃する側」ではなく「攻撃を受ける側」に Rigidbody をつけることになるのですが、 Rigidbody は物理演算によってかなり独特な動きをするため、物理演算が不要な物体にまでつけるのは、逆に話が難しくなります。

Character Controller なら(一部)呼ばれるが……

 Character Controller を使うと上記のようなメッセージも呼ばれますが、 CapsuleCollider 相当のコライダーしか使えないので、痒いところに届きません。

 ほとんどの人型キャラクターは Character Controller で実装できると思いますが、用途によってはもっと別の形状にしたい、というのもまた事実。

 そうなると自然にStatic Collider 同士を衝突させたいという話になってくるわけです。

Q. で、どうやるの?

 A. Physics.Overlap*** 関数を使います。

パラメタが難しすぎて解らないんだけど(おこ

 しょうがないにゃぁ……。いいよ。

Box Collider バージョン

using UnityEngine;

var boxCollider = GetComponent<BoxCollider>();
var layerMask = Physics.AllLayers;
var queryTriggerInteraction = QueryTriggerInteraction.UseGlobal;

// results = ヒットした Collider の配列(8 = 最大ヒット数)
var results = new Collider[8];

// count = ヒットした Collider の数
var count = Physics.OverlapBoxNonAlloc(
    boxCollider.gameObject.transform.position + boxCollider.center,
    boxCollider.size / 2.0f,
    results,
    boxCollider.gameObject.transform.rotation,
    layerMask,
    queryTriggerInteraction
);

// for (var i = 0; i < count; ++i) {
//   var collider = results[i];
//   /* collider を使って色々する */
// }

Sphere Collider バージョン

using UnityEngine;

var sphereCollider = GetComponent<SphereCollider>();
var layerMask = Physics.AllLayers;
var queryTriggerInteraction = QueryTriggerInteraction.UseGlobal;

// results = ヒットした Collider の配列(8 = 最大ヒット数)
var results = new Collider[8];

// count = ヒットした Collider の数
var count = Physics.OverlapSphereNonAlloc(
    sphereCollider.gameObject.transform.position + sphereCollider.center,
    sphereCollider.radius,
    results,
    layerMask,
    queryTriggerInteraction
);

// for (var i = 0; i < count; ++i) {
//   var collider = results[i];
//   /* collider を使って色々する */
// }

Capsule Collider バージョン

using UnityEngine;

var capsuleCollider = GetComponent<CapsuleCollider>();
var layerMask = Physics.AllLayers;
var queryTriggerInteraction = QueryTriggerInteraction.UseGlobal;

var capsuleColliderDirection =
    capsuleCollider.direction == 0
        ? Vector3.right
        : capsuleCollider.direction == 1
        ? Vector3.up
        : Vector3.forward;

// results = ヒットした Collider の配列(8 = 最大ヒット数)
var results = new Collider[8];

// count = ヒットした Collider の数
var count = Physics.OverlapCapsuleNonAlloc(
    capsuleCollider.gameObject.transform.position + capsuleCollider.center - capsuleColliderDirection * (capsuleCollider.radius - capsuleCollider.height) / 2.0f,
    capsuleCollider.gameObject.transform.position + capsuleCollider.center + capsuleColliderDirection * (capsuleCollider.radius - capsuleCollider.height) / 2.0f,
    capsuleCollider.radius,
    results,
    layerMask,
    QueryTriggerInteraction.Ignore
);

// for (var i = 0; i < count; ++i) {
//   var collider = results[i];
//   /* collider を使って色々する */
// }

おまけ特典コード

 せっかくなので、フォロワー限定公開でマジで Hijack 内部で使っているコードのうち、汎用的な部分を一部公開したいと思います!

 これは「なに? RigidbodyをつけないとOnTriggerEnterが呼ばれない? それは無理やり使おうとするからだよ。逆に考えるんだ。「自作しちゃえばいいさ」と考えるんだ」という発想で、 OnTriggerEnter相当のメッセージを自作したコンポーネントです。

 これを使うとRigidbodyなしでも、OnTriggerEnterのようなメッセージが使えるようになります!

 そして、これ自体は Hijack に依存しないように書かれています。そのまま使うもよし、参考にするもよしです。

 なお、著作権は放棄していません。MPL 2.0 というライセンスで提供します。使用する場合は、クレジット表記・ライセンス表記はお願い致します。

 また無保証です(OSSでよくある条件です)。このコードを使うことで起きたいかなる事態にも責任は負いかねますので、ご了承ください。

特典コードの利用規約

 特典コードは Mozilla Public License, version 2.0 (MPL 2.0) で提供されています。 MPL 2.0 の日本語参考訳もあります。

おわりに

 こういう技術記事を書くのは久々だったので、良い気分転換になりました。需要があったらまた書きたいと思います!

 OSS 活動は GitHub でやることが多いですが、 Ci-en というのもひょっとしたらありかもしれませんね!?(w

フォロワー以上限定無料

おまけ:秒で使えるコンポーネント

無料

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

1 2 »

記事のタグから探す

月別アーカイブ

限定特典から探す

記事を検索