뒤끝서버의 차트에서 데이터를 받아온 다음에 class에 저장해서 사용하면 멀티스레드에서 동작할 수 없다. 그래서 BlobAsset으로 저장해서 멀티스레드에서 동작하도록 만들어야 한다.
선행 학습
[Unity DOTS/Dots Custom Manual] - UNITY DOTS - BLOB assets 에 대해 알아보자
뒤끝서버 관련 지식 필요
[프로그램 강좌/ 유니티 + 뒤끝서버] - 뒤끝서버 + 유니티 강좌 #1 시작하기
뒤끝서버 관련 지식은 꼭 알 필요는 없습니다. 서버에서 데이터를 받아와서 BlobAsset으로 만드는 과정에 대해 설명합니다.
뒤끝서버 관련 작업
뒤끝서버 관련 내용은 이 글의 주요 주제가 아니므로 빠르게 넘어가겠습니다.
1. 뒤끝서버 Initialize
https://www.thebackend.io/ 로 가서 계정과 프로젝트를 만들고 뒤끝콘솔과 유니티의 프로젝트 셋팅을 완료합니다.
[프로그램 강좌/ 유니티 + 뒤끝서버] - 뒤끝서버 + 유니티 강좌 #1 시작하기
2. 회원 가입과 로그인 구현
뒤끝서버 커스텀 계정 회원 가입과 로그인을 구현합니다.
[프로그램 강좌/ 유니티 + 뒤끝서버] - 뒤끝서버 + 유니티 #2 회원가입 / 로그인
public class BackEndAuthentication : MonoBehaviour
{
public void OnClickSignUp()
{
BackendReturnObject bro = Backend.BMember.CustomSignUp("MyTestID", "000000");
if (bro.IsSuccess())
{
Debug.Log("회원가입에 성공했습니다");
}
}
public void OnClickSignIn()
{
BackendReturnObject bro = Backend.BMember.CustomLogin("MyTestID", "000000");
if (bro.IsSuccess())
{
Debug.Log("로그인 성공");
}
}
}
3. 차트 만들기
뒤끝서버의 차트는 몬스터, 경험치 등의 테이블 정보를 올려놓는 기능입니다. 만들 차트의 내용은 아래와 같습니다. 엑셀로 데이터를 입력 후 CSV 로 저장합니다. 뒤끝 콘솔에 업로드할 예정입니다.
아래의 정보를 보고 차트를 만들고 위 데이터를 차트에 업로드 합니다.
https://developer.thebackend.io/unity3d/guide/chart/saveChart/
차트 관련 구버전 내용이지만 참고하세요.
[프로그램 강좌/ 유니티 + 뒤끝서버] - 뒤끝서버 + 유니티 #21 게임 차트 - 저장하기
* 이 글의 주제는 뒤끝서버가 아니라서 간략하게 건너뛰었습니다.
[플레이어 경험치 데이터] 구조체 정의
1. LevelUpData
아래의 코드를 작성합니다.
using System.Collections;
using Unity.Collections;
using Unity.Entities;
public struct LevelUpData
{
public int ExperiencePoints;
public FixedString32Bytes LevelName;
}
#코드 설명
BlobAsset은 관리되는 데이터는 저장할 수 없습니다. 그래서 String이 아닌 FixedString32Bytes 타입으로 만들었습니다.
2. LevelUpBlobAsset
위 구조체를 BlobArray 로 값을 저장할 구조체를 만듭니다.
using Unity.Entities;
public struct LevelUpBlobAsset
{
public BlobArray<LevelUpData> Array;
}
#코드 설명
BlobAssetArray는 번거롭지만 데이터 구조체를 Array로 들고 있을 구조체를 만들어야 합니다.
3. PlayerExperienceData
아래의 코드를 작성합니다.
using Unity.Entities;
[GenerateAuthoringComponent]
public struct PlayerExperienceData : IComponentData
{
public BlobAssetReference<LevelUpBlobAsset> LevelUpReference;
public int CurrentLevel;
public int CurrentExperience;
}
#코드 설명
외부에서 BlobAssetArray를 사용하려면 해당 데이터의 Root값인 BlobAssetReference를 이용해야 합니다.
서버에서 받은 데이터를 BlobAsset 만들기
뒤끝서버에서 받아온 차트의 데이터를 BlobAsset으로 만들어 보겠습니다.
1. 뒤끝서버에서 차트 불러와서 로컬에 저장하기
using BackEnd;
using LitJson;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BackEndChart : MonoBehaviour
{
public List<LevelUpData> GetChartAndSave()
{
// 55624는 저의 차트 번호입니다. 여러분의 차트 번호를 넣으세요.
var bro = Backend.Chart.GetOneChartAndSave("55624");
if (!bro.IsSuccess())
{
Debug.LogError(bro.ToString());
return null;
}
JsonData json = bro.FlattenRows();
List<LevelUpData> itemList = new List<LevelUpData>();
// 차트의 데이터를 LevelUpData에 넣습니다.
for (int i = 0; i < json.Count; i++)
{
LevelUpData item = new LevelUpData();
item.LevelName = json[i]["LevelName"].ToString();
item.ExperiencePoints = int.Parse( json[i]["ExperiencePoints"].ToString());
itemList.Add(item);
}
Debug.Log("불러온 차트의 아이템 개수 : " + itemList.Count);
return itemList;
}
}
2. SetUpEXPData
LevelUpData로 저장한 값을 BlobAsset으로 만들겠습니다. 아래의 코드를 작성해 주세요.
using System.Collections.Generic;
using Unity.Collections;
using Unity.Entities;
using UnityEngine;
public class SetUpEXPData : MonoBehaviour
{
private PlayerExperienceData GetPlayerData()
{
// 1. BlobBuilder 메모리 할당
var blobBuilder = new BlobBuilder(Allocator.Temp);
// 2. BlobAsset 루트 만들기
ref var levelUpRoot = ref blobBuilder.ConstructRoot<LevelUpBlobAsset>();
// 3. BlobArray 초기화
var levelUpArray = blobBuilder.Allocate(ref levelUpRoot.Array, 3);
// 뒤끝서버에서 불러온 차트의 데이터를 LevelUpData로 저장하기
List<LevelUpData> lvData = FindObjectOfType<BackEndChart>().GetChartAndSave();
if (lvData == null)
{
Debug.LogError("PlayerData null");
return new PlayerExperienceData { };
}
// 4. BlobArray에 LevelUpData 전달
for (int i = 0; i < lvData.Count; i++)
{
levelUpArray[i]
= new LevelUpData { ExperiencePoints = lvData[i].ExperiencePoints, LevelName = lvData[i].LevelName };
}
// 5. PlayerExperienceData 구조체 생성 후 LevelUpReference 만들기
var playerExperienceData
= new PlayerExperienceData { LevelUpReference = blobBuilder.CreateBlobAssetReference<LevelUpBlobAsset>(Allocator.Persistent) };
blobBuilder.Dispose();
return playerExperienceData;
}
}
# 코드 설명
BlobAsset Array를 만드는 순서입니다.
- BlobBuilder 메모리 할당
- BlobAsset 루트 만들기
- BlobArray 초기화
- BlobArray에 LevelUpData 값 전달
- PlayerExperienceData 구조체 생성 후 LevelUpReference 만들기
중간에 뒤끝서버에서 받아온 차트를 LevelUpData로 저장하는 것이 추가되었습니다.
Entity Component Data 변경
로그인이 되면 Player를 생성합니다. Player 생성이 완료되면 위의 [서버에서 받은 데이터를 BlobAsset 만들기] 통해 BlobAsset을 만들고 Player Entity의 PlayerExperienceData를 변경할 겁니다.
1. SetUpEXPData 수정
SetUpEXPData 에 아래의 전역변수와 함수를 추가합니다.
public GameObject player;
public void SetEntityData()
{
BlobAssetStore blobAssetStore = new BlobAssetStore();
var settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, blobAssetStore);
var entity = GameObjectConversionUtility.ConvertGameObjectHierarchy(player, settings);
var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
var instance = entityManager.Instantiate(entity);
var PlayerExperienceData = GetPlayerData();
entityManager.SetComponentData(instance, PlayerExperienceData );
blobAssetStore.Dispose();
}
#코드 설명
player Prefab을 Entity로 변경해서 생성하고 player의 PlayerExperienceData 값을 변경하는 코드입니다.
2. BackEndAuthentication 수정
BackEndAuthentication의 OnClickSignIn() 함수를 수정합니다.
public void OnClickSignIn()
{
BackendReturnObject bro = Backend.BMember.CustomLogin("MyTestID", "000000");
if (bro.IsSuccess())
{
Debug.Log("로그인 성공");
FindObjectOfType<SetUpEXPData>().SetEntityData();
}
}
#코드 설명
로그인이 완료되면 위에서 작성한 SetEntityData가 호출됩니다.
레벨 업 구현하기
스페이스를 누르면 Player에게 ExpUpTag가 추가되고 System에서 ExpUpTag가 부착된 Player의 경험치를 증가시키는 System을 만들겁니다.
1. AddExpTagSystem
스페이스 키를 누르면 Player에게 ExpUpTag를 추가하는 시스템을 만들겠습니다.
using System;
using Unity.Entities;
using UnityEngine;
[Serializable]
public struct ExpUpTag : IComponentData{ }
public partial class AddExpTagSystem : SystemBase
{
protected override void OnUpdate()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var playerEntity = GetSingletonEntity<PlayerExperienceData>();
if (!HasComponent<ExpUpTag>(playerEntity))
{
EntityManager.AddComponentData(playerEntity, new ExpUpTag());
}
}
}
}
#코드 설명
GetSingletonEntity<PlayerExperienceData>는 PlayerExperienceData를 가지고 있는 0번째 Entity를 가져오는 코드입니다. 예제에서는 Player가 1명이므로 GetSingleton을 사용했습니다.
HasComponent<ExpUpTag>(playerEntity) 는 playerEntity에게 ExpUpTag가 존재하는지 확인하는 코드입니다.
2. LevelUpSystem 만들기
ExpUpTag를 가지고 있는 PlayerEntity의 경험치를 올리는 시스템을 만들겠습니다.
public partial class LevelUpSystem : SystemBase
{
protected override void OnUpdate()
{
Entities.ForEach((Entity entity, ref ExpUpTag expTag) => {
PlayerExperienceData playerExpData = EntityManager.GetComponentData<PlayerExperienceData>(entity);
ref LevelUpBlobAsset levelUpBlobAsset = ref playerExpData.LevelUpReference.Value;
var needLvUpExp = levelUpBlobAsset.Array[playerExpData.CurrentLevel].ExperiencePoints;
// 경험치와 레벨 변경
playerExpData.CurrentExperience = GetChandedEXP(10, playerExpData.CurrentExperience);
playerExpData.CurrentLevel = GetChangedPlayerLevel(playerExpData, needLvUpExp);
// 참조가 아닌 값이므로 Entity의 컴포넌트 값을 수동으로 변경해줘야 한다.
EntityManager.SetComponentData(entity, playerExpData);
EntityManager.RemoveComponent<ExpUpTag>(entity);
// WithStructuralChanges: 구조 변경 가능하게 한다.
// Run : 메인스레드에서 동작
}).WithStructuralChanges().Run();
}
private int GetChandedEXP(int gainedExp, int curExp)
{
var changedExp = gainedExp + curExp;
return changedExp;
}
private int GetChangedPlayerLevel(PlayerExperienceData playerExpData, int needExp)
{
var curExp = playerExpData.CurrentExperience;
var curLevel = playerExpData.CurrentLevel;
if (curExp >= needExp && curLevel < 2)
{
curLevel++;
}
return curLevel;
}
}
유니티 에디터에서 작업
1. Player 만들기
Cube를 만들기 이름을 Player로 변경합니다. Player Experience를 추가합니다. Prefab으로 만들고 Hierarchy 뷰의 Player는 삭제합니다.
2. Spawner 만들기
빈 게임 오브젝트를 만들고 Spawner로 이름을 변경합니다. SetUpEXPData를 추가하고 Player 프리팹을 연결합니다.
3. 뒤끝서버 오브젝트 만들기
빈게임 오브젝트를 만들고 로그인, 회원가입, 초기화, 차트 관련 기능이 붙은 Scripts를 연결합니다.
테스트 하기
재생 버튼을 누르고 로그인 후 Player 개체가 생성되면 Space를 눌러보고 DOTS Hierarchy뷰에서 Player의 경험치와 레벨이 증가하는지 확인한다.
예제를 2개 생각하고 있었는데 1번 예제를 너무 어렵게 만들었다. 만들고나니 드는 생각은 멀티스레드에서 동작하도록 BlobAsset으로 데이터를 저장했는데 정작 예제는 메인스레드에서 동작하도록 만들었다. EntityCommandBuffer를 이용해서 멀티스레드에서 동작하도록 만들어보자. (숙제!)
'Unity DOTS > 따라하며 배우기' 카테고리의 다른 글
UNITY DOTS - State Machine(상태 머신) 만들기 #3 Guard 이동 (0) | 2022.08.06 |
---|---|
UNITY DOTS - State Machine(상태 머신) 만들기 #2 Player (0) | 2022.08.05 |
UNITY DOTS - State Machine(상태 머신) 만들기 #1 개요 (0) | 2022.08.05 |
UNITY DOTS - BlobAsset 예제2 Waypoint (0) | 2022.08.04 |
댓글