2026-03-31 물체 상호작용

This commit is contained in:
2026-03-31 18:08:26 +09:00
parent 71dfbf1af2
commit 902b1b76fb
141 changed files with 11063 additions and 343 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4a0919c1b07731240838f8afcc713de2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
using UnityEngine;
public interface IInteractable
{
public void InteractOpen();
public void InteractClose();
public void InteractExec(PlayerCharacterController player);
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8a75892d4d295c54e88d2933e38776c4

View File

@@ -0,0 +1,37 @@
using UnityEngine;
using UnityEngine.AI;
public class InteractableSit : MonoBehaviour,IInteractable
{
private bool interactionOnOff = false;
private void Update()
{
if(interactionOnOff)
{
//메인카메라를 기준으로 좌표 변환
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position + Vector3.up * 0.5f);
//변환된 좌표로 InteractionBox 이동
GameManager.Instance.InGameUI.Interaction.UpdateSitBoxPos(pos);
}
}
public void InteractOpen()
{
interactionOnOff = true;
GameManager.Instance.InGameUI.Interaction.OnOffSitBox(true);
}
public void InteractClose()
{
interactionOnOff = false;
GameManager.Instance.InGameUI.Interaction.OnOffSitBox(false);
}
public void InteractExec(PlayerCharacterController player)
{
player.transform.position = gameObject.transform.position;
player.transform.rotation = gameObject.transform.rotation;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 36992c9e66ae1344bb485ca8bce2aa5b

View File

@@ -42,7 +42,6 @@ public class Item : ScriptableObject
//ItemEffectType.RECOVERY_HP
public float RecoveryHP;
public float RecoveryHPTime;
//ItemEffectType.INTERVAL_DAMAGE 일 경우
public float IntervalDamage;
public float IntervalDamageTime;

View File

@@ -25,6 +25,7 @@ public class InputManager : MonoBehaviour
public event Action<InputState> OnDodgeEvent;
public event Action OnNormalAttackEvent;
public event Action OnHeavyAttackEvent;
public event Action OnInteractionEvent;
//키조작
@@ -34,6 +35,7 @@ public class InputManager : MonoBehaviour
public event Action OnKeyDown_IKeyEvent;
private void Awake()
{
if (Instance == null)
@@ -102,7 +104,11 @@ public void SetCharacterInputMap(string mapName)
BindActionCharacter("Dodge", OnDodge);
BindActionCharacter("NormalAttack", OnNormalAttack);
BindActionCharacter("HeavyAttack", OnHeavyAttack);
BindActionCharacter("Interaction", OnInteraction);
BindActionCharacter("OnkeyDown_IKey", OnKeyDown_IKey);
_characterInputActionMap.Disable();
@@ -211,6 +217,12 @@ private void OnHeavyAttack(InputAction.CallbackContext ctx)
{
OnHeavyAttackEvent?.Invoke();
}
private void OnInteraction(InputAction.CallbackContext ctx)
{
if (ctx.started)
OnInteractionEvent?.Invoke();
}
#endregion
#region

View File

@@ -1,6 +1,8 @@
using System.Collections;
using System.Threading;
using System.Threading.Tasks;
using Unity.VisualScripting;
using Unity.VisualScripting.Antlr3.Runtime;
using UnityEngine;
using UnityEngine.SceneManagement;
@@ -13,22 +15,40 @@ public void OnSceneLoaded(Scene scene, LoadSceneMode mode)
public void ItemUse(ItemInstance item)
{
Debug.Log("아이템 사용");
switch(item.Data.ItemEffectType)
{
case ItemEffectType.RECOVERY_HP:
{
Debug.Log("HP회복 작동!!");
//RecoveryHPHealthEffect(GameManager.Instance.Level.CurrentCharacter.GetComponent<Health>(), item.Data.IntervalDamage, item.Data.IntervalDamageTime, item.Data.ItemEffectVisual);
Debug.Log($"전 HP : {GameManager.Instance.Level.CurrentCharacter.GetComponent<Health>().CurrentHP}");
RecoveryHPHealthEffect(GameManager.Instance.Level.CurrentCharacter.GetComponent<Health>(), item.Data.RecoveryHP, item.Data.ItemEffectVisual);
Debug.Log($"후 HP : {GameManager.Instance.Level.CurrentCharacter.GetComponent<Health>().CurrentHP}");
}
break;
case ItemEffectType.INTERVAL_DAMAGE:
{
Debug.Log("틱데미지 아이템 타입임");
IntervalDamageHealthEffect(GameManager.Instance.Level.CurrentCharacter.GetComponent<Health>(), item.Data.IntervalDamage, item.Data.IntervalDamageTime, item.Data.ItemEffectVisual);
}
break;
}
}
public void RecoveryHPHealthEffect(Health health,float recoveryHP,GameObject itemEffectVisual)
{
PlayerHealth playerHealth = health as PlayerHealth;
if (playerHealth != null)
{
GameObject fx_Visual = Instantiate(itemEffectVisual, playerHealth.transform); // health의 자식으로 이펙트 생성
fx_Visual.transform.localPosition = new Vector3(0, 1.5f, 0);
playerHealth.ChangeHP(Mathf.Clamp(playerHealth.CurrentHP + Mathf.FloorToInt(recoveryHP), 0, playerHealth.Pstat.MaxHp)); //소수점 전부 버림
}
}
public void IntervalDamageHealthEffect(Health health,float damage,float time,GameObject itemEffectVisual)
{
IntervalDamage(health, damage,time, itemEffectVisual);
@@ -37,17 +57,39 @@ public void IntervalDamageHealthEffect(Health health,float damage,float time,Gam
//일정 시간 동안 틱대미지 일으키는 함수
public async void IntervalDamage(Health health, float damage,float time, GameObject itemEffectVisual)
{
// 유니티 오브젝트 자체의 CancellationToken 가져오기 (오브젝트 파괴 시 자동 취소)
CancellationToken token = health.destroyCancellationToken;
GameObject fx_Visual = Instantiate(itemEffectVisual, health.transform); // health의 자식으로 이펙트 생성
fx_Visual.transform.localPosition = new Vector3(0, 1.5f, 0);
float tickDamage = damage / time;
while(time <=0)
Debug.Log($"damage: {damage}");
Debug.Log($"time: {time}");
Debug.Log($"tickDamage: {tickDamage}");
Debug.Log($"CurrentHP : {health.CurrentHP}");
try
{
health.ChangeHP(Mathf.FloorToInt(tickDamage)); //소수점 전부 버림
if (health.CurrentHP <= 0) break;
time--;
await Task.Yield();
while (time > 0)
{
Debug.Log($"진입");
health.ChangeHP(health.CurrentHP - Mathf.FloorToInt(tickDamage)); //소수점 전부 버림
if (health.CurrentHP <= 0) break;
time--;
Debug.Log($"중독 HP : {GameManager.Instance.Level.CurrentCharacter.GetComponent<Health>().CurrentHP}");
await Task.Delay(1000, token);
}
}
catch (System.OperationCanceledException)
{
Debug.Log("데미지 루프가 취소됨");
}
finally
{
// 정상 종료되든, 취소되든(Exception) 이펙트는 확실히 제거
if (fx_Visual != null) Destroy(fx_Visual);
}
Destroy(fx_Visual);
}
}

View File

@@ -108,9 +108,14 @@ public void OnSceneLoaded(Scene scene, LoadSceneMode mode)
InputManager.Instance.OnAimToggleEvent += CurrentCharacterController.AimToggleInput;
InputManager.Instance.OnLookEvent += CurrentCharacterController.LookInput;
InputManager.Instance.OnDodgeEvent += CurrentCharacterController.DodgeInput;
//공격매핑
//InputManager.Instance.OnNormalAttackEvent;
//InputManager.Instance.OnHeavyAttackEvent;
//기타매핑
InputManager.Instance.OnInteractionEvent += CurrentCharacterController.InteractInput;
//UI매핑
InputManager.Instance.OnKeyDown_IKeyEvent += GameManager.Instance.InGameUI.InventoryToggle;

View File

@@ -3,8 +3,9 @@
public class InGameUIManager : BaseUIManager
{
public SplitWindowUI SplitWindowUI;
public TooltipUI TooltipUI;
public InteractionUI Interaction;
public SplitWindowUI SplitWindow;
public TooltipUI Tooltip;
public Transform DragCanvas;
public GameObject InventoryRoot;
@@ -17,7 +18,7 @@ public void VisibleCrossHair(bool isOn)
public SplitWindowUI GetSplitWindowUI()
{
return SplitWindowUI;
return SplitWindow;
}
public void InventoryToggle()

View File

@@ -5,6 +5,7 @@
using UnityEditor.Experimental.GraphView;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.InputSystem;
using static Unity.Cinemachine.CinemachineSplineDolly;
using static UnityEngine.Rendering.DebugUI;
@@ -75,6 +76,10 @@ public enum PlayerRotationMode {CameraCoupled, CameraDecoupled}
public PlayerStat PlayerCharacterStat{ get; set; }
private Renderer[] _renderers;
//상호작용
public SphereCollider InteractionCollider;
public List<IInteractable> InteractionTargets = new List<IInteractable>();
//무기
//[SerializeField] private Weapon _weapon;
@@ -127,6 +132,9 @@ private void Update()
TickTimer();
//PlayerDebug();
Debug.Log($"InteractionTargetsCount : {InteractionTargets.Count}");
}
private void FixedUpdate()
@@ -395,6 +403,13 @@ private void JumpAction()
}
#endregion
#region
private void PointSitAction()
{
}
#endregion
#region
private void TickTimer()
{
@@ -516,6 +531,15 @@ public void AimToggleInput(InputState inputState)
}
}
public void InteractInput()
{
if (InteractionTargets.Count > 0)
{
IInteractable target = InteractionTargets[0];
target.InteractExec(this); // 실제 상호작용 실행
}
}
public void LookInput(Vector2 lookInput)
{
_lookInput = lookInput;
@@ -562,4 +586,27 @@ public void SetCursorLockState(bool isLocked)
Cursor.visible = !isLocked;
}
#endregion
private void OnTriggerEnter(Collider other)
{
// 상호작용 객체인지 확인
if (other.TryGetComponent<IInteractable>(out IInteractable interactable))
{
Debug.Log($"interactableName : {interactable}");
interactable.InteractOpen();
InteractionTargets.Add(interactable);
return;
}
}
private void OnTriggerExit(Collider other)
{
// 상호작용 객체인지 확인
if (other.TryGetComponent<IInteractable>(out IInteractable interactable))
{
interactable.InteractClose();
InteractionTargets.Remove(interactable);
}
}
}

View File

@@ -2,7 +2,9 @@
public class PlayerHealth : Health
{
[SerializeField] private PlayerStat _pstat;
[SerializeField] PlayerStat _pstat;
public PlayerStat Pstat { get { return _pstat; } }
void Start()
{

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 887b7a57494d12e408e866f712b84e7e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,31 @@
using TMPro;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.UIElements;
public class InteractionUI : MonoBehaviour
{
public TextMeshProUGUI NameText; //대화창 이름
public TextMeshProUGUI DialogText; //대화창 내용
public GameObject DialogPopup; // 대화창
[SerializeField] private GameObject _sit_Interaction_Box;
[SerializeField] private GameObject _pushHard_Interaction_Box;
public void UpdateSitBox(Transform sitTransform) //앉는 위치를 아이콘이 따라 다니도록
{
if (sitTransform == null) return;
Vector3 pos = Camera.main.WorldToScreenPoint(sitTransform.position + sitTransform.up * 0.5f); //아이콘 위치
}
public void OnOffSitBox(bool isOn) //앉기 아이콘 온오프용
{
if (_sit_Interaction_Box != null)
_sit_Interaction_Box.gameObject.SetActive(isOn);
}
public void UpdateSitBoxPos(Vector3 pos)
{
_sit_Interaction_Box.transform.position = pos;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 401e7ae784ecbac46af9332f1347cd78

View File

@@ -76,7 +76,7 @@ public void OnEndDrag(PointerEventData eventData)
{
if (slot.currentItem != null && slot.currentItem.Data != null)
{
GameManager.Instance.InGameUI.TooltipUI.ShowTooltip(slot.currentItem, slot.GetComponent<RectTransform>());
GameManager.Instance.InGameUI.Tooltip.ShowTooltip(slot.currentItem, slot.GetComponent<RectTransform>());
}
}
}

View File

@@ -151,12 +151,12 @@ public void OnPointerEnter(PointerEventData eventData)
if (eventData.dragging)
{
_highlightBox.gameObject.SetActive(false);
GameManager.Instance.InGameUI.TooltipUI.HideTooltip();
GameManager.Instance.InGameUI.Tooltip.HideTooltip();
return;
}
_highlightBox.gameObject.SetActive(true);
GameManager.Instance.InGameUI.TooltipUI.ShowTooltip(currentItem, GetComponent<RectTransform>());
GameManager.Instance.InGameUI.Tooltip.ShowTooltip(currentItem, GetComponent<RectTransform>());
}
}
@@ -169,19 +169,19 @@ public void OnPointerMove(PointerEventData eventData)
if (eventData.dragging || !RectTransformUtility.RectangleContainsScreenPoint(_rectTransform, eventData.position, eventData.pressEventCamera))
{
_highlightBox.gameObject.SetActive(false);
GameManager.Instance.InGameUI.TooltipUI.HideTooltip();
GameManager.Instance.InGameUI.Tooltip.HideTooltip();
return;
}
_highlightBox.gameObject.SetActive(true);
GameManager.Instance.InGameUI.TooltipUI.ShowTooltip(currentItem, GetComponent<RectTransform>());
GameManager.Instance.InGameUI.Tooltip.ShowTooltip(currentItem, GetComponent<RectTransform>());
}
}
public void OnPointerExit(PointerEventData eventData)
{
_highlightBox.gameObject.SetActive(false);
GameManager.Instance.InGameUI.TooltipUI.HideTooltip();
GameManager.Instance.InGameUI.Tooltip.HideTooltip();
}
public void OnPointerClick(PointerEventData eventData)