3DCG覚書とかとか

参考書のすみっこ

~普段メモとかしても忘れちゃうのでブログにしました~

目指せUnityマスター[2週目]

Q.何この記事? A.新卒Webエンジニアが、Unityマスターになるまでの過程を描いていく記事。
二週間続いた(*’ω’ノノ゙☆パチパチ

今回の目玉は、RigidbodyのAddforceがどう作用してるのか、理論で考えてみたことかな。

今週やったこと

Create with Code

learn.unity.com

先週と同じ、今回はUnit3だけ。

Unit3 - Sound and Effects

タイトルの通り、効果音とかパーティクル使ってエフェクト付けたりした。

ゴール

・Rigidbodyを使ったオブジェクトの移動
・Audio clip/Sourceを使った効果音&BGMの再生
・Animatorのフラグをスクリプトから制御
・Particleを使ったエフェクトの再生
こいつらができるようになる事かな?あとは背景ループさせたりとかの小ネタ。

Lesson3.1 Jump Force

private Rigidbody rb;
private jumpForce = 10.0f;
void Start(){
    rb = GetComponent<Rigidbody>();
}
void Update(){
    if(GetKeyDown(KeyCode.Space)){
        rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        //rb.AddForce(Vector3.up * jumpForce);    //こうしても動くには動く。
    }
}

上みたいな感じで、スペースバーを押したらジャンプするようにした。

この時気になったのが、AddForceでどれくらいの力が与えられてるのかだった。
仮にこのAddForceの引数でF(N)力を与えているのだとすれば、
まず簡単な方の運動方程式が、

F(N)=m(kg)a(m/(s2)) ∴a=F/m

そんで、高さyを求める式は初速をv0とすると、

y=v0t-(1/2)gt2

AddForceで与えられる加速度をa0、力が与えられる時間をΔtとするとv0は、

v0 = a0Δt

よって最終的に高さを求めるには、

y=(a0Δt)t - (1/2)gt2

これを頭に入れておいて、実際に引数に値を入れて調べてみると、
・二番目の引数無し → Δtがわからず(というかこの場合固定じゃない気がする)当てはまらなかった。
・二番目の引数がImpulse → Δt=1(s) とするとバッチリ当てはまった。

二番目の引数にForceMode.Impulseを入れた時の処理は、理論と実際に引数を当てはめてUnityで動かしてみた時を比べた。

    rb.AddForce(Vector3.up * 10,ForceMode.Impulse);

この時

a=F/m=10/1 ∴a0=10(m/s2)

Δt=1,v0=a0Δt ∴v0=10(m/s)

y=10t-(1/2)gt2

このとき放物線の頂点は速さが0になるところだから、g=9.8≒10(m/s2)
v=v0-gt=10-gt=0 ∴t=1

t=1の時
y=10-5=5

で、理論値では放物線の頂点は5になるから、これをUnityで調べたら、

position.x : 5.xx

ぐらい上がって、g=9.8で考えればちょうど理論値と同じように力が加わっていることが分かった。
だから、試算するときはΔt=1で考えればできるってことだ(そんなことしないと思うけど。)

あと加速度aがn倍になると、yの頂点はn2倍になる(aがn倍=頂点を求めるときtがn倍=yは(1/2)at2だから)
から、これを使って、
・質量1(kg)で一番目の引数10(N) → 5m ∴質量0.1(kg)で一番目の引数10(N) → 500m (a=F/m)
っていうふうに概算できそう。いやしないが。

Lesson3.2 Make the World Whiz By

ここでは、
・背景画像を滑らかに動かす方法
・タグを使って衝突判定を分ける方法

背景画像は同じ画像を横に2枚つなげたような画像が用意されていて、それにBoxColliderをつけるとぴったりくっついてくれる。
それを使って、

//BackgroundImageRepeater
private Vector3 startPosition;
private float repeatWidth;
void Start(){
    startPosition = transform.position;
    repeatWidth = GetComponent<BoxCollider>().size.x / 2;    //背景画像に着けたBoxColliderの半分の幅を取得
}
void Update(){
    //背景画像がstartPositionからrepeatWidthだけ動いたら、背景画像をstartPositionに戻す
    if(transform.position.x < startPosition - repeatWidth){
        transform.position = startPosition;
    }
}

こんな感じに書いたら滑らかに動くようになった。

タグを使って衝突判定を分けるには、

gameObject.CompareTag("TagName");    //自オブジェクトがTagNameという名前のタグを持っていたらtrue
collision.gameObject.CompareTag("TagName");    //自オブジェクトに衝突したオブジェクトがTagNameという名前のタグを持っていたらtrue

//gameObject.tag == "TagName" でも可
//gameObject.name == "ObjectName" なんてのもあるらしい

こんな感じのメソッド使ってif文使えば分けられる。

※3Dのコライダは、「動いてる方にRigidbodyが追加されていないと衝突判定にならない」から、衝突判定をさせるための条件がそろってるのに衝突しない時は、動いてる方にRigidbodyを追加して確かめた方が良い。

Lesson3.3 Don't Just Stand There

AnimatorであらかじめAnimationブレンドとかステートとか設定されているものの、フラグをスクリプトから制御したりした。

フラグとか閾値でステートが切り替わる、具体的には
・歩く(speed<0.5) → 走る(0.5<speed)
みたいにして、スクリプトからspeedフラグを0.5よりも大きくしてやったりする。

ただここはアニメーションはこんなので制御するよっていうイントロみたいなもんだろうから、特記事項はない。

Lesson3.4 Particles and Sound Effects

ここでは、
・Particleを使ったゲームオーバー&砂煙エフェクトの追加
・AudioClip/AudioSouceを使った効果音とBGMの追加
をした。

ParticleはInspectorから操作できるんだけど、やたら項目が多いから眩暈が...。
動きとしては3Dモデルのシミュレーションとかと同じで、アニメーションをPlayすると動き始めて、Stopすると止まる。
※3Dモデルとして使うのではなく、あくまでシミュレーションとして使うイメージ。

private ParticleSystem particle;
void Start(){
    //スクリプトを適用したオブジェクトの子オブジェクトを検索して、パーティクルコンポネントを取得
    particle = transform.Find("ChildObjectName").GetComponent<ParticleSystem>();
}

transform.Find()で子オブジェクトの検索になるのは、直感的じゃないから違和感があった。
でもまぁ、GameObject.Find()はstaticで、transform.Find()はインスタンスからじゃないと取得できないから棲み分けはできてると思う。

AudioClip/AudioSourceは、常時流れるBGMはMainCameraにAudioSourceコンポネントを追加してAudioClip欄に音楽はめればいいだけ。
効果音は、鳴らしたいオブジェクトにAudioSourceコンポネントくっつけて、そのAudioClip欄にスクリプトから音源をとっかえひっかえして、鳴らすイメージ。

private AudioSource playerAudio;
private AudioClip jumpSound;
void Start(){
    playerAudio = GetComponent<AudioSource>();
    jumpSound = Resources.Load<AudioClip>("jump1");    //ProjectのResourcesフォルダから音源jump1ファイルを取得
}
void Update(){
    if(GetKeyDown(KeyCode.Space)){
        playerAudio.PlayOneShot(jumpSound, 1.0f)    //jumpSoundを1.0f秒間、1回だけ鳴らす。
    }
}

こんな感じに実装した。

Challenge3 Balloons, Bombs, &Booleans

ここでは今までにやったことを復習しながら、間違いを訂正するだけかな?ちょっと調べたのが、
・オブジェクトを弾ませる
ことかな。

オブジェクトを弾ませるには、今までにやった方法だと"地面についた瞬間に上向きの力を発生させる"とかで実現できそうだけど、今回はUnityの機能で実装してみた。
といっても、

1.ProjectでPhysicsMaterialを生成
2.DynamicFriction(動摩擦力:動いている時の摩擦力)やStaticFriction(静止摩擦力:止まっている時の摩擦力>動摩擦力)と、Bounciness(反発係数)を指定する。
※反発係数が1になったら初期地点まで弾みそうなものだが、そうでもなかった。そのため物理の反発係数とはちょっと違いそう。
3.弾ませたいColliderのMaterial欄に、PhysicsMaterialを追加する。

これだけでできた。気軽にできるから、ボールとかを扱う時は試してみると良い。

振り返り

今週はちょっと進みが遅かったかも?まぁでもその分深く身についたような気がする。
別で「すっきりわかるJava入門」読んでて、RPGとか作ってみたいなぁとか思ってたりするけど、いきなりRPGは難易度高い気もするから、このチュートリアル終わったらくそげー何本か作って鳴らさないとな~。

一応の目標では、「ミニゲーム → パズルゲーム → 2Dアクション → RPG」みたいな感じで作っていきたいなぁとか思ってる。

余談だけど最近デュラララ(アニメ)見始めて、面白いんだけど愛がどうのって意味不明(悪い意味ではなく)で凄い気持ち悪くてエピソード10~12ぐらいまで鳥肌たちまくってた(断じて悪い意味ではない)。
いくら納得がいかないといっても、ここまで反応を起こすのはいったい何が原因なんだろうか。まぁでもやっぱり続きが気になるので最後まで見ようと思う。

あ、あとUnity勉強してて思ったのが、C#ジェネリックちゃんと理解しててよかったなぁと思った。
自分で実装したことはないけど、ジェネリックだらけやんけ...。

今週もお疲れ様です( ´_ゝ`)