[Unity] Unityちゃんのサンプルプロジェクトをしらべてみる3

導入

かっこいいエフェクトとか、演出したいんだけどどこから手をつけていいか
全然わからかったので、とりあえず動かせて、ソースコードが全部公開されてる、
なおかつかっこいいサンプルプロジェクトを探してました。

で、公開されたのは結構前みたいですが、Unityちゃんが踊るサンプルプロジェクトが
公式で公開されていたので、これを参考にしてみることにしました。

サンプルプロジェクトの動かし方とか、そういうのは配布ページ等を参照してください。

※前提としてこれを書いてる人は、プログラムとかはある程度仕事でやってるけど
3DCGやらUnity歴一ヶ月弱程度で、理解が追いついていない部分が多々あるかともいます。。。
なにか間違ったことを言っていればTwitter等で教えていただけれると喜びます!

前回は

StageDirector内のobjectsNeedsActivationがもつConfettiという
Prefabについて解説しました。

もうひとつ、Stageのほうを解説していきます
(要素多すぎて書き始めの現段階ではどれくらい長くなるかわかりませんが。。。)

前提として、MonoBehaviourのライフサイクル

参考リンク: Execution Order of Event Functions

Stage

とりあえずPrefabを利用して、Scene上にインスタンス化してみます。

Screen Shot 2016-03-05 at 13.24.17

StageというPrefabは子に以下のようなObjectをもっています。

  • Orbit Inner
  • Orbit Outer
  • Stage Effect Group
  • Stage Lights Group
  • Stage Object Group

簡単そうなやつから順番に説明していきます。

Stage Lights Group

(そのままですが)ステージに対するライトのグループ

このグループは4つのライトをもってます。

  • Main Light (メイン) (真っ白)
  • Reflection (おそらく床の反射) (青っぽい光)
  • Side Light (黄色っぽい光)
  • Side Light (黄色っぽい光)

レイヤーとライトのCullingMaskはStageというものに設定されていて
stageレイヤーに所属するライトにのみ影響が出るように
設定がされています。

多分レイヤーごとに作りこんでPrefabにして、Sceneではスクリプトで読み込む、
というのが全体の方針

Stage Effect Group

  • Laser Gun (オブジェクトから発射されるレーザ)
  • Sonic Boom (スピーカーがぼわぼわするやつ)

Stage Effect Groupには

子をActiveにする、だけ

Laser Gun

Leser GunにはSpawner.csがアタッチされています。
これは以前にも登場したReaktionというパッケージで提供されているクラスで
指定したポイントから指定したPrefabをランダムに出してくれるもの。
(ソース載せるの長いしGithubにあるので)

PrefabにはLaserが指定されています。
これはFBXで書きだされたモデルとアニメーションをインポートしたものに
いくつかスクリプトを足した感じです。
(かっこいいエフェクトとか作りたいとき、自分でモデルも作れないとダメっすね><)

Laserプレハブには以下のスクリプトがアタッチされています。

  • SelfDestration.cs
    • これもReaktionに含まれるスクリプト。でもそんなに長くないから読んでみます。

自分自身を設定した値に応じて破棄するスクリプトです。

  • 距離(Distance)
  • 範囲(Bounds)
  • 時間(PerticleSystem)
  • ParticleSystem(パーティクルシステムが動作しているか)

でDestructionを自動で制御しています。

更にその子にも同名のLaserというオブジェクトが存在していて
そちらにはJitterMotion.csというスクリプトがアタッチされています。
(これもReaktion製)

ポジションの移動と回転を与えます。
Laserに対しては回転のみ、っぽいですね。
起動した瞬間ぐる!っとしてる。

ちなみにInspectorとかは、Reaktionのほかのスクリプトで整形されています。
(JitterMotionEditor.csとか)

Sonic Boom

スピーカーから、ボワっっとなるエフェクト

SonicBoomオブジェクトは4つのRingというオブジェクトをもっています。
このRingというオブジェクトにはAnimationControllerと
AnimatorGear.csというスクリプトがアタッチされています。

それが以下。

Reaktionのクラスでどういう処理をしてるか、は省略します。

設定値は(Ring1を例に)

  • Reactor : Bind By Name
  • Name : Spectrum1
  • Trigger : True
  • TriggerName : Bang

それでRing1のAnimatorControllerには、どういう設定がされているか、というと
こんな感じ

Screen Shot 2016-03-06 at 18.31.05

BangというTriggerが有効になったときだけ、Largeのアニメーションが動作する、という仕組みになってます。

更にRingはstage_ringEffectというオブジェクトをもっていて、これがメッシュに実体になっています。
ここでその中のCylinderオブジェクトのシェーダーが以下

以前見てきたConfettiシェーダとほとんど同じですが
_Amplitudeというパラメータでスカラ値を受け取って、_Emissionとの積を
オブジェクトのEmissionに設定しています。
(Emissionの効き具合をスカラ値で設定できる感じ)

このUntextured Emission Surfaceというシェーダはこのプロジェクトで多用されています。

次にみるのは同じ階層にあるRingというオブジェクト

これはCylinderにアタッチされているMeshRendererではなく
SkinnedMeshRendererが適用されています。

これは何か、というと

参考リンク Unity-スキンメッシュレンダラー

インポートしたモデルのメッシュがスキニングされている場合に適用されるらしい。
(※ぼくもわかってないけど、要するにボーンアニメーションさせるためのMeshRenderer、みたいな?)

Orbit Outer/ Orbit Inner

基本的にはスケールと一部のパラメータが違うだけで、同じもの。

Orbit Innerを元に説明していきます。

Orbit Innerには以下の2つのスクリプトがアタッチされています。

  • ConstantMotion.cs
  • MaterialGear.cs

MaterialGear.csはAnimatiorGearと同じ。
Animatorの特定のTriggerを有効にするのではなく、特定のMaterialの
値をReaktorのオブジェクトから受け取った値に応じて変化させるものです。

OrbitInnerの場合は、黄緑色の部分を音楽に合わせて発光量を増減させるために利用しています。

ConstantMotion.csは、以下の通り

回転か、移動かをずっと繰り返しつづけるためのスクリプトになってます。

その子にあたるorbitFX1_coreにアタッチされているVariableMotion.csは
ConstantsMotion.csとは少し違っていて、一定の範囲内での移動の繰り返しになってます。


public class VariableMotion : MonoBehaviour
{
// Options for transfomation.
public enum TransformMode {
Off, XAxis, YAxis, ZAxis, Arbitrary, Random
};

<pre><code>// A class for handling each transformation.
[System.Serializable]
public class TransformElement
{
// Basic transform and time parameters.
public TransformMode mode = TransformMode.Off;
public AnimationCurve curve = AnimationCurve.Linear(0, -1, 1, 1);
public float amplitude = 1;
public float speed = 1;

// Used only in the arbitrary mode.
public Vector3 arbitraryVector = Vector3.up;

// Affects amplitude and time scale.
public float randomness = 0;

// Randomizer states.
Vector3 randomVector;
float randomAmplitude;

// Time parameter.
float time;

public void Initialize()
{
randomVector = Random.onUnitSphere;
randomAmplitude = Random.value;
time = 0;
}

// Get a vector corresponds to the current transform mode.
public Vector3 Vector {
get {
switch (mode)
{
case TransformMode.XAxis: return Vector3.right;
case TransformMode.YAxis: return Vector3.up;
case TransformMode.ZAxis: return Vector3.forward;
case TransformMode.Arbitrary: return arbitraryVector;
case TransformMode.Random: return randomVector;
}
return Vector3.zero;
}
}

// Advance the time parameter.
public void Step()
{
time += Time.deltaTime * speed;
}

// Get the current scalar value.
public float Scalar {
get {
var amp = amplitude * (1.0f - randomAmplitude * randomness);
return curve.Evaluate(time) * amp;
}
}
}

// Transformation elements.
public TransformElement position = new TransformElement();
public TransformElement rotation = new TransformElement{ amplitude = 90 };

// Scale options.
public TransformElement scale = new TransformElement{
curve = AnimationCurve.Linear(0, 0, 1, 1),
arbitraryVector = Vector3.one
};
public bool scaleByShader = false;
public string scalePropertyName = "_Scale";

// Options for applying transformations.
public bool useLocalCoordinate = true;
public bool useDifferentials = false;

// Transformation history.
Vector3 previousPosition;
Quaternion previousRotation;
Vector3 initialScale;

// Rewind the animation.
public void Rewind()
{
// Initialization.
position.Initialize();
rotation.Initialize();
scale.Initialize();

// Store the initial states.
previousPosition = position.Vector * position.Scalar;
previousRotation = Quaternion.AngleAxis(rotation.Scalar, rotation.Vector);
initialScale = transform.localScale;

// Apply the initial transform.
ApplyTransform();
}

void OnEnable()
{
Rewind();
}

void Update()
{
// Advance the time parameters.
position.Step();
rotation.Step();
scale.Step();

// Apply the transforms.
ApplyTransform();
}

void ApplyTransform()
{
// Position and rotation.
var p = position.Vector * position.Scalar;
var r = Quaternion.AngleAxis(rotation.Scalar, rotation.Vector);

if (position.mode != TransformMode.Off)
{
if (useDifferentials)
{
if (useLocalCoordinate)
transform.localPosition += p - previousPosition;
else
transform.position += p - previousPosition;
}
else
{
if (useLocalCoordinate)
transform.localPosition = p;
else
transform.position = p;
}
}

if (rotation.mode != TransformMode.Off)
{
if (useDifferentials)
{
var dr = r * Quaternion.Inverse(previousRotation);
if (useLocalCoordinate)
transform.localRotation = dr * transform.localRotation;
else
transform.rotation = dr * transform.rotation;
}
else
{
if (useLocalCoordinate)
transform.localRotation = r;
else
transform.rotation = r;
}
}

previousPosition = p;
previousRotation = r;

// Scale.
if (scale.mode != TransformMode.Off)
{
var so = (useDifferentials && !scaleByShader) ? initialScale : Vector3.one;
var s = Vector3.Scale(so, Vector3.one + scale.Vector * (scale.Scalar - 1));
if (scaleByShader)
GetComponent<Renderer>().material.SetVector(scalePropertyName, s);
else
transform.localScale = s;
}
}
</code></pre>

}

Stage Object Group

これは長くなったので次回にします。

ライセンス

この記事の内容は以下のライセンスで提供されます

© Unity Technologies Japan/UCL

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です