다음은 Q 키를 통해 핫바에 있는 아이템을 바닥으로 뱉는 기능.
우선 핫바로 아이템을 버리려면 선택 슬롯을 마우스 휠로 편하게 돌려야한다.
Input.mouseScrollDelta를 통해 선택 슬롯을 변경한다.
다음은 Q키를 눌렀을때 아이템이 있다면 IV_DropItem 프리팹을 통해 새로 드롭 아이템을 생성.
이후에는 핫바 슬롯에 있는 아이템 갯수를 1개 줄인다.
if (!Inventory.gameObject.activeSelf)
{
Vector2 WheelValue = Input.mouseScrollDelta;
if (WheelValue.y < 0)
{
Inventory.AddSelectedSlot();
}
else if (WheelValue.y > 0)
{
Inventory.ReduceSelectedSlot();
}
}
if (Input.GetKeyDown(KeyCode.Q))
{
if (Inventory.HotbarSlots[Inventory.SelectedSlotIndex].IsItemExist)
{
IV_DropItem NewDrop = Instantiate(DropItemPrefab, this.transform.position, Quaternion.identity, this.transform.parent).GetComponent<IV_DropItem>();
NewDrop.Init(Inventory.DeepCopyItem(Inventory.HotbarSlots[Inventory.SelectedSlotIndex].CurrentItem.ItemNum, 1));
switch (PlayerSeeing)
{
case ViewWay.F:
NewDrop.Droped(Vector3.down);
break;
case ViewWay.B:
NewDrop.Droped(Vector3.up);
break;
case ViewWay.R:
NewDrop.Droped(Vector3.right);
break;
case ViewWay.L:
NewDrop.Droped(Vector3.left);
break;
}
Inventory.HotbarSlots[Inventory.SelectedSlotIndex].ReduceCount(1);
Inventory.HUDSynchronize();
}
else
{
Debug.Log("버릴 아이템이 존재하지 않습니다.");
}
}
여기서 처음으로 아이템 복사 버그가 등장했는데...
이유는 얕은 복사와 깊은 복사 때문이다.
우선 C에는 포인터라는 놈이 있다.
말하자면 변수 상자가 아니고 변수 상자를 가리키는 주소를 저장하는 놈이다.
주소를 가져다 쓰게되면 일을 반복할 필요가 없어서 포인터를 쓴다는데 그건 모르겠고 C#에는 포인터가 없다(만세!!)
아니 그럼 포인터를 알아야할 이유가 뭐가 있죠???
그건... C#이 포인터처럼 주소를 저장해버리는 바람에 복사가 제대로 안되는 문제가 있기 때문입니다.
우리가 평상시에 int a = b; 를 실행할 경우 b의 값을 복사해 a 상자에 넣어준다.
(즉 a=/=b 이다)
하지만 class의 경우에는 조금 다르게 C#이 그 클래스(스크립트)의 주소를 저장해버린다...!
class a = b; 를 실행할 경우 b 클래스의 주소를 복사해 a에 넣어준다.
(즉 a==b 이다)
그럼 a와 b에 저장된 클래스는 동일해서 a에서 값을 수정하면 b도 동일하게 수정되어버린다...!
위 그림처럼 상자에 아이템이 저장되는 게 아니라 원본 Item.cs의 주소가 저장되어
접근하려고 하면 원본 Item.cs로 자동으로 전화연결을 시켜버린다.
이것이 얕은 복사.
그럼 어떻게 해결해야하는가.
new를 통해 완전히 새로운 상자를 따로 만들고
내부의 값을 하나하나 각각 넣어주면 주소가 아니라 내용물이 복사된다!
그럼 깊은 복사가 일어나 서로에게 영향을 주지 않게 된다.
// IV_Item은 Class이기 때문에 직접 참조하면 얕은 복사가 일어난다. 그래서 깊은 복사를 위해 만든 함수.
public IV_Item DeepCopyItem(int Num, int Count = 1)
{
IV_Item NewItem = new IV_Item();
IV_Item ItemData = Datas.ItemDatas.Find(x => x.ItemNum == Num);
NewItem.ItemNum = ItemData.ItemNum;
NewItem.ItemName = ItemData.ItemName;
NewItem.ItemDescript = ItemData.ItemDescript;
NewItem.ItemCount = Count;
NewItem.ItemSprite = ItemData.ItemSprite;
return NewItem;
}
실제 구현한 DeepCopyItem 함수.
더 정확한 내용은 여러 블로그를 참고하는 편이 좋다.
(참고:https://cwkcw.tistory.com/389)
다음은 Lerp를 통한 드롭아이템이
천천히 플레이어 위치에서 밀려나다가 완전히 멈추면 다시 획득 가능하도록 하는 스크립트를 작성했다.
public void Droped(Vector3 Dir)
{
IsDroped = true;
t_Time = 0;
DropedPos = this.transform.localPosition;
DestinationPos = this.transform.localPosition + (Dir * DropDistance);
}
private void Update()
{
if (IsDroped)
{
t_Time += Time.deltaTime;
if(t_Time >= DropTime)
{
t_Time = DropTime;
IsDroped = false;
}
float t = t_Time / DropTime;
t = Mathf.Sin(t * Mathf.PI * 0.5f);
this.transform.localPosition = Vector3.Lerp(DropedPos, DestinationPos, t);
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (IsDroped) return;
IV_Player PL;
if (collision.gameObject.TryGetComponent<IV_Player>(out PL))
{
PL.Inventory.GainItem(CurrentItem.ItemNum, CurrentItem.ItemCount);
this.gameObject.SetActive(false);
}
}
Vector3.Lerp는 A 좌표와 B 좌표, 그리고 0~1값을 가진 float를 받아서
0을 A좌표, 1을 B 좌표라 할때 그 중간값을 자동으로 구해주는 함수이다.
거기에 일정하게 증가하는 Time.deltaTime을 곡선 그래프로 만들어서 부드럽게 이동하게 하는 것이 원리!
(참고: https://m.blog.naver.com/dooya-log/221636320321)
Lerp는 정말 많이 사용하는 함수라 자주 사용하는 이론 사이트를 첨부한다.
https://solhsa.com/interpolation/
그리고 다양한 Lerp 를 위한 수식(다항식에서부터 Sin까지)을
정리해둔 블로그를 참고하면 원하는 자연스러운 이동을 구현할 수 있다.
https://chicounity3d.wordpress.com/2014/05/23/how-to-lerp-like-a-pro/
'📁 개발 히스토리' 카테고리의 다른 글
유니티 - 인벤토리 구현하기(6) (0) | 2025.02.24 |
---|---|
유니티 - 인벤토리 구현하기(5) (0) | 2025.02.24 |
유니티 - 인벤토리 구현하기(3) (0) | 2025.02.23 |
유니티 - 인벤토리 구현하기(2) (0) | 2025.02.22 |
유니티 - 인벤토리 구현하기(1) (0) | 2025.02.22 |