Unity Chapter 6-3. 어메이징 볼링 게임 만들기 : 파워 슬라이더, 볼 슈터
카테고리: Unity Lesson 1
태그: Unity Game Engine
인프런에 있는 이제민님의 레트로의 유니티 C# 게임 프로그래밍 에센스 강의를 듣고 정리한 필기입니다. 😀
🌜 [레트로의 유니티 C# 게임 프로그래밍 에센스] 강의 들으러 가기!
Chapter 6. 어메이징 볼링 게임 만들기
🔔 Big Prop 오브젝트도 추가해주자
- Small Prop을 복사하여 만들기
- 이름은 “Big Prop”
- scale은 (2, 2, 2)
- Material로 색 입혀주기
- hp는 50, score는 10으로 변경 (public이라 슬롯에서 변경 가능)
- Big Prop은 다른 파티클 오브젝트를 사용할 것이다.- Import해 온 파티클 프리팹들 중 “BigExplosion”
- 마찬가지로 이 프리팹을 Hierarchy 창으로 옮겨준 후 SmallExplosison 때랑 똑같이 수정하자.
- AudioSource 붙여주고 audio clip에 효과음 넣어주고 Play on awake 꺼주고 looping 꺼주고 등등 이전 포스트 참고
- 참고로 “BigExplosion”은 손자들까지 있어서 😂 그냥 shift말고Alt Shift를 눌러주면 모든 자식들까지 다 선택된다. 이걸 활용하여 선택하고 모두 looping, play on awake 꺼주기.
 
- 수정한 “BigExplosion”을 📁Prefabs 로 옮겨 다시 프리팹으로 만들어주자.
- Big Prop의 Explosion Particle 슬롯에 방금 만든 프리팹 “BigExplosion”을 드래그 해주자.
- 완성한 Big Prop도 📁Prefabs 로 옮겨 프리팹으로 만들어주자.
- Ball,- Small prop,- Big Prop은 Hierarchy 상에서 잠시 지워 주도록 한다.- Prefab으로 있으니 다시 찍어내면 그만이라 괜춘
 
🔔 파워 슬라이더
힘이 얼만큼 충전 됐는지를 보여주는 역할을 하는 UI다.
- 힘이 끝까지 다 차거나
- 혹은 힘을 채우다가 중간에 마우스를 떼면
 그때Ball을 날린다.
파워 슬라이더 만들기 : Slider UI
- UI Slider 오브젝트를 생성해준다. UI - Slider 선택
- Inspector창에서 Canvas의 Render Mode를 World Space로 바꿔준다.
    - UI의 Canvas는 원래 게임 세상과는 관련이 없다. 화면과 관련이 있음.
- Shooter Pivot게임 오브젝트에 UI를 붙이기 위해 World Space로 바꿔 화면이 아니라 게임 세상으로 끌고 온다.
- 이제 AR처럼 게임 세상 속에서 UI가 배치된다.
 
- Inspector창에서 Canvas의 Canvas Scaler에서 Reference Pixels Per Unit 값을 1로 바꿔준다.
    - 이 값이 올라갈 수록 고화질이다. 그냥 1로 해두자.
- 1로 해두니 게임이 마인크래프트처럼 픽셀이 작아져 귀여워 졌다.
 
- Inspector창에서 Canvas의 Rect Transform에서 width = 3.5, height = 3.5 로 해준다. position은 0, 0, 0으로.
- Canvas를 Shooter Pivot의 자식으로 넣는다.
- EventSystem은 지워준다.
    - 유저의 입력을 처리하는 UI 요소인데
- 유저 입력도 필요 없을 뿐더러 괜히 두었다가 유저의 클릭 같은 것을 먹을 수도 있기 때문에
 
- Shooter Pivot의 자식으로 들어간 Canvas의 Rect Transform에서 rotation 값을 x축 90 으로 해준다.- 캔버스가 Plane 바닥에 딱 붙어 누워있는 모습이 된다
 
- Canvas의 Rect Transform에서 position의 y값은 0.25로 해준다.
    - 바닥이랑 겹쳐서 UI가 가려지는 것을 막기 위해 y 값을 조금 올려주었다.
 
- Canvas의 자식인 Slider의 자식인 Background와 Handle Slide Area를 지워준다.
    - Slider는 보통 손잡이(Handle Slide Area)가 있어 유저가 이를 잡고 밀고 당겨 조절할 수 있는데 어메이징 볼링 게임에선 그렇게 하면 반칙이므로 이 기능을 지워주는 것.
- 유저가 파워를 충전할 때 슬라이더 손잡이를 잡고 충전하게 하지는 않을 것이므로 지워줌. 마우스 버튼이나 스페이스바를 눌러 충전할 것이기 때문에…
 
- Slider 컴포넌트의 Fill Rect, value 설명
    - Fill Rect : 이 슬롯에 어떤 오브젝트를 넣으면
- value : Fill Rect에 넣어준 오브젝트는 자신의 부모 오브젝트에 비해서 이 value 값에 따라 채워지게 된다. 예를 들어 value 값이 50이면 Fill Rect에 들어오는 오브젝트가 자신의 부모 오브젝트에 비해서 50%만 채워진다 이 value 바를 한번 조절해서 슬라이더가 어떻게 동작하고 보여지는지 봐 보자.
 
- Slider 컴포넌트 값들 설정
    - Interatable : 체크 해제해준다. 기본적으로 슬라이더는 아까 지운 Handle Slide Area 핸들을 지워도 유저가 잡아당길 순 있긴 하다. 그래서 이걸 체크 해제해주어 아예 유저가 슬라이더를 못 만지게끔 하는것
- transition : None으로 해주어 없애준다. UI요소가 유저에 의해 색깔이 옅어진다던지 하는 효과로 눈에 보이는 피드백을 주는 것. 현재 이런 효과가 필요 없으므로 None.
- Direction : botton to top으로 해준다. 슬라이더가 채워지는 방향!
- min value : 15로 해준다. value의 최소값을 나타냄. 유저가 주는 최소 힘을 15로 할 것.
- max value : 30로 해준다. value의 최대값을 나타냄. 유저가 주는 최소 힘을 30로 할 것.
 
- 부모 자식 관계에 있는 이 Slider, Fill Area, Fill 3개를 선택한 후 anchor presets 값을 설정해주자.
    - Alt키를 누른 체로 맨 오른쪽 하단에 있는 stretch-stretch를 선택
- 이제 Canvas 크기에 딱 맞게 쫙쫙 늘려졌다.
 
- 슬라이더 모양을 바꾸자
    - 편하게 보기 위해 Scene 기즈모의 y축을 눌러 씬 카메라 시점을 변경한 후
- 트랜스폼 툴바에서 Rect Tool을 선택한다. Rect Tool은 2D나 UI 요소의 모양을 편집할 때 많이 쓰인다.
- 슬라이더 전체 모양을 보기 위해 일단 슬라이더 컴포넌트의 value값을 최대로 해놓자.
 
- 위 사진처럼 트랜스폼 툴바를 사용하여 모양과 위치를 바꿔주자.
 
- Slider의 자식인 Fill 에서 슬라이더의 이미지를 설정해주자.
    - 색 입히기
 
- 최종적으로 Slider의 이름은 “Power Slider”로 바꿔주자.
📜BallShooter.cs : 파워 슬라이더 제어하기
- Shooter Pivot오브젝트에 붙여준다.- Ball오브젝트를 생성하고- Ball프리팹을 가져와서 찍어낼 것
 
- 힘을 주어서 날리는 역할을 한다.
        - 파워 슬라이더를 제어해서 힘을 충전해서 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 옆으로 조정해준다.
- Ball의 발사 위치가 될 이- firePos오브젝트를 firePos 슬롯에 드래그 앤 드롭 해주자.
 
 
- public Slider powerSlider
    - PowerSliderUI를 슬롯에 넣어 줄 것.- 슬라이더 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 파일을 이 슬롯에 넣어 줄 것이다.
 
 
- public AudioClip fireClip;
                
- play on awake는 꺼주기. 게임이 시작될 때가 아닌 볼이 발사될 때 재생할거니까
 
- audio clip 에 효과음 mp3 파일 넣기
            
- 이 슬롯에 붙인 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();
        }
    }
- if (fired == true) return;
    - 한번이라도 발사가 됐으면 이 📜BallShooter.cs 의 update()가 실행 되지 않도록 해준다.
- 한번이라도 발사 했었으면 그 아래 코드들이 실행되지 않으므로 차징, 발사, 그리고 그에 따른 효과음 재생 전부 안됨.
 
- maxForce에 도달 해서 강제 발사해야 하는 경우
    - currentForce = maxForce; (maxForce를 넘으면 안되므로.)
- 발사 private void Fire()
 
- 발사 버튼을 누르는 순간 충전 시작 및 충전 효과음 재생 (GetButtonDown)
    - currentForce = minForce; (minForce 최소 힘부터 시작)
- charging 효과음 넣어주고 재생 시작
 
- 발사 버튼을 누르고 있는 동안에 힘이 충전되고 (GetButton) & 발사 상태 X
    - 속도에 맞춰 힘 충전
- 그에 맞춰 슬라이더 value 갱신
 
- 발사 버튼 떼는 순간에 발사 (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의 프리팹을 넣어주자.

문제점 수정
- 위까지의 작업을 마친 후 실행하면
    - 그냥 회전 중에도 maxForce에 도달하면 먼저 터져버린다. maxForce에 도달 하더라도, 회전을 다 끝낸 후 발사하게끔 하고 싶다면!
        📜BallShooter.cs 는 꺼둔 상태로 시작하고 📜ShooterRotator.cs 회전 처리를 먼저 실행하여 Reday 상태가 되고 난 후 그 다음에 📜BallShooter.cs를 켜서 발사 동작을 처리하면 된다. 
- 슬라이더 value값이 다시 초기화가 되지 않고 있다.
 
- 그냥 회전 중에도 maxForce에 도달하면 먼저 터져버린다. maxForce에 도달 하더라도, 회전을 다 끝낸 후 발사하게끔 하고 싶다면!
        
📜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.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 를 꺼준다.
            - 순서
                - 게임 시작하자마자 📜ShootRotator.cs의 Start()와OnEnable()이 1회 실행되며 📜BallShooter.c가 꺼진다.
- 📜ShootRotator.cs 의 update()가 매 프레임 실행 됨
- 유저 입력이 들어오고 GetButtonUp(“Fire1”) 까지 오게되면 📜BallShooter.cs 가 켜진다.
- 이제 📜BallShooter.cs 가 일을 한다. 📜BallShooter.cs의 update()가 매 프레임 유니티로부터 메세지를 받을 수 있는 상태가 됨
 
- 게임 시작하자마자 📜ShootRotator.cs의 
 
- 순서
                
 
 
- ballShooter.enabled = false;
        
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
 
      
    
댓글 남기기