이전 포스팅에서는 몬스터를 처치하면 확률에 따른 드랍테이블로부터 아이템을 획득할 수 있었다.
이제 획득한 아이템을 장비에 착용해보자.
아이템 착용하기 흐름
간단하게 흐름을 알고가자.
- 인벤토리에 존재하는 아이템을 클릭 시 착용 요청 패킷을 보냄. 패킷의 내용은 어떤 아이템인지(DbID), 누가 착용 요청을 했는지를 포함한다.
- 패킷을 받은 서버는 아이템을 착용하기 위해 다양한 로직을 검수한다.
- 착용 요청한 장비와 같은 부위를 착용하고 있다면 착용중인 장비를 인벤토리로 옮긴다.
- 그리고 그 결과를 DB 쓰레드에 넘겨주고 클라이언트에게 장비 해제 패킷을 보낸다.
- 마지막으로 착용 요청 받은 장비를 Equip 장비에 추가하고 인벤에서 제거한다. 이 역시DB 쓰레드에게 넘겨주고 패킷을 만들어 클라에게 알려준다.
- 패킷을 받은 클라이언트는 각종 상황에 맞춰(장비를 해제 하는것, 장비를 착용하는 것) 자신의 인벤 또는 Equip에서 제고 및 추가를 한다. 그리고 외형적으로도 보여주기위해 플레이어의 위치에 장비 Prefab을 소환한다.
생각보다 단순한 흐름이다.
코드로 살펴보자.
아이템 착용 요청하기
클라이언트에서 특정 유저가 인벤토리에서 아이템을 클릭했을 때, 아이템 착용 요청 패킷을 보내보자.
_icon.gameObject.BindEvent((e) =>
{
if (_icon.color.a == 0f)
return;
if (itemData == null)
return;
// TODO : C_USE_ITEM
if (itemData.itemType == ItemType.Consumable)
return;
if (description != null)
Managers.Resource.Destroy(description);
C_EquipItem equipPacket = new C_EquipItem();
equipPacket.ItemDbId = ItemDbID;
equipPacket.Equipped = true;
equipPacket.ObjectId = Managers.Object.MyPlayer.Id;
Managers.Network.Send(equipPacket);
});
여기서 _icon은 아이템의 이미지를 말한다. 아이템의 이미지를 클릭하면 여러 체크를 하게 되는데 일단 장비를 착용하는 과정이기때문에 물약같은 Consumable 타입은 나중에 처리하는 걸로 하자.
이렇게 아이템 착용 패킷을 보낼 수 있다.
아이템 착용 서버 처리
public void HandleEquipItem(C_EquipItem equipPacket)
{
// 착용 요청이라면, 겹치는 부위 해제
if (equipPacket.Equipped)
{
Item item = Inven.Get(equipPacket.ItemDbId);
if (item == null)
return;
if (item.ItemType == ItemType.Consumable)
return;
Item unequipItem = null;
if (item.ItemType == ItemType.Weapon)
{
WeaponType weaponType = ((Weapon)item).WeaponType;
unequipItem = Inven.EquipFind(
i => i.Equipped && i.ItemType == ItemType.Weapon
&& ((weaponType == WeaponType.Assistance && ((Weapon)i).WeaponType == weaponType)
|| (weaponType != WeaponType.Assistance)));
}
else if (item.ItemType == ItemType.Armor)
{
ArmorType armorType = ((Armor)item).ArmorType;
unequipItem = Inven.EquipFind(
i => i.Equipped && i.ItemType == ItemType.Armor
&& ((Armor)i).ArmorType == armorType);
}
if (unequipItem != null)
{
// 메모리 선적용
int tempSlot = unequipItem.Slot;
Inven.EquipRemove(unequipItem.Slot);
unequipItem.Equipped = false;
unequipItem.Slot = item.Slot;
Inven.Add(unequipItem);
// DB에 Noti
DbTransaction.EquipItemNoti(this, unequipItem);
// 클라에 통보
S_EquipItem equipOkItem = new S_EquipItem();
equipOkItem.ItemDbId = unequipItem.ItemDbId;
equipOkItem.Equipped = unequipItem.Equipped;
equipOkItem.Slot = tempSlot;
equipOkItem.ObjectId = Id;
equipOkItem.TemplateId = item.TemplateId;
equipOkItem.NextSlot = unequipItem.Slot;
Room.Broadcast(equipOkItem);
}
{
int equipSlot = -1;
if (item.ItemType == ItemType.Weapon)
{
WeaponType weaponType = ((Weapon)item).WeaponType;
if (weaponType == WeaponType.Assistance)
{
equipSlot = 8;
}
else
{
equipSlot = 6;
}
}
else if (item.ItemType == ItemType.Armor)
{
ArmorType armor = ((Armor)item).ArmorType;
switch (armor)
{
case ArmorType.Helmet:
equipSlot = 1;
break;
case ArmorType.Armor:
equipSlot = 2;
break;
case ArmorType.Boots:
equipSlot = 4;
break;
case ArmorType.Cape:
equipSlot = 5;
break;
case ArmorType.Gloves:
equipSlot = 7;
break;
}
}
// 메모리 선적용
Inven.Remove(item);
item.Slot = equipSlot;
item.Equipped = equipPacket.Equipped;
Inven.EquipAdd(equipSlot, item);
// DB에 Noti
DbTransaction.EquipItemNoti(this, item);
// 클라에 통보
S_EquipItem equipOkItem = new S_EquipItem();
equipOkItem.ItemDbId = equipPacket.ItemDbId;
equipOkItem.Equipped = equipPacket.Equipped;
equipOkItem.Slot = item.Slot;
equipOkItem.ObjectId = Id;
equipOkItem.TemplateId = item.TemplateId;
equipOkItem.NextSlot = item.Slot;
Room.Broadcast(equipOkItem);
}
}
else
{
Item item = Inven.EquipFind(i => i.ItemDbId == equipPacket.ItemDbId);
if (item == null) return;
int? slot = Inven.GetEmptySlot();
if (slot == null)
return;
int tempSlot = item.Slot;
Inven.EquipRemove(item.Slot);
item.Equipped = false;
item.Slot = (int)slot;
Inven.Add(item);
DbTransaction.EquipItemNoti(this, item);
S_EquipItem equipOkItem = new S_EquipItem();
equipOkItem.ItemDbId = equipPacket.ItemDbId;
equipOkItem.Equipped = equipPacket.Equipped;
equipOkItem.Slot = tempSlot;
equipOkItem.ObjectId = Id;
equipOkItem.TemplateId = item.TemplateId;
equipOkItem.NextSlot = item.Slot;
Room.Broadcast(equipOkItem);
}
RefreshAdditionalStat();
}
코드가 생각보다 길고 복잡하지만 위의 흐림쪽에서 말하는 모든 코드를 여기서 처리한 것이기 때문에 그렇게 보이는 것이다.
여기서 중요한 부분은 DB에 저장하는 부분은 오래걸리는 부분이기때문에 이걸 GameLogic을 담당하는 Thread에서 처리하게되면 게임이 버벅거릴 수 밖에 없다. 그렇기 때문에 DB를 저장하는 Thread를 만들어서 일을 떠넘겨주는 것이 올바른 방법이다.
클라이언트 아이템 착용
클라이언트에서 이제 온 패킷을 분석해서 아이템을 인벤에 넣거나 빼야한다.
public static void S_EquipItemHandler(PacketSession session, IMessage packet)
{
S_EquipItem equipItem = (S_EquipItem)packet;
GameObject go = Managers.Object.FindById(equipItem.ObjectId);
if (go == null)
return;
PlayerController pc = go.GetComponent<PlayerController>();
if (pc == null)
return;
if (equipItem.Equipped)
{
if(Managers.Object.MyPlayer.Id == equipItem.ObjectId)
{
Item item = Managers.Inven.Get(equipItem.ItemDbId);
Managers.Inven.Remove(item);
Managers.Inven.EquipAdd(equipItem.Slot, item);
item.Equipped = equipItem.Equipped;
item.Slot = equipItem.Slot;
if (Managers.Object.MyPlayer != null)
Managers.Object.MyPlayer.RefreshAdditionalStat();
UI_GameScene gameSceneUI = Managers.UI.SceneUI as UI_GameScene;
gameSceneUI.InvenUI.RefreshUI();
gameSceneUI.StatUI.RefreshUI();
gameSceneUI.EquipUI.RefreshUI();
}
pc.EquipItem(equipItem.TemplateId);
}
else
{
if (Managers.Object.MyPlayer.Id == equipItem.ObjectId)
{
Item item = Managers.Inven.EquipFind(i => i.ItemDbId == equipItem.ItemDbId);
Managers.Inven.EquipRemove(item.Slot);
Managers.Inven.Add(item);
item.Equipped = equipItem.Equipped;
UI_GameScene gameSceneUI = Managers.UI.SceneUI as UI_GameScene;
item.Slot = equipItem.NextSlot;
if (Managers.Object.MyPlayer != null)
Managers.Object.MyPlayer.RefreshAdditionalStat();
gameSceneUI.InvenUI.RefreshUI();
gameSceneUI.StatUI.RefreshUI();
gameSceneUI.EquipUI.RefreshUI();
}
pc.Disarm(equipItem.Slot);
}
}
public void EquipItem(int id)
{
ItemData data = null;
if (Managers.Data.ItemDict.TryGetValue(id, out data) == false) return;
switch (data.itemType)
{
case ItemType.Weapon:
WeaponData weapon = (WeaponData)data;
if(weapon.weaponType == WeaponType.Assistance)
{
if (curLeftWeapon != null) Managers.Resource.Destroy(curLeftWeapon);
curLeftWeapon = Managers.Resource.Instantiate($"Item/{data.name}", LeftHand.transform);
}
else
{
if (curRightWeapon != null) Managers.Resource.Destroy(curRightWeapon);
curRightWeapon = Managers.Resource.Instantiate($"Item/{data.name}", RightHand.transform);
if (data.name == "낡은 검")
{
curRightWeapon.transform.SetLocalPositionAndRotation(new Vector3(0,0,0), Quaternion.identity);
}
}
break;
case ItemType.Armor:
ArmorData armor = (ArmorData)data;
if(armor.armorType == ArmorType.Helmet)
{
if (curHeadItem != null) Managers.Resource.Destroy(curHeadItem);
curHeadItem = Managers.Resource.Instantiate($"Item/{data.name}", Head.transform);
}
else
{
}
break;
}
}
public void Disarm(int index)
{
if (index < 0) return;
switch (index)
{
case 1:
if (curHeadItem != null) Managers.Resource.Destroy(curHeadItem);
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
if (curRightWeapon != null) Managers.Resource.Destroy(curRightWeapon);
break;
case 7:
break;
case 8:
if (curLeftWeapon != null) Managers.Resource.Destroy(curLeftWeapon);
break;
}
}
아직 부위별로 전부 만들지는 않고 어차피 단순 노가다에 불구하기 때문에 그냥 몇개는 비워뒀다.
결과
아이템을 착용하고 벗는 모습이다.
'Unity > 온라인 RPG' 카테고리의 다른 글
[Unity 3D] UI 이미지 드래그 그리고 퀵슬롯 등록하기 (0) | 2024.06.21 |
---|---|
[Unity 3D] 스탯 포인트 사용하기 (0) | 2024.06.11 |
[Unity 3D] 아이템 드랍 및 획득 (0) | 2024.05.29 |
[Unity 3D] 각종 UI 제작! Stat창, Inven창, Equip창 (0) | 2024.05.28 |
[Unity 3D] 오브젝트 전투 및 몬스터의 플레이어 추격 (0) | 2024.05.13 |