Unity 강의 정리 6-4장: [게임 제작 : 어메이징 볼링] 볼 슈터 🎮

나무에물주기·2023년 9월 12일
1

Unity

목록 보기
21/21
post-thumbnail

인프런에 있는 레트로의 유니티 C# 게임 프로그래밍 에센스 강의를 듣고 정리하는 글입니다!

게임 제작: 어메이징 볼링 🎳


1. BallShooter 스크립트의 주요 구성 🛠️

클래스와 변수 선언

BallShooter 클래스는 MonoBehaviour를 상속받아 Unity 게임 오브젝트에 붙일 수 있는 스크립트이다. 이 클래스에서 사용되는 변수들은 다음과 같다.

public Rigidbody ball; // 볼의 물리적 특성을 조절할 Rigidbody
public Transform firePos; // 볼을 발사할 위치
public Slider powerSlider; // 파워를 표시할 UI 슬라이더
public AudioSource shootingAudio; // 발사 소리를 재생할 오디오 소스
public AudioClip fireClip; // 발사 소리
public AudioClip chargingClip; // 충전 소리
  • Rigidbody ball: 이는 발사될 볼의 Rigidbody 컴포넌트를 저장한다. Rigidbody는 물리 엔진에서 물체의 운동 상태를 계산하기 위해 사용된다.
  • Transform firePos: 볼이 발사될 시작점의 위치와 방향을 나타낸다.
  • Slider powerSlider: 발사 힘의 크기를 시각적으로 표현하기 위한 UI 슬라이더 컴포넌트이다.
  • AudioSource shootingAudio, AudioClip fireClip, AudioClip chargingClip: 이 변수들은 발사와 충전 소리를 처리한다.

초기화 함수: OnEnable()과 Start()

OnEnable()Start() 함수는 게임 오브젝트나 스크립트가 활성화될 때 호출된다.

  1. OnEnable(): 이 함수에서는 발사 힘(currentForce)과 파워 슬라이더의 값(powerSlider.value)을 초기 상태(minForce)로 설정한다. 이는 스크립트가 활성화되거나 재활성화될 때마다 호출되므로, 각 발사 이벤트 후에도 이 값들이 초기화된다.

    private void OnEnable()
    {
        currentForce = minForce;
        powerSlider.value = minForce;
    }
  2. Start(): Start() 함수에서는 chargeSpeed를 초기화한다. 이 값은 maxForceminForcechargingTime으로 나눠 계산한다. 이렇게 계산하면, 충전 버튼을 누르고 있을 때 currentForce가 얼마나 빠르게 증가해야 하는지 알 수 있다.

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

Update() 함수: 발사 로직과 파워 슬라이더 업데이트

Update() 함수는 매 프레임마다 호출되며, 여기에서 발사 로직과 파워 슬라이더의 업데이트가 이루어진다.

  1. 발사 힘의 최대치 확인: currentForcemaxForce를 초과하거나 동일할 경우, 볼을 발사한다.

  2. Fire1 버튼 입력 처리: Fire1 버튼(기본적으로 마우스 왼쪽 버튼)이 처음 눌렸을 때, currentForceminForce로 재설정하고 충전 소리를 재생한다.

  3. Fire1 버튼을 계속 누르고 있는 경우: Fire1 버튼을 누르고 있으면 currentForce가 시간에 따라 증가하며, 이에 따라 powerSlider.value 역시 업데이트된다.

  4. Fire1 버튼을 뗐을 때: 볼을 발사한다.

private void Update()
{
    if (Input.GetButtonDown("Fire1"))
    {
        currentForce = minForce;
        shootingAudio.clip = chargingClip;
        shootingAudio.Play();
    }
    else if (Input.GetButton("Fire1"))
    {
        currentForce += chargeSpeed * Time.deltaTime;
        powerSlider.value = currentForce;
    }
    else if (Input.GetButtonUp("Fire1") && !fired)
    {
        Fire();
    }
}
  1. Fire1 버튼이 처음 눌렸을 때: Input.GetButtonDown("Fire1") 메서드로 확인하고, currentForceminForce로 재설정하며, 충전 소리를 재생합니다.
  2. Fire1 버튼을 계속 누르고 있는 경우: Input.GetButton("Fire1") 메서드로 확인하고, currentForce를 증가시킵니다. 이 값을 powerSlider.value로 설정하여 UI 슬라이더를 업데이트합니다.
  3. Fire1 버튼을 뗐을 때: Input.GetButtonUp("Fire1") 메서드로 확인하고, Fire() 함수를 호출하여 볼을 발사합니다.

Fire() 함수: 볼 발사 로직

Fire() 함수는 실제로 볼을 발사하는 코드를 담고 있다. 여기에서는 Instantiate 함수를 사용하여 ball 프리팹을 firePos 위치에 생성한다. 그리고 Rigidbody의 velocity 속성을 사용하여 발사 힘을 적용한다.

private void Fire()
{
    fired = true;

    Rigidbody ballInstance = Instantiate(ball, firePos.position, firePos.rotation) as Rigidbody;
    ballInstance.velocity = currentForce * firePos.forward;

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

    currentForce = minForce;
    powerSlider.value = minForce;
}
  1. 볼 인스턴스 생성: Instantiate 함수를 사용하여 볼의 Rigidbody 인스턴스를 생성합니다. 생성된 인스턴스의 위치(position)와 회전(rotation)은 firePos로 설정됩니다.

  2. 볼에 힘 적용: 생성된 볼의 Rigidbody에 발사 힘을 적용합니다. 이 힘은 currentForcefirePos.forward의 곱으로 계산됩니다.

  3. 발사 소리 재생: 발사 소리 클립을 설정하고 재생합니다.

  4. 볼 발사 후 초기화: 발사한 후 currentForcepowerSlider.valueminForce로 리셋합니다.


2. 볼 발사와 파워 슬라이더 조절 🚀

볼 발사의 주요 구성요소

볼을 발사하는 로직은 주로 Update() 함수와 Fire() 함수에서 처리된다. 이들 함수는 다음과 같은 단계로 이루어져 있다.

  1. 사용자 입력 감지: Update() 함수에서는 Input.GetButtonDown("Fire1"), Input.GetButton("Fire1"), Input.GetButtonUp("Fire1") 메서드를 사용하여 사용자의 입력을 감지한다.

  2. 파워 충전: Input.GetButton("Fire1")이 true일 경우, currentForce 변수를 chargeSpeed * Time.deltaTime 만큼 증가시킨다.

  3. 파워 슬라이더 업데이트: currentForce 값이 변경될 때마다 powerSlider.value를 이 값으로 업데이트한다.

  4. 볼 발사: Input.GetButtonUp("Fire1")이 true일 경우 또는 currentForcemaxForce에 도달하면 Fire() 함수를 호출하여 볼을 발사한다.

private void Update()
{
    if (Input.GetButtonDown("Fire1"))
    {
        currentForce = minForce;
    }
    else if (Input.GetButton("Fire1"))
    {
        currentForce += chargeSpeed * Time.deltaTime;
    }
    else if (Input.GetButtonUp("Fire1"))
    {
        Fire();
    }
    powerSlider.value = currentForce;
}

파워 슬라이더의 동작 메커니즘

  1. 초기화: OnEnable() 함수에서 powerSlider.valueminForce로 초기화한다. 이렇게 함으로써, 새로운 볼을 발사할 준비가 완료된다.

  2. 실시간 업데이트: Update() 함수에서 powerSlider.valuecurrentForce로 업데이트한다. 이렇게 하면, 파워 슬라이더가 현재 충전된 파워를 정확하게 반영한다.

  3. 볼 발사 시 리셋: 볼을 발사한 후에는 currentForcepowerSlider.valueminForce로 리셋한다. 이는 Fire() 함수에서 처리된다.

private void Fire()
{
    Rigidbody ballInstance = Instantiate(ball, firePos.position, firePos.rotation);
    ballInstance.velocity = firePos.forward * currentForce;
    currentForce = minForce;
    powerSlider.value = minForce;
}

사용자 피드백

볼이 발사될 때와 파워가 충전될 때 사운드 효과를 재생한다. 이는 AudioSourceAudioClip을 사용하여 구현되며, shootingAudio.clipfireClip 또는 chargingClip으로 설정하고 shootingAudio.Play()를 호출하여 처리한다.

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

3. ShooterRotator 스크립트 변경점 🎯

기존의 ShooterRotator 스크립트는 단순히 발사대의 회전만을 담당했다. 새롭게 추가된 부분은 다음과 같다.

추가된 변수: BallShooter 컴포넌트 참조

이전 버전에서는 ShooterRotator 스크립트 내에서 BallShooter 스크립트를 참조하지 않았다. 새로운 버전에서는 BallShooter 스크립트를 활성화 및 비활성화하기 위해 참조한다.

public BallShooter ballShooter; // 추가

OnEnable() 함수에 추가된 로직

OnEnable() 함수가 추가되어 스크립트가 활성화될 때 발사대의 회전을 초기화하고, BallShooter 스크립트를 비활성화한다.

private void OnEnable() // 추가
{
    transform.rotation = Quaternion.identity;
    state = RotateState.Idle;
    ballShooter.enabled = false;
}

Update() 함수에서의 BallShooter 활성화

RotateState.Ready 상태일 때, BallShooter 스크립트가 활성화된다. 이 부분은 기존 스크립트에 없던 새로운 로직이다.

case RotateState.Ready:
    ballShooter.enabled = true; // 추가
    break;

ShooterRotator 전체 코드

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;
    }
}
profile
개인 공부를 정리함니다

0개의 댓글