using System.Collections.Generic; using UnityEngine; public class CharacterAttack : MonoBehaviour { [SerializeField] private AttackEffect attackEffect; [SerializeField] private Collider attackCollider; [SerializeField] private int damage = 1; [SerializeField] private float attackDuration = 0.5f; [SerializeField] private float hitStartTime = 0.15f; [SerializeField] private float hitEndTime = 0.3f; // 1回の攻撃で同じ相手に何度も当たらないよう、 // 命中済みの DamageReceiver を覚えておく。 private readonly List _hitReceivers = new(); // 攻撃中かどうか。 // EnemyController など、外側の処理が行動判断に使えるよう公開する。 private bool _isAttacking; // 攻撃判定Colliderが現在有効かどうか。 // Collider.enabled を直接読まず、攻撃状態としてコード側でも保持する。 private bool _isHitActive; // 攻撃開始からの経過時間。 // hitStartTime / hitEndTime / attackDuration の判定に使う。 private float _attackElapsedTime; public bool IsAttacking => _isAttacking; private void Awake() { // 攻撃していない間は、攻撃判定を必ず無効にしておく。 // attackCollider は必須参照なので、設定漏れはエラーとして気づける状態にする。 attackCollider.enabled = false; } private void Update() { if (!_isAttacking) return; _attackElapsedTime += Time.deltaTime; // 攻撃開始から一定時間後に、攻撃判定を有効にする。 if (!_isHitActive && _attackElapsedTime >= hitStartTime) StartHit(); // 攻撃判定の有効時間が終わったら、判定だけを先に閉じる。 if (_isHitActive && _attackElapsedTime >= hitEndTime) EndHit(); // 攻撃全体の時間が終わったら、攻撃状態を終了する。 if (_attackElapsedTime >= attackDuration) EndAttack(); } public void Attack() { // 攻撃中に再度 Attack() が呼ばれても、攻撃を重ねない。 if (_isAttacking) return; // attackEffect は演出用なので、未設定でも攻撃処理は成立させる。 attackEffect?.Play(); // 新しい攻撃なので、前回の命中履歴を消す。 _hitReceivers.Clear(); _isAttacking = true; _isHitActive = false; _attackElapsedTime = 0f; } private void OnTriggerEnter(Collider other) { // 攻撃判定に入ったColliderの親から、ダメージ受付コンポーネントを探す。 var receiver = other.GetComponentInParent(); if (receiver == null) return; // 同じ攻撃中に同じ相手へ複数回ダメージを与えない。 if (_hitReceivers.Contains(receiver)) return; _hitReceivers.Add(receiver); // 攻撃者とダメージ量をまとめ、実際の判定は AttackResolver に任せる。 var attackContext = new AttackContext(gameObject, damage); AttackResolver.Resolve(attackContext, receiver); } private void EndAttack() { _isAttacking = false; EndHit(); } private void StartHit() { _isHitActive = true; attackCollider.enabled = true; } private void EndHit() { _isHitActive = false; attackCollider.enabled = false; } }