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
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 파일을 이 슬롯에 넣어 줄 것이다.
- 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;
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기