소소한 개발 공부
[Unity] 애니팡식 하트 충전 : 시간에 따른 값 충전 본문
📝[참고] [Unity | 유니티] 하트 충전 스크립트(로컬 디바이스 시간 기준) 출처: https://tenlie10.tistory.com/177 [유니티 게임 개발자] |
애니팡처럼 시간에 따라 하트를 충전하는 기능을 만들고자 한다.게임을 껐을 때도 하트를 지속적으로 충전해야 한다.
1. 게임을 켰을 때 태초의 하트 값은 Max로 한다.- public 으로 된 love 값을 Max 로 설정해둔다.
- 혹은 Save된 AppQuitTime이 없는 태초의 상태에서 AppQuitTime은 1970년 1월1일로 되어 있으므로 (현재시간 - AppQuitTime)을 계산했을 때 방치 시간이 어마어마하게 많아 love은 자동으로 Max 값이 되어있을 것이다.
2. 게임을 껐을 때 게임을 나간 시간 (AppQuitTime)을 저장해 게임을 재시작했을 때 현재 시간과 계산하여 그 차를 게임을 켜지않고 방치한 시간으로 한다. (Now - AppQuitTime = 게임을 켜지않고 흐른 시간)
3. 게임을 켰을 때 흐르고 있었던 시간 (RechargeTime)에 방치한 시간을 더하고 하트를 충전하는데 걸리는 시간만큼 나눠 하트를 충전한다.
하트 충전 스크립트를 만듦에 있어 사용하는 변수는 다음과 같다.
#region
public int loveAmount = 0; // 보유 애정 개수
public int loveAmountMax = 5; // 가질 수 있는 최대 애정 개수
public int timeOfChargeLoveMax; // 애정 충전 시간
// UIManager에 빼는 등 따로 UI를 관리하는 스크립트가 가져가야 할 변수
// 참조하는 값에 변경이 있을 때마다 바로바로 UI에 적용해준다.
public Text textLove; // 현재 애정 개수
public Text textTimerOfLove; // 애정이 충전되기까지 현재 남은 시간
private DateTime m_AppQuitTime = new DateTime(1970, 1, 1).ToLocalTime(); // 유저 게임 이탈 시간 변수
private Coroutine m_RechargeTimerCoroutine = null;
private int m_RechargeRemainTime = 0; // 현재 남은 시간
#endregion
#region - #endregion은 코드를 접을 수 있다. (코드 관리에 좋다)
게임을 켜거나 껐을 때 혹은 휴대폰 화면이 점멸되어 게임의 포커스 변경이 있을 때 게임 정보(하트, 게임 종료 시간, 현재 남은 시간)를 저장/로드해야한다.
// 휴대폰 화면 점멸될 때 등. 앱에 포커스가 있는지 없는지 변경이 있을 때 동작
private void OnApplicationFocus(bool focusStatus)
{
if (focusStatus == true)
{
LoadLoveInfo();
LoadAppQuitTime();
SetRechargeScheduler();
}
else
{
SaveLoveInfo();
SaveAppQuitTime();
}
}
// 게임 종료 시 동작
private void OnApplicationQuit()
{
SaveLoveInfo();
SaveAppQuitTime();
}
OnApplicationFocus는 현재 앱에 포커스가 있는지 없는지 확인하는 Unity MonoBehaviour함수.
(휴대폰을 오래 방치해두어 화면이 어두워졌을 때=false, 게임을 완전히 끄지 않은 상태로 다른 앱을 실행할 때=false, 게임을 할 때=true)
게임을 켰을 때(focusStatus = true) 저장해둔 게임 정보를 로드한다.
게임을 끄거나 포커스가 나갈 때 지금까지 진행한 게임 정보를 저장한다.
public bool SaveLoveInfo()
{
bool result = false;
try
{
PlayerPrefs.SetInt("LoveAmount", loveAmount);
PlayerPrefs.Save();
result = true;
}
catch (System.Exception e)
{
Debug.LogError("SaveLoveInfo Failed (" + e.Message + ")");
}
return result;
}
public bool LoadLoveInfo()
{
bool result = false;
try
{
if (PlayerPrefs.HasKey("LoveAmount"))
{
loveAmount = PlayerPrefs.GetInt("LoveAmount");
if (loveAmount < 0)
{
loveAmount = 0;
}
}
else
{
loveAmount = loveAmountMax;
}
result = true;
}
catch (System.Exception e)
{
Debug.LogError("LoadLoveInfo Failed : " + e.Message + ")");
}
return result;
}
public bool SaveAppQuitTime()
{
bool result = false;
try
{
var appQuitTime = DateTime.Now.ToLocalTime().ToBinary().ToString();
PlayerPrefs.SetString("AppQuitTime", appQuitTime);
var rechargeRemainTime = m_RechargeRemainTime;
PlayerPrefs.SetInt("RechargeRemainTime", rechargeRemainTime);
PlayerPrefs.Save();
result = true;
}
catch (System.Exception e)
{
Debug.LogError("SaveAppQuitTime Failed (" + e.Message + ")");
}
return result;
}
public bool LoadAppQuitTime()
{
bool result = false;
try
{
if (PlayerPrefs.HasKey("AppQuitTime"))
{
var appQuitTime = string.Empty;
appQuitTime = PlayerPrefs.GetString("AppQuitTime");
m_AppQuitTime = DateTime.FromBinary(Convert.ToInt64(appQuitTime));
m_RechargeRemainTime = PlayerPrefs.GetInt("RechargeRemainTime");
}
result = true;
}
catch (System.Exception e)
{
Debug.LogError("LoadAppQuitTime Failed (" + e.Message + ")");
}
return result;
}
- PlayerPrefs로 게임 정보를 저장한다. PlayerPrefs는 특정 자료형만 저장가능하다는 단점이 있어 다양한 정보를 저장하려면 정보를 Serializable하게 만들어 파일로 저장한다.
- 시간(DateTime)은 PlayerPrefs로 저장할 수 없으므로 Binary로 변경한 것을 string으로 변경해 SetString으로 저장한다.
=> 로드할 때는 반대로...
- try-catch문으로 에러를 처리한다.
public void SetRechargeScheduler(Action onFinish = null)
{
// 방치된 시간 계산하기
// 애정은 얼마나 충전되었는가?
if (m_RechargeTimerCoroutine != null)
{// 현재 코루틴이 돌고 있는지...
StopCoroutine(m_RechargeTimerCoroutine);
}
// (게임을 켠 현재 시간 - 게임을 껐던 시간) = 흐른 시간
var timeDifferenceInSec = (int)((DateTime.Now.ToLocalTime() - m_AppQuitTime).TotalSeconds); // 방치한 동안 흐른 시간(초) 계산
var loveToAdd = timeDifferenceInSec / timeOfChargeLoveMax; // 몫 : 추가된 애정
var remainTime = timeDifferenceInSec % timeOfChargeLoveMax; // 나머지 : 계산되고 남은 방치된 시간
if (m_RechargeRemainTime <= remainTime)
{// 게임을 끄기 전에 남아있던 시간이 흐른 시간 보다 작거나 같을 때
if (m_RechargeRemainTime == remainTime)
loveToAdd++;
remainTime = timeOfChargeLoveMax + m_RechargeRemainTime - remainTime;
}
else
{// 게임을 끄기 전 남아있던 시간이 방치한 시간보다 길 때
remainTime = m_RechargeRemainTime - remainTime;
}
loveAmount += loveToAdd;
if (loveAmount >= loveAmountMax)
{// 애정 충전 완료
loveAmount = loveAmountMax;
}
else
{
m_RechargeTimerCoroutine = StartCoroutine(DoRechargeTimer(remainTime, onFinish));
}
textLove.text = loveAmount.ToString();
}
private IEnumerator DoRechargeTimer(int remainTime, Action onFinish = null)
{ // 게임 켠 동안 애정 충전하기
if (remainTime <= 0)
{// 공교롭게도 흐른시간이 0초일 때(애정을 충전하고 남은 시간이 0초)
// 남은 시간을 애정 충전 시간 Max로 대입
m_RechargeRemainTime = timeOfChargeLoveMax;
}
else
{
m_RechargeRemainTime = remainTime;
}
while (m_RechargeRemainTime > 0)
{
m_RechargeRemainTime -= 1;
textTimerOfLove.text = (m_RechargeRemainTime / 60).ToString() + " : " + (m_RechargeRemainTime % 60).ToString();
yield return new WaitForSeconds(1f);
}
loveAmount++;
if (loveAmount >= loveAmountMax)
{// 애정 충전 완료
loveAmount = loveAmountMax;
m_RechargeRemainTime = 0;
m_RechargeTimerCoroutine = null;
}
else
{
m_RechargeTimerCoroutine = StartCoroutine(DoRechargeTimer(timeOfChargeLoveMax, onFinish));
}
textLove.text = loveAmount.ToString();
}
예를 들어
게임을 끄기 전 하트가 충전되기까지 남은 시간(저장된 m_RechargeRemainTime): 30초
게임을 방치한 뒤 흐른 시간(remainTime) : 20초
=> 게임을 켰을 때 남은 시간(사용할 remainTime DoRechargeTimer에서 m_RechargeRemainTime에 넣을 값) : 10초
remainTime = m_RechargeRemainTime - remainTime
게임을 끄기 전 하트가 충전되기까지 남은 시간(저장된 m_RechargeRemainTime): 10초
게임을 방치한 뒤 흐른 시간(remainTime) : 20초
=> 게임을 켰을 때 남은 시간(사용할 remainTime DoRechargeTimer에서 m_RechargeRemainTime에 넣을 값) : Max-10초
remainTime = timeOfChargeLoveMax + m_RechargeRemainTime - remainTime
public void UseLove(Action onFinish = null)
{
if (loveAmount <= 0)
{
return;
}
loveAmount--;
textLove.text = loveAmount.ToString();
if (m_RechargeTimerCoroutine == null)
{
m_RechargeTimerCoroutine = StartCoroutine(DoRechargeTimer(timeOfChargeLoveMax));
}
if (onFinish != null)
{
onFinish();
}
}
하트를 사용했을 때 사용하는 함수.
하트를 사용하면 다시 하트를 채워야하므로 DoRechargeTimer 코루틴을 호출한다.
UI를 연결하고 timeOfCharLoveMax값을 설정하는 등 과정을 거치면 게임을 껐을 때도 하트를 충전할 수 있는 스크립트를 사용할 수 있다...
'개발 > Unity' 카테고리의 다른 글
[유니티] Text 타이핑 효과 내기 (0) | 2021.08.18 |
---|---|
[Unity] 2D 핀치 줌 Pinch Zoom 구현 + 화면 끌기(패닝 Panning) (0) | 2021.05.19 |
[Unity] 스크립트 실행 우선순위 정하기 Script Execution Order (0) | 2021.05.09 |
[Unity] Raycast (0) | 2021.04.16 |
[Unity] DrawRay (0) | 2021.04.16 |