기록 보관소

[Unity/유니티] 기초-뱀서라이크: 캐릭터 해금 시스템[14+] 본문

유니티 프로젝트/뱀서라이크

[Unity/유니티] 기초-뱀서라이크: 캐릭터 해금 시스템[14+]

JongHoon 2023. 7. 25. 21:47

개요

유니티 독학을 위해 아래 링크의 골드메탈님의 영상들을 보고 직접 따라 해보면서 진행 상황을 쓰고 배웠던 점을 요약한다.

https://youtube.com/playlist?list=PLO-mt5Iu5TeYI4dbYwWP8JqZMC9iuUIW2 

 

📚유니티 기초 강좌

유니티 게임 개발을 배우고 싶은 분들을 위한 기초 강좌

www.youtube.com


뱀서라이크: 캐릭터 해금 시스템[14+]

0. 시작하기에 앞서 애니메이터 컨트롤러 만들기

플레이어 캐릭터를 2개 더 늘려야 하므로, 애니메이터 컨트롤러를 더 만들고 Player에 할당.

  • 일단 시작하기에 앞서, 지난 시간에 만들지 않았던 애니메이터 컨트롤러를 Sprites에 있는 스프라이트들을 이용해 애니메이션을 만들고, 해당 애니메이션을 Override 해주면 쉽게 만들 수 있다.
  • 애니메이터 컨트롤러 강의 영상은 아래 유튜브 영상을 통해서 보면 된다.
    • 아래 골드메탈님 영상에서는 Farmer 2, 3에 대한 제작은 따로 없어서 그냥 따로 직접 해야한다. 그래서 지난 시간에 AC가 2개가 더 없어서 2개만 했었고, 오늘 필요한 것 같아서 따로 추가하게 되었다.

https://youtu.be/vizfd1TeRMI?si=jF8MddRHRwhPq2db


1. 추가 캐릭터 버튼

캐릭터 선택 버튼을 2개 더 복사하고, 그에 맞춰 스프라이트, 이름, 설명, 버튼 색상, 아웃라인 색상을 변경
On Click에서 각 버튼에 맞춰 인자값을 변경
캐릭터 추가는 완성


2. 잠금과 해금

잠금할 캐릭터 버튼을 복사하고, 이름을 변경
버튼으로 사용하지 못하게 Button 컴포넌트를 삭제
Icon을 검은색으로 바꾸고, Text Name을 지우고, Text Adv를 변경
복사 후 Character 3에 맞춰서 변경

  • 해금은 조건 완료시 해당 캐릭터의 버튼을 활성화하고, Lock을 비활성화 하는 식으로 구현.

해당 기능을 구현할 ArchiveManager 스크립트 파일 생성
ArchiveManager 오브젝트도 생성해서 스크립트 파일을 넣는다

//ArchiveManager Script

using System;   //Enum 사용을 위한 namespace
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ArchiveManager : MonoBehaviour {
    public GameObject[] lockCharacter;
    public GameObject[] unlockCharacter;

    enum Archive { UnlockPotato, UnlockBean }
    Archive[] archives;

    void Awake() {
        archives = (Archive[])Enum.GetValues(typeof(Archive));

        if (!PlayerPrefs.HasKey("MyData")) {
            Init();
        }
    }

    void Init() {
        PlayerPrefs.SetInt("MyData", 1);

        foreach (Archive archive in archives) {
            PlayerPrefs.SetInt(archive.ToString(), 0);
        }
    }

    void Start() {
        UnlockCharacter();
    }

    void UnlockCharacter() {
        for (int index = 0; index < lockCharacter.Length; index++) {
            string archiveName = archives[index].ToString();
            bool isUnlock = PlayerPrefs.GetInt(archiveName) == 1;
            lockCharacter[index].SetActive(!isUnlock);
            unlockCharacter[index].SetActive(isUnlock);
        }
    }
}
  • PlayerPrefs : 간단한 저장 기능을 제공하는 유니티 제공 클래스

변수를 할당해준다
테스트 실행. 클릭도 안되고 잘 잠겨있다.
잠금 해제 확인을 위해 변수를 잠시 바꿔보자
Clear All PlayerPrefs를 누르고 다시 실행해보면?
실행과 동시에 잠금이 해제되는 것을 확인할 수 있다

  • 테스트 실행이 끝난 뒤에 다시 변수를 0으로 하고, Clear All PlayerPrefs를 해주었다.
  • 뒤늦게 알았는데, Achive를 실수로  Archive로 썼다. 위 캡쳐 이미지들에도 보이듯이 스크립트 파일 이름부터 내용, 오브젝트까지 전부 다 잘못 작성해버렸다... 다음 파트부터는 이 부분들을 다 고친 뒤에 진행했다.

3. 업적 달성 로직

//AchiveManager Script

using System;   //Enum 사용을 위한 namespace
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AchiveManager : MonoBehaviour {
    public GameObject[] lockCharacter;
    public GameObject[] unlockCharacter;

    enum Achive { UnlockPotato, UnlockBean }
    Achive[] achives;

    void Awake() {
        achives = (Achive[])Enum.GetValues(typeof(Achive));

        if (!PlayerPrefs.HasKey("MyData")) {
            Init();
        }
    }

    void Init() {
        PlayerPrefs.SetInt("MyData", 1);

        foreach (Achive Achive in achives) {
            PlayerPrefs.SetInt(Achive.ToString(), 0);
        }
    }

    void Start() {
        UnlockCharacter();
    }

    void UnlockCharacter() {
        for (int index = 0; index < lockCharacter.Length; index++) {
            string AchiveName = achives[index].ToString();
            bool isUnlock = PlayerPrefs.GetInt(AchiveName) == 1;
            lockCharacter[index].SetActive(!isUnlock);
            unlockCharacter[index].SetActive(isUnlock);
        }
    }

    void LateUpdate() {
        foreach (Achive achive in achives) {
            CheckAchive(achive);
        }
    }

    void CheckAchive(Achive achive) {
        bool isAchive = false;

        switch (achive) {
            case Achive.UnlockPotato:
                isAchive = GameManager.instance.kill >= 10;
                break;
            case Achive.UnlockBean:
                isAchive = GameManager.instance.gameTime == GameManager.instance.maxGameTime;
                break;
        }

        if (isAchive && PlayerPrefs.GetInt(achive.ToString()) == 0) {
            PlayerPrefs.SetInt(achive.ToString(), 1);
        }
    }
}

테스트 실행. 아직 달성 전이라 잠겨있다.
10킬 달성. 이제 게임을 강제로 종료해보자.
10킬을 달성해서 감자 농부가 해금되었다. 이제 생존을 통해서 콩농부를 해금해보자.
생존 완료. 이후 돌아가기 버튼을 누르면?
콩농부가 해금된 것을 확인할 수 있다


4. 해금 알려주기

업적 달성 여부를 알려주기 위한 Image 추가 및 수정
자식 오브젝트로 해당 업적을 알려줄 빈 오브젝트 생성 및 수정(앵커 전체)
빈 오브젝트 아래에 해금된 캐릭터를 표시할 Image 추가
Text도 추가
복사해서 콩농부에 맞춰서 변경
자식 오브젝트들과 부모 오브젝트 모두 비활성화

//AchiveManager Script

using System;   //Enum 사용을 위한 namespace
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AchiveManager : MonoBehaviour {
    public GameObject[] lockCharacter;
    public GameObject[] unlockCharacter;
    public GameObject uiNotice;

    enum Achive { UnlockPotato, UnlockBean }
    Achive[] achives;
    WaitForSecondsRealtime wait;

    void Awake() {
        achives = (Achive[])Enum.GetValues(typeof(Achive));
        wait = new WaitForSecondsRealtime(5);

        if (!PlayerPrefs.HasKey("MyData")) {
            Init();
        }
    }

    void Init() {
        PlayerPrefs.SetInt("MyData", 1);

        foreach (Achive Achive in achives) {
            PlayerPrefs.SetInt(Achive.ToString(), 0);
        }
    }

    void Start() {
        UnlockCharacter();
    }

    void UnlockCharacter() {
        for (int index = 0; index < lockCharacter.Length; index++) {
            string AchiveName = achives[index].ToString();
            bool isUnlock = PlayerPrefs.GetInt(AchiveName) == 1;
            lockCharacter[index].SetActive(!isUnlock);
            unlockCharacter[index].SetActive(isUnlock);
        }
    }

    void LateUpdate() {
        foreach (Achive achive in achives) {
            CheckAchive(achive);
        }
    }

    void CheckAchive(Achive achive) {
        bool isAchive = false;

        switch (achive) {
            case Achive.UnlockPotato:
                isAchive = GameManager.instance.kill >= 10;
                break;
            case Achive.UnlockBean:
                isAchive = GameManager.instance.gameTime == GameManager.instance.maxGameTime;
                break;
        }

        if (isAchive && PlayerPrefs.GetInt(achive.ToString()) == 0) {
            PlayerPrefs.SetInt(achive.ToString(), 1);

            for (int index = 0; index < uiNotice.transform.childCount; index++) {
                bool isActive = index == (int)achive;
                uiNotice.transform.GetChild(index).gameObject.SetActive(isActive);
            }

            StartCoroutine(NoticeRoutine());
        }
    }

    IEnumerator NoticeRoutine() {
        uiNotice.SetActive(true);

        yield return wait;

        uiNotice.SetActive(false);
    }
}

uiNotice 변수 할당
테스트 실행 전 Clear All PlayerPrefs 하기
최종 테스트