본문으로 건너뛰기
게임 프로젝트 기초집LESSON 16

재시작과 최고 점수: PlayerPrefs로 로컬 기록 저장하기

난이도초급 프로젝트
예상 시간50분
선수지식15강 사운드 연결

16강. 재시작과 최고 점수: PlayerPrefs로 로컬 기록 저장하기

1. 이번 강의에서 해결할 문제​

게임 오버 뒤 새 Run을 시작하면 이전 택배·점수·구독이 남거나 최고 점수가 사라집니다. 민감하지 않은 최고 점수만 로컬에 저장하고 Scene 상태를 초기화합니다.

2. 학습 목표​

3. 핵심 개념​

PlayerPrefs는 간단한 로컬 설정·기록을 저장하지만 암호화된 보안 저장소가 아닙니다. 최고 점수와 음량에는 사용할 수 있지만 비밀번호, 토큰, 결제·개인정보는 절대 저장하지 않습니다. 잦은 Save()는 플레이 중 끊김을 만들 수 있어 결과가 확정된 시점에 호출합니다.

먼저 알아야 할 저장·재시작 용어​

용어의미주의할 점
PlayerPrefs문자열 Key로 int·float·string을 저장하는 플랫폼별 로컬 저장소사용자가 수정하거나 지울 수 있으므로 보안·권위 데이터에 쓰지 않습니다.
Key저장 값을 다시 찾는 고유 문자열이름을 바꾸면 이전 값이 자동 이전되지 않습니다.
SetInt메모리의 PlayerPrefs 값에 정수를 기록영구 반영 시점은 플랫폼과 종료 상황을 고려해 Save 정책을 정합니다.
Save변경된 PlayerPrefs를 저장소에 기록하도록 요청매 프레임 호출하지 않고 결과 확정 시점에 호출합니다.
Scene reload현재 Scene 에셋을 다시 로드해 Scene 소유 객체를 새로 만드는 재시작DontDestroyOnLoad 객체와 정적 값은 자동 초기화되지 않습니다.

PlayerPrefs는 브라우저·클라우드 동기화나 서버 계정 저장이 아닙니다. 저장 위치는 운영체제·플랫폼마다 다르며 앱 삭제, 사용자 초기화, 저장 손상으로 사라질 수 있습니다. 따라서 최고 점수처럼 잃어도 복구 가능한 작은 편의 기록에만 사용합니다.

Scene을 다시 로드하면 Scene에 속한 GameObject는 파괴되고 새 인스턴스가 만들어집니다. 하지만 DontDestroyOnLoad, static 필드, 외부 파일과 PlayerPrefs는 남을 수 있습니다. “재로드하면 모든 상태가 무조건 초기화된다”고 가정하지 말고 각 상태의 소유 위치를 확인합니다.

4. 단계별 실습​

C# · 파일 경로: Assets/_Project/Scripts/Core/HighScoreStore.cs

HighScoreStore.cs
using UnityEngine;

namespace DeliverySorter.Core
{
public sealed class HighScoreStore : MonoBehaviour
{
private const string Key = "delivery-sorter-high-score-v1";
public int Value { get; private set; }

private void Awake() => Value = PlayerPrefs.GetInt(Key, 0);

public bool SaveIfHigher(int score)
{
if (score <= Value) return false;
Value = score;
PlayerPrefs.SetInt(Key, Value);
PlayerPrefs.Save();
return true;
}
}
}

HighScoreStore 코드 한 줄씩 이해하기​

  1. const string Key는 저장·조회에 같은 철자를 쓰게 합니다. v1은 향후 값 의미가 바뀔 때 새 Key로 구분할 여지를 줍니다.
  2. Value는 현재 실행에서 읽은 최고 점수 복사본이며 외부에서는 읽기만 가능합니다.
  3. Awake의 GetInt(Key, 0)은 저장 값이 있으면 읽고, 처음 실행이면 기본값 0을 사용합니다.
  4. 새 점수가 Value보다 작거나 같으면 저장하지 않고 false를 반환합니다.
  5. 더 높으면 메모리 값 → PlayerPrefs 값 → 저장소 반영 순으로 갱신하고 true를 반환합니다.
  6. bool 반환값을 이용하면 “신기록!” UI와 사운드는 실제 최고 점수가 바뀐 경우에만 표시할 수 있습니다.

C# · GameFlowController.cs에 추가

GameFlowController.cs
using UnityEngine.SceneManagement;

[SerializeField] private HighScoreStore highScoreStore;

private void GameOver()
{
highScoreStore.SaveIfHigher(session.Score);
input.SetInputEnabled(false);
spawner.Stop();
Time.timeScale = 1f;
SetState(GameState.GameOver);
}

public void RestartRun()
{
Time.timeScale = 1f;
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}

GameOver와 재시작 흐름​

RunEnded
→ 현재 점수와 최고 점수 비교
→ 신기록일 때만 PlayerPrefs 저장
→ 입력·Spawner 중지
→ Time Scale을 1로 복원
→ GameOver 화면 표시
→ Restart 버튼
→ 현재 Scene 다시 로드
→ HighScoreStore.Awake에서 저장값 다시 읽기

SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex)를 사용하려면 현재 Scene이 Build Settings 또는 사용 중인 Unity 버전의 Build Profiles Scene 목록에 포함돼 유효한 buildIndex를 가져야 합니다. Editor에서 열려 있기만 하고 빌드 목록에 없으면 재로드가 실패할 수 있습니다.

Unity Editor에서 연결하고 가장 작은 저장 확인​

  1. Hierarchy에 HighScoreStore GameObject를 만들고 HighScoreStore Component를 붙입니다.
  2. GameFlowController의 High Score Store 슬롯에 연결합니다.
  3. GameOverPanel의 Restart Button OnClick에 GameFlowController.RestartRun을 연결합니다.
  4. 최고 점수를 표시하는 TMP_Text는 UI Presenter가 Store의 Value를 읽어 표시하게 합니다. TMP_Text가 직접 PlayerPrefs Key를 알게 하지 않습니다.
  5. 현재 Game Scene이 빌드 Scene 목록에 포함됐는지 확인합니다.
  6. 100점으로 종료한 뒤 Restart하고, HighScoreStore의 Value 또는 UI가 100으로 다시 로드되는지 확인합니다.

테스트용 값을 초기화할 때는 개발 중에만 PlayerPrefs.DeleteKey("delivery-sorter-high-score-v1")를 한 번 실행하고 즉시 제거합니다. DeleteAll()은 음량 등 다른 설정도 지울 수 있으므로 이 실습에 사용하지 않습니다.

5. 코드가 동작하는 이유​

결과가 기존 값보다 높을 때만 쓰고 Scene을 다시 로드해 Scene 소유 런타임 상태와 이벤트 구독을 새로 만듭니다. 저장 키에 버전을 넣어 포맷 변경 시 구분할 수 있습니다.

6. 자주 하는 실수와 해결법​

7. 직접 실습​

실습 목표​

낮은 점수는 기존 최고 기록을 덮지 않고, 높은 점수만 저장되며, Scene 재로드 뒤 기록과 런타임 상태가 각각 올바르게 복원·초기화되는지 확인합니다.

시작 전 상태​

테스트 Key 하나만 삭제해 최고 점수를 0으로 만들고 Game Scene을 다시 시작합니다. 점수·목숨·생성 중인 택배도 초기 상태인지 기록합니다.

1단계: 따라 하기​

100점으로 GameOver를 만들고 Restart합니다. 재로드 뒤 최고 점수는 100, 현재 Run 점수는 0이어야 합니다. 저장되는 값과 초기화되는 값의 소유자가 다름을 확인합니다.

2단계: 값 바꿔 보기​

50점으로 종료하면 SaveIfHigher가 false이고 최고 점수는 100을 유지해야 합니다. 다음 Run에서 200점으로 종료하면 true가 되고 재시작 뒤 200이 보여야 합니다.

종료 점수저장 전 최고반환값저장 후 최고
1000true100
50100false100
200100true200

3단계: 직접 적용​

SaveIfHigher의 반환값을 받아 신기록일 때만 NEW HIGH SCORE 표시가 켜지도록 UI Presenter에 전달하세요. Store가 직접 TMP_Text나 Panel을 참조하지 않게 이벤트 또는 Flow의 결과 전달을 사용합니다.

4단계: 스스로 확인​

막혔을 때​

증상원인해결
재시작 뒤 최고 점수가 0Store가 Scene에 없거나 Key 철자가 다름Awake 로그로 Key와 읽은 Value를 확인합니다.
Restart 버튼에서 Scene 로드 오류현재 Scene이 빌드 목록에 없음Build Settings 또는 Build Profiles에 Scene을 추가합니다.
재시작 뒤 게임이 계속 멈춤Time Scale이 0인 채 재로드LoadScene 전에 Time.timeScale = 1f를 실행합니다.
50점이 100점을 덮음비교 조건이 반대이거나 항상 SetInt 호출score <= Value에서 즉시 false로 반환하는지 확인합니다.
테스트 초기화로 음량도 사라짐DeleteAll 사용학습용 최고 점수 Key만 DeleteKey로 지웁니다.

8. 이해 점검 질문 3개​

9. 핵심 요약​

다음 강의 연결​

다음 강의: Object Pool과 성능에서는 Scene 재시작뿐 아니라 한 Run 안에서 반복 생성·파괴되는 택배 비용도 줄이고 측정으로 개선 효과를 확인합니다.

MINI QUIZ

재시작과 최고 점수: PlayerPrefs로 로컬 기록 저장하기 미니 퀴즈

선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.

0 / 2
  1. 문제 1“재시작과 최고 점수: PlayerPrefs로 로컬 기록 저장하기”에서 다음 단계로 넘어가기 전에 확인할 핵심은 무엇인가요?
  2. 문제 2‘점수 증가마다 Save’ 상태에 관한 “재시작과 최고 점수: PlayerPrefs로 로컬 기록 저장하기” 본문의 설명으로 가장 알맞은 것은 무엇인가요?
LESSON STATUS

학습을 마쳤나요?

직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.

16강. 재시작과 최고 점수: PlayerPrefs로 로컬 기록 저장하기 미완료 상태