
어제는 캐릭터를 만들고 움직이게 만들었다.
오늘은 Scriptable Object를 이용해 캐릭터의 정보를 적용해볼 것이다.
Scriptable Object를 사용해 캐릭터 정보를 관리한다면, 만약 같은 유형의 정보를 가지고 있는 캐릭터들 간에 코드를 재사용할 수 있게 되며, 만약 같은 값의 정보를 가진 캐릭터가 매우 많게 된다면(ex. 적 몬스터) 그 모든 적 몬스터 객체에 그 정보 값이 저장 되어 있는 것 보다, 이 Scriptable Object에 있는 데이터를 그대로 불러오기만 하면 되기 때문에 유용하다.
이 프로젝트에 들어가는 캐릭터 정보는 아직 많지 않다. 때문에 굳이 Scriptable Object를 이용할 필요가 있나 의문이 들 수는 있지만, 연습삼아 Scriptable Object을 이용해본 것이다.
1. 정보가 담긴 Scriptable Object 생성
using UnityEngine;
[CreateAssetMenu(fileName = "CharaDataSO", menuName = "Project/CharaData/Default", order = 0)]
public class CharaDataSO : ScriptableObject
{
[Header("Character Data")]
public string charaName;
public float charaSpeed;
}
캐릭터의 데이터가 담긴 CharaDataSO를 만들기 위해, ScriptableObject를 상속받았다.
위에 쓰인 [CreateAssetMenu(fileName = "CharaDataSO", menuName = "Project/CharaData/Default", order = 0)]는
만든 Scriptable Object 를 생성하기 위해 필요하다.


이렇게 생성할 수 있는 메뉴를 만들게 해주는 코드이다. fileName은 생성시의 디폴트 명칭을 정한다.
생성하면 인스펙터 창에 다음과 같이 값을 입력할 수 있다.


2. 해당 SO를 받는 데이터 클래스 생성
using System;
using UnityEngine;
[Serializable]
public class CharaData
{
public CharaDataSO dataSO;
}
위의 코드처럼 1에서 생성한 CharaDataSO를 받을 수 있다.
3. 해당 데이터를 적용하는 DataHandler 생성
2까지 하면 캐릭터의 데이터가 완성된다.
이 데이터를 DataHandler를 이용해 캐릭터에 적용할 예정이다.
using UnityEngine;
public class CharaDataHandler : MonoBehaviour
{
[SerializeField] private CharaData baseData;
public CharaData CurrentData { get; private set; }
private void Awake()
{
UpdateCharacterData();
}
private void UpdateCharacterData()
{
CharaDataSO dataSO = null;
if (baseData.dataSO != null)
{
dataSO = Instantiate(baseData.dataSO);
}
CurrentData = new CharaData { dataSO = dataSO };
}
}
[SerializeField] private CharaData baseData;에서는 위에서 만든 CharaData 값을 받을 것이며,
public CharaData CurrentData { get; private set; }가 실제 적용되는 값이 될 것이다.
CharaDataSO dataSO = null;
if (baseData.dataSO != null)
{
dataSO = Instantiate(baseData.dataSO);
}
에서는 받을 CharaDataSO를 초기화 한 다음, baseData.dataSO를 한번 더 생성한 다음 부여했다.
CurrentData = new CharaData { dataSO = dataSO };
는 CurrentData를 생성하는 부분인데, dataSO = dataSO 부분이 헷갈릴 수도 있지만,
CurrentData.dataSO = baseData.dataSO라고 생각하면 된다.

CharaDataHandler 컴포넌트를 캐릭터에 추가하고, BaseData에 PlayerDataSO를 삽입하면 캐릭터 정보가 적용이 된다.
4. 실 적용
그럼 이전에 만든 캐릭터의 움직임을 구현하는 코드를 다음과 같이 바꿔줄 수 있을 것이다.
using UnityEngine;
public class CharaMovement : MonoBehaviour
{
private float playerX;
private float playerY;
private CharaController movementController;
private Rigidbody2D movementRigidbody;
private Vector2 movementDirection = Vector2.zero;
// 추가된 부분
private CharaDataHandler stats;
// -----
private void Awake()
{
movementController = GetComponent<CharaController>();
movementRigidbody = GetComponent<Rigidbody2D>();
// 추가된 부분
stats = GetComponent<CharaDataHandler>();
// -----
}
private void Start()
{
movementController.OnMoveEvent += Move;
}
private void Update()
{
playerX = transform.position.x;
playerY = transform.position.y;
Camera.main.transform.position = new Vector3(playerX, playerY, Camera.main.transform.position.z);
}
private void FixedUpdate()
{
// 움직임함수 작동
ApplyMovement(movementDirection);
}
private void Move(Vector2 direction)
{
// 방향 설정
movementDirection = direction;
}
private void ApplyMovement(Vector2 direction)
{
// 움직임 함수 변경된 부분
direction = direction * stats.CurrentData.dataSO.charaSpeed;
// 변경된 부분 끝
movementRigidbody.velocity = direction;
}
}
움직임을 구현하는 코드에서, 캐릭터의 정보를 받아올 수 있게하고, 그 받은 정보의 스피드 값을 이동 메서드에 적용했다.
만약 여러 캐릭터를 구현한다면, 직접 숫자를 바꿔 넣지 않더라도, 사전에 설정된 스피드 값에 따라 이동속도를 달라지게 할 수 있을 것이다.
------
TextMeshPro와 TextMeshProUGUI
using TMPro;
using UnityEngine;
public class NameInput : MonoBehaviour
{
[SerializeField] private TextMeshPro inputPlayerName;
[SerializeField] private CharaDataHandler charaData;
public void FinishButton()
{
charaData.CurrentData.dataSO.charaName = inputPlayerName.text;
GameManager.instance.isPause = false;
this.gameObject.SetActive(false);
}
public void OpenNameEdit()
{
GameManager.instance.isPause = true;
this.gameObject.SetActive(true);
}
}
캐릭터의 이름을 변경하는 기능을 만들고, 그 입력받은 값에 따라 실제 캐릭터의 데이터도 변경할려고 했다.
그러기 위해 inputPlayerName의 text값을 구하기 위해, inputPlayerName의 TextMeshPro를 드래그하여 참조하려 했으나, 참조 자체가 활성화 되지 않았다.
해당 문제는 의외로 간단한 부분이었는데, UI에서 사용하는 TextMeshPro와 그냥 TextMeshPro가 구분되어 있다는 점이었다.
내가 참조하고자 했던 TextMeshPro는 UI에서 사용하던 TextMeshPro였기에 TextMeshPro에서 참조하는 것이 불가능 했다.
위 코드를 아래와 같이 고칠 수 있었다.
using TMPro;
using UnityEngine;
public class NameInput : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI inputPlayerName;
[SerializeField] private CharaDataHandler charaData;
public void FinishButton()
{
charaData.CurrentData.dataSO.charaName = inputPlayerName.text;
GameManager.instance.isPause = false;
this.gameObject.SetActive(false);
}
public void OpenNameEdit()
{
GameManager.instance.isPause = true;
this.gameObject.SetActive(true);
}
}
UI에서의 TextMeshPro를 받기 위해서는 TextMeshProUGUI를 이용하여 받을 수 있었다.
'내일배움캠프 TIL' 카테고리의 다른 글
| 내일배움캠프 20일차 TIL "애니메이터와 애니메이션 컨트롤러" (0) | 2024.10.11 |
|---|---|
| 내일배움캠프 19일차 TIL "맵 내 존재하는 캐릭터들 이름 출력" (0) | 2024.10.10 |
| 내일배움캠프 17일차 TIL "Input System을 이용한 캐릭터 조작법" (0) | 2024.10.07 |
| 내일배움캠프 16일차 TIL "ref, out" (0) | 2024.10.04 |
| 내일배움캠프 15일차 TIL "상태 머신" (0) | 2024.10.02 |