private void FixedUpdate() { if (IsDead) { return; } // 공격 시작 또는 공격 중일 경우, 대상을 향해 Y축을 기준으로 회전시킨다. if (state == State.AttackBegin || state == State.Attacking) { float targetAngleY = Quaternion.LookRotation(targetEntity.transform.position - transform.position).eulerAngles.y; targetAngleY = Mathf.SmoothDamp(transform.eulerAngles.y, targetAngleY, ref turnSmoothVelocity, turnSmoothTime); transform.eulerAngles = Vector3.up * targetAngleY; } // 공격 중이라면, 공격범위 내 적들을 감지하여 공격 대상일 경우 데미지 처리를 한다. if (state == State.Attacking) { Vector3 dir = transform.forward; float deltaDistance = agent.velocity.magnitude * Time.deltaTime; // Physics.OverlapSphere는 해당 범위가 너무 빨리 움직일 경우 이동 경로에 있는 오브젝트가 감지되지 않는 경우가 있다. // SphereCast 사용 시 시작 지점과 끝 방향을 입력하여, 해당 경로로 구(sphere)가 이동한다고 가정하여 경로상의 모든 오브젝트를 감지한다. // * NonAlloc: 감지된 물체의 RaycastHit 배열을 반환하지 않고 우리가 입력해준 배열을 사용한다. 배열에 입력된 원소 개수를 반환한다. // ㄴ 매 프레임 배열을 새로 만드는 작업을 하지 않아도 되어 메모리에 이점이 있다. 참조 타입인 배열을 입력하므로 ref는 필요없음. int hitCount = Physics.SphereCastNonAlloc(attackRoot.position, attackRadius, dir, hits, deltaDistance, whatIsTarget); for (int i = 0; i < hitCount; i++) { LivingEntity hitEntity = hits[i].collider.GetComponent <LivingEntity>(); if (hitEntity != null && !lastAttackedTargets.Contains(hitEntity)) { DamageMessage dmg = new DamageMessage(); dmg.amount = _damage; dmg.attacker = gameObject; dmg.hitNormal = hits[i].normal; // SphereCast가 시작되는 프레임에서 바로 겹쳐있는 대상이 있다면, 거리 및 위치값이 0으로 나온다. // 따라서 이런 경우 타격 지점은 0이 아니라 이 오브젝트의 공격 지점으로 설정해준다. dmg.hitPoint = (hits[i].distance <= 0f) ? attackRoot.position : hits[i].point; hitEntity.ApplyDamage(dmg); lastAttackedTargets.Add(hitEntity); break; } } } }