https://github.com/Kimhyogyeom/ProjectA
GitHub - Kimhyogyeom/ProjectA: Unity-MOBA-Prototype
Unity-MOBA-Prototype. Contribute to Kimhyogyeom/ProjectA development by creating an account on GitHub.
github.com
https://github.com/Kimhyogyeom/ProjectB
GitHub - Kimhyogyeom/ProjectB: Drill-inspired Project
Drill-inspired Project. Contribute to Kimhyogyeom/ProjectB development by creating an account on GitHub.
github.com
안녕하세요.
먼저 현재 작업 중인 녹화 영상을 보겠습니다.
파괴가 될 그라운드를 10개로 지정 후 진행중인 작업을 녹화한 영상입니다.
작업은 크게 4가지로 다음과 같습니다.
1. Lobby 씬 관련 UI 작업
2. 로비 <-> 게임 씬 전환 관련 작업
3. 코인 획득 관련 작업
4. 스태미나 소모 관련 작업
사실 오늘 자정이 넘기 전에, 블로그에 글을 업로드를 할 계획을 하고 있었는데요!
(일단 그러지 못했습니다😂)
작업을 진행하다 보니, 배치한 UI 중에
작업을 안 한 Setting 버튼만 달랑 남아있었고 다음과 같이 UI를 작업해 주었습니다.
이후 Setting 버튼에 간단히 상호작용만 한 뒤 블로그에 일지를 올리려고 했는데
이게 씬 전환을 생각 못한 채로 작업을 하다 보니 생각보다 오래 걸렸습니다.
결국 current 값과 제어할 변수 값을 나눠서 작업을 했는데!
이 부분은 작업한 내용을 하나씩 보면서 이야기를 더 진행하겠습니다.
먼저 Lobby 씬입니다.
동적인 느낌을 '드드드드릴' 앱과 비슷하게 살리려고 노력을 했습니다. 😁
그리고 Sprite는 네.. 정말 원하는 것을 쉽게 찾을 수 없어서
LMArena.ai를 사용해서 생성한 이미지입니다.
먼저 패턴 반복은 "BackgroundPattern" 스크립트를 작성해 작업해 주었습니다.
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 백그라운드 패턴 루프
/// </summary>
public class BackgroundPattern : MonoBehaviour
{
[SerializeField] private RawImage _BgStar; // 백그라운드 이미지(Star)
[SerializeField] private float _scrollSpeedX = 0.2f; // X 스크롤 스피드
[SerializeField] private float _scrollSpeedY = 0.2f; // Y 스크롤 스피드
[Header("Scroll Check")]
[SerializeField] private bool _scrollX = true; // X축 스크롤 여부
[SerializeField] private bool _scrollY = false; // Y축 스크롤 여부
void Update()
{
// uvRect 가져오기
Rect rect = _BgStar.uvRect;
// X 스크롤
if (_scrollX)
rect.x += _scrollSpeedX * Time.deltaTime;
// Y 스크롤
if (_scrollY)
rect.y += _scrollSpeedY * Time.deltaTime;
// 변경된 uvRect(rect) 적용
_BgStar.uvRect = rect;
}
}
RawImage에 UV Rect x, y를 이용해 무한으로 스크롤되게 구현해 주었습니다.
다음은 씬 전환 (Lobby <-> Game)입니다.
캔버스 자체를 DontDestroyOnLoad()를 활용해 씬 전환 시 유지를 해주었습니다.
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
/// <summary>
/// 씬 전환용 UI/애니메이션 매니저
/// </summary>
public class UITransition : MonoBehaviour
{
[SerializeField] private Animator _doorAnim; // 문 닫힘/열림 애니메이터
[SerializeField] private Canvas _canvas; // Slice 담고 있는 캔버스
public static UITransition Instance;
private void Awake()
{
// 싱글톤 처리 : 중복 오브젝트 삭제
if (Instance != null)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject); // 씬 전환 시 유지
}
private void Start()
{
// Canvas를 항상 최상위로 렌더링 : 넉넉하게 10?
_canvas.sortingOrder = 10;
}
/// <summary>
/// 슬라이스 오픈
/// </summary>
public void OpenSlice()
{
_doorAnim.SetTrigger("Open");
}
/// <summary>
/// 슬라이스 클로즈
/// </summary>
public void CloseSlice()
{
_doorAnim.SetTrigger("Close");
}
/// <summary>
/// 슬라이드 Open Setting
/// </summary>
public void SetSliceOpen(string sceneName)
{
// 코루틴 실행
StartCoroutine(TransitionCoroutine(sceneName));
}
/// <summary>
/// 씬 전환 + 문 애니메이션 코루틴
/// </summary>
private IEnumerator TransitionCoroutine(string sceneName)
{
// 씬 비동기 로드
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
// 씬 로드 완료될 때까지 기다리기
while (!asyncLoad.isDone)
{
yield return null;
}
// 씬 로드 완료되면 Open 애니메이션 전이 조건 실행
_doorAnim.SetTrigger("Open");
}
}
또한 Open / Close Slice의 함수를 만들어주어
외부에서 쉽게 호출을 하게 해 주었으며
SceneManager.LoadSceneAsync()를 사용해
비동기로 씬을 로드시켜 주었습니다.
마지막으로 씬이 완료될 때를 어떻게 감지할지 찾아보다가
asyncLoad.isDone으로 씬이 로드가 됐는지 결괏값을 true/false로 쉽게 알 수 있다고 하여
while을 통해 반복문에서 기다리다가 slice가 Close 되는
애니메이션 전이 조건을 실행해 주었습니다.
Slice의 애니메이터입니다.
초기 기본 상태에 있다가 Trigger를 통해 Slice / SliceOpen이 되게 해 주었습니다.
다음은 Lobby에서 코인을 획득할 때입니다.
기존 Play(Game) 씬에서만 작업을 했었기에,
스크립트 이동 및 몇 가지 부가 작업들이 필요했는데요.
우선 획득한 코인의 총수량을 PossessionManager에서 관리를 해 주었습니다.
using TMPro;
using UnityEngine;
/// <summary>
/// InGame
/// 소지 요소
/// </summary>
public class PossessionManager : MonoBehaviour
{
// 소지 코인
public float _inGameCoin = 0;
// 싱글톤 인스턴스
public static PossessionManager Instance = null;
private void Awake()
{
// 싱글톤 처리 : 중복 오브젝트 삭제
if (Instance != null)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject); // 씬 전환 시 유지
}
}
다음 플레이 씬에서 게임이 끝나고 보상이 나올 때 Coin을 가져오게 됩니다.
코인을 획득하게 되면 현재 갖고 있는 코인을 총 소지하고 있는 코인에 더해주게 작업을 했습니다.
또한
GetCoin()은 Public으로 선언을 해 외부에서 호출이 가능하게 해주었습니다.
using System;
using TMPro;
using UnityEngine;
/// <summary>
/// 코인 매니저
/// </summary>
public class CoinManager : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI _cointText; // 코인 텍스트 (Game)
private float _gainCoinValue = 2.0f; // + 코인 Value
public float _currentCoin = 0f; // 현재 코인 값
public void GetCoin()
{
_currentCoin += _gainCoinValue;
_cointText.text = Convert.ToInt32(_currentCoin).ToString();
PossessionManager.Instance._inGameCoin += _gainCoinValue;
}
}
다음은 스태미나 관련 작업입니다.
스태미나는 씬이 전환돼도 시간을 동일하게 흐르게 하기 위하여 DontDestroyOnLoad()를 사용하여
파괴되지 않게 해 주었습니다.
또한
시간을 정해주어서 일정 시간이 흐르면
recoverCount를 활용해 해당 수만큼 스태미나를 회복하게 해 주었습니다.
using UnityEngine;
using TMPro;
using UnityEngine.SceneManagement;
/// <summary>
/// 스테미너 매니저
/// </summary>
public class StaminaManager : MonoBehaviour
{
public static StaminaManager Instance;
public int _currentStamina = 50; // 현재 스테미나
private int _maxStamina = 50; // 최대 스테미나
private int _decreaseAmount = 5; // - 스테미나
private int _increaseAmount = 5; // + 스테미나
[SerializeField] private float _recoveryInterval = 30f; // 5분 = 300초
private float _lastUpdateTime; // 마지막으로 회복한 시간 계산
private void Awake()
{
if (Instance != null)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
// 시작 시점 시간 기록 : DateTime 앱 종료
_lastUpdateTime = Time.time;
}
private void Update()
{
// if (Input.GetKeyDown(KeyCode.Space))
// {
// Decrease();
// }
// 최대치라면 계산할 필요 x
if (_currentStamina >= _maxStamina) return;
// 마지막 업데이트 이후 시간 계산
float elapsed = Time.time - _lastUpdateTime;
// _recoveryInterval 지났다면
if (elapsed >= _recoveryInterval)
{
// 경과한 시간 5분 단위 계산
int recoverCount = Mathf.FloorToInt(elapsed / _recoveryInterval);
// 스테미나 회복 / 최대치 : _maxStamina (50)
_currentStamina = Mathf.Min(_currentStamina + recoverCount * _increaseAmount, _maxStamina);
// 마지막 업데이트 시각 갱신
_lastUpdateTime += recoverCount * _recoveryInterval;
}
}
/// <summary>
/// 스테미나 감소 호출 함수 (외부)
/// </summary>
public void Decrease()
{
_currentStamina -= _decreaseAmount;
}
}
위 해당 스크립트는 단순히 스태미나의 값 자체만 제어합니다.
다음 Decrease() 함수는 현재 스태미나를 소모값에 맞게 계산을 합니다.
Decrease() 함수는 이 Start 버튼을 클릭할 때 호출됩니다.
using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Lobby to Play : Start Button
/// </summary>
public class StartButton : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI _staminaText;
[SerializeField] private Button _startButton;
[SerializeField] private Animator _sliceAnimator;
/// <summary>
/// Awake is called when the script instance is being loaded.
/// </summary>
void Awake()
{
_startButton.onClick.AddListener(OnClickStartButton);
}
private void OnClickStartButton()
{
// 슬라이드 클로즈 : 오픈
UITransition.Instance.CloseSlice();
// 스테미너 감소
StaminaManager.Instance.Decrease();
// 코루틴 실행
StartCoroutine(LobbyToGameCorutine());
}
IEnumerator LobbyToGameCorutine()
{
yield return new WaitForSecondsRealtime(1.0f);
UITransition.Instance.SetSliceOpen("Play");
}
}
특이사항으로 Start 버튼을 클릭하면
코루틴을 활용해 시간 지연 후 씬 이동을 하게 되는데요.
여기서 기다린 시간 지연은 Slice 애니메이션의 재생 시간과 똑같습니다.
게임 씬에서 로비로 넘어오는 것과 마찬가지로
로비에서 씬으로 넘어갈 때에도 같은 로직을 사용해 재사용을 해주었습니다.
단순히 이동할 SceneName만 지정해 주면 됩니다.
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
/// <summary>
/// 씬 전환용 UI/애니메이션 매니저
/// </summary>
public class UITransition : MonoBehaviour
{
[SerializeField] private Animator _doorAnim; // 문 닫힘/열림 애니메이터
[SerializeField] private Canvas _canvas; // Slice 담고 있는 캔버스
public static UITransition Instance;
private void Awake()
{
// 싱글톤 처리 : 중복 오브젝트 삭제
if (Instance != null)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject); // 씬 전환 시 유지
}
private void Start()
{
// Canvas를 항상 최상위로 렌더링 : 넉넉하게 10?
_canvas.sortingOrder = 10;
}
/// <summary>
/// 슬라이스 오픈
/// </summary>
public void OpenSlice()
{
_doorAnim.SetTrigger("Open");
}
/// <summary>
/// 슬라이스 클로즈
/// </summary>
public void CloseSlice()
{
_doorAnim.SetTrigger("Close");
}
/// <summary>
/// 슬라이드 Open Setting
/// </summary>
public void SetSliceOpen(string sceneName)
{
// 코루틴 실행
StartCoroutine(TransitionCoroutine(sceneName));
}
/// <summary>
/// 씬 전환 + 문 애니메이션 코루틴
/// </summary>
private IEnumerator TransitionCoroutine(string sceneName)
{
// 씬 비동기 로드
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
// 씬 로드 완료될 때까지 기다리기
while (!asyncLoad.isDone)
{
yield return null;
}
// 씬 로드 완료되면 Open 애니메이션 전이 조건 실행
_doorAnim.SetTrigger("Open");
}
}
마지막으로 Lobby 씬의 Setting 버튼입니다.
SettingController 스크립트를 선언해 작업해 주었는데요
스크립트를 작성하면서 반복되는 코드는 재사용할 수 있게 정리를 하며 작업을 했는데
아직 약간 더 수정을 할 필요가 있어 보입니다.
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 세팅 버튼 컨트롤러
/// </summary>
public class SettingController : MonoBehaviour
{
[Header("Object")]
[SerializeField] private GameObject _settingObj; // 세팅 UI 오브젝트
[Header("Button")]
[SerializeField] private Button _settingOpneButton; // 세팅 Opne버튼
[SerializeField] private Button _settingCloseButton; // 세팅 Close버튼
[Header("Bgm & Sfx & Vib")]
[SerializeField] private Button _bgmToggleBtn; // BGM 토글 버튼
[SerializeField] private Button _sfxToggleBtn; // SFX 토글 버튼
[SerializeField] private Button _vibToggleBtn; // VIB 토글 버튼
[Header("Sprite & Text")]
[SerializeField] private Sprite _toggleOnImg;
[SerializeField] private Sprite _toggleOffImg;
[SerializeField] private Image _bgmBtnImg;
[SerializeField] private Image _sfxBtnImg;
[SerializeField] private Image _vibBtnImg;
[SerializeField] private TextMeshProUGUI _bgmToggleText;
[SerializeField] private TextMeshProUGUI _sfxToggleText;
[SerializeField] private TextMeshProUGUI _vibToggleText;
[Header("Save & Cancle")]
[SerializeField] private Button _settingSaveButton; // SaveButton
[SerializeField] private Button _settingCancleButton; // CancleButton
private bool _bgmBtnActive = true;
private bool _sfxBtnActive = true;
private bool _vibBtnActive = true;
void Awake()
{
_settingOpneButton.onClick.AddListener(OpenSettingUI);
_settingCloseButton.onClick.AddListener(CloseSettingUI);
_bgmToggleBtn.onClick.AddListener(OnClickBgmButton);
_sfxToggleBtn.onClick.AddListener(OnClickSfxButton);
_vibToggleBtn.onClick.AddListener(OnClickVibButton);
_settingSaveButton.onClick.AddListener(OnClickSaveButton);
_settingCancleButton.onClick.AddListener(OnClickCancleButton);
}
/// <summary>
/// 활성화 될 때
/// </summary>
void OnEnable()
{
StartCoroutine(SetButtonsCorutine());
}
private IEnumerator SetButtonsCorutine()
{
// Instance가 생성될 때까지 대기
yield return new WaitUntil(() => PossessionManager.Instance != null);
_bgmBtnActive = SoundManager.Instance._currentBgmBtnActive;
_sfxBtnActive = SoundManager.Instance._currentSfxBtnActive;
_vibBtnActive = SoundManager.Instance._currentVibBtnActive;
ResetBtnState();
}
/// <summary>
/// UI 활성화
/// </summary>
private void OpenSettingUI()
{
_settingObj.SetActive(true);
}
/// <summary>
/// UI 비활성화
/// </summary>
private void CloseSettingUI()
{
_settingObj.SetActive(false);
}
/// <summary>
/// 비지엠 버튼
/// </summary>
private void OnClickBgmButton()
{
SoundManager.Instance.BgmToggle();
BgmBtnState();
}
/// <summary>
/// 에스에프엑스 버튼
/// </summary>
private void OnClickSfxButton()
{
SoundManager.Instance.SfxToggle();
SfxBtnState();
}
/// <summary>
/// 진동 버튼
/// </summary>
private void OnClickVibButton()
{
SoundManager.Instance.VibToggle();
VibBtnState();
}
/// <summary>
/// 세이브 버튼
/// </summary>
private void OnClickSaveButton()
{
SoundManager.Instance.OnClickSavebutton();
SoundManager.Instance._currentBgmBtnActive = _bgmBtnActive;
SoundManager.Instance._currentSfxBtnActive = _sfxBtnActive;
SoundManager.Instance._currentVibBtnActive = _vibBtnActive;
CloseSettingUI();
}
/// <summary>
/// 캔슬 버튼
/// </summary>
private void OnClickCancleButton()
{
SoundManager.Instance.OnClickCanclebutton();
_bgmBtnActive = SoundManager.Instance._currentBgmBtnActive;
_sfxBtnActive = SoundManager.Instance._currentSfxBtnActive;
_vibBtnActive = SoundManager.Instance._currentVibBtnActive;
ResetBtnState();
CloseSettingUI();
}
private void BgmBtnState()
{
if (_bgmBtnActive)
{
_bgmBtnImg.sprite = _toggleOffImg;
_bgmToggleText.text = "Off";
_bgmBtnActive = false;
}
else
{
_bgmBtnImg.sprite = _toggleOnImg;
_bgmToggleText.text = "On";
_bgmBtnActive = true;
}
}
private void SfxBtnState()
{
if (_sfxBtnActive)
{
_sfxBtnImg.sprite = _toggleOffImg;
_sfxToggleText.text = "Off";
_sfxBtnActive = false;
}
else
{
_sfxBtnImg.sprite = _toggleOnImg;
_sfxToggleText.text = "On";
_sfxBtnActive = true;
}
}
private void VibBtnState()
{
if (_vibBtnActive)
{
_vibBtnImg.sprite = _toggleOffImg;
_vibToggleText.text = "Off";
_vibBtnActive = false;
}
else
{
_vibBtnImg.sprite = _toggleOnImg;
_vibToggleText.text = "On";
_vibBtnActive = true;
}
}
private void ResetBtnState()
{
if (!SoundManager.Instance._currentBgmBtnActive)
{
_bgmBtnImg.sprite = _toggleOffImg;
_bgmToggleText.text = "Off";
}
else
{
_bgmBtnImg.sprite = _toggleOnImg;
_bgmToggleText.text = "On";
}
if (!SoundManager.Instance._currentSfxBtnActive)
{
_sfxBtnImg.sprite = _toggleOffImg;
_sfxToggleText.text = "Off";
}
else
{
_sfxBtnImg.sprite = _toggleOnImg;
_sfxToggleText.text = "On";
}
if (!SoundManager.Instance._currentVibBtnActive)
{
_vibBtnImg.sprite = _toggleOffImg;
_vibToggleText.text = "Off";
}
else
{
_vibBtnImg.sprite = _toggleOnImg;
_vibToggleText.text = "On";
}
}
}
핵심 로직을 살펴보자면 다음과 같습니다.
1. 단순 값 만 변경되는 경우는 스크립트 내 변수를 활용합니다.
2. 값이 저장되면 싱글톤 패턴 스크립트 + DontDestroyLoad()인 SoundManager에서
current값 도 같이 활용을 합니다.
3. cancel이 되면 변경 됐던 스크립트 내 변숫값은 current값으로 되돌립니다.
이 외의 함수는
각 버튼을 클릭 됐을 때 실행 되어야 할 상호작용을 구현해 주었습니다.
SettingController 스크립트는
각 버튼에 대한 상호작용과 해당 변수의 값 만 제어합니다.
다음은 음향 재생 작업은 하지 않았지만
UI에 BGM, SFX, VIB 가 있기에 만든 SoundManager 스크립트입니다.
using UnityEngine;
/// <summary>
/// 사운드 매니저
/// </summary>
public class SoundManager : MonoBehaviour
{
public static SoundManager Instance;
[Header("Audio Sources")]
public AudioSource _bgmSource;
public AudioSource _sfxSource;
[Header("Database")]
public SoundDatabase _soundDatabase;
[Header("Setting value")]
public bool _bgmCtrl = true;
private bool _currentBgmCtrl = true;
public bool _sfxCtrl = true;
private bool _currentSfxCtrl = true;
public bool _vibCtrl = true;
private bool _currentVibCtrl = true;
[Header("UI")]
public bool _currentBgmBtnActive = true;
public bool _currentSfxBtnActive = true;
public bool _currentVibBtnActive = true;
private void Awake()
{
if (Instance == null) Instance = this;
else Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
/// <summary>
/// BGM 플레이
/// </summary>
public void PlayBGM(AudioClip clip, float volume = 1f)
{
if (clip == null) return;
if (_currentBgmCtrl)
_bgmSource.PlayOneShot(clip, volume);
}
/// <summary>
/// SFX 플레이
/// </summary>
public void PlaySFX(AudioClip clip, float volume = 1f)
{
if (clip == null) return;
if (_currentSfxCtrl)
_sfxSource.PlayOneShot(clip, volume);
}
/// <summary>
/// Vib 플레이
/// </summary>
public void PlayVib()
{
if (_currentVibCtrl)
{
Handheld.Vibrate();
}
}
/// <summary>
/// BGM 버튼 클릭
/// </summary>
public void BgmToggle() => _bgmCtrl = _bgmCtrl ? false : true;
/// <summary>
/// SFX 버튼 클릭
/// </summary>
public void SfxToggle() => _sfxCtrl = _sfxCtrl ? false : true;
/// <summary>
/// Vib 버튼 클릭
/// </summary>
public void VibToggle() => _vibCtrl = _vibCtrl ? false : true;
/// <summary>
/// 세이브 버튼 클릭
/// </summary>
public void OnClickSavebutton()
{
_currentBgmCtrl = _bgmCtrl;
_currentSfxCtrl = _sfxCtrl;
_currentVibCtrl = _vibCtrl;
_bgmSource.mute = _currentBgmCtrl ? false : true;
_sfxSource.mute = _currentSfxCtrl ? false : true;
}
/// <summary>
/// 캔슬 버튼 클릭
/// </summary>
public void OnClickCanclebutton()
{
_bgmCtrl = _currentBgmCtrl;
_sfxCtrl = _currentSfxCtrl;
_vibCtrl = _currentVibCtrl;
}
}
가장 먼저 버튼이 클릭되면 해당 current + 변수 값을 통해
Audio Source의 mute를 제어합니다.
SoundManger는 오직 Sound에 관련된 변수 및 상태값만 제어해 줍니다.
다음은 SoundManager의 해당 소스의 재생 부분인데요
public void PlayBGM(AudioClip clip, float volume = 1f)
{
if (clip == null) return;
if (_currentBgmCtrl)
_bgmSource.PlayOneShot(clip, volume);
}
/// <summary>
/// SFX 플레이
/// </summary>
public void PlaySFX(AudioClip clip, float volume = 1f)
{
if (clip == null) return;
if (_currentSfxCtrl)
_sfxSource.PlayOneShot(clip, volume);
}
/// <summary>
/// Vib 플레이
/// </summary>
public void PlayVib()
{
if (_currentVibCtrl)
{
Handheld.Vibrate();
}
}
BGM / SFX는 PlayOnShot을 사용해 소리가 중첩되게 재생이 되게 해 주었으며
진동은 Handheld.Vibrate()로 실행되게 해 주었습니다.
아직 재생 작업은 하지 않았습니다.
BGM / SFX 오디오 클립은 ScriptableObject트로 편리하게 나눠서 관리할 예정입니다.
using UnityEngine;
[CreateAssetMenu(fileName = "SoundDatabase", menuName = "Sound/SoundDatabase")]
public class SoundDatabase : ScriptableObject
{
}
어느덧 ProjectB도 마무리가 되어가는 것 같습니다.😁
작업을 진행하면서, 반복되는 로직을 줄여 함수로 만들고 각 스크립트는 한 가지의
목적만 갖게끔 작업을 해주려고 하고는 있는데요..
이와 마찬가지로 조금 애매한 점이
첫 번째로 스크립트 명을 어떻게 지어야 할 지의 부분과
두 번째로 스크립트를 선언하면 어느 오브젝트에 부착을 하는 게 맞는지..
(현재 Lobby 씬을 보면 예를 들어 GetCoin의 스크립트를 선언했다면, Unity 하이어라키에 빈 오브젝트를 GetCoin의 이름으로 생성해 부착해 주는 방식으로 작업을 했습니다.
/
아니면, 오브젝트가 비활성화될 일이 없다면 해당 오브젝트에 부착을 하는 게 맞는 건지의 부분입니다.)
이게 두 번째 같은 경우에는 사람마다 조금씩 다른 것 같아서, 협업 시 규칙에 따라 맞추면 될 것 같은데
첫 번째 같은 경우엔 조금 더 다양한 소스코드들을 찾아봐야겠습니다.😂
오늘도 찾아와 주셔서 감사드리며,
모두 행복(x999)한 하루 보내세요!!
감사합니다.
