LoginSignup
2
2

More than 5 years have passed since last update.

[Unity][ParticleSystem]3D座標を起点にuGUI上にパーティクルを発生・収束させる

Last updated at Posted at 2019-03-09

サンプル動画

3Dアクションゲームなどで良くある、
「敵を攻撃したらパーティクルが発生して、UI上のゲージに向かって飛んでゲージが溜まる。」
というものを実装してみました。
(割と需要あると思っているのですが、どこにもサンプルが無かったので自作することに)

サンプルプロジェクト

以下のGithubに上げてあります
https://github.com/madoramu/ParticleHorming
※サンプル動画は別プロジェクトの物なので、上記URLのプロジェクトは少し内容が異なります。

環境

Unity2018.3
WindowsとAndroidで動作確認済み
重要:ScreenSpaceCamera設定のCanvasのみ対応しています

実装方法

サンプルプロジェクトの中身を抜粋して説明していきます。
細かい部分などはプロジェクトを閲覧してください。

メインカメラとUIカメラの用意・設定

メインカメラ

こちらはキャラクターの追尾、およびUI以外の描画を担当。

  • CullingMaskで「UI」を除外する。
  • Depthを「0」にする。
    • UIカメラより低ければおk

UIカメラ

UIの描画のみ担当。

  • メインカメラには絶対映らない場所に移動させる
    • AudioListenerは外しておく
  • ClearFlagsを「Depth Only」にする。
  • CullingMaskで「UI」だけにする。
  • Depthを「10」にする。
    • メインカメラより高ければおk

上記二つのカメラ設定により、メインカメラの描画の後にUIレイヤーの情報が上に描画されます。

キャンバスの用意と設定(重要)

UI表示用・座標変換用で2つのScreenSpaceCamera設定Canvasを用意します。

  • UI表示用にはUIカメラ、座標変換用にはメインカメラをそれぞれ設定する
  • RectTransform以外のパラメーターは全て同じにする事
    • 一応念のため

※何故設定カメラ以外同じパラメーターのCanvasを二つ使用したのかは感想に記載しています。

ParticleSystemの設定

ここは好みに合わせて弄って問題ありませんが、以下の2点は必須です。

  • ParticleSystemオブジェクトのLayerは「必ず「UI」に設定する事
  • UI表示用のCanvas直下に配置する事

今回は以下のように設定しました(プロジェクトから抜粋)
キャプチャ.PNG

「何故World設定か?」

  • 後述のパーティクル発生処理で解説しますが、ParticleSystemそのものを移動させるので、Localだとその移動にパーティクルも追従してしまうため。

「Rederer→Order in Layerについて」

  • 上記画像には映っていませんが、今回はUIより前面に出したかったため値を増やしました。ここはお好みでおk。

パーティクル発生処理

ParticleSystem.Emitを使用して、意図的にパーティクルを発生させることが出来ます。
https://docs.unity3d.com/ja/current/ScriptReference/ParticleSystem.Emit.html

処理の流れ

  • 3D座標からScreenSpaceCanvas上の2D座標に変換
    • ゲームだと主に敵に攻撃が当たった時の、敵座標位置が3D座標になります。
    • テラシュールブログを参考(というかほぼ丸パクリ)にして作成しました。
    • 下記ページの「World Space を Screen Space Cameraへ」です。
    • http://tsubakit1.hateblo.jp/entry/2016/03/01/020510
  • 変換した2D座標にParticleSystemを移動
  • Emitでパーティクル生成。

以下コード抜粋

ParticleHorming.cs
public void CreateParticle()
{
    // 3D空間座標からカメラスクリーン上の座標に変換する
    Vector3 basePos = m_Emit3DTransformList.GetRandom().position;   // GetRandom()についてはListExtensionsを参照
    Vector2 screenPos = m_Camera.WorldToScreenPoint(basePos);

    // カメラスクリーン座標をキャンバス上のローカル座標に変換する
    Vector2 cameraCanvasPos = Vector2.zero;
    RectTransformUtility.ScreenPointToLocalPointInRectangle(m_RaycasterCameraCanvasRectTransform, screenPos, m_Camera, out cameraCanvasPos);

    // 座標確認
    Debug.LogFormat("rectPos{0}", cameraCanvasPos);

    // ParticleSystemを放出位置に移動させてEmit
    m_ParticleSystem.transform.localPosition = cameraCanvasPos;
    m_ParticleSystem.Emit(m_EmitParticleCount);
}

パーティクル更新(収束)処理

普段パーティクルをスクリプト側で操作することは余り無いですが、今回は特定位置に収束させたかったのでググった結果、公式サイトで以下のページが出てきました。
https://docs.unity3d.com/ja/current/ScriptReference/ParticleSystem.GetParticles.html

このページを参考に以下の様な処理で更新させることにしました。

ParticleHorming.cs
// 更新
for(int i = 0; i < m_ActiveParticleCount; ++i)
{
    float rate = (1.0f - m_ParticleList[i].remainingLifetime / m_ParticleList[i].startLifetime);
    rate = Mathf.Pow(rate, m_ReactionDistance); // 指数関数を加えることにより、収束する勢いを変更できるようにしてる
    m_ParticleList[i].position = Vector3.Lerp(m_ParticleList[i].position, m_TargetTransform.position, rate);
}
m_ParticleSystem.SetParticles(m_ParticleList, m_ActiveParticleCount);

今回は指数関数を使って、等速直線移動ではなく少しアレンジをしています。
ここに関しては書き方によって幾通りも表現が出来ると思います。

感想

当初はScreenSpaceCameraのCanvas一つで解決できないか試行錯誤していましたが、キャラを追尾する = Canvasが移動する関係上、パーティクルが毎フレームぶれたり座標がおかしなことになったりした結果、今の形に落ち着きました。
冒頭でも言った通りScreenSpaceOverlay設定のCanvasには対応できないので、結構使いどころは限定的かも。
ですが、ParticleSystem上でパーティクルをいつも通り設定できるメリットは大きいので、個人的には満足度高めです。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2