Unity Chapter 6-3. 어메이징 볼링 게임 만들기 : 파워 슬라이더, 볼 슈터

Date:     Updated:

카테고리:

태그:

인프런에 있는 이제민님의 레트로의 유니티 C# 게임 프로그래밍 에센스 강의를 듣고 정리한 필기입니다. 😀
🌜 [레트로의 유니티 C# 게임 프로그래밍 에센스] 강의 들으러 가기!


Chapter 6. 어메이징 볼링 게임 만들기

🔔 Big Prop 오브젝트도 추가해주자

  1. Small Prop을 복사하여 만들기
  2. 이름은 “Big Prop”
  3. scale은 (2, 2, 2)
  4. Material로 색 입혀주기
  5. hp는 50, score는 10으로 변경 (public이라 슬롯에서 변경 가능)
  6. Big Prop은 다른 파티클 오브젝트를 사용할 것이다.
    • Import해 온 파티클 프리팹들 중 “BigExplosion”
    • 마찬가지로 이 프리팹을 Hierarchy 창으로 옮겨준 후 SmallExplosison 때랑 똑같이 수정하자.
    • AudioSource 붙여주고 audio clip에 효과음 넣어주고 Play on awake 꺼주고 looping 꺼주고 등등 이전 포스트 참고
    • 참고로 “BigExplosion”은 손자들까지 있어서 😂 그냥 shift말고 Alt Shift를 눌러주면 모든 자식들까지 다 선택된다. 이걸 활용하여 선택하고 모두 looping, play on awake 꺼주기.
  7. 수정한 “BigExplosion”을 📁Prefabs 로 옮겨 다시 프리팹으로 만들어주자.
  8. Big Prop의 Explosion Particle 슬롯에 방금 만든 프리팹 “BigExplosion”을 드래그 해주자.
  9. 완성한 Big Prop도 📁Prefabs 로 옮겨 프리팹으로 만들어주자.
  10. Ball, Small prop, Big Prop은 Hierarchy 상에서 잠시 지워 주도록 한다.
    • Prefab으로 있으니 다시 찍어내면 그만이라 괜춘


🔔 파워 슬라이더

힘이 얼만큼 충전 됐는지를 보여주는 역할을 하는 UI다.

  1. 힘이 끝까지 다 차거나
  2. 혹은 힘을 채우다가 중간에 마우스를 떼면
    그때 Ball을 날린다.

파워 슬라이더 만들기 : Slider UI

  1. UI Slider 오브젝트를 생성해준다. UI - Slider 선택
  2. Inspector창에서 Canvas의 Render Mode를 World Space로 바꿔준다.
    • UI의 Canvas는 원래 게임 세상과는 관련이 없다. 화면과 관련이 있음.
    • Shooter Pivot 게임 오브젝트에 UI를 붙이기 위해 World Space로 바꿔 화면이 아니라 게임 세상으로 끌고 온다.
    • 이제 AR처럼 게임 세상 속에서 UI가 배치된다.
  3. Inspector창에서 Canvas의 Canvas Scaler에서 Reference Pixels Per Unit 값을 1로 바꿔준다.
    • 이 값이 올라갈 수록 고화질이다. 그냥 1로 해두자.
    • 1로 해두니 게임이 마인크래프트처럼 픽셀이 작아져 귀여워 졌다.
  4. Inspector창에서 Canvas의 Rect Transform에서 width = 3.5, height = 3.5 로 해준다. position은 0, 0, 0으로.
  5. Canvas를 Shooter Pivot의 자식으로 넣는다.
  6. EventSystem은 지워준다.
    • 유저의 입력을 처리하는 UI 요소인데
    • 유저 입력도 필요 없을 뿐더러 괜히 두었다가 유저의 클릭 같은 것을 먹을 수도 있기 때문에
  7. Shooter Pivot의 자식으로 들어간 Canvas의 Rect Transform에서 rotation 값을 x축 90 으로 해준다.
    • 캔버스가 Plane 바닥에 딱 붙어 누워있는 모습이 된다
  8. Canvas의 Rect Transform에서 position의 y값은 0.25로 해준다.
    • 바닥이랑 겹쳐서 UI가 가려지는 것을 막기 위해 y 값을 조금 올려주었다.
  9. Canvas의 자식인 Slider의 자식인 Background와 Handle Slide Area를 지워준다.
    • Slider는 보통 손잡이(Handle Slide Area)가 있어 유저가 이를 잡고 밀고 당겨 조절할 수 있는데 어메이징 볼링 게임에선 그렇게 하면 반칙이므로 이 기능을 지워주는 것.
    • 유저가 파워를 충전할 때 슬라이더 손잡이를 잡고 충전하게 하지는 않을 것이므로 지워줌. 마우스 버튼이나 스페이스바를 눌러 충전할 것이기 때문에…
  10. Slider 컴포넌트의 Fill Rect, value 설명
    • Fill Rect : 이 슬롯에 어떤 오브젝트를 넣으면
    • value : Fill Rect에 넣어준 오브젝트는 자신의 부모 오브젝트에 비해서 이 value 값에 따라 채워지게 된다. 예를 들어 value 값이 50이면 Fill Rect에 들어오는 오브젝트가 자신의 부모 오브젝트에 비해서 50%만 채워진다 이 value 바를 한번 조절해서 슬라이더가 어떻게 동작하고 보여지는지 봐 보자.
  11. Slider 컴포넌트 값들 설정
    • Interatable : 체크 해제해준다. 기본적으로 슬라이더는 아까 지운 Handle Slide Area 핸들을 지워도 유저가 잡아당길 순 있긴 하다. 그래서 이걸 체크 해제해주어 아예 유저가 슬라이더를 못 만지게끔 하는것
    • transition : None으로 해주어 없애준다. UI요소가 유저에 의해 색깔이 옅어진다던지 하는 효과로 눈에 보이는 피드백을 주는 것. 현재 이런 효과가 필요 없으므로 None.
    • Direction : botton to top으로 해준다. 슬라이더가 채워지는 방향!
    • min value : 15로 해준다. value의 최소값을 나타냄. 유저가 주는 최소 힘을 15로 할 것.
    • max value : 30로 해준다. value의 최대값을 나타냄. 유저가 주는 최소 힘을 30로 할 것.
  12. 부모 자식 관계에 있는 이 Slider, Fill Area, Fill 3개를 선택한 후 anchor presets 값을 설정해주자.
    • Alt키를 누른 체로 맨 오른쪽 하단에 있는 stretch-stretch를 선택
    • 이제 Canvas 크기에 딱 맞게 쫙쫙 늘려졌다.
  13. 슬라이더 모양을 바꾸자
    • 편하게 보기 위해 Scene 기즈모의 y축을 눌러 씬 카메라 시점을 변경한 후
    • 트랜스폼 툴바에서 Rect Tool을 선택한다. Rect Tool은 2D나 UI 요소의 모양을 편집할 때 많이 쓰인다.
    • 슬라이더 전체 모양을 보기 위해 일단 슬라이더 컴포넌트의 value값을 최대로 해놓자.
    • image
    • 위 사진처럼 트랜스폼 툴바를 사용하여 모양과 위치를 바꿔주자.
  14. Slider의 자식인 Fill 에서 슬라이더의 이미지를 설정해주자.
    • 색 입히기
  15. 최종적으로 Slider의 이름은 “Power Slider”로 바꿔주자.


📜BallShooter.cs : 파워 슬라이더 제어하기

  • Shooter Pivot 오브젝트에 붙여준다.
    1. Ball 오브젝트를 생성하고
      • Ball 프리팹을 가져와서 찍어낼 것
    2. 힘을 주어서 날리는 역할을 한다.
      • 파워 슬라이더를 제어해서 힘을 충전해서 Ball을 날릴 것
      • Slider UI요소인 value값을 지정하여 힘이 얼마나 충전되는지를 표시해줄 것.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class BallShooter : MonoBehaviour
{
    public Rigidbody ball;
    public Transform firePos;
    public Slider powerSlider;

    public AudioSource shootingAudio;
    public AudioClip fireClip;
    public AudioClip chargingClip;

    public float minForce = 15f;
    public float maxForce = 30f;
    public float chargingTime = 0.75f;

    private float currentForce;
    private float chargeSpeed;
    private bool fired;

    private void OnEnable()
    {
        currentForce = minForce;
        powerSlider.value = minForce;
        fired = false;
    }

    private void Start()
    {
        chargeSpeed = (maxForce - minForce) / chargingTime;
    }

    private void Update()
    {
        if (fired == true)
        {
            return;
        }

        if (currentForce >= maxForce && !fired)
        {
            currentForce = maxForce;
            Fire();
        }
        else if (Input.GetButtonDown("Fire1"))
        {
            // fired = false;  cf) 이렇게 하면 연사 가능. 근데 연사는 못하게 막아 놓을거라 ..
            currentForce = minForce;
            shootingAudio.clip = chargingClip;
            shootingAudio.Play();
        }
        else if (Input.GetButton("Fire1") && !fired)
        {
            currentForce += chargeSpeed * Time.deltaTime;
            powerSlider.value = currentForce;
        }
        else if (Input.GetButtonUp("Fire1") && !fired)
        {
            Fire();
        }
    }

    private void Fire()
    {
        fired = true;

        Rigidbody ballInstance = Instantiate(ball, firePos.position, firePos.rotation);

        ballInstance.velocity = currentForce * firePos.forward;

        shootingAudio.clip = fireClip;
        shootingAudio.Play();

        currentForce = minForce;
    }
} 

🚖 변수

  • public Rigidbody ball;
    • 찍어낼 Ball
    • Ball 프리팹을 가져와서 슬롯에 넣어 줄 것이다.
    • Rigidbody로서 가져 온 이유는 바로 Rigidbody의 함수들을 가져와 쓰기 위해서.
  • public Transform firePos;
    • Ball이 발사될 위치
    • Ball이 생성될 위치
    • firePos라는 이름의 빈 오브젝트를 만들어 Shooter Pivot 오브젝트의 자식으로 넣어준다.
      • firePos 오브젝트의 역할은 Ball의 발사 위치와 회전값
      • firePos의 위치를 이렇게 Barrel 옆으로 조정해준다.
        • image
      • Ball의 발사 위치가 될 이 firePos 오브젝트를 firePos 슬롯에 드래그 앤 드롭 해주자.
  • public Slider powerSlider
    • PowerSlider UI를 슬롯에 넣어 줄 것.
      • 슬라이더 UI의 value값을 다룰 것
    • using UnityEngine.UI; 를 꼭 해주어야 가능하다.
  • public AudioSource shootingAudio;
    • Shooter Pivot에 Audio Source 컴포넌트를 붙여준 후
      • audio clip 에 효과음 mp3 파일 넣기
        • public AudioClip fireClip;
          • 1️⃣ 포탄을 쏠 때 효과음 mp3 파일을 이 슬롯에 넣어 줄 것이다.
        • public AudioClip chargingClip;
          • 2️⃣ 차징할 때 효과음 mp3 파일을 이 슬롯에 넣어 줄 것이다.
      • play on awake는 꺼주기. 게임이 시작될 때가 아닌 볼이 발사될 때 재생할거니까
    • 이 슬롯에 붙인 Audio Source 컴포넌트를 넣어 주자.
  • public float minForce
    • 누르자마자 바로 지정되는 최소 힘.
    • 15로 설정했다.
  • public float maxForce
    • 누르자마자 바로 지정되는 최소 힘.
      • 15로 설정했다.
    • 차징한 힘이 maxForce를 넘어선 순간 자동으로 발사되버리게 할 것이다.
  • public float chargingTime
    • 0.75 초로 설정
    • 1.0f만큼 차징되는데 0.75초가 걸리도록 설정.
  • private float currentForce;
    • 현재 힘을 나타냄
  • private float chargeSpeed
    • 충전 속도
    • 누르고 있는 동안 1초에 힘이 얼마나 지정되는지
  • private bool fired
    • 이미 발사가 된 상태라면 또 다시 발사 못하도록 체크해주는 플래그

🚖 함수

private void OnEnable()

OnEnable() 이벤트 함수 설명

컴포넌트가 꺼져 있다가 켜지는 상태마다 발동된다. Start()와 비슷하지만 Start()는 게임 시작시 한번 발동되는 반면 OnEnable()은 컴포넌트가 꺼져있다가 켜질 때마다 1회씩 실행된다.

  • 게임 시작되자마자 SetActive(true) 상태인 오브젝트들에게 발동
  • 게임 도중에 SetActive(false)이다가 SetActive(true)가 되는 오브젝트들에게 발동
역할

매번 다음 라운드 때마다 📜BallShotter.cs 스크립트 컴포넌트를 껏다 켜서 OnEnable() 함수를 호출해 초기화를 시켜주자.

private void OnEnable()
{
    currentForce = minForce; // 현재 힘의 시작값은 최소힘
    powerSlider.value = minForce; // 슬라이더 초기 값도 최소힘으로
    fired = false; // 발사 상태 X
}

private void Start()

private void Start()
{
  chargeSpeed = (maxForce - minForce)/chargingTime;
}
  • chargeSpeed 계산
    • 누르고 있는 동안 1초에 힘이 충전되는 속도

private void Update()

👉 유저의 입력을 받아야 하므로 Update에 구현

    private void Update()
    {
        if (fired == true)
        {
            return;
        }

        if (currentForce >= maxForce && !fired)
        {
            currentForce = maxForce;
            Fire();
        }
        else if (Input.GetButtonDown("Fire1"))
        {
            currentForce = minForce;
            shootingAudio.clip = chargingClip;
            shootingAudio.Play();
        }
        else if (Input.GetButton("Fire1") && !fired)
        {
            currentForce += chargeSpeed * Time.deltaTime;
            powerSlider.value = currentForce;
        }
        else if (Input.GetButtonUp("Fire1") && !fired)
        {
            Fire();
        }
    }
  1. if (fired == true) return;
    • 한번이라도 발사가 됐으면 이 📜BallShooter.cs 의 update()가 실행 되지 않도록 해준다.
    • 한번이라도 발사 했었으면 그 아래 코드들이 실행되지 않으므로 차징, 발사, 그리고 그에 따른 효과음 재생 전부 안됨.
  2. maxForce에 도달 해서 강제 발사해야 하는 경우
    • currentForce = maxForce; (maxForce를 넘으면 안되므로.)
    • 발사 private void Fire()
  3. 발사 버튼을 누르는 순간 충전 시작 및 충전 효과음 재생 (GetButtonDown)
    • currentForce = minForce; (minForce 최소 힘부터 시작)
    • charging 효과음 넣어주고 재생 시작
  4. 발사 버튼을 누르고 있는 동안에 힘이 충전되고 (GetButton) & 발사 상태 X
    • 속도에 맞춰 힘 충전
    • 그에 맞춰 슬라이더 value 갱신
  5. 발사 버튼 떼는 순간에 발사 (GetButtonUp)
    • 발사 private void Fire()

private void Fire()

    private void Fire()
    {
        fired = true;

        Rigidbody ballInstance = Instantiate(ball, firePos.position, firePos.rotation);

        ballInstance.velocity = currentForce * firePos.forward;

        shootingAudio.clip = fireClip;
        shootingAudio.Play();

        currentForce = minForce;
    }
  • fired = ture;
    • 발사 상태로 변경
  • Ball오브젝트를 Rigidbody로서 생성한다.
    • 물리 처리시에 필요한 속도를 지정해주기 위해서
  • ballInstance.velocity = currentForce * firePos.forward.
    • 속도 지정
    • firePos.forward
      • Vector3에서 지원
      • 앞쪽 방향 벡터
    • 앞으로 날아가는 방향벡터 * currentForce(스칼라) 으로 속도 벡터값을 지정해준다.
  • 발사 효과음 clip 넣어주고 재생
  • 현재 힘은 다시 최소힘으로 리셋

잊지 말고 📜BallShooter.cs 슬롯들에 각 컴포넌트들을 다 드래그 앤 드롭 해주자 Ball의 경우 발사 하면 생성될 것이기 때문에 Ball의 프리팹을 넣어주자.

image

문제점 수정

  • 위까지의 작업을 마친 후 실행하면
    1. 그냥 회전 중에도 maxForce에 도달하면 먼저 터져버린다. maxForce에 도달 하더라도, 회전을 다 끝낸 후 발사하게끔 하고 싶다면!

      📜BallShooter.cs 는 꺼둔 상태로 시작하고 📜ShooterRotator.cs 회전 처리를 먼저 실행하여 Reday 상태가 되고 난 후 그 다음에 📜BallShooter.cs를 켜서 발사 동작을 처리하면 된다.

    2. 슬라이더 value값이 다시 초기화가 되지 않고 있다.

📜ShootRotator.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ShooterRotator : MonoBehaviour
{
    private enum RotateState
    {
        Idle, Vertical, Horizontal, Ready
    }

    private RotateState state = RotateState.Idle;
    public float verticalRotateSpeed = 360f; 
    public float horizontalRotateSpeed = 360f; 

    public BallShooter ballShooter; // ⭐추가

    void Update()
    {
        switch (state)
        {
            case RotateState.Idle:
                if (Input.GetButtonDown("Fire1")) 
                {
                    state = RotateState.Horizontal; 
                }
                break;

            case RotateState.Horizontal:
                if (Input.GetButton("Fire1"))
                {
                    transform.Rotate(new Vector3(0, horizontalRotateSpeed * Time.deltaTime, 0));
                }
                else if (Input.GetButtonUp("Fire1")) 
                {
                    state = RotateState.Vertical; 
                }
                break;

            case RotateState.Vertical:
                if (Input.GetButton("Fire1")) 
                {
                    transform.Rotate(new Vector3(-verticalRotateSpeed * Time.deltaTime, 0, 0));/
                }
                else if (Input.GetButtonUp("Fire1")) 
                {
                    state = RotateState.Ready; 
                    ballShooter.enabled = true; // ⭐추가
                }
                break;

            case RotateState.Ready:
                break;
        }
    }

    private void OnEnable() // ⭐추가
    {
        transform.rotation = Quaternion.identity;
        state = RotateState.Idle;
        ballShooter.enabled = false;
    }
}
🚍 추가 변수
  • public BallShooter ballShooter;
    • 📜BallShooter.cs 컴포넌트를 가져온다.
      • 이 슬롯에 📜BallShooter.cs 드래그 앤 드롭 해주기
  • ballShooter.enabled = true;
    • 📜BallShooter.cs 를 'Ready'상태일 때 켜서(*enabled = true*) 그제서야 발사 처리를 해주도록 하자.
🚍 추가 함수
  • transform.rotation = Quaternion.identity;
    • Shooter Pivot의 회전 값을 0, 0, 0으로 되돌린다.
  • private void OnEnable()
    • ballShooter.enabled = false;
      • 켜져있던 📜BallShooter.cs 를 꺼준다.
      • 📜ShootRotator.cs 컴포넌트가 새롭게 실행될 때마다 📜BallShooter.cs 를 꺼준다.
        • 순서
          1. 게임 시작하자마자 📜ShootRotator.cs의 Start()OnEnable()이 1회 실행되며 📜BallShooter.c가 꺼진다.
          2. 📜ShootRotator.cs 의 update()가 매 프레임 실행 됨
          3. 유저 입력이 들어오고 GetButtonUp(“Fire1”) 까지 오게되면 📜BallShooter.cs 가 켜진다.
          4. 이제 📜BallShooter.cs 가 일을 한다. 📜BallShooter.cs의 update()가 매 프레임 유니티로부터 메세지를 받을 수 있는 상태가 됨


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

댓글남기기