배틀레드에서의 상호작용은 두가지 타입으로 구현되었습니다.
첫 번째.
플레이어가 바라보는 카메라를 기준으로 상호작용.
두 번째.
플레이어블 캐릭터의 현재 위치를 기준으로 상호작용.
첫 번째는 몬스터 사냥 후에 드랍된 아이템을 획득할 때, 사용됩니다.
두 번째는 플레이어가 상호작용 오브젝트(합성대, 던전, 보물상자)에 다가갔을 때, 사용합니다.
상단의 링크는 카메라 기준 상호작용 기법을 이용하여 드랍한 아이템을 획득하는 영상입니다.
사진과 같이 플레이어가 카메라를 내렸을 때, 바닥에 놓인 아이템을 확인하고 UI 형태로 출력하는 것을 확인할 수 있습니다.
이렇게 출력된 버튼을 클릭하면 드랍한 아이템을 실제로 획득하여 인벤토리에 저장하게 됩니다.
조금 더 자세한 상호작용 시스템의 구조는 다음과 같습니다.
ItemDropManager 클래스에서 카메라가 바라보는 방향(카메라의 forward)으로 ray를 발사합니다.
이때, ray가 Ground 레이어와 충돌한다면, 충돌지점을 기준으로 OverlapSphere를 사용하여, 일정 범위 내의 객체를 탐색합니다.
만약 동일한 id값을 가지는 객체가 없다면 아이템을 출력하는 스크롤뷰 UI에 획득한 아이템을 출력합니다.
동일한 id값을 가진 객체가 있다면, 중복 객체인 것으로 판단. 무시합니다.
상단의 코드가 카메라의 forward 방향으로 ray를 쏴서, 객체를 탐색하는 로직입니다.
DropItem 클래스는 드랍된 아이템에 컴포넌트로써 부착된 클래스이며, 해당 객체는 ItemClass와 id 값 등을 저장하여 중복 확인 및 아이템 객체 가져오기 등을 할 수 있습니다.
리스트를 순회하며 중복된 id를 가진 객체가 있는 확인 후, 없다면 UI를 출력하여 플레이어가 아이템을 가져올 수 있도록 합니다.
이후, 태그를 확인하여 아이템의 종류에 따라 필요한 스프라이트를 출력하는 등의 UI 작업을 합니다.
마지막으로 버튼 리스너를 연결하고, 리스트에 Add하는 것으로 마무리합니다.
해당 함수는 몬스터가 죽을 때, 호출되는 함수입니다.
GetGroundPosition 함수를 호출하여, 아이템 객체를 놓을 수 있는 지면 좌표를 가져옵니다.
이후, index에 따라서 무기 혹은 성유물로 분기하여 랜덤으로 아이템 클래스를 Get하여, 초기화합니다.
지면 좌표를 얻을 수 있는 함수입니다.
현재 Transform을 기준으로, Down 방향으로 ray를 쏴서, 가장 먼저 부딪치는 Ground 레이어의 Vector3 값을 리턴합니다.
랜덤으로 아이템을 얻어서 Return하는 함수입니다.
디폴트 아이템 클래스 객체를 가져와 태그가 일치하는 객체를 새로운 리스트에 저장합니다.
이후, 아이템을 가져올 인덱스를 랜덤으로 Get하여, 리스트에서 깊은복사하여 가져와, 저장합니다.
UI 버튼을 클릭했을 시에 호출되는 함수입니다.
태그에 따라서 분기하여, 플레이어의 인벤토리에 저장합니다.
이후, List에서 제거하고 오브젝트 풀을 Return하여 획득한 객체를 지웁니다.
상단의 링크들은 현재 위치를 기준으로 오브젝트와 상호작용하는 영상입니다.
보다시피 카메라가 정면에서 빗겨서 바라보고 있음에도 상호작용할 수 있는 객체의 UI가 출력된느 것을 확인할 수 있습니다.
이는, 카메라의 Forward가 아니라 위치를 기준으로 하기 때문입니다.
조금더 자세히 상호작용 시스템의 구조에 대해 살펴보면,
다음과 같습니다.
ViewRange 객체(이전 Part1 게시글에서 설명함)를 이용해서, 캐릭터와 근접한 상호작용 객체(레이어로 판단)를 탐지합니다.
출력된 객체를 저장하는,
Dictionary<InteractionObject, DropItem_UI>() 딕셔너리에 상호작용 오브젝트와, 출력된 UI를 함께 Add합니다.
중복 체크를 거쳐서 스크롤뷰에 해당하는 객체를 출력하여, 플레이어가 클릭할 수 있도록 합니다.
덧붙여 모든 상호작용 오브젝트의 이벤트 리스너를 관리하는 ObjectManager를 싱글턴 패턴으로 접근하여, 객체를 넘기고 버튼을 연결합니다.
클릭되었다면, 딕셔너리에서 제외하고 오브젝트풀로 리턴하여 제거합니다.
여기서 주의해야하는 점은,
상호작용 객체 중에는 반복 상호작용을 허용하는 객체와 허용하지 않는 객체로 나뉜다는 것입니다.
그렇기에, 오브젝트 매니저에 접근하여 객체의 반복 여부를 관리하는 리스트를 참조 후, 반복을 불허한다면 객체 생성을 막아야 합니다.
캐릭터 매니저에서 호출되는, 월드 체크 함수입니다.
탐지한 ViewRange를 옵저버 패턴으로 받아서, 주위의 상호작용 오브젝트를 가져옵니다.
받아온 상호작용 오브젝트는 딕셔너리 형태로 저장되며, InteactionObject가 값을 false로 가지는지 체크 하여, 반복 여부를 체크할 수 있습니다.
객체 생성 후에는 클릭 이벤트 리스너를 연결하기 위해서, 오브젝트 매니저의 FunctionConnecter() 함수에 상호작용 객체를 넘깁니다.
또한, 클릭된 객체(반복을 불허하는)는 Dictionary에서 Remove하고 오브젝트 풀에 리턴합니다.
마지막으로 오브젝트 풀을 캐릭터의 주위에 상호작용 오브젝트가 존재하지 않는다면, 즉. List의 Count가 0이라면 딕셔너리 내의 모든 객체를 제거하고 출력 UI도 오브젝트 풀로 Return하는 것으로 마무리합니다.
상호작용 오브젝트를 매개변수로 받아서 분기합니다.
여기서 Name은, InteractionObject클래스에 저장된 public string 변수 입니다.
합성대와 던전 입구 오브젝트에 연결된 버튼 이벤트 함수입니다.
합성대는 UI_Manager 클래스의 멤버 클래스인 Synthesis 클래스의 인스턴스를 생성합니다.
이후, Synthesis 인스턴스의 SynthesisObjectFunc_UI_Print() 함수를 호출하여, 합성대의 기능을 구현합니다.
던전 입구는 SceneLoardManager의 SceneLoadder 함수를 호출하여, 던전 씬을 불러오도록 해줍니다.
던전 중간 통로를 Open 했을 때,
캐릭터를 정해진 포인트로 이동하고 오브젝트 방향을 바라보도록 합니다.
이후, 이펙트와 함께, 문 객체를 SetActiveFalse 하여, 문을 엽니다.
보물상자를 열었을 때는, 아래와 같이 출력용 UI 오브젝트에 출력됩니다.
아이템을 깊은 복사하여, 코루틴 함수에 호출합니다.
이후, 출력용 UI 컴포넌트를 관리하는 클래스인 DataPrintScreenScrollManager에 접근하여, Item들을 List형태로 전달합니다.
isPrintSkip 변수는, 출력 과정을 스킵했는지 체크하는 변수입니다.
출력 객체의 위에 존재하는 투명한 버튼 객체에 isPrintSkip 변수를 true로 바꾸는 이벤트를 연결했습니다.
코루틴 함수를 통해서, GetItemPrint가 스킵되지 않았다면, 일정한 주기를 두고 GetItemPrint 함수를 호출함으로써, 데이터를 출력합니다.
만약 스킵이 true라면, 인터벌 값을 극도로 줄여, 빠르게 데이터를 출력하도록 합니다.
아이템 클래스를 매개변수로 받아서, 데이터를 출력하는 함수입니다.
파티클 이펙트를 UI에도 사용할 수 있도록 해주는 오픈 소스 코드를 이용하여, 이펙트를 생성합니다.
이후, 매개변수로 받은 아이템 클래스의 데이터를 기반으로 UI 컴포넌트를 채웁니다.
인벤토리와 플레이어 인포창을 구현하면서 생성해 두었던, 코드를 재활용하여(static 함수 이용) 출력하였습니다.
합성대는 UI매니저 클래스의 멤버 클래스인 Synthesis 클래스에서 기능을 담당합니다.
합성대의 스크롤뷰 출력은 다른 UI 출력과는 다르게 모든 아이템을 저장한, 디폴트 데이터의 아이템 리스트를 기반으로 출력합니다.
여기에, 만약 아이템의 합성이 가능한 경우에는 백그라운드 이미지와 알파값을 조정하고, 합성이 불가능한 객체의 경우에는 백그라운드 색을 회색으로, 알파값을 낮추도록 합니다.
여기에 생성할 수 없는 객체의 경우에는 아이템을 얻을 수 있는 좌표를 PathFind로 넘겨서, 플레이어가 찾아갈 수 있도록 해줍니다.
아이템 합성은 크게 두 가지 타입으로 나뉩니다.
장비(무기,성유물) 합성과 성장재화 합성.
기본적으로 합성은 저등급의 아이템을 소모하여, 고등급의 아이템을 만드는 것입니다.
하지만, 장비의 경우에는 저등급의 아이템에 레벨업과 돌파 등이 가능하기 때문에, 무작위로 소모해서는 안됩니다.
그렇기 때문에, 플레이어가 직접 합성 재료로 소모할 아이템을 고를 수 있어야 합니다.
아래는 각 타입에 따른 합성부 출력입니다.
우측의 아이템 합성부는 클릭한 아이템 객체를 변수를 기준으로 세팅합니다.
구현 방식은, 플레이어 인포에서 구현했던 방식과 유사합니다.
SelectButton을 스크롤뷰로 출력하고, 해당 버튼에 선택한 아이템 객체를 저장합니다.
3개의 장비를 합성하는 것으로 상위 장비를 제조할 수 있으며, 만약 여분의 객체가 있다면.
예를 들어 5개의 아이템을 재료로 넣는다면, 1개의 아이템을 합성하고 선택한 3개의 아이템을 제거하는 대신에 2개의 아이템은 무시합니다.
선택한 아이템 객체를 기준으로 세팅하는 것은 동일합니다.
다만, 육성재화의 경우에는 플레이어가 재료로 소모할 아이템을 따로 고르지 않고 한번에 합성할 개수를 선택한다는 점에서 다릅니다.
개수의 선택은 스크롤바를 드래그하여 수정하거나, +- 버튼을 클릭하는 것으로 바꿀 수 있습니다.
데이터 초기화 등의 코드는 생략하도록 하겠습니다.
상단의 코드는 현재 선택된 아이템 타입에 따라서, 스크롤 뷰에 디폴트 아이템들을 출력하는 함수입니다.
조건을 모두 만족하여 합성이 가능한 상태에서
아이템을 선택하면, 위에서 설명했던 바와 같이 우측의 출력부에 선택된 타입에 따라서 합성할 아이템에 따라, UI를 초기화하고 기능을 구현합니다.
조건을 불만족하였다면, PathFind 클래스의 함수를 호출하여, 해당 아이템을 얻을 수 있는 위치를 선택하여 안내받을 수 있도록 합니다.
위 코드가 바로 이에 해당됩니다.
SynthesisButtonSelect 함수는 합성이 가능한 객체를 클릭했을 때, 호출되는 함수로
필요한 변수들을 초기화하고 출력부를 출력하는 함수를 호출합니다.
만약 합성 가능 객체가 아니라면, PathFind할 수 있도록 관련 함수를 호출합니다.
위 함수는 장비 합성의 출력부를 출력하는 함수입니다.
게임매니저의 오브젝트 풀을 호출하여, UI를 세팅하고 각 UI에 버튼을 연결합니다.
또한, 출력부의 각 컴포넌트에 선택된 아이템 객체에 대한 정보를 출력할 수 있도록 합니다.
여기서 장비 합성으로 만들 상위 아이템의 갯수를 나타내기 위해서, 현재 선택된 객체 숫자. equips_currentNum 변수를 이용하여 계산 후 텍스트를 출력합니다.
위 로직은 육성재화 합성의 출력부를 출력하는 함수입니다.
장비 합성과는 다르게, 슬라이더와 +- 버튼에 각 버튼 이벤트를 연결합니다.
그리고 필요한 아이템 객체와 합성할 상위 아이템을 각 컴포넌트에 출력합니다.
다음은 사용버튼 함수입니다.
기존의 아이템, 광물과 골드 등의 재화를 실질적으로 소모하고 상위 등급의 장비를 깊은복사로써 생성하여 플레이어 데이터에 삽입하는 역할을 합니다.
이때, 장비와 육성재화는 각 요구하는 재화가 다릅니다.
장비는 광물과 하위등급의 아이템.
육성재화는 하위등급의 아이템과 골드.
합성할 갯수만큼 필요로 하는 재화를 플레이어가 보유하고 있는지 체크하여 삭감하고 없을 경우에는 리턴하도록 합니다.
이후 모든 데이터
(선택한 상위 아이템, 선택한 아이템 개수, 장비데이터(게임매니저에 저장된, 합성 데이터 테이블 구조체) 등을 초기화 합니다.)