UI 출력용 자동차 프리팹 추가
기존에 만들었던 게임 플레이용 프리팹을 모두 복사한 후 아래 작업들 진행하였습니다.
- PlayerCar, BoxCollider, Rigidbody, CinemachineVirtualCamera 제거
- 왼쪽, 오른쪽을 구분하기 위해 배치 했던 빈 오브젝트 제거
- 자동차의 중심축을 가운데로 변경
- 레이어 UI로 수정
추가한 프리팹들은 Resources 폴더에 추가하였습니다.
추가한 프리팹을 게임에서 불러올 수 있도록 Car 템플릿을 수정하였습니다.
Car 템플릿에 있던 Prefab 컬럼 Player Prefab 으로 수정하고, Car 템플릿에 UI Prefab 컬럼을 추가하였습니다.
template_generator.py 수정한 후 DB에 수정한 템플릿을 새로 등록하였습니다.
def generate_car(file_path, sheet_name):
book = load_workbook(file_path, data_only=True)
sheet = book[sheet_name]
skip = 0
temp_group = []
for row in sheet.rows:
if skip < 2:
skip += 1
continue
if row[0].value == None:
continue
new_temp = {
'Id': row[0].value,
'Name': {
'Table': row[1].value,
'Key': row[2].value
},
'Icon': row[3].value,
'Player Prefab': row[4].value,
'UI Prefab': row[5].value,
'Cost': int(row[6].value),
'EnableReward': int(row[7].value)
}
temp_group.append(new_temp)
return temp_group
클라이언트 CarTemplate 도 변경된 자동차 데이터들을 저장할 수 있도록 수정하였습니다.
public class CarTemplate
{
[JsonIgnore]
public int Index
{
get;
set;
}
public string Id
{
get;
set;
}
public LocalizationTemplate Name
{
get;
set;
}
[JsonProperty("Icon")]
public string IconPath
{
get;
set;
}
[JsonIgnore]
public Sprite Icon => Resources.Load<Sprite>(IconPath);
[JsonProperty("PlayerPrefab")]
public string PlayerPrefabPath
{
get;
set;
}
[JsonIgnore]
public GameObject PlayerPrefab => Resources.Load<GameObject>(PlayerPrefabPath);
[JsonProperty("UiPrefab")]
public string UiPrefabPath
{
get;
set;
}
[JsonIgnore]
public GameObject UiPrefab => Resources.Load<GameObject>(UiPrefabPath);
public int Cost
{
get;
set;
}
}
자동차 목록창 수정
자동차 선택창에서 자동차를 자주 선택하기 때문에 계속 오브젝트를 생성/제거하지 않고, 활성화/비활성화로 관리할 수 있도록 UICarManager를 추가하였습니다.
public class UICarManager : MonoBehaviour
{
Dictionary<string, GameObject> unused = new();
public string SelectedId
{
get;
private set;
}
public GameObject SelectedObject
{
get;
private set;
}
public void Select(string id)
{
if (SelectedId == id)
return;
if (SelectedObject != null)
Deselect();
if (!unused.TryGetValue(id, out var go))
{
var template = ClientManager.Instance.TemplateService.Cars.Find(e => e.Id == id);
go = Instantiate(template.UiPrefab, transform);
go.transform.localPosition = Vector3.zero;
go.transform.localRotation = Quaternion.identity;
go.transform.localScale = Vector3.one;
}
go.SetActive(true);
SelectedId = id;
SelectedObject = go;
}
public void Deselect()
{
if (SelectedObject == null)
return;
SelectedObject.SetActive(false);
unused[SelectedId] = SelectedObject;
SelectedObject = null;
SelectedId = null;
}
}
자동차 모델은 RenderTexture가 연결된 다른 카메라로 자동차 모델을 촬영하고, RenderTexture는 RawImage를 이용하여 UI에 출력하도록 구현하였습니다.
보상획득창 구현
UGUI를 이용하여 보상획득 창 제작하였습니다.
RewardPopupViewUI 스크립트 구현하였습니다.
보상으로 자동차를 획득하면 자동차 모델을 출력하고, 돈을 획득하였다면 코인 아이콘을 출력하도록 구현하였습니다.
과거에 획득했던 자동차를 또 획득할 때는 자동차 모델을 출력하고, 아래 이미 획득했던 자동차라고 알려주는 텍스트가 출력되도록 구현하였습니다.
public class RewardPopupViewUI : MonoBehaviour
{
[SerializeField]
Image coinImage;
[SerializeField]
RawImage carImage;
[SerializeField]
UICarManager carManager;
[SerializeField]
TMP_Text contentText;
[SerializeField]
Button closeButton;
void Start()
{
closeButton.onClick.AddListener(() =>
{
carManager.Deselect();
gameObject.SetActive(false);
});
}
public void Show(int coin)
{
coinImage.gameObject.SetActive(true);
carImage.gameObject.SetActive(false);
contentText.text = $"Gain {coin}";
gameObject.SetActive(true);
}
public void Show(string carId, bool newCar)
{
coinImage.gameObject.SetActive(false);
carImage.gameObject.SetActive(true);
carManager.Select(carId);
if (newCar)
{
contentText.text = $"Gain {carId}";
}
else
{
var car = ClientManager.Instance.UserService.User.Cars.Find(e => e.Id == carId);
contentText.text = $"{carId} is already exit. Gain {car.Cost}";
}
gameObject.SetActive(true);
}
}
룰렛으로 자동차 획득했을 때, 출석보상 받았을 때, 자동차 구매했을 때 보상회득창 출력하도록 구현하였습니다.
보상획득창을 출력하도록 구현하면서 UserService 클래스도 약간 수정하였습니다.
출석 보상을 받을 수 없을 때는 보상팝업창의 출력하지 않도록 구현하기 위해 출석보상 받을 수 있는지 없는지 체크하는 메소드를 추가하였습니다.
public bool CheckEnableAttendance()
{
var now = DateTime.UtcNow;
if (User.NumberOfAttendance >= templateService.DailyRewards.Count)
{
Debug.LogWarning("Attendance failed. Because attendance is already completed.");
return false;
}
if (now <= User.DailyRewardedAtUtc.Date.AddDays(1))
{
Debug.LogWarning($"Attendance failed. Because of already rewarded today({now}/{User.DailyRewardedAtUtc}).");
return false;
}
return true;
}
룰렛으로 획득한 자동차가 이미 획득했던 자동차인지 아닌지 구분할 수 있도록 UserService의 SpinRoulette 메소드를 수정하였습니다.
public async UniTask<(int index, bool newCar)> SpinRoulette()
{
var now = DateTime.UtcNow;
var res = await http.Put<RouletteResponse>("User/SpinRoulette", new DateRequest
{
UtcDate = now
});
if (!res.Item1.IsSuccess())
{
Debug.LogWarning($"Spin roulette failed. - {res}");
UserUpdateFailed?.Invoke(this, EventArgs.Empty);
return (-1, false);
}
User.RouletteSpunAtUtc = now;
var car = User.RouletteCarRewards[res.Item2.Index];
if (User.Cars.Contains(car))
{
User.Coin += car.Cost;
return (res.Item2.Index, false);
}
else
{
User.Cars.Add(car);
return (res.Item2.Index, true);
}
}
이번주는 밖으로 자주 나가서 글을 오랜만에 게시하였습니다.
다음 주도 밖에 자주 나갈 예정이어서 글을 많이 못 올릴 것 같습니다

다음에는 게임 사운드를 추가하고, 옵션창을 제작해보도록 하겠습니다.
깃 허브 저장소 : taxi-game-3d-unity
'개발노트 > Taxi Game 3D' 카테고리의 다른 글
Devlog) Taxi Game 3D) 23) 이펙트 추가 (0) | 2024.02.14 |
---|---|
Devlog) Taxi Game 3D) 22) 사운드 구현 (0) | 2024.02.08 |
Devlog) Taxi Game 3D) 20) 룰렛, 빨간점 구현 (2) | 2024.01.26 |
Devlog) Taxi Game 3D) 19) 출석보상, 재화 수집 구현 (0) | 2024.01.23 |
Devlog) Taxi Game 3D) 18) 서버 구현 3 (2) | 2024.01.17 |