부트캠프/사전캠프

[내일배움 사전캠프_2025JUN26]쉽게 배우는 모바일 게임 5주차 - 게임 완성하기 1 (마무리)

Young_A 2025. 6. 26. 18:51

쉽게 배우는 모바일 게임 5주차 - 게임 완성하기 1 (마무리)

 

4주차 과제는 그냥 어제 포스트 마지막에 붙여넣었다.

 

5주차 강의는 실제 어플을 추출했을때의 디테일을 살리는 요소들을 점검해보았다.

 

스플래시 이미지 설정하기

스플래시 이미지는 앱을 오픈했을 때 떴다가 사라지는 이미지들을 말한다. 주로 어플의 로고나 개발사 등 개발과 연관된 이미지를 넣는다.

Edit - Project Setting - Player - Splash Image에서 설정할 수 있다.

우리는 유니티 무료 버전을 사용하기 때문에 위와 같은 Made with Unity가 스플래시 이미지로 자동 설정되어있다.

Draw mode를 all Sequential로 변경해주어 내가 원하는 이미지가 추가로 보여지게 할 수 있다.

 

원하는 이미지를 추가하기 전에, Inspector에서 Mesh Type을 Tight에서 FullRect로 변경해주었는데 이유가 궁금해서 찾아봤다.

- Tight: 이미지의 실제 픽셀 형태에 맞춰 경계선을 따라 꼭 맞게 메시를 생성함 -> 투명 영역 제거로 성능 및 메모리 최적화 / 복잡한 메시로 인해 일부 셰이더나 마스크와 호환성 문제 가능

- Full Rect: 이미지의 전체 직사각형 영역을 사용하여 메시 생성 -> 단순한 사각형 메시로 호환성 높음 / 투명한 영역까지 포함하므로 비효율적일 수 있음

 

즉, 다양한 해상도, 디바이스 비율에서 이미지가 자동으로 스케일되는 스플래시 화면은 Full Rect를 이용하는 게 났다.

그 외에도 브랜드 로고 등이 주로 삽입되므로 정확히 표현되어야하므로 외곡될 여지가 있는 Tight은 부적합하다는 결론을 얻었다.


카드 뒤집기 효과음 넣기

효과음을 생성할 오브젝트에 AudioSource 컴포넌트를 추가 후 아래와 같은 코드를 작성하였다.

clip에는 flip 사운드를 넣어주었다.

//Card.cs
AudioSource audioSource;
public AudioClip clip;
//Start()
audioSource = GetComponent<AudioSource>();
//OpenCard()
audioSource.PlayOneShot(clip);

배경음악 재생하기 AudioManager

AudioManager 오브젝트와 AudioManager.cs 파일을 만들어준다.

public AudioSource audioSource;
public AudioClip clip;
// Start is called before the first frame update
void Start()
{
    audioSource = GetComponent<AudioSource>();

    audioSource.clip = this.clip;
    audioSource.Play();
}

 

StartScene과 MainScene을 오가면서도 하나의 음악만 재생되어야하니 반드시 싱글톤 처리를 해줘야 한다.

이전에 하던 싱글톤과 조금 달라졌는데, 이렇게 진행하면 이후 씬이 전환되었을 때 Awake()가 또 실행되지만 기존 인스턴스가 있으므로 새로 생긴건 파괴된다.

public static AudioManager instance;

private void Awake()
{
    if (instance == null)
    {
        instance = this;
        DontDestroyOnLoad(gameObject); //씬이 전환되더라도 이 오브젝트는 파괴되지 않고 유지된다.
    }
    else
    {
        Destroy(gameObject); //이미 instance가 존재하면, 새로 생기는 AudioManager는 제거한다.
    }
}

빌드하기

File - Build Setting 에서 Add Open Scenes를 활용해 모든 Scene들이 포함되었는지 확인한다.

앱을 켰을 때 제일 먼저 보이는 Scene을 0순위로 올려야함.

 

Android 빌드한다고 가정했을 때, Switch Platform을 클릭하여 기다린다.

적용되고나면 Game 창에서 Aspect이 초기화되는데 Phone(760*1280)으로 다시 설정해준다.

 

이후 Build Setting 좌측 하단의 Player Setting으로 들어가서 게임 상세 내용(이름 등)을 설정할 수 있따.

 

Resolution and Presentation에서 Portrait/Landscape 허용을 설정할 수 있다. (처음엔 안나왔는데 유니티 프로젝트를 껐다가 켜니까 나왔다. Library 오류가 떴었는데 안드로이드 전환하면서 뭔가 오류가 생겼던 듯. 라이브러리 오류가 너무 자주 발생한다.)

 

Other Settings에서 ARM64 옵션을 추가해줘야한다(구글플레이 필사항). 이를 위해서 Scripting Backend를 IL2CPP로 변경해준다.

Package Name 에러가 뜬다. 제시하는 형식에 따라 작성.

*Scripting Backend IL2CPP는 보안과 관련된 부분인 것 같다. C# 코드를 더 빠르고 안전하게 실행되도록 C++로 바꿔주는 백엔드 컴파일러라고 한다. 빌드 속도는 느리지만 성능은 높아지고 디컴파일이 어려워지므로 보안성이 높아진다. 또한 C++로 코드가 변환되기 때문에 디버깅이 복잡해진다고 한다.

 

Publishing Setting에서 Keystore를 설정하여 Add Key.

이는 앱을 인증하고 배포할 수 있도록 디지털 서명을 포함한 정보를 저장하는 파일이다.

이를 설정하지 않으면 Google Play에 업로드가 불가능해진다.

분실하게 될 경우 기존 앱과 서명이 달라지므로 앱 업데이트가 불가능하다.

복구 불가하고 분실 시 업로드 키 교체 요청을 진행해야한다.

 

빌드 준비 완료!!! Build 하기.

 

학습자료에는 App Store나 Google Play에 등록하는 방법까지 세세히 나와있었지만,

이런건 Apple이나 Google에서 확인할 수 있는 부분이기도 하고, 실제로 진행하려면 돈을 내야해서 스킵했다.


광고 붙이기

Unity 자체에 Ads 서비스가 있다.

Windows - General - Services > Advertisement Legacy에서 Configure 한번 클릭

Services에서 문제 없는지 확인. (Cloud 연결이 되지 않으면 문제 생김)

Dashboard 클릭하여 Unity Cloud로 접속

 

Cloud Dashboard에서 Unity Ads (Unity Ads Monetization을 Shortcut에 추가하면 접근 쉬움)

Enable Ads.

더 자세한 건 이후 광고 달 때 다시 한번 Unity에서 제공하는 가이드를 읽는게 나을 듯.

 

AdInitialize.cs

using UnityEngine;
using UnityEngine.Advertisements;

public class AdInitialize : MonoBehaviour, IUnityAdsInitializationListener
{
    [SerializeField] string _androidGameId;
    [SerializeField] string _iOSGameId;
    [SerializeField] bool _testMode = true;
    private string _gameId;

    void Awake()
    {
        InitializeAds();
    }

    public void InitializeAds()
    {
#if UNITY_IOS
            _gameId = _iOSGameId;
#elif UNITY_ANDROID
        _gameId = _androidGameId;
#elif UNITY_EDITOR
            _gameId = _androidGameId; //Only for testing the functionality in the Editor
#endif
        if (!Advertisement.isInitialized && Advertisement.isSupported)
        {
            Advertisement.Initialize(_gameId, _testMode, this);
        }
    }


    public void OnInitializationComplete()
    {
        Debug.Log("Unity Ads initialization complete.");
    }

    public void OnInitializationFailed(UnityAdsInitializationError error, string message)
    {
        Debug.Log($"Unity Ads Initialization Failed: {error.ToString()} - {message}");
    }
}

 

AdsManager 준비하기

AdsManager 오브젝트에 AdInitialize.cs 컴포넌트 붙여주기. 아까 Enabe Ads하고 받았던 Game Ids 입력.

 

RewardedButton.cs 작성

using UnityEngine;
using UnityEngine.Advertisements;
using UnityEngine.SceneManagement;

public class RewardedButton : MonoBehaviour, IUnityAdsLoadListener, IUnityAdsShowListener
{
    [SerializeField] string _androidAdUnitId = "Rewarded_Android";
    [SerializeField] string _iOSAdUnitId = "Rewarded_iOS";
    string _adUnitId = null; // This will remain null for unsupported platforms

    void Awake()
    {
        // Get the Ad Unit ID for the current platform:
#if UNITY_IOS
        _adUnitId = _iOSAdUnitId;
#elif UNITY_ANDROID || UNITY_EDITOR
        _adUnitId = _androidAdUnitId;
#endif

        // Disable the button until the ad is ready to show:
    }

    // Call this public method when you want to get an ad ready to show.
    public void LoadAd()
    {
        // IMPORTANT! Only load content AFTER initialization (in this example, initialization is handled in a different script).
        Debug.Log("Loading Ad: " + _adUnitId);
        Advertisement.Load(_adUnitId, this);
    }

    // If the ad successfully loads, add a listener to the button and enable it:
    public void OnUnityAdsAdLoaded(string adUnitId)
    {
        Debug.Log("Ad Loaded: " + adUnitId);

        if (adUnitId.Equals(_adUnitId))
        {
            // Configure the button to call the ShowAd() method when clicked:
        }
    }

    // Implement a method to execute when the user clicks the button:
    public void ShowAd()
    {
        // Disable the button:
        // Then show the ad:
        Advertisement.Show(_adUnitId, this);
    }

    // Implement the Show Listener's OnUnityAdsShowComplete callback method to determine if the user gets a reward:
    public void OnUnityAdsShowComplete(string adUnitId, UnityAdsShowCompletionState showCompletionState)
    {
        if (adUnitId.Equals(_adUnitId) && showCompletionState.Equals(UnityAdsShowCompletionState.COMPLETED))
        {
            Debug.Log("Unity Ads Rewarded Ad Completed");
            // Grant a reward.
            SceneManager.LoadScene("MainScene");
        }
    }

    // Implement Load and Show Listener error callbacks:
    public void OnUnityAdsFailedToLoad(string adUnitId, UnityAdsLoadError error, string message)
    {
        Debug.Log($"Error loading Ad Unit {adUnitId}: {error.ToString()} - {message}");
        // Use the error details to determine whether to try to load another ad.
    }

    public void OnUnityAdsShowFailure(string adUnitId, UnityAdsShowError error, string message)
    {
        Debug.Log($"Error showing Ad Unit {adUnitId}: {error.ToString()} - {message}");
        // Use the error details to determine whether to try to load another ad.
    }

    public void OnUnityAdsShowStart(string adUnitId) { }
    public void OnUnityAdsShowClick(string adUnitId) { }

    void OnDestroy()
    {
        // Clean up the button listeners:
    }
}

EndTxt에 붙여준 RetryButton.cs를 지우고 RewardedButton.cs를 추가해주고 ShowAd()를 버튼에 연결해준다.

 

실제 출시할때는 AdManager에 달아준 AdInitialize.cs 컴포넌트에서 Test Mode를 체크 해제해주어야함.


광고를 미리 로드해두기 위해 코드를 조금씩 변경.

기본적으로 제일 처음 이니셜라이즈할때 광고 하나를 미리 로드해두고,

리워드가 지급될 때 다음번 광고를 미리 로드해둔다.

큰 광고를 로드할 때 걸리는 시간을, 게임 플레이 하는 동안 진행함으로써 바로 로드하게끔 하는 것.

//RewardedButton.cs
using UnityEngine;
using UnityEngine.Advertisements;
using UnityEngine.SceneManagement;

public class RewardedButton : MonoBehaviour, IUnityAdsLoadListener, IUnityAdsShowListener
{
    [SerializeField] string _androidAdUnitId = "Rewarded_Android";
    [SerializeField] string _iOSAdUnitId = "Rewarded_iOS";
    string _adUnitId = null; // This will remain null for unsupported platforms

    void Awake()
    {
        // Get the Ad Unit ID for the current platform:
#if UNITY_IOS
        _adUnitId = _iOSAdUnitId;
#elif UNITY_ANDROID || UNITY_EDITOR
        _adUnitId = _androidAdUnitId;
#endif

        // Disable the button until the ad is ready to show:
    }

    // Call this public method when you want to get an ad ready to show.
    public void LoadAd()
    {
        // IMPORTANT! Only load content AFTER initialization (in this example, initialization is handled in a different script).
        Debug.Log("Loading Ad: " + _adUnitId);
        Advertisement.Load(_adUnitId, this);
    }

    // If the ad successfully loads, add a listener to the button and enable it:
    public void OnUnityAdsAdLoaded(string adUnitId)
    {
        Debug.Log("Ad Loaded: " + adUnitId);

        if (adUnitId.Equals(_adUnitId))
        {
            // Configure the button to call the ShowAd() method when clicked:
        }
    }

    // Implement a method to execute when the user clicks the button:
    public void ShowAd()
    {
        // Disable the button:
        // Then show the ad:
        Advertisement.Show(_adUnitId, this);
    }

    // Implement the Show Listener's OnUnityAdsShowComplete callback method to determine if the user gets a reward:
    public void OnUnityAdsShowComplete(string adUnitId, UnityAdsShowCompletionState showCompletionState)
    {
        if (adUnitId.Equals(_adUnitId) && showCompletionState.Equals(UnityAdsShowCompletionState.COMPLETED))
        {
            Debug.Log("Unity Ads Rewarded Ad Completed");
            // Grant a reward.
            LoadAd(); //리워드 지급 후 씬 전환 이전에 다음번 광고를 미리 로드해둠.
            SceneManager.LoadScene("MainScene");
        }
    }

    // Implement Load and Show Listener error callbacks:
    public void OnUnityAdsFailedToLoad(string adUnitId, UnityAdsLoadError error, string message)
    {
        Debug.Log($"Error loading Ad Unit {adUnitId}: {error.ToString()} - {message}");
        // Use the error details to determine whether to try to load another ad.
    }

    public void OnUnityAdsShowFailure(string adUnitId, UnityAdsShowError error, string message)
    {
        Debug.Log($"Error showing Ad Unit {adUnitId}: {error.ToString()} - {message}");
        // Use the error details to determine whether to try to load another ad.
    }

    public void OnUnityAdsShowStart(string adUnitId) { }
    public void OnUnityAdsShowClick(string adUnitId) { }

    void OnDestroy()
    {
        // Clean up the button listeners:
    }
}
//AdInitialize.cs
using UnityEngine;
using UnityEngine.Advertisements;

public class AdInitialize : MonoBehaviour, IUnityAdsInitializationListener, IUnityAdsLoadListener
{
    [SerializeField] string _androidGameId;
    [SerializeField] string _iOSGameId;
    [SerializeField] bool _testMode = true;

    private string _gameId;
    [SerializeField] string _androidAdUnitId = "Rewarded_Android";
    [SerializeField] string _iOSAdUnitId = "Rewarded_iOS";
    string _adUnitId = null; // This will remain null for unsupported platforms


    void Awake()
    {
        InitializeAds();
    }

    public void InitializeAds()
    {
#if UNITY_IOS
            _gameId = _iOSGameId;
            _adUnitId = _iOSAdUnitId;
#elif UNITY_ANDROID
        _gameId = _androidGameId;
        _adUnitId = _androidAdUnitId;
#elif UNITY_EDITOR
            _gameId = _androidGameId; //Only for testing the functionality in the Editor
#endif
        if (!Advertisement.isInitialized && Advertisement.isSupported)
        {
            Advertisement.Initialize(_gameId, _testMode, this);
        }
    }

    // Call this public method when you want to get an ad ready to show.
    public void LoadAd()
    {
        // IMPORTANT! Only load content AFTER initialization (in this example, initialization is handled in a different script).
        Debug.Log("Loading Ad: " + _adUnitId);
        Advertisement.Load(_adUnitId, this);
    }

    public void OnInitializationComplete()
    {
        Debug.Log("Unity Ads initialization complete.");
        LoadAd();
    }

    public void OnInitializationFailed(UnityAdsInitializationError error, string message)
    {
        Debug.Log($"Unity Ads Initialization Failed: {error.ToString()} - {message}");
    }

    public void OnUnityAdsAdLoaded(string placementId)
    {
        Debug.Log("Ad Loaded: " + placementId);

        if (placementId.Equals(_adUnitId))
        {
            // Configure the button to call the ShowAd() method when clicked:
        }
    }

    public void OnUnityAdsFailedToLoad(string placementId, UnityAdsLoadError error, string message)
    {
        //throw new System.NotImplementedException();
    }
}

콘솔창을 보면 제일 처음 Initialization이 끝나고 바로 Ad를 하나 로드한다.

빨간 선이 내가 게임을 끝내고 본 광고의 close 버튼을 누른 시점이고,

광고 리워드가 주어지자마자 바로 또 다른 광고를 로드하는 것을 확인할 수 있다.


느낀점

놓칠 수 있는 디테일들을 확인할 수 있었다.

그리고 무엇보다 광고를 다는걸 배운게 기쁘다ㅋㅋ

생각보다... 간단하잖아..? 유니티가 진짜 게임 개발 진입장벽을 확 낮춘 것 같다.


내일 학습 할 것은 무엇인지

내일은 팩맨 게임과 숫자야구게임 프로그래밍을 마치려고 한다.

시간이 있다면 페어프로그래밍을 할 사람을 찾고, 아니라면 혼자라도 제출하려고 한다.