일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 에이세프라이트
- 인디게임 개발
- 연습
- 스마일게이트
- 픽셀 아트
- Aseprite
- pixel art
- 개발
- 모작
- 도트
- 자원순환보증금관리센터
- layer
- 채색
- 드로잉 연습
- 픽셀아트
- TOOL
- 멋쟁이사자처럼
- COSMO
- 서포터즈
- photoshop
- menu
- Pixelart
- 노하우
- 장학팀
- 포토샵
- 드로잉
- 반환원정대
- 도트공부
- 기초
- 애니메이션
- Today
- Total
소소한 나의 하루들
탑다운 2d RPG(7) - 서브 메뉴와 저장기능 만들기 본문
출처: https://youtube.com/playlist?list=PLO-mt5Iu5TeYI4dbYwWP8JqZMC9iuUIW2&feature=shared
게임을 하는 도중에 esc 버튼을 누르면 메뉴가 나오게 된다. 그 메뉴를 만들어보도록 한다.
#1. UI 구축
게임 중 서브메뉴가 나올 때 게임화면을 살짝 어둡게 처리한다.
Image UI를 생성한다. 이미지는 기본으로 제공하는 sprite를 사용해도 된다. 크기는 Canvas 면적만큼 전체적으로 확장될 수 있게 Anchor Presets을 조절한다.
그리고 투명한 검은색(검은색 설정 + 알파값을 살짝 내리기)으로 설정한다.'
esc를 누를 때 이 화면이 뜨면서 어두워진다.
다시 Image UI를 안에 생성한다. (Canvas>Black Background 내부에 생성) 이 이미지는 버튼들을 감싸줄 이미지가 될 것이다. 이미지 안에 메뉴 버튼 3개를 생성하고 적절하게 모양을 잡아주면 된다.
이제 버튼 3개는 무슨 기능을 갖고있을지 설정해야한다.
1) 계속하기
2) 저장하기
3) 종료하기 *종료버튼은 위험성을 알리기 위해 빨간색으로 설정하는 경우가 있다. (플레이어에게 주의를 주기위함)
이러한 것을 UX라고 한다. (User Experience) 제품이나 서비스에 대해 사용자의 경험한 것을 토대로 게임을 디자인해야하기 때문에 그 사용자의 경험을 향상시키는 것을 목적으로 설계해야한다.
UI는 User Interface로, 사용자 인터페이스를 의미한다. 시각적으로 마주하는 디자인을 뜻한다. 이는 가독성, 직관성이 좋은 UI 디자인을 목적으로 게임을 디자인해야한다.
그리고 하나 더 '현재 진행중인 퀘스트'를 esc를 눌렀을 때 게임 화면에 서브 메뉴와 함께 같이 보여주도록 하겠다. 퀘스트 이름을 띄울 UI도 생성한다. (먼저 Image UI로 영역을 설정하고, 그 안에 내용을 담을 Text UI를 생성한다)
#2. 계속하기
먼저 esc를 눌렀을 때 서브메뉴 화면이 보이도록 해야한다.
//GameManager
private void Update()
{
//Sub Menu
if (Input.GetButtonDown("Cancel"))
{
if (menuSet.activeSelf)
menuSet.SetActive(false);
else
menuSet.SetActive(true);
}
}
보통 이런 단발성 버튼 입력은 Update()에서 한다. esc키(=Cancel키)를 누르면 서브 메뉴화면이 나오고, 다시 화면을 끄려면 '계속하기' 버튼을 누르거나 다시 esc키를 누르면 된다.
: esc키로 켜고 끄기 가능하도록 작업
'계속하기' Button UI에서 OnClick() 옵션에서 기본으로 제공해주는 GameObject의 메소드로 SetActive (bool)을 설정한다.
: GameObject 기본 함수는 인스펙터 창에서 바로 할당 가능하다.
#3. 퀘스트 확인
//GameManager
public class GameManager : MonoBehaviour
{
public TalkManager talkManager;
public QuestManager questManager;
public Animator talkSpace;
public Animator portraitAnime;
public Sprite prePortrait;
public TypeEffect talk;
public Image portraitImg;
public GameObject scanObject;
public GameObject menuSet;
public TextMeshProUGUI questText;
public bool isAction;
public int talkIndex;
private void Start()
{
questText.text = questManager.CheckQuest();
}
...
}
지금까지 Debug.Log()를 사용해서 로그로 퀘스트를 표시했지만, 이제는 게임 화면에 표시해보도록 한다.
우선 퀘스트 텍스트 UI를 GameManager 스크립트에 변수로 할당하여 QuestText UI를 전달한다.
#4. 종료하기
public void GameExit()
{
Application.Quit();
}
게임을 종료하는 기능을 사용하기 위해서는 Application 이라는 클래스를 사용해야한다. (최상위 클래스) 그리고 .Quit() 메소드를 사용해서 종료함수를 추가하면 된다. (GameExit())
public으로 선언한 함수에 작성해서 버튼의 OnClick() 이벤트와 연결시키면 된다.
※Application.Quit()는 에디터에서는 실행되지 않는다. (에디터에서 실행하는 디버깅성 실행이기 때문 에디터를 종료할 수는 없으니까)
이러한 종료함수는 확인하려면 빌드해서 실제 게임으로 돌리면 된다.
#4-1. 게임 빌드하기
File메뉴>Build Settings에서 현재 돌리고있는 Scene을 추가해주고(게임 진행에 필요한 Scene을 모두 추가) Build해준다.
#5. 저장하기
저장하기
GameManager에 저장/ 불러오기 함수를 생성한다. (GameSave() / GameLoad())
어떤 것을 Save해야할까? 퀘스트 진행상태를 저장해야할 것이다.
따라서 우리가 저장해야할 것은 Quest Id, QuestActionIndex, 플레이어의 위치 3가지이다.
우선 플레이어 오브젝트를 불러올 GameObject 변수를 생성한다. 그리고 이제는 PlayerPrefs 클래스를 활용한다.
PlayerPref : 간단한 데이터 저장 기능을 지원하는 클래스
PlayerPref에서 SetInt / Setfloat / SetString 3개의 메소드가 있는데, 이것으로 세팅을 하고 저장하게되면 우리가 플레이하는 기기(pc, 모바일 등)에 저장이 된다.
[저장되는 곳: File메뉴>Build Settings에서 Player Settings클릭, 'Company Name', 'Product Name'으로 pc나 모바일 기기의 레지스트리에 저장)
*여기서 아이콘도 설정할 수 있다.
먼저 Company Name과 Product Name을 먼저 정하고나서 PlayerPrefs 사용하는 것이 좋다.
//GameManager
public void GameSave()
{
PlayerPrefs.SetFloat("PlayerX", player.transform.position.x);
PlayerPrefs.SetFloat("PlayerY", player.transform.position.y);
PlayerPrefs.SetInt("QuestId", questManager.questId);
PlayerPrefs.SetInt("QuestActionIndex", questManager.questActionIndex);
PlayerPrefs.Save();
menuSet.SetActive(false);
}
이제 세팅을 한다. 플레이어의 좌표는 float형 좌표일 것이다.
SetFloat(), SetInt(), SetString() 모두 매개변수로 (string형 key, value)가 들어간다.
저장할 데이터들을 다 Set한 다음에는 PlayerPrefs의 Save() 메소드를 사용한다. 그러면 플레이하는 기기의 레지스트리에 저장이 된다.
그리고 메뉴화면을 끈다.
불러오기
//GameManager
private void Start()
{
GameLoad();
questText.text = questManager.CheckQuest();
}
public void GameLoad()
{
float x = PlayerPrefs.GetFloat("PlayerX");
float y = PlayerPrefs.GetFloat("PlayerY");
int questId = PlayerPrefs.GetInt("QuestId");
int questActionIndex = PlayerPrefs.GetInt("QuestActionIndex");
player.transform.position = new Vector3(x, y, 0);
questManager.questId = questId;
questManager.questActionIndex = questActionIndex;
}
그러면 이제 저장한 데이터들을 불러와야한다. 불러오기 또한 데이터 타입에 맞게 Get 함수를 사용한다.
불러오면서, 불러온 데이터를 게임 오브젝트에 적용시킨다.
GameLoad()는 언제 불러올까? 게임은 시작할 때 로딩하도록 한다. Start()에서 호출한다.
//GameManager
public void GameLoad()
{
if (!PlayerPrefs.HasKey("PlayerX"))
return;
float x = PlayerPrefs.GetFloat("PlayerX");
float y = PlayerPrefs.GetFloat("PlayerY");
int questId = PlayerPrefs.GetInt("QuestId");
int questActionIndex = PlayerPrefs.GetInt("QuestActionIndex");
player.transform.position = new Vector3(x, y, 0);
questManager.questId = questId;
questManager.questActionIndex = questActionIndex;
}
※이렇게 하고 실행하면 맨 처음에 레지스트리에 게임 데이터가 저장되어있지 않은 상황에서 게임을 시작하게될 때 GameLoad()를 실행해도 불러올 데이터가 없어서 에러가 뜰 것이다.
따라서 게임 로드를 바로 하지말고 최초 게임 실행했을 때는 데이터가 없으므로 예외처리 로직을 작성한다.
*실행하기 전에, 에디터에서 GameManager 스크립트에 Player 오브젝트를 연결시키고, 저장하기 버튼의 OnClick() 이벤트에도 GameManager의 GameSave() 함수를 연결시켜주는 것 잊지 않아야한다.
저장위치: C:\Users\[사용자명]\AppData\LocalLow\[Company Name]\[Product Name] *Windows OS
레지스트리 편집기 저장경로: 컴퓨터\HKEY_CURRENT_USER\Software\[Company Name]\[Product Name]
실행해보면 (저장 후 게임 재실행했을 때) 플레이어 위치, 플레이어 퀘스트 단계가 저장이 된 것을 확인해볼 수 있다.
※그런데, 동전 오브젝트가 다시 게임을 재실행했을 때 사라지는 문제가 있다.
//QuestManager
void GenerateData()
{
questList.Add(10, new QuestData("마을 사람들과 대화하기", new int[] { 1000, 2000 }));
questList.Add(20, new QuestData("루도의 동전 찾아주기", new int[] { 1000, 2000, 5000, 2000}));
questList.Add(30, new QuestData("퀘스트를 클리어하였습니다.", new int[] { 0 }));
}
...
void ControlObject()
{
switch (questId)
{
case 10:
if (questActionIndex == 2) //퀘스트1에서의 대화가 모두 끝났을 때
questObject[0].SetActive(true);
break;
case 20:
if (questActionIndex == 3) //동전 먹고난 후 첫 NPC
questObject[0].SetActive(false);
break;
}
}
/* 수정 후 */
//QuestManager
public void ControlObject()
{
switch (questId)
{
case 10:
if (questActionIndex == 2) //퀘스트1에서의 대화가 모두 끝났을 때
questObject[0].SetActive(true);
break;
case 20:
if (questActionIndex <= 2)
questObject[0].SetActive(true);
else if (questActionIndex == 3) //동전 먹고난 후 첫 NPC
questObject[0].SetActive(false);
break;
}
}
//GameManager
public void GameLoad()
{
if (!PlayerPrefs.HasKey("PlayerX"))
return;
float x = PlayerPrefs.GetFloat("PlayerX");
float y = PlayerPrefs.GetFloat("PlayerY");
int questId = PlayerPrefs.GetInt("QuestId");
int questActionIndex = PlayerPrefs.GetInt("QuestActionIndex");
player.transform.position = new Vector3(x, y, 0);
questManager.questId = questId;
questManager.questActionIndex = questActionIndex;
questManager.ControlObject();
}
퀘스트 오브젝트는 QuestManager에서 ControlObject() 함수로 관리하고 있다.
루도와 대화를 해서 "루도의 동전찾아주기" 퀘스트로 넘어가서 questId 20번일 때도, 재접속 시 questId 10번일때 퀘스트오브젝트가 true되어있어야한다.
: 불러오기 했을 당시의 퀘스트 순서와 연결된 오브젝트 관리를 추가해준다.
*꼭 GameManager의 GameLoad() 함수 내에도 호출해줘야한다. (GameLoad() 내에서 호출 안하면, 재실행 후 대화를 해야 오브젝트가 나타나는 문제가 생긴다)
'개발 > 유니티' 카테고리의 다른 글
탑다운 2d RPG(최종) - 피드백 추가 및 개선사항 (0) | 2024.02.08 |
---|---|
탑다운 2d RPG(8) - 모바일 UI & 안드로이드 빌드 (0) | 2024.02.05 |
탑다운 2d RPG(6) - 대화 애니메이션 느낌있게 만들기 (0) | 2024.02.02 |
탑다운 2d RPG(5) - 퀘스트 시스템 구현하기 (0) | 2024.02.01 |
탑다운 2d RPG(4) - 대화 시스템 구현하기 (0) | 2024.01.31 |