본문 바로가기
Tech Review : Unity & C#

3. Project Boost : Switch Statements ~ Refactor With Extract Method

by chrn0907 2023. 11. 2.

Udemy 강좌 'Complete C# Unity Game Developer 3D' 리뷰 및 기술 기록지입니다.

Project Boost : 44. Switch Statements ~ 53. Refactor With Extract Method

 

 

 

44. Switch Statements

이번 강의에서는 로켓의 상태를 바꾸는 방법에 대해서 학습하게 됩니다.

console 창에서 friendly point에 있을 때, obstacle에 부딪혔을 때, 혹은 finish 지점에 도착했을 때 각기 다른 상태를 출력하게 됩니다.

 

tag를 생성하여 friendly, fuel, finish 지정

CollisionHandler 스크립트를 생성해줍니다.

 

using UnityEngine;

 

public class CollisonHandlerMonoBehaviour

{

 

 void OnCollisionEnter(Collision other)

{

    switch(other.gameObject.tag)

{

        case "Friendly":

            UnityEngine.Debug.Log("This thing is friendly");

            break;

        case "Finish":

            UnityEngine.Debug.Log("Congrats. you finished!");

            break;

        case "Fuel":

            UnityEngine.Debug.Log("You picked up fuel");

            break;

        default:

            UnityEngine.Debug.Log("Sorry. you blow up!");

            break;    

}

}

}

 

 

다음과 같은 화면이 뜬 것을 볼 수 있습니다.

 

45. Respawn Using Scenemanager

이번 강의에서는 게임에서 장애물에 부딪혔을 때, 다시 제자리로 돌아가 새로운 게임을 시작할 수 있도록 학습하게 됩니다.

장애물에 부딪혔을 때, 현재의 Scene을 새롭게 불러와 재시작해봅시다.

그 전에 Build Settings에서 현재 Scene을 끌어온 뒤, 체크해주시면 되고 아래 사진에서는 다른 Scene을 추가한 상태라 신경 쓰지 않으셔도 됩니다.

using UnityEngine;

using UnityEngine.SceneManagement;

 

public class CollisonHandlerMonoBehaviour

{

 void OnCollisionEnter(Collision other)

{

    switch(other.gameObject.tag)

{

        case "Friendly":

            UnityEngine.Debug.Log("This thing is friendly");

            break;

        case "Finish":

            StartSuccessSequence();

            UnityEngine.Debug.Log("Congrats. you finished!");

            break;

        case "Fuel":

            UnityEngine.Debug.Log("You picked up fuel");

            break;

        default:

            ReloadLevel();

            UnityEngine.Debug.Log("Sorry. you blow up!");

            break;    

}

}

 

 void ReloadLevel()

{  

    int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;

    SceneManager.LoadScene(currentSceneIndex);

}

}

 

 

 

46. Load Next Level

이번 강의에서는 첫 번째 단계를 완료하면, 다음 단계로 넘어가도록 하는 방법에 대해서 배워보겠습니다.

 

다음 단계가 될 수 있는 Scene을 추가해줍니다.

45차 강의와 같이 Build Settings에서 Scene을 끌어와 추가해줍니다.

만일 finish pad에 도착한다면 다음 단계로 넘어갈 수 있도록 해보겠습니다.

 

using UnityEngine;

using UnityEngine.SceneManagement;

 

public class CollisonHandlerMonoBehaviour

{

 void OnCollisionEnter(Collision other)

{

 

    switch(other.gameObject.tag)

{

        case "Friendly":

            UnityEngine.Debug.Log("This thing is friendly");

            break;

        case "Finish":

            LoadNextLevel();

            UnityEngine.Debug.Log("Congrats. you finished!");

            break;

        case "Fuel":

            UnityEngine.Debug.Log("You picked up fuel");

            break;

        default:

            ReloadLevel();

            UnityEngine.Debug.Log("Sorry. you blow up!");

            break;    

}

}

 

 void LoadNextLevel()

{  

    int currentSceneIndex =SceneManager.GetActiveScene().buildIndex;

    int nextSceneIndex =currentSceneIndex+1;

   

   if(nextSceneIndex ==SceneManager.sceneCountInBuildSettings)

{

      nextSceneIndex =0;

}

   SceneManager.LoadScene(nextSceneIndex);

}

 

 void ReloadLevel()

{  

    int currentSceneIndex =SceneManager.GetActiveScene().buildIndex;

    SceneManager.LoadScene(currentSceneIndex);

}

}

 

 

 

47. Using Invoke

이번에는 Invoke를 사용해서 로켓이 장애물에 부딪혔을 때, 약간의 delay를 가지고 처음 상태로 돌아갈 수 있도록 해보겠습니다.

Invoke(“MethodName”, delayInSeconds);

 

using UnityEngine;

using UnityEngine.SceneManagement;

 

public class CollisonHandlerMonoBehaviour

{

[SerializeField] float levelLoadDelay =2f;

 

 void OnCollisionEnter(Collision other)

{

   if(isTransitioning) {return;}

    switch(other.gameObject.tag)

{

        case "Friendly":

            UnityEngine.Debug.Log("This thing is friendly");

            break;

        case "Finish":

            StartSuccessSequence();

            UnityEngine.Debug.Log("Congrats. you finished!");

            break;

        case "Fuel":

            UnityEngine.Debug.Log("You picked up fuel");

            break;

        default:

            StartCrashSequence();

            UnityEngine.Debug.Log("Sorry. you blow up!");

            break;    

}

}

 void StartSuccessSequence()

{

      GetComponent<Movement>().enabled =false;

      Invoke("LoadNextLevel", levelLoadDelay);

}

 void StartCrashSequence()

{    

      GetComponent<Movement>().enabled =false;

      Invoke("ReloadLevel", levelLoadDelay);

}

 void LoadNextLevel()

{  

    int currentSceneIndex =SceneManager.GetActiveScene().buildIndex;

    int nextSceneIndex =currentSceneIndex+1;

   

   if(nextSceneIndex ==SceneManager.sceneCountInBuildSettings)

{

      nextSceneIndex = 0;

}

   SceneManager.LoadScene(nextSceneIndex);

}

 void ReloadLevel()

{  

    int currentSceneIndex =SceneManager.GetActiveScene().buildIndex;

    SceneManager.LoadScene(currentSceneIndex);

}

}

 

 

 

48. Mutiple Audio Clip

로켓이 장애물에 부딪혔을 때, finish pad에 도착했을 때 적절한 음향을 낼 수 있도록 해보겠습니다.

AudioSource.PlayOneShot : public void PlayOneShot(AudioClip clip, float volumeScale = 1.0F);

 

1. 원하는 음향을 Asset에 가져옵니다.

2. 코드를 수정해줍니다.

Movement.cs

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class MovementMonoBehaviour

{

[SerializeField] float mainThrust =100f;

[SerializeField] float rotationThrust =1f;

[SerializeField] AudioClip mainEngine;

   

Rigidbody rb;

    AudioSource audioSource;

 

bool isAlive;

    // Start is called before the first frame update

    void Start()

{

        rb =GetComponent<Rigidbody>();

        audioSource =GetComponent<AudioSource>();

}

    // Update is called once per frame

    void Update()

{

        ProcessThrust();

        ProcessRotation();

}

    void ProcessThrust()

{

        if(Input.GetKey(KeyCode.Space))

{

            rb.AddRelativeForce(Vector3.up *mainThrust *Time.deltaTime);

            if(!audioSource.isPlaying)

{

                audioSource.Play();

}

            else

{

                audioSource.Stop();

                mainEngineParticles.Stop();

}

}

}

 

    void ProcessRotation()

{

        if(Input.GetKey(KeyCode.A))

{

            ApplyRotation(-rotationThrust);

            if(!rightThrusterParticles.isPlaying)

{

                rightThrusterParticles.Play();

}

}

        else if(Input.GetKey(KeyCode.D))

{

            ApplyRotation(rotationThrust);

            if(!leftThrusterParticles.isPlaying)

{

                leftThrusterParticles.Play();

}

}

        else

{

            rightThrusterParticles.Stop();

            leftThrusterParticles.Stop();

}

}

    void ApplyRotation(float rotationThisFrame)

{

        rb.freezeRotation =true;

        transform.Rotate(Vector3.forward *rotationThisFrame *Time.deltaTime);

        rb.freezeRotation =false;

}

}

 

 

CollisionHandler.cs

using UnityEngine;

using UnityEngine.SceneManagement;

public class CollisonHandlerMonoBehaviour

{

[SerializeField] float levelLoadDelay =2f;

[SerializeField] AudioClip success;

[SerializeField] AudioClip crash;

 

   AudioSource audioSource;

   void Start()

{

      audioSource = GetComponent<AudioSource>();

}

 void OnCollisionEnter(Collision other)

{

    switch(other.gameObject.tag)

{

        case "Friendly":

            UnityEngine.Debug.Log("This thing is friendly");

            break;

        case "Finish":

            StartSuccessSequence();

            UnityEngine.Debug.Log("Congrats. you finished!");

            break;

        case "Fuel":

            UnityEngine.Debug.Log("You picked up fuel");

            break;

        default:

            StartCrashSequence();

            UnityEngine.Debug.Log("Sorry. you blow up!");

            break;    

}

}

 void StartSuccessSequence()

{

 

      audioSource.PlayOneShot(success);

      GetComponent<Movement>().enabled =false;

      Invoke("LoadNextLevel", levelLoadDelay);

}

 void StartCrashSequence()

{    

      audioSource.PlayOneShot(crash);

      GetComponent<Movement>().enabled =false;

      Invoke("ReloadLevel", levelLoadDelay);

}

 void LoadNextLevel()

{  

    int currentSceneIndex =SceneManager.GetActiveScene().buildIndex;

    int nextSceneIndex =currentSceneIndex+1;

   

   if(nextSceneIndex ==SceneManager.sceneCountInBuildSettings)

{

      nextSceneIndex =0;

}

   SceneManager.LoadScene(nextSceneIndex);

}

 void ReloadLevel()

{  

    int currentSceneIndex =SceneManager.GetActiveScene().buildIndex;

    SceneManager.LoadScene(currentSceneIndex);

}

}

 

3. 이후에 원하는 음향을 Assets에서 끌어와 rocketinspector 창에서 collisionhandler를 선택 후

사진처럼 원하는 음향을 지정해줍니다.

 

49. Bool Variable For State

Bool을 지정해서 상태에 따라서 코드를 수정할 수도 있습니다.

 

Movement.csbool isAlive;를 지우고 코드를 다음과 같이 수정합니다.

CollisionHandler.cs

using UnityEngine;

using UnityEngine.SceneManagement;

public class CollisonHandlerMonoBehaviour

{

[SerializeField] float levelLoadDelay =2f;

[SerializeField] AudioClip success;

[SerializeField] AudioClip crash;

 

   AudioSource audioSource;

   bool isTransitioning =false;

   void Start()

{

      audioSource =GetComponent<AudioSource>();

}

 void OnCollisionEnter(Collision other)

{

   if(isTransitioning) {return;}

    switch(other.gameObject.tag)

{

        case "Friendly":

            UnityEngine.Debug.Log("This thing is friendly");

            break;

        case "Finish":

            StartSuccessSequence();

            UnityEngine.Debug.Log("Congrats. you finished!");

            break;

        case "Fuel":

            UnityEngine.Debug.Log("You picked up fuel");

            break;

        default:

            StartCrashSequence();

            UnityEngine.Debug.Log("Sorry. you blow up!");

            break;    

}

}

 void StartSuccessSequence()

{

      isTransitioning =true;

      audioSource.Stop();

      audioSource.PlayOneShot(success);

      GetComponent<Movement>().enabled =false;

      Invoke("LoadNextLevel", levelLoadDelay);

}

 void StartCrashSequence()

{    

      isTransitioning =true;

      audioSource.Stop();

      audioSource.PlayOneShot(crash);

      GetComponent<Movement>().enabled =false;

      Invoke("ReloadLevel", levelLoadDelay);

}

 void LoadNextLevel()

{  

    int currentSceneIndex =SceneManager.GetActiveScene().buildIndex;

    int nextSceneIndex =currentSceneIndex+1;

   

   if(nextSceneIndex ==SceneManager.sceneCountInBuildSettings)

{

      nextSceneIndex =0;

}

   SceneManager.LoadScene(nextSceneIndex);

}

 void ReloadLevel()

{  

    int currentSceneIndex =SceneManager.GetActiveScene().buildIndex;

    SceneManager.LoadScene(currentSceneIndex);

}

}

 

50. Make Rocket Look Spiffy

환경에 맞는 적절한 Rocket을 디자인합니다.

 

51. How To Trigger Particles / 52. Particles For Rocket Boosters

로켓이 미션을 성공하거나 실패했을 Particles가 방출되도록 해보겠습니다.

로켓이 날아다니는 방향을 따라서 Particles가 방출되도록 해보겠습니다.

1. 다음과 같이 Scene에서 위치를 확인한 후, Particles의 위치를 지정해줍니다.

2. 코드를 수정해줍니다.

Movement.cs

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class MovementMonoBehaviour

{

[SerializeField] float mainThrust =100f;

[SerializeField] float rotationThrust =1f;

[SerializeField] AudioClip mainEngine;

    Rigidbody rb;

    AudioSource audioSource;

[SerializeField] ParticleSystem mainEngineParticles;

[SerializeField] ParticleSystem leftThrusterParticles;

[SerializeField] ParticleSystem rightThrusterParticles;

    // Start is called before the first frame update

    void Start()

{

        rb =GetComponent<Rigidbody>();

        audioSource =GetComponent<AudioSource>();

}

    // Update is called once per frame

    void Update()

{

        ProcessThrust();

        ProcessRotation();

}

    void ProcessThrust()

{

        if(Input.GetKey(KeyCode.Space))

{

            rb.AddRelativeForce(Vector3.up *mainThrust *Time.deltaTime);

            if(!audioSource.isPlaying)

{

                audioSource.PlayOneShot(mainEngine);

}

            if(!mainEngineParticles.isPlaying)

{

                mainEngineParticles.Play();

}

            else

{

                audioSource.Stop();

                mainEngineParticles.Stop();

}

}

}

 

    void ProcessRotation()

{

        if(Input.GetKey(KeyCode.A))

{

            ApplyRotation(-rotationThrust);

            if(!rightThrusterParticles.isPlaying)

{

                rightThrusterParticles.Play();

}

}

        else if(Input.GetKey(KeyCode.D))

{

            ApplyRotation(rotationThrust);

            if(!leftThrusterParticles.isPlaying)

{

                leftThrusterParticles.Play();

}

}

        else

{

            rightThrusterParticles.Stop();

            leftThrusterParticles.Stop();

}

}

    void ApplyRotation(float rotationThisFrame)

{

        rb.freezeRotation =true;

        transform.Rotate(Vector3.forward *rotationThisFrame *Time.deltaTime);

        rb.freezeRotation =false;

}

}

CollisionHandler.cs

using UnityEngine;

using UnityEngine.SceneManagement;

public class CollisonHandlerMonoBehaviour

{

[SerializeField] float levelLoadDelay =2f;

[SerializeField] AudioClip success;

[SerializeField] AudioClip crash;

[SerializeField] ParticleSystem successParticles;

[SerializeField] ParticleSystem crashParticles;

   AudioSource audioSource;

   bool isTransitioning =false;

   void Start()

{

      audioSource =GetComponent<AudioSource>();

}

 void OnCollisionEnter(Collision other)

{

   if(isTransitioning) {return;}

    switch(other.gameObject.tag)

{

        case "Friendly":

            UnityEngine.Debug.Log("This thing is friendly");

            break;

        case "Finish":

            StartSuccessSequence();

            UnityEngine.Debug.Log("Congrats. you finished!");

            break;

        case "Fuel":

            UnityEngine.Debug.Log("You picked up fuel");

            break;

        default:

            StartCrashSequence();

            UnityEngine.Debug.Log("Sorry. you blow up!");

            break;    

}

}

 void StartSuccessSequence()

{

      isTransitioning =true;

      audioSource.Stop();

      audioSource.PlayOneShot(success);

      successParticles.Play();

      GetComponent<Movement>().enabled =false;

      Invoke("LoadNextLevel", levelLoadDelay);

}

 void StartCrashSequence()

{    

      isTransitioning =true;

      audioSource.Stop();

      audioSource.PlayOneShot(crash);

      crashParticles.Play();

      GetComponent<Movement>().enabled =false;

      Invoke("ReloadLevel", levelLoadDelay);

}

 void LoadNextLevel()

{  

    int currentSceneIndex =SceneManager.GetActiveScene().buildIndex;

    int nextSceneIndex =currentSceneIndex+1;

   

   if(nextSceneIndex ==SceneManager.sceneCountInBuildSettings)

{

      nextSceneIndex =0;

}

   SceneManager.LoadScene(nextSceneIndex);

}

 void ReloadLevel()

{  

    int currentSceneIndex =SceneManager.GetActiveScene().buildIndex;

    SceneManager.LoadScene(currentSceneIndex);

}

}

 

 

 

53. Refactor With Extract Method

: refactor할 구간을 선택한 후, ctrl + .을 누르면 refactoring 할 수 있습니다.

 

 

실행화면

 

 

출처 | https://docs.unity3d.com/2019.2/Documentation/ScriptReference/AudioSource.html