개요
설명
2일차에 슬라임건의 흡입과 인벤토리 그리고 UI까지 구현했는데 이번에는 나머지 배출기능 구현과 사운드를 적용할 예정임

기능 구현
슬라임건 배출
> 배출 코드(Controller)
[Header("배출")]
[Tooltip("배출력")]
[SerializeField] private float ejectImpulse = 8f;
[Tooltip("배출간격")]
[SerializeField] private float ejectInterval = 0.15f; // 0.15초마다 1개
private InputAction releaseAction;
private void Awake()
{
releaseAction = playerInput.actions["Release"];
}
private void Update()
{
CheckButtons();
Release();
}
//============= 입력 처리 ================
private void CheckButtons()
{
isRelease = releaseAction.IsPressed();
}
//============= 배출 기능 ================
private void Release()
{
if(!isRelease) return;
if (isLookOff) return;
if (Time.time < nextEjectTime) return;
if (GameManager.Unit.game1Player.inventory.TryEject(selectedSlot, muzzle.position, muzzle.forward, ejectImpulse))
nextEjectTime = Time.time + ejectInterval;
else
nextEjectTime = Time.time + 0.05f; // 배출 실패 시 재시도 간격 (조정 가능)
}
> 배출 코드(Game1Inventory)
public bool TryEject(int slotIndex, Vector3 spawnPos, Vector3 dir, float impulse = 8f)
{
if ((uint)slotIndex >= (uint)slots.Length) return false;
if (slots[slotIndex].IsEmpty) return false;
int id = slots[slotIndex].id;
if (!defById.TryGetValue(id, out var def) || def == null) return false;
if (string.IsNullOrEmpty(def.prefabPath)) return false;
Vector3 fwd = (dir.sqrMagnitude < 1e-6f) ? Vector3.forward : dir.normalized;
Quaternion rot = Quaternion.LookRotation(fwd);
GameObject go = GameManager.Resource.Instantiate<GameObject>(def.prefabPath, spawnPos, rot);
if (go == null) return false;
if (go.TryGetComponent<Rigidbody>(out var rb))
{
#if UNITY_6000_0_OR_NEWER
rb.maxLinearVelocity = Mathf.Max(rb.maxLinearVelocity, impulse);
rb.linearVelocity = fwd * impulse;
#else
rb.velocity = fwd * impulse;
#endif
}
slots[slotIndex].count--;
if(slots[slotIndex].count > 0) GameManager.Sound.PlaySFX("Game1/EjectSFX");
if (slots[slotIndex].count <= 0) slots[slotIndex].Clear();
RaiseChanged(slotIndex);
return true;
}
private void RaiseChanged(int i)
{
defById.TryGetValue(slots[i].id, out var def);
GameManager.Event.Publish(EventType.SlotChanged, i, slots[i], def);
}
| ● 설명 |
| - 배출시 Slot에 있는 Slime이 Rigidbody의 힘을 받아 배출되어 날아간다 그리고 한번에 배출되지 않게 하나씩 방출되면서 일정 간격 시간을 주어 머신건처럼 연사로 빠르게 배출되지 않게 설정함 - 슬롯에 Slime이 0보다 클시 배출 바운드가 출력됨 |
> 슬롯 선택 코드(Controller)
// 인벤토리 슬롯 선택 (0~3)
private int selectedSlot = 0; // 0~3
private float nextEjectTime = 0f;
private void Update()
{
CheckButtons();
HandleSlotHotkeys();
}
//============= 인벤토리 슬롯선택 기능 ================
private void HandleSlotHotkeys()
{
var kb = Keyboard.current;
if (kb == null) return;
if (kb.digit1Key.wasPressedThisFrame || kb.numpad1Key.wasPressedThisFrame)selectedSlot = 0;
else if (kb.digit2Key.wasPressedThisFrame || kb.numpad2Key.wasPressedThisFrame)selectedSlot = 1;
else if (kb.digit3Key.wasPressedThisFrame || kb.numpad3Key.wasPressedThisFrame) selectedSlot = 2;
else if (kb.digit4Key.wasPressedThisFrame || kb.numpad4Key.wasPressedThisFrame) selectedSlot = 3;
GameManager.Event.Publish(EventType.SelectSlot, selectedSlot); // 슬롯 선택 이벤트 발행
}
> 슬롯 선택 코드(InventoryUI)
private void OnEnable()
{
GameManager.Event.Subscribe<int>(EventType.SelectSlot, SelectUIAnime);
}
private void OnDisable()
{
GameManager.Event.Unsubscribe<int>(EventType.SelectSlot, SelectUIAnime);
}
// ========= UI 애니메이션 ==========
private void SelectUIAnime(int i)
{
switch (i)
{
case 0:
slot1.transform.DOScale(new Vector3(1.2f, 1.2f, 1),0.1f);
slot2.transform.localScale = new Vector3(1f, 1f, 1);
slot3.transform.localScale = new Vector3(1f, 1f, 1);
slot4.transform.localScale = new Vector3(1f, 1f, 1);
break;
case 1:
slot2.transform.DOScale(new Vector3(1.2f, 1.2f, 1), 0.1f);
slot1.transform.localScale = new Vector3(1f, 1f, 1);
slot3.transform.localScale = new Vector3(1f, 1f, 1);
slot4.transform.localScale = new Vector3(1f, 1f, 1);
break;
case 2:
slot3.transform.DOScale(new Vector3(1.2f, 1.2f, 1), 0.1f);
slot2.transform.localScale = new Vector3(1f, 1f, 1);
slot1.transform.localScale = new Vector3(1f, 1f, 1);
slot4.transform.localScale = new Vector3(1f, 1f, 1);
break;
case 3:
slot4.transform.DOScale(new Vector3(1.2f, 1.2f, 1), 0.1f);
slot2.transform.localScale = new Vector3(1f, 1f, 1);
slot3.transform.localScale = new Vector3(1f, 1f, 1);
slot1.transform.localScale = new Vector3(1f, 1f, 1);
break;
}
}
| ● 설명 |
| - 키보드 숫자패드 1,2,3,4 버튼에 바인딩하여 숫자버튼을 누를시 selectedSlot 변수에 0~3까지(총4개) 변수가 선택되며 선택된 변수에 따라 슬롯이 결정됨 - 선택된 슬롯을 DOtween 애니메이션을 통해 스케일이 변경되는 애니메이션이 출력됨 |

버그수정

> Case1
| 발생 | 몬스터 새로운 프리펩 모델 적용이후 흡입이 되질 않음 |
| 문제 | 몬스터 프리펩의 자식오브젝에 Collider가 있는데 하위 오브젝트는 물리접촉 적용이 되질않음 |
| 해결 | Collider를 최상위 부모오브젝트에 옮기고 수정해서 접촉가능하게 만듬 |
> Case2
| 발생 | 배출시 슬롯에는 슬라임이 없는데 사운드가 계속 출력됨 |
| 문제 | 슬롯의 슬라임이 없을때에 대한 사운드 출력 조건 자체가 없었음 |
| 해결 | 슬롯에 슬라임이 0보다 많은시에만 출력되도록 if조건문을 추가하여 해결함 |
> Case3
| 발생 | EventManager를 통해 이벤트 등록하려는데 매개변수 에러가 남 |
| 문제 | 기존 EventManager를 사용시 매개변수가 두개이상은 등록이 되질 않음 |
| 해결 | 제네릭 변수를 추가로 하는 Overloading으로 함수를 추가적으로 생성해서 해결함 |
마치며.
플레이어 움직임은 없고 슬라임 또한 움직임은 없고 오로지 '슬라임건'만 핵심적으로 분석해서 기능구현을 3일동안 치뤘는데
개인적으로 설정한 구현성공여부를 살펴보며 이번 슬라임랜처_슬라임건 기능 구현을 성공적으로 마무리 하도록 하겠음

'연구노트' 카테고리의 다른 글
| [슬라임랜처] 슬라임건 기능구현-2 (0) | 2026.02.19 |
|---|---|
| [슬라임랜처] 슬라임건 기능구현-1 (1) | 2026.02.18 |
| 연구노트_기존 사운드 매니저 방식 수정 (0) | 2026.02.13 |