今回は今までそれぞれのスクリプトで制御していた(武器だけですが・・・)キャラクターに関するパラメータをパラメータスクリプトを作ってそこで管理し、必要に応じてそこからデータを取得し利用するようにしてみます。
今回の機能は以前作成した機能に処理を追加するので、以前作成した機能を知りたい方は
から機能作成の流れを確認してみてください。
↑のような感じになります。
前回作ったChangeEquipですがさっそく改良する必要が出ました。(^_^;)
まずはスクリプトを追加するオブジェクトを変更します。
今までEquipというオブジェクトに設定していましたが、主人公のroot、わたしの場合は「masasi」君に設定します。
配置する位置を変更した事で、スクリプト内の処理にも変更を加えます。
myStatusフィールド宣言と武器の親のTransformを指定するフィールドの宣言を追加します。
1 2 3 4 5 6 7 8 | // キャラクターのステータススクリプト [SerializeField] private MyStatus myStatus; // 武器の親のTransform [SerializeField] private Transform equipTransform; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | void InstantiateWeapon() { equipment++; if (equipment >= weapons.Length) { equipment = -1; } // 今装備している武器を削除 if (equipTransform.childCount != 0) { Destroy(equipTransform.GetChild(0).gameObject); } // 素手ではない時だけ武器をインスタンス化 if (equipment != -1) { // 新しく装備する武器をインスタンス化 var weapon = Instantiate<GameObject>(weapons[equipment]); processCharaAnimEvent.SetCollider(weapon.GetComponent<Collider>()); // サンプルの為、直接武器の位置や角度を設定 if (equipment == 0) { weapon.transform.SetParent(equipTransform); weapon.transform.localPosition = new Vector3(0.002f, 0.035f, 0.065f); weapon.transform.localEulerAngles = new Vector3(315f, 99f, 264f); weapon.transform.localScale = new Vector3(0.03f, 0.1f, 0.03f); } else { weapon.transform.SetParent(equipTransform); weapon.transform.localPosition = new Vector3(0.004f, 0.029f, 0.065f); weapon.transform.localEulerAngles = new Vector3(345f, 275f, 84f); weapon.transform.localScale = new Vector3(1f, 1f, 1f); } myStatus.SetEquip(weapon); } } |
ChangeEquipスクリプトを設定するゲームオブジェクトを変えたので、今装備している武器を削除する処理ではtransformの代わりにequipTransformを指定します。
武器をインスタンス化する時の親もequipTransformにします。
最後にMyStatusスクリプトのSetEquipメソッドで切り替えた武器を渡します(現時点ではMyStatusスクリプトを作成していない為エラーになります)。
これでChangeEquipスクリプトの修正が完了しました。
キャラクターのステータス保持スクリプトMyStatusの作成
主人公キャラクターのステータス管理を行うMyStatusスクリプトを作っていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | using UnityEngine; using System.Collections; public class MyStatus : MonoBehaviour { private int hp; private GameObject equip; public void SetHp(int hp) { this.hp = hp; } public int GetHp() { return hp; } public void SetEquip(GameObject weapon) { equip = weapon; } public GameObject GetEquip() { return equip; } } |
キャラクターのHPと装備している武器の情報を保持しているだけです。
今回はMyStatusは特に使わないので適当な感じです・・・(^_^;)
敵キャラ用のステータススクリプトの作成
敵キャラにも同じようにステータスを管理するスクリプトを設定してみましょう。
主人公も敵キャラも同じようなステータスを持つので共通のHPや攻撃力等をStatusクラスとして作っておき、それを継承してそれぞれのクラスを作った方がいいと思いますが、
個別に作ってしまいます・・・・(^_^;)
EnemyStatusスクリプトを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | using UnityEngine; using System.Collections; public class EnemyStatus : MonoBehaviour { // 敵のHP [SerializeField] private int hp = 3; // 敵の攻撃力 [SerializeField] private int attackPower = 1; public void SetHp(int hp) { this.hp = hp; } public int GetHp() { return hp; } } |
hpとattackPowerはインスペクタで設定出来るようにしています。
hpのSetterとGetterを用意します。
敵の操作をするEnemyスクリプト内では自身のステータスを取得する為のフィールドを宣言し、主人公から攻撃を受けた時に実行するTameDamageメソッド内で自身のhpを減らす為にステータススクリプトを使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public enum EnemyState { Walk, Wait, Chase, Attack, Freeze, Damage, Dead }; // 敵のステータス管理スクリプト [SerializeField] private EnemyStatus enemyStatus; public void TakeDamage(Vector3 attackedPlace) { SetState(EnemyState.Damage); handCollider.enabled = false; var damageEffectIns = Instantiate<GameObject>(damageEffect); damageEffectIns.transform.position = attackedPlace; Destroy(damageEffectIns, 1f); enemyStatus.SetHp(enemyStatus.GetHp() - 1); if (enemyStatus.GetHp() <= 0) { Dead(); } } |
追加分だけを記述しているので、他の処理に追加してください。
敵が死んだときの状態をEnemyStateに追加しています。
TakeDamageではEnemyStatusスクリプトが保持しているHPからダメージ分を減らし、敵のHPが0以下になったらDeadメソッドを呼び出します。
Deadメソッドでは
1 2 3 4 5 | void Dead() { SetState(EnemyState.Dead); } |
SetStateでEnemyState.Dead状態に変更しています。
SetStateメソッドの処理にDeadの処理を追記します。
1 2 3 4 5 6 7 | } else if (tempState == EnemyState.Dead) { animator.SetTrigger("Dead"); Destroy(this.gameObject, 3f); velocity = Vector3.zero; } |
アニメーションパラメータ―のDeadをトリガーして3秒後に敵自身のゲームオブジェクトを削除します。
敵のAnimatorControllerにDead状態と遷移を作成する
敵のAnimatorControllerにDead状態を作成します。
アニメーションパラメータにTrigger型のDeadを作成します。
敵キャラのアニメーターではDeadという状態を作りAny StateからDeadがトリガーされた時に遷移するようにします。
SettingsのCan Transition To Selfのチェックを外しDead状態からDead状態へと遷移しないようにします。
いちおう完成しましたが、敵キャラが攻撃を受けた時に一律でダメージは1に設定してます。
これでは武器を変更しても必ずダメージは1となってしまうので、後々問題になるはずです。
武器のステータス管理スクリプトを作成する
武器固有で攻撃力を保持するようにして、そこから値を取得するようにします。
WeaponStatusというスクリプトを作り武器のプレハブにスクリプトを設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | using UnityEngine; using System.Collections; public class WeaponStatus : MonoBehaviour { public enum WeaponType { Sword, Gun, Other } [SerializeField] private int attackPower; [SerializeField] private int shotPower; [SerializeField] private WeaponType weaponType; [SerializeField] private float weaponRange; public int GetAttackPower() { return attackPower; } public int GetShotPower() { return shotPower; } public WeaponType GetWeaponType() { return weaponType; } public float GetWeaponRange() { return weaponRange; } } |
WeaponStatusの中身は攻撃力を保持し、値を返すメソッドが宣言されているだけになります。
attackPowerは打撃武器として使用した時の力
shotPowerは遠距離攻撃として使用した時の力
weaponTypeは武器の種類。
weaponRangeは遠距離攻撃の武器の射程距離
を表します。次にMyStatusのスクリプトを変更します。
1 2 3 4 5 6 7 8 | [SerializeField] private int hp; // 力 [SerializeField] private int power; private WeaponStatus weaponStatus; |
自身の力と武器のステータスを保持するフィールドを宣言します。
weaponStatusを取得出来るゲッターを用意します。
1 2 3 4 5 | public WeaponStatus GetWeaponStatus() { return weaponStatus; } |
武器のステータスはSetEquipメソッドが呼ばれた時に設定します。
1 2 3 4 5 6 | public void SetEquip(GameObject weapon) { equip = weapon; weaponStatus = equip.GetComponent <WeaponStatus> (); } |
また敵キャラに与えるダメージを計算する為に
1 2 3 4 5 6 | // 自身の力と武器の攻撃力を合わせたダメージ力を返す public int GetAttackPower() { return power + weaponStatus.GetAttackPower (); } |
主人公自身の力と武器の力を合わせたものを返してダメージとします。
剣に設定したAttackSwordスクリプトを変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | using UnityEngine; using System.Collections; public class AttackSword : MonoBehaviour { private MyStatus myStatus; private void Start() { myStatus = transform.root.GetComponent<MyStatus>(); } void OnTriggerEnter(Collider col) { if (col.tag == "Enemy") { var enemyScript = col.GetComponent<Enemy>(); if (enemyScript.GetState() != Enemy.EnemyState.Damage && enemyScript.GetState() != Enemy.EnemyState.Dead) { col.GetComponent<Enemy>().TakeDamage(myStatus.GetAttackPower(), col.ClosestPointOnBounds(transform.position)); } } } } |
剣が敵に当たったら敵の状態がDamage、Dead状態でない時にダメージを与えます。
主人公キャラのステータスMyStatusからGetAttackPowerでダメージ力を取得し、敵のTakeDamageメソッドを呼び出す時に渡します。
敵キャラのTakeDamageメソッド内でダメージを1と固定していた部分を変更します。
1 2 3 4 5 6 7 8 9 10 11 12 | public void TakeDamage(int damage, Vector3 attackedPlace) { SetState(EnemyState.Damage); var damageEffectIns = Instantiate<GameObject>(damageEffect); damageEffectIns.transform.position = attackedPlace; Destroy(damageEffectIns, 1f); enemyStatus.SetHp(enemyStatus.GetHp() - damage); if (enemyStatus.GetHp() <= 0) { Dead(); } } |
ダメージ値を引数として受け取るようにし、1と書いていた場所を主人公キャラに設定してあるMyStatusの攻撃力にします。
これで主人公キャラの攻撃力分が敵の体力から引かれる事になります。
主人公の力を考慮しなくていい場合はAttackSwordスクリプトとWeaponStatusが武器に設定されているので、そこから直接WeaponStatusの攻撃力を取得しても良さそうですね。
敵の攻撃判定用コライダのオフ
最後に一つ修正を加えます。
敵が攻撃中にダメージを受けた場合、アニメーションイベントのAttackEndが呼ばれる前にDamage状態へと遷移してしまい、手の攻撃用のコライダがオフにならない事があります。
これはアニメーターのビヘイビアを使うと出来ますが、まだやっていないので、ダメージを受けた時に手のコライダをオフにする処理を加えます。
Enemyスクリプトに処理を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // 手のコライダ [SerializeField] private SphereCollider handCollider; public void TakeDamage(int damage, Vector3 attackedPlace) { SetState(EnemyState.Damage); handCollider.enabled = false; var damageEffectIns = Instantiate<GameObject>(damageEffect); damageEffectIns.transform.position = attackedPlace; Destroy(damageEffectIns, 1f); enemyStatus.SetHp(enemyStatus.GetHp() - damage); if (enemyStatus.GetHp() <= 0) { Dead(); } } |
手のコライダをインスペクタで設定しておき、TakeDamageメソッドが呼ばれたら手のコライダをオフにするようにします。
各種ステータスの設定
機能を作り終えたのでステータスの設定をします。
まずは武器毎に設定するWeaponStatusの設定です。
上の画像ではWeaponTypeを文字列で設定していますが、列挙型に変更したので選択式で選択出来ます。
次に主人公に設定するMyStatusの設定です。
最後に敵キャラクターに設定するEnemyStatusの設定です。
終わりに
これでキャラクターや武器に自分のデータを持たせる事が出来るようになりました。
実際に敵を攻撃し、敵が倒れる様子や、武器の強さを変更して試してみてください。
主人公側のダメージ処理はやっていませんが・・・同じような感じで出来ると思いますのでやってみてください。