213 lines
5.2 KiB
C#
213 lines
5.2 KiB
C#
|
|
using UnityEngine;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 바닥에 떨어진 화살 아이템 (발사체로도 사용 가능)
|
|||
|
|
/// 모드 전환: 아이템 모드 ↔ 발사 모드
|
|||
|
|
/// </summary>
|
|||
|
|
public class ArrowItem : MonoBehaviour
|
|||
|
|
{
|
|||
|
|
[Header("--- 화살 정보 ---")]
|
|||
|
|
[SerializeField] private string arrowName = "기본 화살";
|
|||
|
|
[SerializeField] private float damage = 15f;
|
|||
|
|
[SerializeField] private float speed = 20f;
|
|||
|
|
[SerializeField] private float range = 15f;
|
|||
|
|
|
|||
|
|
[Header("--- UI 표시 (아이템 모드) ---")]
|
|||
|
|
[SerializeField] private GameObject pickupUI;
|
|||
|
|
[SerializeField] private float uiDisplayRange = 3f;
|
|||
|
|
|
|||
|
|
[Header("--- 비주얼 ---")]
|
|||
|
|
[SerializeField] private GameObject visualModel; // 화살 모델
|
|||
|
|
|
|||
|
|
private Transform playerTransform;
|
|||
|
|
private Rigidbody rb;
|
|||
|
|
private Collider col;
|
|||
|
|
|
|||
|
|
// 모드 구분
|
|||
|
|
private bool isItemMode = true; // true: 아이템 모드, false: 발사 모드
|
|||
|
|
private Vector3 startPos;
|
|||
|
|
private bool isFired = false;
|
|||
|
|
|
|||
|
|
private void Awake()
|
|||
|
|
{
|
|||
|
|
rb = GetComponent<Rigidbody>();
|
|||
|
|
col = GetComponent<Collider>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void Start()
|
|||
|
|
{
|
|||
|
|
if (isItemMode)
|
|||
|
|
{
|
|||
|
|
SetupAsItem();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void Update()
|
|||
|
|
{
|
|||
|
|
if (isItemMode)
|
|||
|
|
{
|
|||
|
|
UpdateItemMode();
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
UpdateProjectileMode();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#region 아이템 모드
|
|||
|
|
|
|||
|
|
private void SetupAsItem()
|
|||
|
|
{
|
|||
|
|
// 플레이어 찾기
|
|||
|
|
GameObject player = GameObject.FindGameObjectWithTag("Player");
|
|||
|
|
if (player != null)
|
|||
|
|
{
|
|||
|
|
playerTransform = player.transform;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// UI 숨기기
|
|||
|
|
if (pickupUI != null)
|
|||
|
|
{
|
|||
|
|
pickupUI.SetActive(false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 물리 설정 (바닥에 떨어진 아이템)
|
|||
|
|
if (rb != null)
|
|||
|
|
{
|
|||
|
|
rb.useGravity = true;
|
|||
|
|
rb.isKinematic = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (col != null)
|
|||
|
|
{
|
|||
|
|
col.isTrigger = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void UpdateItemMode()
|
|||
|
|
{
|
|||
|
|
if (playerTransform == null || pickupUI == null) return;
|
|||
|
|
|
|||
|
|
// 플레이어와의 거리 체크 (UI 표시용)
|
|||
|
|
float distance = Vector3.Distance(transform.position, playerTransform.position);
|
|||
|
|
bool isNear = distance <= uiDisplayRange;
|
|||
|
|
|
|||
|
|
pickupUI.SetActive(isNear);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// PlayerInteraction에서 호출
|
|||
|
|
/// </summary>
|
|||
|
|
public void Pickup(PlayerAttack playerAttack)
|
|||
|
|
{
|
|||
|
|
if (playerAttack == null) return;
|
|||
|
|
|
|||
|
|
// 자기 자신을 복제해서 발사용 프리팹으로 제공
|
|||
|
|
GameObject arrowPrefab = gameObject;
|
|||
|
|
playerAttack.SwapArrow(arrowPrefab);
|
|||
|
|
|
|||
|
|
Debug.Log($"화살이 [{arrowName}](으)로 교체되었습니다!");
|
|||
|
|
|
|||
|
|
// 아이템 제거
|
|||
|
|
Destroy(gameObject);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 발사 모드
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// PlayerAttack에서 호출 (발사 초기화)
|
|||
|
|
/// </summary>
|
|||
|
|
public void Initialize(float dmg, float arrowSpeed, float maxRange)
|
|||
|
|
{
|
|||
|
|
Debug.Log($"🏹 화살 발사! 방향: {transform.forward}, 속도: {arrowSpeed}");
|
|||
|
|
|
|||
|
|
// 발사 모드로 전환
|
|||
|
|
isItemMode = false;
|
|||
|
|
isFired = true;
|
|||
|
|
|
|||
|
|
// 스탯 설정
|
|||
|
|
this.damage = dmg;
|
|||
|
|
this.speed = arrowSpeed;
|
|||
|
|
this.range = maxRange;
|
|||
|
|
this.startPos = transform.position;
|
|||
|
|
|
|||
|
|
// UI 숨기기
|
|||
|
|
if (pickupUI != null)
|
|||
|
|
{
|
|||
|
|
pickupUI.SetActive(false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 물리 설정 (발사체)
|
|||
|
|
if (rb != null)
|
|||
|
|
{
|
|||
|
|
rb.useGravity = false;
|
|||
|
|
rb.isKinematic = false;
|
|||
|
|
|
|||
|
|
// ✅ [중요] transform.forward 방향으로 발사
|
|||
|
|
// 화살이 Z축(파란 화살표) 방향을 앞으로 향하고 있어야 함!
|
|||
|
|
Vector3 shootDirection = transform.forward;
|
|||
|
|
rb.velocity = shootDirection * speed;
|
|||
|
|
|
|||
|
|
Debug.Log($"📍 발사 방향: {shootDirection}, 속도 벡터: {rb.velocity}");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (col != null)
|
|||
|
|
{
|
|||
|
|
col.isTrigger = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 5초 후 자동 삭제
|
|||
|
|
Destroy(gameObject, 5f);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void UpdateProjectileMode()
|
|||
|
|
{
|
|||
|
|
if (!isFired) return;
|
|||
|
|
|
|||
|
|
// 최대 사거리 체크
|
|||
|
|
if (Vector3.Distance(startPos, transform.position) >= range)
|
|||
|
|
{
|
|||
|
|
Destroy(gameObject);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnTriggerEnter(Collider other)
|
|||
|
|
{
|
|||
|
|
// 아이템 모드에서는 충돌 무시
|
|||
|
|
if (isItemMode) return;
|
|||
|
|
|
|||
|
|
// 발사 모드: 적 또는 벽 충돌
|
|||
|
|
if (other.CompareTag("Enemy"))
|
|||
|
|
{
|
|||
|
|
var monster = other.GetComponent<MonsterClass>();
|
|||
|
|
if (monster != null)
|
|||
|
|
{
|
|||
|
|
monster.TakeDamage(damage);
|
|||
|
|
}
|
|||
|
|
Destroy(gameObject);
|
|||
|
|
}
|
|||
|
|
else if (other.CompareTag("Wall") || other.CompareTag("Ground"))
|
|||
|
|
{
|
|||
|
|
Destroy(gameObject);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 외부 설정용
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 몬스터 드롭 시 화살 정보 설정
|
|||
|
|
/// </summary>
|
|||
|
|
public void SetArrowData(string name, float dmg, float spd, float rng)
|
|||
|
|
{
|
|||
|
|
arrowName = name;
|
|||
|
|
damage = dmg;
|
|||
|
|
speed = spd;
|
|||
|
|
range = rng;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
}
|