6.Player Health
前回作ったHPバーをもとに、実際にHPの値を設定して、敵に当たったら減っていくようにするぞ。いよいよゲームらしくなってくるで~。
プレイヤーにスクリプトを設定する
PlayerHealthスクリプトをPlayerにアタッチ。このスクリプトには、下記がふくまている。
- 被ダメージ時に画面をフラッシュさせる(Updateメソッド)
- プレイヤー被弾時にSEを鳴らす(TakeDamageメソッド)
- プレイヤー死亡時に死亡アニメ、SEを鳴らす(Deathメソッド)
- シーンをはじめから再生する(RestartLevelメソッド)
一つ一つ確認していきます。
Awakeメソッド
1 2 3 4 5 6 7 8 |
void Awake () { anim = GetComponent <Animator> (); playerAudio = GetComponent <AudioSource> (); playerMovement = GetComponent <PlayerMovement> (); //playerShooting = GetComponentInChildren <PlayerShooting> (); currentHealth = startingHealth; } |
変数にガシガシComponentを突っ込んでいく。特に注意するところはなさそう。
Updateメソッド
1 2 3 4 5 6 7 8 9 10 11 12 |
void Update () { if(damaged) { damageImage.color = flashColour; } else { damageImage.color = Color.Lerp (damageImage.color, Color.clear, flashSpeed * Time.deltaTime); } damaged = false; } |
ここでは、被ダメージ時に画面を点滅させる処理を実行している。damegeImage.colorには、Canvasを作った時に合わせて用意した一枚絵を入れるので、damagedフラグがTrueになるとflashColour変数に入っている赤色に染まるわけ。直後にdamagedフラグはFalseになり、次フレームではelseの中身が実行される。Color.Lerpは、CameraFollowスクリプトで出てきたVector3.Lerpの色版と言っていいもの。Color.Lerp(始まりの色,終わりの色,何パーセント色を変えるか)で、始まりの色から終わりの色まで、徐々にグラデーションしながら色の値を返してくれる。つまり、赤→透明まで、Flashspeed分の時間をかけて戻っていくということ。
TakeDamageメソッド
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public void TakeDamage (int amount) { damaged = true; currentHealth -= amount; healthSlider.value = currentHealth; playerAudio.Play (); if(currentHealth <= 0 && !isDead) { Death (); } } |
ここでは、ダメージを食らったときにプレイヤーのHPを減らし、それをUI上のスライダーにも反映させている。やられたときのうめき声の再生も行う。もしここで、プレイヤーのHPが0を切ったと判定されたときは、次のDeathメソッドを実行する。なおこのメソッドは、この後に見ていくEnemyAttackメソッドから呼び出される。
Deathメソッド
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void Death () { isDead = true; //playerShooting.DisableEffects (); anim.SetTrigger ("Die"); playerAudio.clip = deathClip; playerAudio.Play (); playerMovement.enabled = false; //playerShooting.enabled = false; } |
isDeadフラグを立て、アニメを死亡モーションに遷移するParameterも立てて、死亡時のSEを再生している。ここで勉強になるのは、Audioの再生の仕方。
プレイヤーにアタッチするAudioSourceは一つのまま、コードの中でAudioClipを代入することで先ほどのやられうめき声と死亡時SEと2種類以上のSEを出し分けている。同時に再生するのでもない限り、むやみに配列とか用意しなくていいのでスマートかも?メモリを食うとかそういうデメリットがあるのかな?
RestartLevelメソッド
1 2 3 4 |
public void RestartLevel () { SceneManager.LoadScene (0); } |
今回の動画の中では解説がなかったが、記述されていたコード。おそらくゲームオーバー後に呼び出す用に用意されている。
InspectorからPublic変数を設定
HealthSlider,DamageImage.DeathClipの3つは、D&Dで該当するオブジェクトを持ってきてやる。
エネミーにスクリプトを設定
EnemyAttackスクリプトをZonBuny\nyにアタッチ。このスクリプトも一つ一つ内容を見ていく。
・ZonBunnyのTriggerがプレイヤーと接触したら、プレイヤーを攻撃する
やってることはこれだけ。
Awakeメソッド
1 2 3 4 5 6 7 |
void Awake () { player = GameObject.FindGameObjectWithTag ("Player"); playerHealth = player.GetComponent <PlayerHealth> (); //enemyHealth = GetComponent<EnemyHealth>(); anim = GetComponent <Animator> (); } |
プレイヤーと同じく変数にがしがいComponentを入れていく
OnTriggerEnter、OnTriggerExitメソッド
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void OnTriggerEnter (Collider other) { if(other.gameObject == player) { playerInRange = true; } } void OnTriggerExit (Collider other) { if(other.gameObject == player) { playerInRange = false; } } |
この二つのメソッドはそれぞれ、”TriggerがほかのColliderにぶつかった(離れた)瞬間、それがPlayerオブジェクトであれば、playerInRangeフラグを立てる(折る)”というものである。playerInRangeフラグは次のUpdateメソッドの中で攻撃判定に使用する。
Updateメソッド
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void Update () { timer += Time.deltaTime; if(timer >= timeBetweenAttacks && playerInRange/* && enemyHealth.currentHealth > 0*/) { Attack (); } if(playerHealth.currentHealth <= 0) { anim.SetTrigger ("PlayerDead"); } } |
こちらも要約すると”前の攻撃から十分な時間がたっていて(timeBetweenAttacksが前回攻撃終了から現在までの時間より小さい)、プレイヤーと接触していれば(playerInRangeフラグがたっていれば)Attackメソッドを実行する。プレイヤーのHPが0を切っていれば、エネミーはIdle状態へ移行する(PlayerDeadパラメータを立てる)。”ということである。
Attackメソッド
1 2 3 4 5 6 7 8 9 |
void Attack () { timer = 0f; if(playerHealth.currentHealth > 0) { playerHealth.TakeDamage (attackDamage); } } |
“プレイヤーのHPが残っていれば、PlayerHealthスクリプトのTakeDamageメソッドを実行する。その時、いくつHPを減らすか、の値についてattackDamageの値をわたす”というメソッド。timerをちゃんと0にリセットしておくこと。
実装確認
問題なく動くものの、プレイヤーのHPが0になった後、シーンがロードされて最初に戻る。PlayerHealthスクリプトの一番ケツにあるRestartLevelメソッドをどこかから読んでいると踏んだのだが、それをコメントアウトしても変化なし。謎である。まあ、そのうちわかるか?
今回もほとんど動画を見ただけだったけど以上でおしまい!
第7回はコチラ
第5回はコチラ
「Unity公式チュートリアルSurvival Shooter日本語実践Chapter.6」への2件のフィードバック