디아블로 스타일 인벤토리 만들기

안녕하십니까! 밤말팅입니다!

두 번째 포스팅으로 만나 뵙게 되었네요~ 오랜만에 들고 온 컨텐츠는 바로!

[디아블로 스타일 인벤토리 만들기]입니다!

 

가방 크기는 전혀 신경 쓰지 않으셔도 되겠습니다~

크기가 얼마나 크던지 상관없는 리스트형 인벤토리를 만들 예정이니까요!

 

한 칸에 한 아이템씩 들어가는 일반적인 인벤토리의 경우에는 2차원 배열이 참 안성맞춤이죠?

그런데 디아블로와 같은 아이템 창을 만들려고 한다면?

아이템 하나를 여러 배열에 넣으려고 하면 무결성도 걱정되고 주울 때 자동으로 빈 공간을 찾기도 힘들다!

가 주된 문제가 되겠네요~

 

아이템 하나당 하나의 인스턴스로 관리를 하면서, 동시에 아이템을 잘 주울 수 있는 방법을 찾아봅시다!

 

전체 코드를 보고싶으신 분들은 아래 글을 참조해주시면 되겠습니다~

 

[C#] 디아블로 스타일 인벤토리 전체 코드

[C#] 디아블로 스타일 인벤토리 만들기 안녕하십니까! 밤말팅입니다! 두 번째 포스팅으로 만나 뵙게 되었네요~ 오랜만에 들고 온 컨텐츠는 바로! [디아블로 스타일 인벤토리 만들기]입니다! 가방

game-part-factory.tistory.com

 

[이 클래스는 아래와 같은 네임스페이스 참조가 필요합니다]

using System;
using System.Collections.Generic;

 

<목표>

20x20 인벤토리가 다 찰 때까지 무작위 아이템을 획득시킨 모습

//왼쪽 클릭입니다.
ItemClickLeft(x, y);

//오른쪽 클릭입니다.
ItemClickRight(x, y);

//아이템 획득
ItemGet(ItemInfo.bread, 30);

//아이템 소모
ItemRemove(ItemInfo.bread, 15);

//해당 아이템 갯수 확인
ItemStackCheck(ItemInfo.bread);

//집은 아이템을 버립니다
ItemThrow();

//집은 아이템을 다시 놓습니다
ItemUngrab();

//해당 위치의 아이템 정보를 받습니다
ItemInfo();

이렇게만 써도 상호작용할 수 있는 인벤토리를 만듭시다!

 

아이템을 획득해서 놓을 수 있는 제일 왼쪽 위에 넣어주고
아이템을 획득해서 겹쳐줄 수 있는 만큼 겹쳐주고
해당 위치에 있는 아이템을 집어주고
해당 위치에 있는 아이템 정보를 알려주고
해당 위치에 있는 아이템과 들고 있는 아이템을 바꿔주고
해당 위치에 있는 아이템에 들고 있는 아이템을 겹쳐주고
해당 위치에 아이템을 놓아주고
원하는 아이템의 정보를 알려주고
원하는 아이템의 개수를 알려주고
원하는 아이템의 개수를 줄여주고
원하는 아이템을 인벤토리에서 없애주고

하는 것이 이번 인벤토리의 목표입니다!

 

 

<구상>

 

아이템의 순서

우선 상단부터 아이템이 들어가야하기 때문에, y축을 기준으로 아이템 순서를 매기도록 합시다.

그리고 마찬가지로 y축이 같은 아이템이 있는 경우 x축을 기준으로 다시 매기도록 합니다.

이렇게 하면, 리스트에서 좌상단->우하단 순서로 아이템을 정렬할 수 있게 됩니다!

 

모든 아이템은 직사각형으로 구성하며, 충돌은 위치와 사이즈로 확인합니다.

네 개의 조건 중 하나라도 만족하면 충돌하지 않는 것으로 친다

x좌표와 y좌표를 대조해서 확인해볼 수 있겠습니다!

 

 

그리고 아이템을 넣기 위해 빈 공간을 찾는 과정은 다음과 같습니다.

원하는 y축에 2개의 아이템이 있다

1. 찾으려는 y축에 있는 아이템을 모두 확인합니다.

 

아이템 사이에 거리를 구한다

2. 아이템과 인벤토리 양 끝을 비교해서 거리를 구합니다.

    이 때! 들어가려는 아이템 크기보다 거리가 작은 경우 해당 위치를 삭제합니다.

 

해당 y축 외에 충돌할 가능성이 있는 아이템을 확인했더니 아이템3이 감지되었다

3. 현재 위치에 아이템을 넣었을 때, 충돌하는 아이템을 모두 확인합니다.

    확인된 아이템이 표시된 거리 안에 있는 경우, 쪼개서 관리합니다.

    물론 이 때에도 아이템 크기보다 작은 거리가 되는 경우 삭제합니다.

 

 

최종적으로 살아남은 아이템을 넣을 수 있는 2개의 공간

4. 충돌하는 아이템을 모두 검출하였고, 아이템을 넣을 크기가 안되는 거리를 모두 제한 후에

   살아남은 위치 중 첫 번째 위치에 아이템을 넣으면 됩니다!

 

아이템이 추가된 모습

그럼 결과적으로 이런 모습이 되겠죠!

 

이런 느낌으로 아이템을 찾고, 집고, 얻고 한다면 인벤토리가 대충 될 것 같습니다.

 

 

 

<구현>

 

public class ItemInfo
{
    public static int nextId = 0;

    public int itemID;
    public int sizeX;
    public int sizeY;
    public int stackMax;

    public ItemInfo(int wantSizeX, int wantSizeY, int wantStackMax)
    {
        itemID = nextId++;
        sizeX = wantSizeX;
        sizeY = wantSizeY;
        stackMax = wantStackMax;
    }
}

가볍게 아이템 정보를 넣는 칸을 넣어봅시다.

 

간단하게 아이템 ID 사이즈, 최대 개수를 정의하도록 하겠습니다.

 

더 필요한 사항은 그냥 여기에다가 넣으시면 됩니다!

 

그리고 아이템 ID의 경우, 지금은 임시로 만든 거라 그냥 만든 순서대로 부여하고 있지만

그냥 직접 입력해서 원하는 ID를 넣으시는 것이 좋습니다~

 

public class ItemContain
{
    private int locationX = 0;
    private int locationY = 0;
    private int stack = 0;
    private ItemInfo itemCurrent = null;

    public ItemContain(int wantLocationX, int wantLocationY, int wantStack, ItemInfo wantItem)
    {
        locationX = wantLocationX;
        locationY = wantLocationY;
        stack = wantStack;
        itemCurrent = wantItem;
    }

    public ItemContain(int wantStack, ItemInfo wantItem)
    {
        stack = wantStack;
        itemCurrent = wantItem;
    }
}

그리고 위의 클래스는 실제로 아이템이 있는 내용입니다.

아이템의 위치와 개수, 정보를 표시합니다.

 

public int GetItemStack()
{
	return stack;
}

public ItemInfo GetItemInfo()
{
	return itemCurrent;
}

public int GetLocationX()
{
	return locationX;
}

public int GetLocationY()
{
	return locationY;
}

public int GetSizeX()
{
	return itemCurrent.sizeX;
}

public int GetSizeY()
{
	return itemCurrent.sizeY;
}

public void SetItemStack(int wantStack)
{
	stack = wantStack;
}

public void SetLocation(int wantX, int wantY)
{
	locationX = wantX;
	locationY = wantY;
}

이런 식으로 Get; Set;함수도 넣어주었습니다.

 

 

 

이제 틀이 준비되었으니, 내용을 넣어보아야겠죠!

 

ItemContain 클래스에서 준비하는 함수들은

 

인벤토리 클래스에서 쓰기 위한 기본적인 단위이니, 가볍게 훑어보고 가도록 할게요~

 

public bool ItemOverlap(int targetLocationX, int targetLocationY, int targetSizeX, int targetSizeY)
{
  if(targetLocationX >= locationX + itemCurrent.sizeX || targetLocationX + targetSizeX <= locationX || targetLocationY >= locationY + itemCurrent.sizeY || targetLocationY + targetSizeY <= locationY)
  {
  	return false;
  }
  else
  {
  	return true;
  };
}

우선, 원하는 아이템의 위치와 크기를 받아온 후에

이 아이템과 충돌하는지 확인하는 함수입니다.

충돌하지 않는 경우

 

아까 올렸던 이것을 그냥 if문 한 줄에 넣어놓은 상태입니다~

 

근데 마우스 클릭을 할 때에는, 크기가 없게 되겠죠!

 

한 점과 아이템이 충돌하는지도 한 번 확인해줍시다.

 

public bool ItemOverlap(int targetLocationX, int targetLocationY)
{
  if(targetLocationX >= locationX && targetLocationX < locationX + itemCurrent.sizeX && targetLocationY >= locationY && targetLocationY < locationY + itemCurrent.sizeY)
  {
  	return true;
  }
  else
  {
  	return false;
  };
}

한 점과 아이템의 충돌은 반대로 적용해보았습니다.

 

true와 false가 아이템간 충돌과 반대로 되어있지요?

 

아이템 위치 <= 원하는 점 위치 < (아이템 위치 + 아이템 크기)

 

로 원하는 점과 현재 아이템의 충돌을 확인하도록 하고 있습니다!

 

public bool ItemCheck(ItemInfo wantItem)
{
  if (itemCurrent.itemID == wantItem.itemID) 
  {
  	return true;
  };
  return false;
}

충돌을 한 아이템이 현재 아이템이랑 같은 것인지 확인할 필요도 있겠지요?

 

그래서 ItemCheck함수를 넣었는데요!

 

지금은 ID로만 확인을 하고 있지만, ItemInfo가 조금 더 많은 정보를 가지고 있다면

 

그 정보까지 추가로 확인해주시면 되겠습니다!

 

public int ItemStack(int wantNumber)
{
  stack += wantNumber;
  if(stack > itemCurrent.stackMax)
  {
    int retval = stack - itemCurrent.stackMax;
    stack = itemCurrent.stackMax;
    return retval;
  }
  else
  {
  	return 0;
  };
}

다음은 아이템을 쌓는 함수입니다~

 

일단 원하는 개수를 넣고 시작하는데요,

 

넣고 나서 최대 개수를 넘으면, 초과 분량을 리턴하고

 

최대 개수를 넘지 않았으면 초과 분량이 없다는 의미로 0을 리턴하도록 합시다.

 

이를 이용해서 아이템이 남지 않을 때까지 계속 쌓기 시도하는 함수를 만들 예정입니다~

 

public int ItemDestack(int wantNumber)
{
	stack -= wantNumber;
	return stack;
}

뭐, 아이템을 넣는 것이 있다면, 빼는 것도 있어야겠죠!

 

아이템 제거는 리스트에서 담당할 것이니 빼고 나서

 

그냥 현재 개수만 보여주도록 하겠습니다.

 

뺀 후 결과값이 마이너스인 경우에 더 윗단계의 함수에서 처리하도록 하죠.

 

public ItemContain ItemSplit(int wantStack)
{
  if (stack > wantStack)
  {
    ItemDestack(wantStack);
    return new ItemContain(locationX, locationY, wantStack, itemCurrent);
  }
  else
  {
    return this;
  };
}

마지막으로 필요한 함수는 개수를 나눠서 두 개의 아이템으로 분리하는 기능입니다~

 

이걸로 인벤토리에 있는 아이템을 적절하게 나눠서 배치하거나

 

원하는 갯수만 이동시키는 기능을 만들 수 있게 되겠죠!

 

 

 

 

 

 

 

이렇게 아이템의 기초는 대강 다졌습니다!

 

아직까지는 포함시킨 네임스페이스를 하나도 쓰지 않았네요~

 

이제부터 본격적인 인벤토리를 들어갈 예정인데요!

 

두 겹의 클래스로 이루어진 인벤토리를 만들 겁니다~

 

방금 만든 기본적인 함수들을 이용하여 인벤토리 기본 기능을 구현하는 InventoryBase 클래스와

 

InventoryBase에서 구현된 기능으로 유저와 쉽게 상호작용하는 Inventory 클래스를 만들어봅시다!

 

public class InventoryBase
{
    public List<ItemContain> itemList = new List<ItemContain>();
    public int inventorySizeX;
    public int inventorySizeY;
}

우선 인벤토리 베이스를 살펴보도록 합시다.

 

아주 간단한 구조죠?

 

아이템을 나열할 리스트 하나와 인벤토리 총 크기만 묘사해주도록 합시다.

 

y가 같은 상태에서 x에 따라 정렬
y가 다르면 따로 계산

우선 먼저 할 일은 아이템을 리스트에 넣는 일이겠죠!

 

위에서 정리한 아이템 순서에 따라 리스트에 넣도록 합시다~

 

public bool ItemInsertList(ItemContain wantItem, int wantX, int wantY)
{
  //아이템을 넣을 최종 위치를 저장해놓읍시다.
  int targetIndex = 0;

  //비교할 대상의 위치를 표시할 x,y좌표입니다.
  int targetLocationX;
  int targetLocationY;

  //아이템의 개수만큼 계속 반복합니다.
  for(int i = 0; i < itemList.Count; ++i)
  {
    //대상의 y축 값을 받아온 후에
    targetLocationY = itemList[i].GetLocationY();
    
    //대상과 y축이 겹쳤을 경우
    if(wantY == targetLocationY)
    {
      //대상의 x축도 받아오고
      targetLocationX = itemList[i].GetLocationX();
      
      //대상의 x축이 더 클 때 해당 위치에 아이템을 삽입합니다.
      //자동으로 대상은 한 칸 뒤로 밀립니다.
      if(wantX < targetLocationX)
      {
        targetIndex = i;
        break;
      }
      //대상의 x축이 작으면 삽입 위치를 뒤로 옮기고 다음 대상을 확인하도록 합니다.
      else if(wantX > targetLocationX)
      {
        targetIndex = i + 1;
      }
      //대상의 x축이 크지도 작지도 않으면 같은 위치이므로 불가능을 알립니다.
      else
      {
        return false;
      };
    }
    //만약 대상의 y축이 더 크면 그 자리에 삽입합니다.
    else if (wantY < targetLocationY)
    {
      targetIndex = i;
      break;
    }
    //대상의 y축이 작다면 아직 삽입할 때가 아니므로 뒤로 물립니다.
    else
    {
      targetIndex = i + 1;
    };
  };
  
  //위의 과정을 거치면 삽입할 위치가 적힌 targetIndex가 넘어올 것입니다.
  //만약 삽입할 위치가 인벤토리 안에 있는 개수를 넘어갈 경우
  if(targetIndex >= itemList.Count)
  {
    //끝에 넣도록 합니다.
  	itemList.Add(wantItem);
  }
  else
  {
    //아니면 정해준 위치에 넣도록 합시다.
  	itemList.Insert(targetIndex, wantItem);
  };
  
  //마무리로 삽입에 성공했다고 알립니다.
  return true;
}

간단한 건데 각주를 계속 달았더니 조금 길어보이는군요~

 

우선 y축을 기준으로 삽입할 위치를 검색한 뒤에, 같은 y축에 아이템이 있으면 x축을 기준으로 검색합니다.

 

이렇게 하면 위 사진과 같이 정렬을 해줄 수 있겠죠!

 

삽입할 위치를 정하면 리스트에 넣는 것이 이번 함수의 전부입니다~

 

그럼 넣었으면 빼는 것도 준비해야겠죠!

 

public ItemContain ItemRemoveList(int wantIndex)
{
  ItemContain resultItem = itemList[wantIndex];

  itemList.RemoveAt(wantIndex);

  return resultItem;
}

리스트에서 제거하는 것은 뺄 위치만 알면 쉽습니다~

 

어차피 다른 함수에서 빼는 위치를 정해줄 것이므로, 위치만 받는 삭제 함수를 넣도록 하죠!

 

대신 뺄 때, 해당 아이템을 반환해주면 유용하게 쓸 수 있겠네요~

 

public void ItemRemoveList(ItemInfo wantItem)
{
  for (int i = 0; i < itemList.Count; ++i)
  {
    if (itemList[i].ItemCheck(wantItem))
    {
    	ItemRemoveList(i);
    };
  };
}

마찬가지로 리스트에서 제거하는 기능을 하나 더 쓸건데요,

 

위치가 아니라 그냥 아이템 종류를 넣으면 해당하는 아이템을 모두 지워버리는 것도 있으면 좋겠네요~

 

퀘스트 완료이벤트가 끝난 아이템 같은 경우 한 번에 날리는 것이 편하겠죠?

 

 

이제 다음으로 구현해 볼 것은 아이템 개수가 무난하고 좋을 것 같네요!

 

우선 아이템 개수를 확인하는 것부터 만들어봅시다.

public int ItemStackCheck(ItemInfo wantItem)
{
  int itemLeft = 0;

  for (int i = 0; i < itemList.Count; ++i)
  {
    if (itemList[i].ItemCheck(wantItem))
    {
    	itemLeft += itemList[i].GetItemStack();
    };
  };

  return itemLeft;
}

아이템 개수 확인 쯤이야 뭐~

 

모든 아이템을 둘러보고 원하는 아이템이면 개수를 카운트해주면 되는 것이지요!

 

가볍게 넘어갑니다.

 

public int ItemStack(ItemInfo wantItem, int wantStack)
{
  int itemLeft = wantStack;
  
  for (int i = 0; i < itemList.Count; ++i)
  {
    if (itemList[i].ItemCheck(wantItem))
    {
      itemLeft = itemList[i].ItemStack(itemLeft);
      
      if (itemLeft <= 0)
      {
      	return 0;
      };
    };
  };

  return itemLeft;
}

그렇다면 아이템을 일정 수량만큼 증가시켜주도록 합시다!

 

원하는 양을 itemLeft에 기록을 해준 뒤에

 

개수 확인할 때처럼 모든 아이템을 돌면서 맞는지 확인합니다.

 

그리고 냅다 아이템을 넣어버립니다!

 

그러면 아이템 개수를 늘려주는 함수를 썼을 때, 초과분량을 반환했었죠!

 

그걸 itemLeft에 다시 넣어주면,

 

자연스럽게 최대치만큼 넣고 남는 값을 알 수 있게 됩니다~

 

남은 값이 없을 때까지 계속 돌면 되겠네요!

 

만약 값이 남은 상태로 모든 아이템을 확인했다면, 남은 양을 반환해줍시다!

 

 

public int ItemDestack(ItemInfo wantItem, int wantStack)
{
  int itemLeft = wantStack;
  int itemNumberChecker;
  
  for(int i = itemList.Count -1; i >= 0; --i)
  {
    if (itemList[i].ItemCheck(wantItem))
    {
      itemNumberChecker = itemList[i].ItemDestack(itemLeft);
      
      //여기서부터
      if (itemNumberChecker <= 0)
      {
          itemLeft = Math.Abs(itemNumberChecker);
          ItemRemoveList(i);
      }
      else
      {
          return 0;
      };
      //여기까지가 증가와 다릅니다!

      if (itemLeft <= 0)
      {
          return 0;
      };
    };
  };
  return itemLeft;
}

그럼 다음은 자연스럽게 아이템을 일정 수량만큼 감소시켜봅시다!

 

증가와는 다르게 하나의 과정이 더 추가되는데요~

 

아이템 개수를 확인해서 리스트에서 제거해주는 것입니다!

 

가운데에 표시되어있는 부분이 그 리스트에서 제거해주는 내용이 되겠네요~

 

ItemDestack함수는 빼고나서 해당 아이템에 남은 개수를 반환합니다.

 

남은 개수가 음수라면, 아직 뺄 것이 남았는데 못 뺐다는 의미이기 때문에

 

반환된 값을 양수로 바꿔줘서 못 뺀 수량을 기록한 뒤

 

음수가 된 아이템을 리스트에서 삭제해주기만 하면 됩니다!

 

그리고 아이템을 계속 돌면서 뺄 아이템을 찾는 것이지요!

 

근데 혹시 눈치 채셨나요?

 

for(int i = itemList.Count -1; i >= 0; --i)

 

감소에서는 반복문이 조금 달랐습니다!

 

아이템 개수 감소는 뒤에서부터 되는 것이 조금 더 자연스럽기 때문에,

 

뒤에서부터 검색하는 것으로 조정해두었습니다!

 

유의해두시면 좋겠네요~

 

public ItemContain ItemFind(int wantX, int wantY)
{
  for (int i = 0; i < itemList.Count; ++i)
  {
    if (itemList[i].ItemOverlap(wantX, wantY))
    {
    	return itemList[i];
    };
  };

  return null;
}

다음은 원하는 위치에 있는 아이템 찾기를 만듭시다.

 

그냥 돌면서 해당 위치에 닿는 녀석이 있다면, 알려줄 수 있겠죠?

 

 

public ItemContain ItemFind(int wantX, int wantY, out int returnIndex)
{
  for (int i = 0; i < itemList.Count; ++i)
  {
    if (itemList[i].ItemOverlap(wantX, wantY))
    {
      returnIndex = i;
      return itemList[i];
    };
  };

  returnIndex = -4;
  return null;
}

똑같이 찾는 것인데, 아이템 리스트에 들어있는 순서까지 알려주는 함수도 같이 만들어 둡시다.

 

 

 

public int ItemCollision(int wantX, int wantY, int wantSizeX, int wantSizeY, out int firstTouch)
{
  //만약 체크하는 범위가 인벤토리를 넘어서면 음수값을 반환합니다.
  if(wantY + wantSizeY > inventorySizeY)
  {
    firstTouch = -4;
    return -1024;
  };
  
  int itemTouch = 0;
  firstTouch = -4;

  for(int i = 0; i < itemList.Count; ++i)
  {
    //현재 아이템과 겹치는지 확인합니다.
    if (itemList[i].ItemOverlap(wantX, wantY, wantSizeX, wantSizeY))
    {
      //겹치면 겹친 개수를 늘려줍니다.
      ++itemTouch;
      
      //닿은 아이템을 표시해둔 것이 없다면, 이 아이템을 표시합니다.
      if(firstTouch == -4)
      {
        firstTouch = i;
      };
    };
  };

  //겹친 아이템의 개수를 반환합니다.
  return itemTouch;
}

원하는 위치의 아이템을 찾았다면,

 

원하는 범위에 있는 아이템도 찾아봐야겠죠?

 

위치와 범위를 지정해주면, 해당하는 아이템이 리스트에서 몇번째에 있는지 알려줍니다.

 

firstTouch로 값을 전해주는 것이구요, 반환값으로 몇 개가 부딪혔는지 가르쳐줄 수 있겠죠!

 

 

public bool ItemPlacement(ItemContain wantItem, int wantX, int wantY)
{
  int touchItemIndex;

  //충돌하는 아이템이 있는 경우
  if (ItemCollision(wantX,wantY,wantItem.GetSizeX(),wantItem.GetSizeY(),out touchItemIndex) > 0)
  {
    //리스트에서 해당 아이템을 불러옵니다.
    ItemContain itemCheck = itemList[touchItemIndex];

    //충돌한 아이템이 넣는 아이템과 같은 경우
    if (itemCheck != null && itemCheck.ItemCheck(wantItem.GetItemInfo()))
    {
      //충돌한 아이템에 넣는 아이템의 개수만큼 더합니다.
      //넘치는 분량은 다시 가져옵니다.
      wantItem.SetItemStack(itemCheck.ItemStack(wantItem.GetItemStack()));

      //만약 넘친 분량이 없는 경우 성공했다고 알립니다.
      if(wantItem.GetItemStack() <= 0)
      {
        return true;
      }
      //넘쳐버렸다면 모두 넣는 데에 실패했다고 알립니다.
      else
      {
        return false;
      };
    }
    //충돌한 아이템이 다른 종류인 경우 그냥 실패했다고 알립니다.
    else
    {
      return false;
    };
  }
  //충돌하는 아이템이 없는 경우
  else
  {
    //인벤토리를 벗어났으면 실패했다고 알립니다.
    if (wantY + wantItem.GetSizeY() > inventorySizeY || wantX + wantItem.GetSizeX() > inventorySizeX)
    {
      return false;
    }
    //모든 상황이 정상적인 경우
    else
    {
      //아이템 리스트에 넣으려던 아이템을 넣습니다.
      ItemInsertList(wantItem, wantX, wantY);
      //원하던 위치에 아이템을 둡니다.
      wantItem.SetLocation(wantX, wantY);
      //성공했다고 알립니다.
      return true;
    };
  };
}

인벤토리 베이스도 거의 끝나가네요!

 

이젠 아이템을 배치해보도록 합시다!

 

약간 길어보일 수 있는데, 역시나 간단하게 요약을 해보면

 

1. 원하는 위치에 충돌하는 아이템이 있는지 확인합니다.

 

2. 충돌하는 아이템이 있다면, 겹쳐봅니다.

 

3. 충돌하는 아이템이 없고 인벤토리도 벗어나지 않았다면, 바로 넣습니다.

 

이 정도가 되겠네요! 어렵지 않죠?

 

 

 

 

그렇다면 인벤토리에서 제일 난이도 높은 녀석을 만날 준비가 된 것 같습니다~

 

자동으로 인벤토리에 아이템을 넣어보죠!

 

 

 

public class XFinder
{
    public int startX;
    public int lastX;

    public XFinder(int wantStartX, int wantLastX)
    {
        startX = wantStartX;
        lastX = wantLastX;
    }
}

이것은 X위치를 표시하는 클래스입니다.

 

아이템 사이에 빈 공간을 표시

아이템 사이에 선으로 표시해둔 부분저장해두기 위해서 클래스 형태로 만든 것입니다!

 

public void HeightSave(List<int> heightList, int findStartIndex, int targetY)
{
  //넣을 위치를 찾기 위해 해당 리스트에서 원하는 시작위치부터 끝까지 확인합니다.
  for (int j = findStartIndex; j < heightList.Count; ++j)
  {
    //만약, 넣을 높이가 이미 있다면, 끝냅니다
    if (targetY == heightList[j])
    {
    	return;
    }
    //만약 찾은 높이가 넣을 높이보다 크다면, 뒤로 밀어두고 그 자리에 넣습니다.
    else if (targetY < heightList[j])
    {
    	heightList.Insert(j, targetY);
    	return;
    };
  };

  //반복문을 끝내도 넣을 위치를 찾지 못해서 여기에 도달했다면
  //해당 높이가 가장 높다는 의미이므로, 맨 마지막에 넣습니다.
  heightList.Add(targetY);
}

그리고 마찬가지로 y축을 저장할 필요도 있겠죠!

 

y축은 찾을 라인만 표시하기 때문에 int하나로 사용하도록 하겠습니다~

 

작은 y값부터 나열하기 위해서 저장용 함수 하나만 만들어놓도록 하겠습니다!

 

이건 static선언으로 해놓고 아무데나 놓아도 되겠지만

 

저는 그냥 InventoryBase에 넣어둘게요!

 

이걸 가지고 아이템이 들어갈 장소를 찾는 함수를 만들어보도록 하죠!

 

 

public bool ItemPlacementFinder(int wantSizeX, int wantSizeY, out int returnLocationX, out int returnLocationY)
{
    //인벤토리에 아이템이 없다면, (0,0)에 넣습니다.
    if(itemList.Count <= 0)
    {
        returnLocationX = 0;
        returnLocationY = 0;
        return true;
    }
    else
    {
        //현재 찾는 중인 x좌표를 저장합니다.
        int currentFindX;
        
        //이미 확인한 아이템 위치를 적어둡니다.
        int ignoreIndex = 0;
        
        //가능한 x범위를 표시할 리스트입니다.
        List<XFinder> finderList = new List<XFinder>();
        
        //확인해야하는 y축 위치를 저장합니다.
        List<int> heightList = new List<int>();
        
        //최초로 확인할 y축 위치를 0으로 넣어줍니다.
        heightList.Add(0);

        //확인할 y축이 남은 경우 반복합니다.
        for(int heightIndex = 0; heightIndex < heightList.Count; ++heightIndex)
        {
            //해당 y축이 인벤토리를 넘은 경우 실패했다고 알립니다.
            if (heightList[heightIndex] + wantSizeY > inventorySizeY)
            {
                returnLocationX = 0;
                returnLocationY = 0;
                return false;
            };

            //찾는 x좌표를 0으로 초기화합니다.
            currentFindX = 0;
            
            //현재 확인하고 있는 아이템의 최종 높이를 저장합니다.
            int targetLastY;
            
            //아직 확인하지 않은 아이템들을 돌면서 범위를 탐색합니다.
            for (int i = ignoreIndex; i < itemList.Count; ++i)
            {
                //현재 확인중인 y축과 대상의 y축이 같은 경우
                if (itemList[i].GetLocationY() == heightList[heightIndex])
                {
                    //최종 높이를 설정하고, 저장 여부를 초기화합니다.
                    targetLastY = itemList[i].GetLocationY() + itemList[i].GetSizeY();
                    
                    //최종 높이를 확인할 높이에 추가합니다.
                    HeightSave(heightList, heightIndex, targetLastY);

                    //확인할 높이를 채우는 것이 끝났으니 이제 실제 비교를 시작합시다.
                    //현재 확인중인 x값과 다음 아이템의 x위치값의 거리가
                    //들어가는 아이템의 사이즈보다 크면 가능성 있는 범위로 할당합니다.
                    if ((itemList[i].GetLocationX() - currentFindX) >= wantSizeX)
                    {
                        finderList.Add(new XFinder(currentFindX, itemList[i].GetLocationX()));
                    };
                    
                    //확인중인 x값을 확인한 아이템의 끝에 맞춥니다.
                    currentFindX = itemList[i].GetLocationX() + itemList[i].GetSizeX();
                    
                    //해당 아이템을 확인했다고 알립니다.
                    ignoreIndex = i;
                }
                //만약, 더 높이 있는 아이템에 도달한 경우, 일단 탐색을 중단합니다.
                else if (itemList[i].GetLocationY() > heightList[heightIndex])
                {
                    //그리고 이번 줄을 모두 확인한 뒤에, 이 줄을 확인해야 하므로
                    //해당 높이도 확인해야한다고 알립니다.
                    HeightSave(heightList, heightIndex, itemList[i].GetLocationY());
                    break;
                };
            };
            //마지막으로 확인한 위치와 인벤토리 끝을 확인해서
            //아이템이 들어갈 공간이 있다면, 그것도 범위에 추가합니다.
            if ((inventorySizeX - currentFindX) >= wantSizeX)
            {
                finderList.Add(new XFinder(currentFindX, inventorySizeX));
            };

            //이제부터 해당 범위 안에 다른 아이템이 충돌하는지 확인하도록 하겠습니다.
            //충돌하는 경우 범위를 줄여야하기 때문에, 양 옆쪽에 남은 공간을 확인합니다.
            bool leftPass;
            bool rightPass;
            
            //아이템 전체를 돌면서 충돌여부를 확인합니다.
            for (int i = 0; i < itemList.Count; ++i)
            {
                //해당 아이템의 높이가 (넣는 높이 + 넢는 크기)보다 작으면
                //충돌할 가능성이 있는 것으로 판단합니다.
                if (itemList[i].GetLocationY() < heightList[heightIndex] + wantSizeY)
                {
                    //동시에 해당 아이템의 최종 높이가 넣는 높이보다는 높아야
                    //충돌할 가능성이 있는 것으로 봅니다.
                    if (itemList[i].GetLocationY() + itemList[i].GetSizeY() > heightList[heightIndex])
                    {
                        //충돌할 가능성이 있다고 판단한 경우, 해당 아이템을
                        //모든 범위에 대해 충돌을 확인하도록 합니다.
                        for (int j = 0; j < finderList.Count; ++j)
                        {
                            //해당 아이템의 x범위가 현재 확인 중인 범위에 닿는지 확인합니다.
                            if (itemList[i].GetLocationX() + itemList[i].GetSizeX() >= finderList[j].startX && itemList[i].GetLocationX() <= finderList[j].lastX)
                            {
                                //아이템의 시작 x위치 - 범위의 시작위치가 넣는 크기보다 크면
                                //왼쪽은 들어갈 공간이 있는 것으로 봅니다.
                                leftPass = (itemList[i].GetLocationX() - finderList[j].startX) >= wantSizeX;
                                
                                //범위의 끝위치 - 아이템의 끝위치 가 넣는 크기보다 크면
                                //오른쪽에 들어갈 공간이 있는 것으로 봅니다.
                                rightPass = (finderList[j].lastX - (itemList[i].GetLocationX() + itemList[i].GetSizeX())) >= wantSizeX;

                                //오른쪽에 공간이 있는 경우
                                if (rightPass)
                                {
                                    //오른쪽도 있고 왼쪽도 공간이 있으면, 아이템이 가운데에 있는 것으로 보고
                                    //오른쪽에 해당하는 부분을 범위에 추가하고
                                    //현재 범위를 충돌한 아이템 위치까지로 줄입니다.
                                    if (leftPass)
                                    {
                                        finderList.Insert(j + 1, new XFinder(itemList[i].GetLocationX() + itemList[i].GetSizeX(), finderList[j].lastX));
                                        finderList[j].lastX = itemList[i].GetLocationX();
                                    }
                                    //왼쪽에는 공간이 없으면 현재 범위의 시작점을 충돌한 아이템 끝에 맞춥니다.
                                    else
                                    {
                                        finderList[j].startX = itemList[i].GetLocationX() + itemList[i].GetSizeX();
                                    };
                                }
                                //오른쪽에 공간이 없는 경우
                                else
                                {
                                    //왼쪽에는 공간이 있으면 현재 범위의 끝부분을
                                    //해당 아이템의 시작부분으로 맞춥니다.
                                    if (leftPass)
                                    {
                                        finderList[j].lastX = itemList[i].GetLocationX();
                                    }
                                    //양쪽 다 공간이 없으면 해당 범위를 삭제합니다.
                                    else
                                    {
                                        finderList.RemoveAt(j);
                                    };
                                };
                            };
                        };
                    };
                }
                //해당 아이템의 높이가 (넣는 높이 + 넣는 크기)보다 크면
                //더 이상 확인할 필요가 없는 것으로 치고 반복을 종료합니다.
                else
                {
                    break;
                };
            };

            //만약 위의 과정을 거친 후에 남은 범위가 있는 경우
            //해당 y축과 첫 번째 범위의 시작점에는 아이템을 넣을 수 있는 것으로 판단하여
            //그 위치에 아이템 넣기를 시도하라고 알립니다.
            if (finderList.Count > 0)
            {
                returnLocationX = finderList[0].startX;
                returnLocationY = heightList[heightIndex];
                return true;
            };
        };
    };
    
    //중간에 아이템을 넣을 수 없었다면, 실패했다고 알립니다.
    returnLocationX = 0;
    returnLocationY = 0;
    return false;
}

 

아이템을 획득했을 때, 빈 공간을 찾아서 아이템을 넣어주는 함수입니다!

 

아주 쬐끔 길다고 느껴지실 수도 있을 것 같은데, 크게 한 번 살펴보면 쉽습니다~

 

 

우선 아이템이 없으면 그냥 (0,0)에 넣습니다! 아주 간단하죠.

 

그 다음은 인벤토리의 높이를 넘지 않을 때까지 반복해서 원하는 높이에 있는 아이템을 확인하는 작업인데요!

 

내용은 아주 간단합니다!

 

인벤토리와 넣을 아이템

이렇게 생긴 인벤토리와 아이템이 있다고 합시다.

 

일단 제일 위에 들어갈 자리가 있는지 확인하는 것이 좋겠죠!

 

해당 라인에 있는 아이템에 색을 칠해둡니다.

 

현재 확인 중인 높이의 아이템들

이제 이 아이템들의 높이를 저장해둡시다.

 

그리고, 다음 높이에 있는 아이템들도 대기열에 추가해주어야 겠죠!

아이템들의 높이를 다음에 확인할 높이로 저장

높이를 저장해둔 후에는, 진짜 계산을 시작합니다.

 

지금 선택된 아이템들 사이의 간격을 생각해보죠!

현재 높이에 있는 아이템 간격을 확인합니다.

뒤 아이템에서 앞 아이템 위치를 빼서 생각해보면, 아이템을 넣을 수 있는 공간이 있는지 확인하는 것은

 

아주 식은 죽 먹기입니다~

 

그냥 크기를 비교만 하면 되겠지요!

 

해당 범위에 충돌하는지 확인

 

그러면 이 범위에, 넣을 아이템과 같은 높이의 상자를 놓는다고 합시다.

 

두 개의 충돌하는 아이템이 발견되었네요!

 

이 아이템에 따라 범위를 조정해봅시다.

 

충돌한 아이템에 따라 범위를 조정한 모습

이런..

 

모든 범위가 넣을 아이템의 크기보다 작게 되었네요!

 

이렇게 되면, 이 높이에는 아이템을 놓을 자리가 없는 것으로 보면 되겠군요~

 

그럼 다음 대기중인 높이를 계속 확인하면서 답을 찾아봅시다.

 

모든 확인 후에도 넣을 수 있는 범위가 남은 상태

세 번째로 저장된 높이를 확인했더니

 

모든 충돌을 감안해도 두 개의 범위가 남았군요!

 

그렇다면, 망설일 것 없이 첫 번째 범위의 왼쪽 끝에 아이템을 놓고

 

높이는 확인하던 높이에 아이템을 올려주면

 

아이템을 해당 위치에 올려놓은 모습

짜잔!

 

여러 과정을 거친 끝에 원하는 아이템을 넣을 수 있는 가장 위, 가장 왼쪽에 넣는 데에 성공했습니다!

 

위에 있는 코드는 이런 과정을 써 놓은 것이니, 잘 확인해주시면 좋겠습니다!

 

각주와 그림을 잘 참고하면 어렵지 않게 이해하실 수 있을 거에요~

 

 

 

 

이제 인벤토리의 베이스는 모두 끝났습니다!

 

이제 유저와 직접적으로 대면할 인벤토리를 만들건데요!

 

기본적으로는 [베이스에서 만든 내용을 쓰기 때문]에 여기서부터는 쉬어간다는 느낌으로

 

가볍게 만들어보도록 하겠습니다~

 

public class Inventory
{
    public InventoryBase currentBase = new InventoryBase();
    public ItemContain itemGrab;
}

참으로 간단하네요!

 

제 인벤토리는 인벤토리 베이스와 더불어서 마우스에 들고 있는 아이템 하나로 만들었습니다.

 

혹시나 추가할 보조 수단이 있거나, 인벤토리를 여러개 쓰고싶으시면 추가하셔도 좋습니다!

 

public int ItemGet(ItemInfo wantItem, int wantStack)
{
  int itemLeft = wantStack;

  if (wantItem.stackMax > 1)
  {
  	itemLeft = currentBase.ItemStack(wantItem, wantStack);
  };

  int targetLocationX;
  int targetLocationY;

  while(itemLeft > 0)
  {
    if (currentBase.ItemPlacementFinder(wantItem.sizeX, wantItem.sizeY, out targetLocationX, out targetLocationY))
    {
    	currentBase.ItemPlacement(new ItemContain(Math.Min(itemLeft, wantItem.stackMax), wantItem), targetLocationX, targetLocationY);
    	itemLeft -= wantItem.stackMax;
    }
    else if(itemGrab == null)
    {
    	itemGrab = new ItemContain(0, wantItem);
    	itemLeft = itemGrab.ItemStack(itemLeft);
    }
    else
    {
    	return itemLeft;
    };
  };

  return 0;
}

일단 방금 아이템을 넣을 방법을 만들었으니

 

아이템을 획득하는 기능을 추가해보도록 하죠~

 

우선 아이템 종류와, 아이템 개수를 받아오도록 합시다.

 

이제 아이템이 남지 않을 때까지 계속 아이템을 인벤토리에 넣을 건데요!

 

아이템을 새로 만들기 전에, 이미 있는 아이템을 돌면서 빈 공간을 채워줍시다~

 

다 채워봤는데 아이템이 남는다!

 

그러면 이제 인벤토리에 새로 아이템 칸을 만들어 넣습니다~

 

넣다 보니 도저히 못 넣는데, 손이 비면 손에다가도 쥐어주고

 

그래도 남으면 남은 개수를 반환해주도록 합시다!

 

간단하네요~

 

 

public void ItemRemove(ItemInfo wantItem, int wantStack)
{
  int stackLeft = wantStack;

  if (itemGrab.ItemCheck(wantItem))
  {
    itemGrab.ItemDestack(stackLeft);

    if(itemGrab.GetItemStack() < 0)
    {
    	stackLeft = Math.Abs(itemGrab.GetItemStack());
        itemGrab = null;
    }
    else
    {
    	return;
    };
  };

  currentBase.ItemDestack(wantItem, stackLeft);
}

아이템을 얻을 수 있다면 아이템을 없애기도 할 수 있어야겠죠!

 

우선 손에 들고 있는 것이 해당 아이템인지 확인합니다.

 

맞으면, 손에 있는 것을 빼앗습니다!

 

그래도 부족하면 인벤토리에서 빼가는걸로 하죠~

 

public void ItemRemove(ItemInfo wantItem)
{
  if (itemGrab.ItemCheck(wantItem))
  {
  	itemGrab = null;
  };

  currentBase.ItemRemoveList(wantItem);
}

뭐, 모든 아이템을 없앨 때는 무자비하게 다 빼앗아갑니다.

 

그냥 종류만 맞으면 다 가져가도록 합시다!

 

public int ItemStackCheck(ItemInfo wantItem)
{
  int returnValue = currentBase.ItemStackCheck(wantItem);

  if (itemGrab.ItemCheck(wantItem))
  {
  	returnValue += itemGrab.GetItemStack();
  };

  return returnValue;
}

상황에 따라 아이템의 개수를 파악해야할 때가 있습니다!

 

퀘스트의 진척도를 파악하거나, 어떤 요구사항을 확인하거나, 아이템 몇 개를 한 번에 사용하거나 할 때 말이죠!

 

그럴 때에는, 인벤토리베이스에 있는 해당 아이템의 개수를 구한 다음

 

손에도 들고 있으면 그것도 더해주는 것으로 합시다~

 

 

public ItemContain ItemThrow()
{
  ItemContain returnItem = itemGrab;
  itemGrab = null;
  return returnItem;
}

아이템 던지기!

 

네 필요하면 손에 들고 있는 아이템을 버려줘야겠죠

 

버릴 때 그냥 버리지 말고, 들고 있던 값을 반환해서 나중에 쓰도록 합시다!

 

 

public void ItemUngrab()
{
  if (itemGrab != null && currentBase.ItemPlacement(itemGrab, itemGrab.GetLocationX(), itemGrab.GetLocationY()))
  {
  	itemGrab = null;
  };
}

아이템 다시 놓기도 만들어둡시다~

 

만약 아이템을 들고 오긴 했는데, 그냥 취소하고 다시 원래 자리에 놓고 싶다

 

아이템을 가져올 때 위치 정보는 바꾸지 않을 것이기 때문에

 

그냥 그 위치에 넣기를 시도합니다.

 

성공하면 손을 비워줍시다!

 

public ItemContain ItemInfo(int wantX, int wantY)
{
	return currentBase.ItemFind(wantX, wantY);
}

뭐... 해당 아이템 정보정도는 가져올 필요도 있죠

 

원하는 위치에 있는 정보를 가져오는 함수입니다.

 

 

public void ItemClickLeft(int wantX, int wantY)
{
  //손에 아이템이 있는 경우
  if (itemGrab != null)
  {
    //아이템을 내려 놓고, 성공하면 손을 비웁니다.
    if (currentBase.ItemPlacement(itemGrab, wantX, wantY))
    {
      itemGrab = null;
    }
    //아이템을 내려놓지 못하는 상태일 때
    else
    {
      //겹친 아이템을 저장합니다.
      int touchItemIndex;
      
      //겹친 아이템이 1이하인 경우에만 상호작용을 시도합니다.
      if (currentBase.ItemCollision(wantX, wantY, itemGrab.GetSizeX(), itemGrab.GetSizeY(), out touchItemIndex) <= 1)
      {
        //겹친 아이템이 손에 들린 것과 같으면
        if (currentBase.itemList[touchItemIndex].ItemCheck(itemGrab.GetItemInfo()))
        {
          //해당 아이템에 쌓을 수 없는 상태인 경우
          if(currentBase.itemList[touchItemIndex].GetItemStack() >= itemGrab.GetItemInfo().stackMax)
          {
            //일단 해당 아이템을 빼놓습니다.
            ItemContain targetItem = currentBase.ItemRemoveList(touchItemIndex);

            //손에 들고 있던 것을 아이템 창에 놓습니다.
            if (currentBase.ItemPlacement(itemGrab, wantX, wantY))
            {
              //성공하면 빼 놓은 아이템을 손에 듭니다.
              itemGrab = targetItem;
            }
            //실패하면 빼 놓은 아이템을 원래 자리에 둡니다.
            else
            {
              currentBase.ItemInsertList(targetItem, targetItem.GetLocationX(), targetItem.GetLocationY());
            };
          }
          //해당 아이템을 쌓을 수 있으면 쌓습니다.
          else
          {
              itemGrab.SetItemStack(currentBase.itemList[touchItemIndex].ItemStack(itemGrab.GetItemStack()));
          };
        }
        //겹친 아이템이 손에 들린 것과 다르면
        else
        {
          //겹친 아이템을 빼둡니다.
          ItemContain targetItem = currentBase.ItemRemoveList(touchItemIndex);

          //들고 있던 아이템을 놓습니다.
          if (currentBase.ItemPlacement(itemGrab, wantX, wantY))
          {
            //성공하면 빼 놓은 아이템을 손에 듭니다.
            itemGrab = targetItem;
          }
          //실패하면 빼 놓은 아이템을 다시 원래 자리에 둡니다.
          else
          {
            currentBase.ItemInsertList(targetItem, targetItem.GetLocationX(), targetItem.GetLocationY());
          };
        };
      };
    };
  }
  //손에 아이템이 없는 경우
  else
  {
    //클릭한 위치에서 아이템을 찾습니다.
    int targetIndex;
    ItemContain targetContain = currentBase.ItemFind(wantX, wantY, out targetIndex);

    //클릭한 위치에 아이템이 있는 경우
    if (targetContain != null)
    {
      //해당 아이템을 손에 들고
      itemGrab = targetContain;
      //인벤토리에서 해당 아이템을 지웁니다.
      currentBase.ItemRemoveList(targetIndex);
    };
  };
}

쉬어가려고 했는데 급발진을 해버린 감이 있네요!

 

아이템을 좌클릭한 경우를 고려해서 상황을 만들어봤습니다!

 

이것만 있으면 다른 곳에서 위치만 줘도 잘 반응하는 인벤토리가 됩니다~

 

크게 "손에 아이템이 있는 경우""손에 아이템이 없는 경우"로 나누어서 진행해보도록 하죠

 

 

[손에 아이템이 있는 경우]

아이템을 놓아봅니다.
놓는 데에 실패하면 쌓아봅니다.
쌓는 데에 실패하면 해당 아이템과 위치를 바꿉니다.
위치를 바꾸는 데 실패하면 포기합니다.

 

[손에 아이템이 없는 경우]

아이템이 있는지 확인합니다.
아이템이 있으면 들어올립니다.

 

이렇게만 해 둬도, 좌클릭으로 물건을 옮기는 것이 가능할 것 같네요~

 

들어서 옮기고, 쌓고, 바꿔치고 하는 것이 쉽습니다!

 

물론 위치를 마우스가 클릭한 곳으로 지정했기 때문에, 아이템이 치우치는 경향이 있는데요!

 

아이템 삽입을 할 때에, 원하는 만큼 잘 조정을 해서 넣어주시면 편의성이 증가하겠죠!

 

이것은 제작자 나름이기 때문에 따로 넣지 않겠습니다~

 

 

 

public void ItemClickRight(int wantX, int wantY)
{
  //손에 아이템이 있는 경우
  if(itemGrab != null)
  {
    //손에 있는 아이템을 하나 빼서 인벤토리에 넣습니다.
    if (currentBase.ItemPlacement(itemGrab.ItemSplit(1), wantX, wantY)) 
    {
      //만약 방금 뺀 걸로 남은 아이템이 없는 경우 손을 비웁니다.
      if (itemGrab.GetItemStack() <= 0)
      {
        itemGrab = null;
      };
    }
    //인벤토리에 넣지 못 한 경우, 한 개를 돌려받습니다.
    else
    {
      itemGrab.ItemStack(1);
    };
  }
  //손에 아이템이 없는 경우
  else
  {
    //해당 위치의 아이템 정보를 받아옵니다.
    int targetIndex;
    ItemContain targetContain = currentBase.ItemFind(wantX, wantY, out targetIndex);

    //받아온 아이템이 있는 경우
    if (targetContain != null)
    {
      //해당 아이템의 남은 개수가 2개 이상일 때
      if(targetContain.GetItemStack() > 1)
      {
        //해당 아이템을 반으로 나누고, 소숫점을 버린만큼 떼어옵니다.
        itemGrab = targetContain.ItemSplit((int)Math.Floor(targetContain.GetItemStack() / 2.0f));
      }
      //해당 아이템의 남은 개수가 하나일 때
      else
      {
        //인벤토리에서 빼서 손으로 가져옵니다.
        itemGrab = targetContain;
        currentBase.ItemRemoveList(targetIndex);
      };
    };
  };
}

좌클릭을 만들었으니, 우클릭을 한 경우도 만들어주면 좋겠죠!

 

마인크래프트의 인벤토리를 참고하여 만들어보았습니다~

 

[손에 아이템이 있는 경우]

들고 있는 아이템에서 하나를 떼어 인벤토리에 넣어봅니다.
넣는 데에 성공하고, 남은 아이템이 없으면 손을 비웁니다.
넣는 데에 실패하면, 떼어낸 아이템을 다시 손으로 가져옵니다.

 

[손에 아이템이 없는 경우]

해당 위치에 아이템이 있는지 확인합니다.
해당 아이템이 2개 이상 쌓인 상태이면, 절반을 손으로 가져옵니다 (소수점 버림)
해당 아이템이 1개인 경우 그 아이템을 손으로 가져옵니다.

 

이것도 물론 만들기 나름이지만, 비교적 만들기 쉬웠던 마인크래프트로 만들어봤습니다~

 

우클릭으로 정보를 나타나게 하셔도 좋고, 바로 버리게 하는 것도 좋으니

 

한 번 본인만의 기능을 만들어보는 것도 좋겠네요~

 

 

 

여기까지가 인벤토리의 구현부분이었습니다!

 

 

<마치며>

 

배열을 쓰지 않고 인벤토리를 만들려다 보니

 

여러가지 방법이 떠올라서 실험을 막 해보았는데요!

 

리스트형식이 제일 만들기 편하고 괜찮았던 것 같네요~

 

 

디아블로 방식의 인벤토리가 쬐끔 불편해서 많이 적용이 안되고 있는 것 같긴 하지만!

 

저는 좋아하므로..ㅎㅎ

 

좀 더 여러 곳에서 이런 인벤토리가 보였으면 좋겠네요!

 

 

 

그리고 읽으시면서 눈치채셨을 부분이 있는데요~

 

이 인벤토리를 그리시거나 상호작용 하시려면, 아이템의 Y위치를 신경 써주셔야 합니다!

 

아이템 위치 좌표

좌표가 이런 식으로 되어있기 때문이죠!

 

이것만 유의해주시고 본인의 환경에 맞춰서 만들어주시면 되겠습니다!

 

 

그럼 다음 포스팅에서 뵙겠습니다!

 

읽어주셔서 감사합니다!

+ Recent posts