[내배캠] 유니티 입문 개인과제#2

Sungchan Ahn(안성찬)·2024년 10월 11일

내일배움캠프

목록 보기
41/104

오늘 구현한 기능

  • Start Scene에서 캐릭터 선택 및 이름 입력
  • Main Scene에서 플레이어 이름 변경
  • Main Scene 현재 시간 표시
  • Main Scene 메뉴 UI

오늘 겪은 문제 1.

MainScene에서 이름을 변경할 때 wasd를 누를 때마다 플레이어가 계속 이동함

문제 1 해결

이름을 설정하는 UI가 SetActive일 때 timeScale을 0으로 만들어서 플레이어 이동을 멈추고, SetActive가 false일 때 다시 1로 만들어 해결했다.

using UnityEngine;

public class ButtonManager : MonoBehaviour
{
    public GameObject SelectedUI;
    
    public void SetActiveUI()
    {
        SelectedUI.SetActive(!SelectedUI.activeSelf);
    }
}

public class SetNameButton : ButtonManager
{
    public void HoldInputSystem()
    {
        if (SelectedUI.activeSelf)
        {
            Time.timeScale = 0.0f;
        }
        else
        {
            Time.timeScale = 1.0f;
        }
    }
}

오늘 겪은 문제 2.

이름을 설정할 때, 아래의 예외마다 안내문구 강조 처리 스크립트를 작성해서 가독성이 떨어짐

  • 입력한 이름이 2 ~ 10글자에 해당하지 않는 경우, 안내문구 강조
  • 저장된 이름이 없을 경우 StartScene에서 Join 버튼을 눌러도 MainScene으로 넘어가지 않고
    안내문구 강조

문제 2 해결

안내문구 강조하는 기능을 따로 분리하여 각 조건에 해당할 때 호출

public class AlertNameSetting : MonoBehaviour
{
    public static AlertNameSetting Instance;

    [SerializeField] private GameObject AlertObject;
    [SerializeField] private TMP_Text AlertText;

    private void Awake()
    {
        Instance = this;
    }

    public void DisplayAlert()
    {
        AlertObject.SetActive(true);
        AlertText.color = Color.red;
        Invoke("ChangeAlertColor", 3f);
    }

    void ChangeAlertColor()
    {
        AlertText.color = Color.white;
    }
}
-------------------------------------------------------------------------------
public class SetPlayerName : MonoBehaviour
{
    [SerializeField] private TMP_InputField setNameInputField;
    [SerializeField] private TMP_Text CurrentNameText;
    
    public void SetName()
    {
        if (setNameInputField.text.Length >= 2 && setNameInputField.text.Length <= 10)
        {
            PlayerPrefs.SetString("Name", setNameInputField.text);
            DisplayCurrentName();
        }
        else
        {
            AlertNameSetting.Instance.DisplayAlert();
        }
    }
}
-------------------------------------------------------------------------------
public class JoinButton : MonoBehaviour
{
    public void Join()
    {
        if (PlayerPrefs.HasKey("Name"))
        {
            SceneManager.LoadScene("MainScene");
        }
        else
        {
            AlertNameSetting.Instance.DisplayAlert();
        }
    }
}

오늘 겪은 문제 3.

이름 변경했을 때 MainScene에서 캐릭터 위에 표시되는 이름UI에 반영되지 않았다. 이름 UI Text에 변경된 이름을 반영하는 기능이 바로 호출되어야 하는데 호출하지 않아서 발생했다. 처음에는 GameManager의 Update문에서 조건문으로 현재 이름과 변경된 이름을 비교해서 바뀌었으면 UI에 반영하는 방식으로 처리했는데, 매 프레임마다 조건을 검사할 필요가 없다고 판단했다.

문제 3 해결

버튼을 눌러 이름을 변경하는 동시에 ChangeName()도 호출되어 이름이 변경된 것을 반영했다.

public class SetPlayerName : MonoBehaviour
{
    [SerializeField] private TMP_InputField setNameInputField;
    [SerializeField] private TMP_Text CurrentNameText;
    
    public void ChangeName()
    {
        if (GameManager.Instance.PlayerName.text != PlayerPrefs.GetString("Name"))
        {
            GameManager.Instance.PlayerName.text = PlayerPrefs.GetString("Name");
        }
    }
}

오늘 겪은 문제 4.

2가지 캐릭터 Penguin과 Astronaut가 있고 기본 캐릭터는 Penguin으로 설정되어 있다. 캐릭터를 선택하면 PlayerPrefs.GetInt("SelectedCharacter")로 선택한 캐릭터가 저장되는데, 게임을 다시 시작하면 저장된 캐릭터 이미지가 표시되지 않고 기본 캐릭터 이미지가 표시되었다.

문제 4 해결

이미지를 표시하는 게임 오브젝트를 모두 비활성화한 상태로 시작해서 Start에서 PlayerPrefs를 가져와 저장된 캐릭터의 이미지 오브젝트를 활성화한다.

public class StartManager : MonoBehaviour
{
    public GameObject[] CharacterUI;
    public GameObject SelectedCharacterUI;

    private void Start()
    {
        if (!PlayerPrefs.HasKey("SelectedCharacter"))
        {
            PlayerPrefs.SetInt("SelectedCharacter", 0);
        }
        SelectedCharacterUI = CharacterUI[PlayerPrefs.GetInt("SelectedCharacter")];
        SelectedCharacterUI.SetActive(true);
    }
}

오늘 겪은 문제 5.

Main Scene에서 캐릭터를 변경할 때, 기존 캐릭터를 파괴한 후 선택한 캐릭터 prefab을 현재 위치에 생성되게 했는데 파괴는 되지만 생성이 안 되는 문제가 있었다.
이 문제는 Instantiate에서 생긴 문제로 확인했다. 기존에 작성한(아래의 코드) Instantiate<T>(T original, Transform parent)를 할 경우 Parent의 자식으로 오브젝트가 생기게 된다. 여기서 문제가 발생했는데 CurrentPlayerPosition을 기존에 생성된 캐릭터 오브젝트의 Transform을 참조하고, 플레이어를 파괴해 버린 순간 Instantiate로 생성할 부모의 Transform이 사라져 버리기 때문에 새 캐릭터 Prefab을 생성할 수 없게 된다.

public class GameManager : MonoBehaviour
{
    public static GameManager Instance;

    public GameObject[] CharacterPrefabs;
    [HideInInspector] public GameObject Player;

    public Transform SpawnPoint;
    [HideInInspector] public Transform CurrentPlayerPosition;
    
    private void Start()
    {
        Player = Instantiate(CharacterPrefabs[PlayerPrefs.GetInt("SelectedCharacter")], SpawnPoint);
        PlayerName = Player.GetComponentInChildren<TMP_Text>();
        PlayerName.text = PlayerPrefs.GetString("Name");
        Time.timeScale = 1.0f;
    }
    
    public void ChangeCharacter()
    {
        CurrentPlayerPosition = Player.transform;
        Destroy(Player);
        Player = Instantiate(CharacterPrefabs[PlayerPrefs.GetInt("SelectedCharacter")], CurrentPlayerPosition);
        PlayerName = Player.GetComponentInChildren<TMP_Text>();
        PlayerName.text = PlayerPrefs.GetString("Name");
    }

문제 5 해결

Instantiate<T>(T original, Vector3 position, Quaternion)을 사용해 벡터3 위치에 바로 생성하여 문제를 해결하였다.

public class GameManager : MonoBehaviour
{
    public Transform SpawnPoint;
    [HideInInspector] public Vector3 CurrentPlayerPosition;

    private void Start()
    {
        Player = Instantiate(CharacterPrefabs[PlayerPrefs.GetInt("SelectedCharacter")], 
        					 SpawnPoint.position, Quaternion.identity);
    }

    public void ChangeCharacter()
    {
        CurrentPlayerPosition = Player.transform.position;
        Destroy(Player);
        Player = Instantiate(CharacterPrefabs[PlayerPrefs.GetInt("SelectedCharacter")], 
        					 CurrentPlayerPosition, Quaternion.identity);
    }

profile
게임 개발 기록

0개의 댓글