이전 시간에 포스팅한 유니티 Drag 이벤트를 통해 간단한 숫자 머지 게임을 작성해보겠습니다. 게임의 내용은 숫자를 가지고 있는 아이템이 병합될 때 숫자가 상승하며 새로운 아이템 생성되는 내용입니다. 사실 게임이라고 하기에는 내용이 부족하지만 merge 게임의 핵심기능이 포함되어있는 포스팅입니다.
전에 작성한 UI Object 드래그하는 방법에 있는 코드를 놓고 추가 작성했으므로 UI 구성 등 기본 스크립트에 대한 자세한 내용이 필요하시다면 이전 포스팅을 참고해 주시기 바랍니다.
1. Item 프리팹 생성 및 Slot 게임 오브젝트에 Grid Layout Group 추가 하기
이전 포스팅의 Icon을 대체하는 프리팹으로 Icon과 동일하게 UI > Image로 오브젝트를 생성한 후 Canvas Group, DragManager.cs, Item.cs를 컴포넌트로 추가하고 인스펙터를 통해 Item의 colors 배열에 9가지 색을 지정해 줬습니다. 마지막으로 자식으로 UI > Legacy > Text 컨포넌트를 추가하고 프리팹으로 만듭니다.
모든 슬롯 게임 오브젝트에 Grid Layout Group을 추가합니다. 이유는 아이템의 생성 이동 등 슬롯과 위치 보정이 필요한데 이런 부분을 코드 없이 처리하기 위함입니다. Grid Layout Group은 자식으로 들어오는 transform을 자동 정렬합니다.
2. 추가 스크립트 및 변경 내역
Item.cs (숫자 정보를 담고 있는 클래스 - 아이템 아이콘의 색상과 숫자를 설정하는 기능)
InventoryManager.cs (인벤토리를 관리하는 클래스 - 빈 슬롯을 확인하고 아이템을 생성하는 기능)
DragManager.cs (드래그 이벤트를 관리하는 클래스 - 이전 포스팅의 IconDrag 클래스 )
Slot.cs (드롭 이벤트 감지하는 클래스 - 드롭 이벤트에서 판단하여 아이템을 장착, 병합, 위치교환 기능을 수행)
▶Item.cs
아이템의 숫자와 색을 지정하는 함수로 되어있고 색은 숫자를 기준으로 변하도록 되어있습니다. 추가로 숫자 설정 시 transform을 받아서 부모로 지정합니다.
using UnityEngine;
using UnityEngine.UI;
// 이 스크립트는 드래그매니저를 필수 요구함.
[RequireComponent (typeof (DragManager))]
public class Item : MonoBehaviour
{
// 아이템 숫자를 저장 할 변수
public int number;
// 아이템의 적용할 색 배열
[SerializeField] Color[] colors;
// 생성된 아이템의 설정 함수
public void SetItem(int newValue, Transform newParent)
{
number = newValue;
// 아이템 배경색 할당
GetComponent<Image>().color = SetColor(number);
// 자식의 텍스트 컴포넌트에 숫자 할당
GetComponentInChildren<Text>().text = number.ToString();
transform.SetParent(newParent);
}
//숫자 별로 색 할당을 위한 함수
public Color SetColor(int colorValue)
{
if(colorValue < 10)
return colors[colorValue -1];
else
return Color.black;
}
}
▶InventoryManager.cs
아이템 생성이나 병합은 코딩 관점에서 어차피 생성으로 통일되기 때문에 주 기능은 아이템 생성입니다. 또한 아이템을 생성하려면 빈 슬롯을 인지해야 하기 때문에 빈 슬롯의 gameobject를 리스트로 받아서 배열로 변환 후 반환하는 함수가 포함되어있습니다. 생성 시는 빈 슬롯을 참고하고 병합 시에는 시점상 생성돼야 할 슬롯을 Drop 이벤트를 통해 알고 있으므로 Slot스크립트에서 transform 값을 받아와 병합되어 생성되는 신규 아이템 설정 함수의 인자로 전달합니다.
using System.Collections.Generic;
using UnityEngine;
public class InventoryManager : MonoBehaviour
{
//InventoryManager 인스턴스에 바로 접근하기 위한 static 변수
public static InventoryManager inst;
// 슬롯들
Slot[] inventorySlots;
//Slot 컨포넌트들을 참조할때 기준점으로 사용할 부모 트랜스폼
public Transform innerPanelTransform;
// Item의 프리팹
public GameObject itemPrefab;
void Start()
{
inst = this;
// 모든 슬롯 참조하기
inventorySlots = innerPanelTransform.GetComponentsInChildren<Slot>();
}
// 빈슬롯을 배열로 반환하는 함수
GameObject[] GetEmptyInventorySlots()
{
List<GameObject> emptySlots = new List<GameObject>();
foreach(Slot s in inventorySlots)
{
if(s.item == null)
emptySlots.Add(s.gameObject);
}
if(emptySlots.Count == 0)
return null;
else
return emptySlots.ToArray();
}
// 신규 아이템 생성 함수
public void CreateItem()
{
GameObject[] emptySlots = GetEmptyInventorySlots();
if(emptySlots != null)
{
int randomNum = Random.Range(0, emptySlots.Length);
var item = Instantiate(itemPrefab, emptySlots[randomNum].transform.position, Quaternion.identity);
item.GetComponent<Item>().SetItem(1, emptySlots[randomNum].transform);
}
}
// 병합 된 신규 아이템 생성 함수
public void CreateUpgradeItem(int newNumber, Transform newParent)
{
var item = Instantiate(itemPrefab, Vector3.zero, Quaternion.identity);
item.GetComponent<Item>().SetItem(newNumber, newParent);
}
}
▶DragManager.cs
전 포스팅 IconDrag.cs랑 동일한 내용입니다. 변경된 부분은 변수 이름과 onDragParent의 참조값을 Inventory의 Transform으로 코드에서 가져오는 내용입니다.
using UnityEngine;
using UnityEngine.EventSystems;
public class DragManager : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
public static GameObject beingDraggedItem;
Vector3 startPosition;
Transform onDragParent;
[HideInInspector]
public Transform startParent;
public void OnBeginDrag(PointerEventData eventData)
{
beingDraggedItem = gameObject;
startPosition = transform.position;
startParent = transform.parent;
onDragParent = InventoryManager.inst.transform;
GetComponent<CanvasGroup>().blocksRaycasts = false;
transform.SetParent(onDragParent);
}
public void OnDrag(PointerEventData eventData)
{
transform.position = Input.mousePosition;
}
public void OnEndDrag(PointerEventData eventData)
{
beingDraggedItem = null;
GetComponent<CanvasGroup>().blocksRaycasts = true;
if(transform.parent == onDragParent)
{
transform.position = startPosition;
transform.SetParent(startParent);
}
}
}
▶Slot.cs
이전 포스팅의 Icon함수를 프로퍼티로 대체하였지만 실제 기능은 변경사항이 없습니다. OnDrop 내부 코드는
기존 빈 슬롯이면 아이템을 장착하는 코드 외에 다른 숫자의 아이템이 드롭됐을 때 위치를 변경하거나 같은 숫자가 드롭되었을 때 병합을 처리하는 코드가 추가되었습니다.
using UnityEngine;
using UnityEngine.EventSystems;
public class Slot : MonoBehaviour, IDropHandler
{
//이전 포스팅의 Icon함수를 대체 하는 프로퍼티
public GameObject item
{
// item에 접근하려고 할때(읽을 때) 실행
get{
// transform (rect transform)에 자식 존재여부 확인
if(transform.childCount > 0)
{
// 슬롯에 아이템이 있으면 아이템의 gameobject를 리턴
return transform.GetChild(0).gameObject;
}
// 슬롯에 아이템이 없다면 null을 리턴
return null;
}
}
// 스크립트가 부착된 RectTransform에 포인터가 놓여질때 해당 이벤트가 발생
public void OnDrop(PointerEventData eventData)
{
// 슬롯이 비어있다면 실행
if(!item)
{
// 이전 포스팅에 존재하던 위치보정 코드는 슬롯게임오브젝트에
// grid layout group을 추가하여 자동 위치 보정되도록 변경
DragManager.beingDraggedItem.transform.SetParent(transform);
}
else // 슬롯이 비어있지 않다면 실행
{
//아이템 간 위치변경 또는 병합을 위한 두 아이템 참조
Item dragItem = DragManager.beingDraggedItem.GetComponent<Item>();
Item soltItem = item.GetComponent<Item>();
// 두 아이템의 숫자가 동일한 경우 실행
if(dragItem.number == soltItem.number)
{
//숫자 백업 후 병합 될 두 아이템 모두 Destory
int backupNum = dragItem.number;
Destroy(dragItem.gameObject); Destroy(soltItem.gameObject);
//인벤트리 매니저를 통해 상위 아이템을 생성
InventoryManager.inst.CreateUpgradeItem(backupNum+1, transform);
}
else // 두 아이템의 숫자가 다른경우 위치변경
{
DragManager.beingDraggedItem.transform.SetParent(transform);
item.transform.SetParent(DragManager.beingDraggedItem.GetComponent<DragManager>().startParent);
}
}
}
}
3. 마무리
포스팅에서 작성된 item을 의미 있는 데이터로 구조화하고 확장하면 머지 게임, 인벤토리 구현 외에 많은 게임 제작에 활용될 수 있다고 생각합니다.
'유니티 이야기' 카테고리의 다른 글
[Unity] 구글 시트에서 데이터 읽어오기 (0) | 2022.09.04 |
---|---|
[Unity] 2022 구글 애드몹 광고 통합 튜토리얼 (0) | 2022.09.03 |
[Unity] UI Object Drag 하는 방법 (0) | 2022.09.01 |
[Unity] 2022 유니티 Ads Tutorial (1) | 2022.08.31 |
[Unity] 2022 Codeless 인앱 결제(IAP) Tutorial (2) (0) | 2022.08.30 |
댓글