今回は主人公キャラクターが剣を振って敵を攻撃する事が出来るようにしていきます。
前回敵キャラが主人公を攻撃してくるようになりました。

今回の機能を作成すると、
↑のような感じになります。
今回の機能の作成の流れとしては、
Asset Storeで剣の3Dオブジェクトを探してインポートし、主人公に持たせます。
剣にはコライダを設定し、敵キャラとの当たり判定が出来るようにします。
主人公キャラ操作スクリプトCharacterScriptと敵キャラのEnemyスクリプト、それぞれのAnimatorControllerを修正していきます。
CharacterScriptスクリプトやEnemyスクリプト等は以前の記事で作成したものに追加の処理を加えています。
なので今までの記事の流れを知りたい方は

を参照してみてください。
剣をキャラクターの手に持たせる
UnityのAsset Storeから剣をインポートします。
検索でswordというキーワードを入れて無料のBroadSwordを探し、インポートします。
インポートしたら主人公キャラボーンの右手の部分にちょうど持つ感じで位置と角度を合わせます。
前回までの記事で懐中電灯を持たせている方はOffにしておきます。
懐中電灯の記事は

こちらになります。
剣に当たり判定(コライダ)を設定する
主人公に持たせた剣にAdd ComponentからCapsuleColliderを追加し、AttackSwordスクリプトを新しく作り取り付けます。
コライダを剣の周りをカバーするぐらいの大きさにします。
上のように設定しました。
接触判定をするAttackSwordスクリプト
AttackSwordスクリプトはこのCapsuleColliderの接触判定をするスクリプトになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | using UnityEngine; using System.Collections; public class AttackSword : MonoBehaviour { void OnTriggerEnter(Collider col) { if(col.tag == "Enemy") { Debug.Log("敵に当たった"); col.GetComponent<Enemy>().SetState(Enemy.EnemyState.Damage); } } } |
接触した相手がEnemyタグを持つインスタンスだった場合、そのインスタンスにSetStateメソッドの実行をさせます。
この辺りは前回の敵キャラの攻撃の判定と同じになります。
敵キャラクターにはEnemyタグを設定しておきます。
現時点ではEnemyスクリプトのEnemyStateにDamageを作っていないのでエラーが出ますが、後で作りますので置いておいてください。
アニメーターコントローラーに攻撃状態と遷移を作成
主人公の攻撃アニメーションはAsset Storeでwarriorで検索し出てくるWarrior Pack Bundle 2 Freeをインポートしその中で使用されているアニメーションを使用しました。
アニメーションパラメータにTrigger型のAttackを作成し、
アニメーションの遷移はそれぞれの状態からAttackがtrueになった時にAttackへと遷移するように作ります。
遷移はHas Exit Timeのチェックを全部外してください。出ないと攻撃ボタンを押してもなかなか攻撃しません。
Attack→Idleへの遷移は
↑のようにHas Exit Timeにチェックを入れ、SettingsのInterruption SourceをNext Stateにして他の条件が成立したらすぐに遷移するようにします。
すべての状態からAttackへと遷移させてるので、Any Stateを使おうと思ったんですが、思うような処理が出来なかった為やめました・・・・(-.-)
Attackにいる状態でもAttackがtrueになった時にAttack状態に遷移する為かもしれません。
--Any Stateを使う方法がわかりましたので追記します(2017/04/08)--
Any StateからAttackへの遷移部分を選択し、インスペクタを表示します。
↑のようにCan Transition To Selfのチェックを外せばAny StateからAttackへと遷移する際にAttackへの遷移途中は無視されるようになります。
しかしスクリプトで条件なしで攻撃の状態へとさせた場合は遷移途中にAttackがtrueになるので攻撃が終わった後に再度攻撃アニメーションへと遷移します。
ここら辺はやっぱり条件を付けないといけません。
遷移途中やある状態にいる時に遷移させたくない場合は

を参照してください。
-- 追記終了 --
アニメーションイベントを作成する
主人公の攻撃アニメーションにアニメーションイベントを加えます。
アニメーションのイベントも敵キャラの時と同じようにAttackStart、AttackEnd、StateEndの3つを作りました。
アニメーションイベントは

の記事内で触れているので参照してみてください。
アニメーションイベントの受け取りスクリプト
前回主人公キャラクターのアニメーションイベントの受け取りスクリプトをProcessCharaAnimEventという名前で作成しました。
そこに処理を追加します。
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 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class ProcessCharaAnimEvent : MonoBehaviour { private CharacterScript characterScript; [SerializeField] private CapsuleCollider capsuleCollider; private void Start() { characterScript = GetComponent<CharacterScript>(); } void AttackStart() { capsuleCollider.enabled = true; } void AttackEnd() { capsuleCollider.enabled = false; } void StateEnd() { characterScript.SetState(CharacterScript.MyState.Normal); } public void EndDamage() { characterScript.SetState(CharacterScript.MyState.Normal); } } |
アニメーションの再生位置によって当たり判定に使うコライダのOn・Offをしています。
これも敵キャラの攻撃時と同じ処理になります。
主人公のゲームオブジェクトに設定したProcessCharaAnimEventのインスペクタでCapsule Colliderに右手に持たせた剣をドラッグ&ドロップし、剣のコライダを設定します。
アニメーションイベントの問題?
このアニメーションイベントですが、場合によっては呼ばれない事があります。
例えば主人公が攻撃中に敵の攻撃を受けてしまい、主人公の攻撃アニメーションのアニメーションイベント部分までの再生がされず、ダメージアニメーションへ遷移してしまう時です。
AttackEndが呼ばれないとすると敵の手のコライダがオフにならないままになってしまいます。
そんな時はビヘイビアを使用するとうまくいきます。

主人公キャラ操作スクリプトの修正
次に主人公キャラの処理をしているCharacterScriptスクリプトを修正します。
まずは主人公の状態を表す列挙型にAttackを追記します。
1 2 3 4 5 6 7 | public enum MyState { Normal, Damage, Attack }; |
次に攻撃ボタンを押した時の処理をUpdateメソッドに追記します。
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 | void Update() { if (state == MyState.Normal) { // 地面に接地してる時は速度を初期化 if (characterController.isGrounded) { velocity = Vector3.zero; var input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); if (input.magnitude > 0f) { animator.SetFloat("Speed", input.magnitude); transform.LookAt(transform.position + input); velocity += input.normalized * walkSpeed; } else { animator.SetFloat("Speed", 0f); } if(Input.GetButtonDown("Fire1")) { SetState(MyState.Attack); } } } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move(velocity * Time.deltaTime); } |
Input.GetButtonDown(“Fire1”)で攻撃ボタンが押されたかどうか判定しています。
Fire1にはデフォルトでマウスの左クリックが設定されています。
SetStateメソッドは
1 2 3 4 5 6 7 8 9 10 11 | public void SetState(MyState tempState) { if (tempState == MyState.Normal) { state = MyState.Normal; } else if (tempState == MyState.Attack) { velocity = Vector3.zero; state = MyState.Attack; animator.SetTrigger("Attack"); } } |
のように作成しました。
攻撃状態になったらvelocityを0にし、状態変数の更新をした後、アニメーションパラメータのAttackをトリガーして攻撃に遷移させます。
敵キャラクターの修正
敵キャラクターのスクリプトへの追加とAnimatorControllerの追加を行います。
Enemyスクリプトの修正
敵が攻撃を受けた時の処理をEnemyスクリプトに追加します。
まずは敵の状態の列挙型にDamage状態を追記します。
1 2 3 4 5 6 7 8 9 10 | public enum EnemyState { Walk, Wait, Chase, Attack, Freeze, Damage }; |
次にSetStateメソッド内に攻撃を受けた状態を追記します。
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 | // 敵キャラクターの状態変更メソッド public void SetState(EnemyState tempState, Transform targetObj = null) { state = tempState; if (tempState == EnemyState.Walk) { arrived = false; elapsedTime = 0f; setPosition.CreateRandomPosition(); } else if (tempState == EnemyState.Chase) { // 待機状態から追いかける場合もあるのでOff arrived = false; // 追いかける対象をセット playerTransform = targetObj; } else if (tempState == EnemyState.Wait) { elapsedTime = 0f; arrived = true; velocity = Vector3.zero; animator.SetFloat("Speed", 0f); } else if (tempState == EnemyState.Attack) { velocity = Vector3.zero; animator.SetFloat("Speed", 0f); animator.SetBool("Attack", true); } else if (tempState == EnemyState.Freeze) { elapsedTime = 0f; velocity = Vector3.zero; animator.SetFloat("Speed", 0f); animator.SetBool("Attack", false); } else if (tempState == EnemyState.Damage) { velocity = Vector3.zero; animator.ResetTrigger("Attack"); animator.SetTrigger("Damage"); } } |
ダメージを受けたら状態フィールドを変更し、velocityを0にし移動値を初期化します。
アニメーションパラメータのAttackがトリガーされていた場合はResetTriggerでリセットします。
(敵が攻撃中に主人公の攻撃が敵に当たったら敵の攻撃をキャンセルします)
その後SetTriggerでDamageをトリガーします。
敵のAnimatorControllerに追加
次に敵がダメージを受けた時の状態を追加します。
Damage状態には主人公のDamageアニメーションと同じものをつかいました。
Any State→Damageに遷移を繋げ、それぞれの条件はDamageのトリガーがされた時にします。
Damage→Idleの条件では、
↑のようにHas Exit timeにチェックを入れアニメーションが最後まで再生されてからIdleへと遷移させますが、Interruption SourceでNext Stateを設定しているので、他の条件が成立したら遷移するようにしています。
これで攻撃が出来るようになりました。
終わりに
これで主人公キャラの攻撃が出来るようになりました。
次回は武器と懐中電灯等の装備を切り替えられるようにしてみます。