1. Space๋ฐ ๋๋ฅผ ์, ์บ๋ฆญํฐ๊ฐ ์ ํํ๋ ๊ธฐ๋ฅ ๋ง๋ค๊ธฐ
์บ๋ฆญํฐ์ ์ค๋ ฅ์ค์ผ ํ๋๊น rigidbody ์ถ๊ฐํ๊ณ ~
using UnityEngine;
public class PlayerController : MonoBehaviour{
const float jumpForce = 10f;
const float gravityModifier = 3f;
Rigidbody playerRb;
void Start(){
playerRb = transform.GetComponent<Rigidbody>();
Physics.gravity *= gravityModifier;
}
void Update(){
if (Input.GetKeyDown(KeyCode.Space)){
playerRb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
}
}
Space๋ฐ ํด๋ฆญ ์, rigidbody ์ ํ์ ์ถ๊ฐํด์ฃผ๋ ์ฝ๋๋ฅผ ์์ฑํ๋ค.
public void AddForce(Vector3 force, ForceMode mode = ForceMode.Force);
//ํ์ ์ด๋์ ์ด๋ป๊ฒ ์์ฉํ๊ฒ ํ ์ง
* ForceMode(Force, Acceleration, Impulse, VelocityChange)
1) Force: ์ฐ์์ ์ธ ํ, ์ง๋ ๋ฌด์ ์ํจ.
2) Acceleration: ์ฐ์์ ์ธ ํ, ์ง๋ ๋ฌด์ํจ.
3) Impulse: ๋ถ์ฐ์์ ์ธ ํ, ์ง๋๋ฌด์ ์ํจ. ์ฆ์์ ์ฉ์ด ๊ฐ๋ฅํจ.
4) VelocityChange:๋ถ์ฐ์์ ์ธ ํ, ์ง๋ ๋ฌด์ํจ.
2. ์์ง์ด๋ ๋ฐฐ๊ฒฝ ๋ง๋ค๊ธฐ
using UnityEngine;
public class MoveLeft : MonoBehaviour
{
private const float SPEED = 15f;
void Update() {
transform.Translate(Vector3.left * Time.deltaTime * SPEED);
}
}
MoveLeft ์คํฌ๋ฆฝํธ๋ฅผ ๋ง๋ค๊ณ ๋ฐฐ๊ฒฝ object ์ ์ถ๊ฐํ๋ค.
Vector3.left = new Vector3(-1, 0, 0) ๊ฐ์ผ๋ก x๊ฐ์ -1์ฉ ์ค์ฌ์ ์ผ์ชฝ์ผ๋ก ์ด๋ํ๊ฒ ํ๋ ๊ฐ์ด๋ค.
3. ์ฅ์ ๋ฌผ ๋ง๋ค๊ธฐ
์ฅ์ ๋ฌผ Prefab์ Hierarchy์ ๋์๋๊ณ Collider์ ์ฃผ๊ณ Collider ๋ฒ์๋ฅผ ์์ ํด์ Prefabs ํด๋์ ๋ฃ๊ณ Hierarchy ์์ ์น ์ง์ด๋ค.
Obstacle ๋ ํ๋ ์ด์ด๋ฅผ ํฅํด ์์ผํ๋ฏ๋ก MoveLeft ์คํฌ๋ฆฝํธ๋ฅผ ๋ฌ์์ค๋ค.
using UnityEngine;
public class SpawnManager : MonoBehaviour
{
[SerializeField] private GameObject[] obstaclePrefabs;
private Vector3 spawnPos = new Vector3(25, 0, 0);
private const float startDelay = 2f;
private const float repeatRate = 2f;
void Start() {
InvokeRepeating("SpawnObstacle", startDelay, repeatRate);
}
private void SpawnObstacle() {
int obstacleIndex = Random.Range(0, obstaclePrefabs.Length);
Instantiate(obstaclePrefabs[obstacleIndex],
spawnPos,
obstaclePrefabs[obstacleIndex].transform.rotation,
transform
);
}
}
SpawnManager ์คํฌ๋ฆฝํธ๋ฅผ ๋ง๋ค๊ณ obstacle ๊ฐ์ฒด๋ค์ ๊ฐ๊ณ ์๋๋ค.
InvokeRepeating ํจ์๋ก 2์ด๋ง๋ค ์์ฑ๋๋๋ก ํด์ค๋ค.
Player๊ฐ ์ฅ์ ๋ฌผ์ ๋ฟ์์ ๋ particle์ ์ค๊ฑฐ๊ธฐ ๋๋ฌธ์ Player๊ฐ ๋์ด์ง๊ฑฐ๋ ํ์ ํ๋ ์ผ์ด ์๋๋ก Rigidbody์ Constraints ๊ฐ์ ์ค๋ค.
//Instantiate ํจ์์ ๋ค์ํ ํจ์ ์ธ์๋ชจ์๋ค
4. ๋ฐฐ๊ฒฝ ๋ฐ๋ณต ์์ฑํ๊ธฐ
์ฃผ์ด์ง ๋ฐฐ๊ฒฝ์ด๋ฏธ์ง๋ ๋๊ฐ์ ์ด๋ฏธ์ง๋ฅผ ์ด์ด๋ถ์ธ ๊ธด ์ด๋ฏธ์ง์ด๋ค.
์ฆ ์ค์ฌ์ ๋ฐ์ผ๋ก ๋๋๋ฉด ๋๊ฐ์ 2๊ฐ์ ์ด๋ฏธ์ง๊ฐ ๋๋ค.
์ด๋ฅผ ์ด์ฉํด์ ์ค์ฌ์ ์ ์ง๋ ๋, ์ด๋ฏธ์ง๋ฅผ ๋ค์ ์ฒ์ ์์น๋ก ๋ณด๋ด๋ฉด ๋ฐ๋ณต๋๋ ๋ฐฐ๊ฒฝ์ด๋ฏธ์ง๋ฅผ ๋ง๋ค ์ ์๋ค.
์ด ๋, ํ๋์ฝ๋ฉ ๊ฐ์ผ๋ก ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง๋ฅผ ์ผ์ ์ง์ (์ค๊ฐ)์ ์ง๋ ๋, ๊ทธ ๋ถ๋ถ๋ถํฐ ๋ค์ ์ด๋ฏธ์ง ์ฎ๊ฒจ์ ์์ํ๋๋ก ํ ์๋ ์์ผ๋ ๋๊ฒจ๋ณด์ด๋ ํ์์ด ์๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์๋ ์ ํํ ๋ฐ ๊ธธ์ด๋ฅผ ์์์ผ ํ๋ค.
์ด๋ฏธ์ง์ ๋ฐ ํฌ๊ธฐ๋ Box Collider์ x size ๊ฐ์ 2๋ก ๋๋๋ฉด ๊ตฌํ ์ ์๋ค.
using UnityEngine;
public class RepeatBackground : MonoBehaviour{
private Vector3 startPos;
private float repeatWidth;
void Start(){
startPos = transform.position;
repeatWidth = transform.GetComponent<BoxCollider>().size.x / 2;
}
void Update(){
if(transform.position.x < startPos.x - repeatWidth) {
transform.position = startPos;
}
}
}
RepeatBackground ์คํฌ๋ฆฝํธ๋ฅผ ๋ง๋ค๊ณ , ์์์ง์ (startPos)๊ณผ ๋ฐ๋ณต ๊ฐ๋ก๊ฐ(repeatWidth)์ ๋ณ์๋ก ๋๊ณ Start๋ฌธ์์ ์ค์ ํด์ค๋ค.
Update() ๋ฌธ์์ ๋ฐฐ๊ฒฝ์ด๋ฏธ์ง์ ๊ธธ์ด๊ฐ ๋ฐ์ ์ง๋ ๋๋ฅผ ์ฒดํฌํด์ ๋ฐฐ๊ฒฝ์ด๋ฏธ์ง๋ฅผ ๋ค์ ์์์ ์ผ๋ก ๋ณด๋ธ๋ค.
=> ์ด๋ฏธ์ง๋ฅผ ๋ฐ ์๋ผ์ ์ด๋ฏธ์ง ๋ ๊ฐ๋ฅผ ์ด์ฉํด์ ์์น๋ฅผ ๊ณ์ ๋ฐ๊ฟ์ผ๋ก์จ ๊ตฌํ๋ ๊ฐ๋ฅํ๋ค!
5. ํ๊ทธ๋ก ์ถฉ๋ ๊ฐ์ฒด ์ฒดํฌํ๊ณ GameOver ์ฒดํฌํ๊ธฐ
Tag > Add Tag์์ Ground์ Obstacle ๋ก ํ๊ทธ๋ฅผ ๋ง๋ค๊ณ ๊ฐ ๊ฐ์ฒด์ ์ถ๊ฐํด์ฃผ์.
//PlayerController.cs
private void OnCollisionEnter(Collision collision){
if(collision.gameObject.CompareTag("Ground")) {
isOnGround = true;
}
else if (collision.gameObject.CompareTag("Obstacle")) {
isGameOver = true;
}
}
playerController ํด๋์ค์์ OnCollisionEnter ํจ์๋ก ์ถฉ๋์ฒดํฌ๋ฅผ ํ ์ ์๋ค.
์ ํ ํ Ground๋ก ๋จ์ด์ก์ ๋ isOnGround = true๋ก ์ฒดํฌํด์ฃผ๊ณ
Obstacle๊ณผ ์ถฉ๋ํ์ ๋ gameOver = true๋ก ์ฒดํฌํด์ฃผ์.
//PlayerController.cs
public bool GetGameOver(){
return isGameOver;
}
//MoveLeft.cs
using UnityEngine;
public class MoveLeft : MonoBehaviour{
private const float SPEED = 15f;
void Update(){
if (!PlayerController.Instance.GetGameOver()){
transform.Translate(Vector3.left * Time.deltaTime * SPEED);
}
}
}
PlayerController.Instance.GetGameOver = true ์ด๋ฉด Game Over ์ํ
PlayerController.Instance.GetGameOver = false ์ด๋ฉด Game Play ์ํ
using UnityEngine;
public class SpawnManager : MonoBehaviour{
[SerializeField] private GameObject[] obstaclePrefabs;
private Vector3 spawnPos = new Vector3(25, 0, 0);
private const float startDelay = 2f;
private const float repeatRate = 2f;
void Start(){
InvokeRepeating("SpawnObstacle", startDelay, repeatRate);
}
private void SpawnObstacle(){
if (PlayerController.Instance.GetGameOver()) return;
int obstacleIndex = Random.Range(0, obstaclePrefabs.Length);
Instantiate(obstaclePrefabs[obstacleIndex],
spawnPos,
obstaclePrefabs[obstacleIndex].transform.rotation,
transform
);
}
}
GameOver ์ํ์ด๋ฉด ๋ฐฐ๊ฒฝํ๋ฉด/์ฅ์ ๋ฌผ ์์ง์, ์ฅ์ ๋ฌผ ์์ฑ์ ๋ชจ๋ ๋ฉ์ถ์.
6. ์ ๋๋ฉ์ด์ ์ ์ฉํ๊ธฐ
1) ๋ฌ๋ฆฌ๊ธฐ ์ ๋๋ฉ์ด์
Player์ ๋ฌ๋ ค์๋ SimpleCharacter ์ ๋๋ฉ์ด์ ์ ํ์ธํด๋ณด์.
P.S) ํ๋ฉด์ด ์งค๋ ค์ ์๋ณด์ด๋ฉด Alt ํค ๋๋ฅด๊ณ ๋๋๊ทธํ๋ฉด ์ด๋๋จ.
1)-1 ํ ํ์ดํ๋ค์ ํด๋ฆญํ๋ฉด ์ํ๋ณํ๋ฅผ ํ์ธํ ์ ์๋ค.
์ ๊ทธ๋ฆผ์์ Idle -> Wark_Static ์ํ๋ก ๋ฐ๋๋ ๋ชจ์ต์ด ๋ณด์ธ๋ค.
๋ฐ๋๋ ์กฐ๊ฑด์ Static_b ๊ฐ์ด true์ด๊ณ Speed_f ๊ฐ์ด 0.25 ์ด์์ผ ๋!
1)-2 ํ ์ํ๋ฅผ ๋๋ธํด๋ฆญํ๋ฉด ํด๋น ์ ๋๋ฉ์ด์ ์ด ๋ณด์ธ๋ค.
์ ์ฌ์ง์ Run ์ํ๋ฅผ ๋๋ธํด๋ฆญํ์ฌ ๋ฌ๋ฆฌ๋ ๋ชจ์ ์ด ํ์ธ๋๋ค.
1)-3 ์ ๋๋ฉ์ด์ ๊ธฐ๋ณธ์ํ ๋ณ๊ฒฝ
์บ๋ฆญํฐ๊ฐ ๊ฒ์ ์์๋ ๋ฐ๋ก ๋ฌ๋ฆฌ๋ ๋ชจ์ ์ ์ทจํ๊ธฐ ์ํด ๊ธฐ๋ณธ์ํ๋ฅผ Idle -> Run_Static ์ํ๋ก ๋ฐ๊พธ์.
๋ง์ฐ์ค ์ค๋ฅธ์ชฝ ๋ฒํผ์ ๋๋ฅธ ํ Set as Layer Defualt Status ๋ฅผ ํด๋ฆญํ๋ฉด ๋๋ค.
๋ํ ์ผ์ชฝ์ Speed_f ์ ๊ฐ์ 1.0 ์ ๋๋ก ์ฌ๋ ค์ฃผ๊ณ Insperctor ์์์ Speed๋ฅผ ๋ฐฐ๊ฒฝ๊ฐ๊ณผ ๋ง์ถฐ์ฃผ์.
2) ์ ํ ์ ๋๋ฉ์ด์
using UnityEngine;
public class PlayerController : MonoBehaviour{
private Animator playerAnim;
void Start(){
playerAnim = transform.GetComponent<Animator>();
}
void Update(){
if (Input.GetKeyDown(KeyCode.Space) && isOnGround) {
playerAnim.SetTrigger("Jump_Trig");
}
}
}
Run -> Running_Jump ์ํ๋ก ๋ณ๊ฒฝํํ๊ธฐ ์ํด ํ์ดํ๋ฅผ ๋๋ฌ๋ณด์.
Run -> Running_Jump ์ํ๋ก ๋ฐ๋๊ธฐ ์ํ ์กฐ๊ฑด์ Jump_trig์ด๋ค.
Trigger๋ animator.SetTrigger("ํธ๋ฆฌ๊ฑฐ๋ช ")์ผ๋ก ์ค ์ ์๋ค.
์ฆ, ์ ์ฝ๋์ฒ๋ผ Animator ๋ฅผ ์ ์ธํ๊ณ Space๋ฅผ ๋๋ฅผ ๋๋ง๋ค Animator์Run -> Runnig_Jump ์กฐ๊ฑด์ธ Jump_trig๋ฅผ Trigger๋ก ์ฃผ๋ฉด ๋๋ค.
์ข ๋ ์์ฐ์ค๋ฌ์ด ์ ํ๋ฅผ ํ๋ ค๋ฉด ์ ํํ ๋๋ Jump ์ ๋๋ฉ์ด์ Speed๋ฅผ ์กฐ๊ธ ์ค์ฌ์ฃผ์. 1 -> 0.7๋ก.
๊ทธ๋ผ ์ ํ ๋ชจ์ ์ ์ข ๋ ๊ธธ๊ฒ ํด์ ์์ฐ์ค๋ฝ๋ค.
3) ์ฅ์ ๋ฌผ์ ๋ถ๋ชํ์ ์ฐ๋ฌ์ง๋ ๋ชจ์
Depth ๋ ์ด์ด๋ฅผ ๋ณด๋ฉด, Alive -> Depth_01 ํน์ Alive -> Depth_02 ๋ชจ์ ์ด ๋ณด์ด๋๋ฐ
๋ณํ ์กฐ๊ฑด์ Depth_b ๊ฐ true์ผ ๋, ๋ํ Type์ id ๋ก 1, 2 ์ค์ ๊ณจ๋ผ์ผ ํ๋ค.
1์ ๋ค๋ก, 2๋ ์์ผ๋ก ์ฐ๋ฌ์ง๋๊ฑฐ๋ผ 1๋ก ์ง์ ํด์ฃผ์.
private void OnCollisionEnter(Collision collision){
if(collision.gameObject.CompareTag("Ground")) {
isOnGround = true;
}
else if (collision.gameObject.CompareTag("Obstacle")) {
isGameOver = true;
playerAnim.SetBool("Death_b", true);
playerAnim.SetInteger("DeathType_int", 1);
}
}
PlayerController ์คํฌ๋ฆฝํธ์์ ์ฅ์ ๋ฌผ๊ณผ ์ถฉ๋ํ์ ๋, SetBool, SetInteger๋ก Depth_01 ๋ชจ์ ์ ์ ์ฉํ ์ ์๋ค.
* ์ ๋๋ฉ์ด์ ํ๋ผ๋ฏธํฐ
https://docs.unity3d.com/kr/2021.3/Manual/AnimationParameters.html
์ ๋๋ฉ์ด์ ์ ํ์กฐ๊ฑด์ ๋ง๊ฒ SetFloat, SetInteger, SetBool, SetTrigger ๋ฐ ResetTrigger ๋ก ํ๋ผ๋ฏธํฐ๋ฅผ ์ค ์ ์๋ค.
7. Particle ์ ์ฉํ๊ธฐ
Particle์ Hierarchy ์์ ์ฌ๋ ค๋์ผ๋ฉด Particle์ ์ฌ์ํด๋ณผ ์ ์๋ค.
Particle์ ์ ์ฉํ ์ ์๋ ๋ค์ํ ์์ฑ๊ฐ. Speed, Size, Rotation, Color ๋ฑ๋ฑ...
public class PlayerController : MonoBehaviour{
[SerializeField]
private ParticleSystem explosionParticle;
private void OnCollisionEnter(Collision collision){
if (collision.gameObject.CompareTag("Obstacle")) {
explosionParticle.Play();
}
}
}
์คํ์ํค๋ ๋ฒ์ ParticleSystem ์ ์ธํ๊ณ ๊ฐ์ฒด ์ฐ๊ฒฐ ํ Play() ํจ์ ํธ์ถํด์ฃผ๋ฉด ๋๋ค.
8. ์ฌ์ด๋ ์ถ๊ฐํ๊ธฐ
1) ๋ฐฐ๊ฒฝ์์ ์ถ๊ฐ
sound ํ์ผ ํ๋๋ฅผ ํด๋ฆญํ๋ฉด ์ฌ์ด๋๋ฅผ ๋ค์ด๋ณผ ์ ์๋ค.
Sound๋ฅผ ์ฌ์์ํค๊ธฐ ์ํด์๋ Audio Listener, Audio Source ์ปดํฌ๋ํธ๊ฐ ํ์ํ๋ฐ ๊ธฐ๋ณธ์ ์ผ๋ก Audio Listener์ Main Camera์ ๋ฌ๋ ค ์๋ค. ์ถ๊ฐ๋ก Audio Source๋ฅผ ๋ฌ์์ฃผ์.
Audio Source์ AudioClip ์ ์ฌ์ด๋ ํ์ผ์ ์ฐ๊ฒฐํ๊ณ Volume์ ์กฐ์ ํด์ฃผ์.
*AudioListener: AudioSource์ ์๋ AudioClip์ ์ฌ์์ํค๋ ์ปดํฌ๋ํธ
*AudioSoucre: Audioํ์ผ์ ์ฐ๊ฒฐํ๊ณ Auio ํ์ผ์ ์์ฑ ๋ฐ ์ค์ ์ ์ ์ฉํ๋ ์ปดํฌ๋ํธ
2) ๋ชจ์ ํจ๊ณผ์ ์ถ๊ฐ
using UnityEngine;
public class PlayerController : MonoBehaviour{
[SerializeField] private AudioClip jumpSound;
[SerializeField] private AudioClip crashSound;
private AudioSource playerAudio;
void Start(){
playerAudio = transform.GetComponent<AudioSource>();
}
void Update(){
if (Input.GetKeyDown(KeyCode.Space) && isOnGround && !isGameOver) {
playerAudio.PlayOneShot(jumpSound, 1f);
}
}
private void OnCollisionEnter(Collision collision){
if (collision.gameObject.CompareTag("Obstacle")) {
playerAudio.PlayOneShot(crashSound, 0.5f);
}
}
}
public void PlayOneShot (AudioClip clip, float volumeScale= 1.0F);
Player ๊ฐ์ฒด์ AudioSource ์ปดํฌ๋ํธ๋ฅผ ๋ฌ์์ฃผ๊ณ , ์ฌ์์ ์ํ๋ AudioClip๋ฅผ ์คํฌ๋ฆฝํธ์ ์ฐ๊ฒฐํด์ค๋ค.
AudioClip ์ฌ์ํจ์๋ PlayOneShot() !