이번에는 Devlog) Taxi Game 3D) 18) 서버 구현 3 에서 서버로만 구현했던 출석방과, 재화 자동수집 기능을 구현해보았습니다.
출석 보상 구현
일일보상 템플릿에 아이콘 컬럼에 실제 아이콘 경로를 입력하고, DB에 다시 업로드하였습니다.
하지만 보상이 자동차일 경우에는 아이콘 경로를 따로 입력하지 않았습니다.
서버에서 미리 보상지급용으로 선택된 자동차의 아이콘을 출력해야 되기 때문에 자동차 템플릿에 등록된 아이콘 경로를 대신 사용하도록 구현하였습니다.
UGUI를 이용하여 출석보상 UI를 제작하였습니다.
유저정보를 변경된 후 각각의 UI를 갱신하는 작업을 줄여보기 위해 GameUI 클래스에 Refresh 메소드를 추가하여 현재 활성화된 UI들만 갱신하도록 구현하였습니다.
public void Refresh()
{
if (readyView.gameObject.activeInHierarchy)
readyView.Refresh();
if (carListView.gameObject.activeInHierarchy)
carListView.Refresh();
if (dailyRewardListView.gameObject.activeInHierarchy)
dailyRewardListView.Refresh();
}
DailyRewardListViewUI, DailyRewardEntryViewUI 클래스를 추가하여 출석보상UI에 연결하였습니다.
public class DailyRewardListViewUI : MonoBehaviour
{
[SerializeField]
DailyRewardEntryViewUI[] entries;
[SerializeField]
Button closeButton;
void Start()
{
closeButton.onClick.AddListener(() =>
{
gameObject.SetActive(false);
});
}
void OnEnable()
{
var templates = ClientManager.Instance.TemplateService.DailyRewards;
for (int i = 0; i < templates.Count; i++)
{
entries[i].gameObject.SetActive(true);
entries[i].Template = templates[i];
}
for (int i = templates.Count; i < entries.Length; i++)
entries[i].gameObject.SetActive(false);
}
public void Refresh()
{
foreach (var e in entries)
{
if (e.gameObject.activeInHierarchy)
e.Refresh();
}
}
}
public class DailyRewardEntryViewUI : MonoBehaviour, IPointerClickHandler
{
[SerializeField]
TMP_Text dayText;
[SerializeField]
Image iconImage;
[SerializeField]
TMP_Text amountText;
[SerializeField]
GameObject taken;
[SerializeField]
GameObject highlight;
DailyRewardTemplate template;
public DailyRewardTemplate Template
{
get => template;
set
{
template = value;
if (enabled)
Refresh();
}
}
void OnEnable()
{
Refresh();
}
public void Refresh()
{
if (template == null)
{
dayText.text = "0";
iconImage.sprite = null;
iconImage.gameObject.SetActive(false);
amountText.text = null;
taken.SetActive(false);
highlight.SetActive(false);
}
var user = ClientManager.Instance.UserService.User;
var day = template.Index + 1;
dayText.text = day.ToString();
iconImage.sprite = template.Type == DailyRewardType.Coin ?
template.Icon :
user.DailyCarRewards[day - 1].Icon;
iconImage.gameObject.SetActive(iconImage.sprite != null);
amountText.text = template.Type == DailyRewardType.Coin ?
template.Amount.ToString() :
null;
taken.SetActive(day <= user.NumberOfAttendance);
highlight.SetActive(day == user.NumberOfAttendance + 1);
}
void IPointerClickHandler.OnPointerClick(PointerEventData eventData)
{
ClientManager.Instance.UserService.Attendance();
GameUI.Instance.Refresh();
}
}
재화 수집 구현
ReadyViewUI 에서 얼마나 수집하였는지 출력하고, 수집버튼을 누르면 재화를 획득할 수 있도록 수정하였습니다.
void Start()
{
playButton.onClick.AddListener(() =>
{
GameUI.Instance.ShowPlayView();
GameLogic.Instance.PlayGame();
gameObject.SetActive(false);
});
carListButton.onClick.AddListener(() =>
{
GameUI.Instance.ShowCarList();
});
attendanceButton.onClick.AddListener(() =>
{
GameUI.Instance.ShowDailyRewardList();
});
collectButton.onClick.AddListener(() =>
{
var userService = ClientManager.Instance.UserService;
var collectMinutes = (DateTime.UtcNow - userService.User.CoinCollectedAtUtc).TotalMinutes;
if (collectMinutes < 1.0)
return;
userService.CollectCoin();
Refresh();
});
}
void Update()
{
var user = ClientManager.Instance.UserService.User;
var collectMinutes = (DateTime.UtcNow - user.CoinCollectedAtUtc).TotalMinutes;
collectButton.interactable = collectMinutes >= 1;
if (collectMinutes >= user.CurrentStage.MaxCollect)
{
collectAmountText.text = user.CurrentStage.MaxCollect.ToString();
collectProgress.fillAmount = 1f;
return;
}
var collectAmount = Math.Truncate(collectMinutes);
collectAmountText.text = collectAmount.ToString();
collectProgress.fillAmount = Convert.ToSingle(collectMinutes - collectAmount);
}
아래 이미지의 게이지는 1분 단위로 한 바퀴를 돌도록 구현하였습니다.
(gif는 4배속으로 편집하였습니다.)
실제 구현하고 테스트했을 때 실제 획득한 금액과 메인화면 UI에 출력되는 금액이 달라 원인을 찾아 봤습니다.
UserService의 CollectCoin 메소드에서 최대 수집양을 넘기지 않도록 처리할 때 실수로 MaxCoin 프로퍼티를 사용하고 있었기 때문에 수정였습니다.
public async void CollectCoin()
{
var now = DateTime.UtcNow;
if (now <= User.CoinCollectedAtUtc)
return;
var minutes = (int)(now - User.CoinCollectedAtUtc).TotalMinutes;
if (minutes <= 0)
return;
User.CoinCollectedAtUtc = now;
User.Coin += Math.Min(minutes, User.CurrentStage.MaxCollect);
var res = await http.Put($"User/CollectCoin", new DateRequest
{
UtcDate = now
});
if (!res.IsSuccess())
{
Debug.LogWarning($"Collect coin failed. - {res}");
UserUpdateFailed?.Invoke(this, EventArgs.Empty);
}
}
버그 수정
게임을 테스트하면서 손님을 태우는 구간이 3개인 스테이지에서 세번째 구간은 손님을 안 태우고 그냥 지나가는 버그를 발견하였습니다.
각각의 스테이지를 만들 때 깜박하고, 맵에 배치했던 CustomerTrigger들을 GameLogic에 모두 연결하지 않아 발생한 버그였기 때문에 스테이지들을 다시 확인하면서 트리거를 모두 연결하였습니다.
다음에는 룰렛을 구현하도록 하겠습니다.
깃 허브 저장소 : taxi-game-3d-unity
'개발노트 > Taxi Game 3D' 카테고리의 다른 글
Devlog) Taxi Game 3D) 21) 자동차 목록창 수정, 보상획득창 구현 (0) | 2024.02.02 |
---|---|
Devlog) Taxi Game 3D) 20) 룰렛, 빨간점 구현 (2) | 2024.01.26 |
Devlog) Taxi Game 3D) 18) 서버 구현 3 (2) | 2024.01.17 |
Devlog) Taxi Game 3D) 17) 손님 대화 구현 (0) | 2024.01.09 |
Devlog) Taxi Game 3D) 16) 게임 플레이 UI 구현 + 게임 개선 (2) | 2024.01.04 |