https://docs.unity3d.com/ScriptReference/Animator.SetBool.html

 

Unity - Scripting API: Animator.SetBool

Use Animator.SetBool to pass Boolean values to an Animator Controller via script. Use this to trigger transitions between Animator states. For example, triggering a death animation by setting an “alive” boolean to false. See documentation on Animation

docs.unity3d.com

 

테스트를 해보려는 도중 점프가 되지 않아서 원인을 찾아보니 Simulated를 체크하지 않았다.

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class TokoController : MonoBehaviour
{
    public AudioClip deathClip;
    public float jumpForce = 700f;
    private int jumpCount = 0;
    private bool isGrounded = false;
    private bool isDead = false;

    private Rigidbody2D playerRigidbody;
    private Animator animator;
    private AudioSource playerAudio;

    void Start()
    {
        //게임 오브젝트로부터 사용할 컴포넌트들을 가져와 변수에 할당
        playerRigidbody = GetComponent<Rigidbody2D>();
        animator = GetComponent<Animator>();
        playerAudio = GetComponent<AudioSource>();
    }

    void Update()
    {
        if (isDead)
        {
            //사망시 더 이상 처리를 하지 않고 종료
            return;
        }
        if (Input.GetMouseButtonDown(0) && jumpCount < 2)
        {
            jumpCount++; //점프가 두번됨
            //점프 직전 속도를 순간적으로 제로(0,0)으로 변경
            playerRigidbody.velocity = Vector2.zero;
            //리지드바디에 위쪽으로 힘 주기
            playerRigidbody.AddForce(new Vector2(0, jumpForce));
            //오디오 소스 재생
            playerAudio.Play();
            Debug.Log("마우스가 클릭됨");

        }
        else if(Input.GetMouseButtonUp(0) && playerRigidbody.velocity.y > 0) // 마우스에서 손을 떼는 순간 속도의 y값이 양수라면(위로 상승) => 현재 속도를 절반으로 변경
        {
            playerRigidbody.velocity = playerRigidbody.velocity * 0.5f;
        }
        //애니메이터의 Grounded 파라미터를 isGrounded 값으로 갱신
        animator.SetBool("Grounded", isGrounded);
        Debug.LogFormat("{0}", playerRigidbody.velocity.y);

    }

    private void Die()
    {

    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        //트리거 콜라이더를 가진 물체와 충돌 감지
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        //바닥에 닿았음을 감지

        //어떤 콜라이더와 닿았으며, 충돌표면이 위쪽을 보고 있으면
        if (collision.contacts[0].normal.y > 0.7f)
        {
            //isGrouded가 true로 변경하고, 누적 점프횟수를 0으로 초기화
            isGrounded = true;
            jumpCount = 0;
            //Debug.Log("충돌 발생");
        }
    }

    private void OnCollisionExit2D(Collision2D collision)
    {
        //바닥에서 벗어났음을 감지하는 처리
        //어떤 콜라이더에서 떼어진 경우
        isGrounded = false;
    }


}

 

velocity.y의 값을 출력해 보았는데 음수가 나왔다.

Rigidbody의 velocity.y 값이 음수가 나오는 이유는 중력의 영향 때문이다.

Rigidbody의 velocity는 해당 객체의 현재 속도를 나타내며, 유니티에서는 아래쪽 방향을 음의 방향으로 간주한다..

따라서 캐릭터가 점프 후에 일정 높이에 도달하면 중력에 의해 아래로 가속됩니다. 이 때 velocity.y 값은 음수가 된다.

그리고 바닥에 닿으면 다시 상승하기 시작할 때 velocity.y 값은 양수가 될 것이다.

즉, velocity.y 값이 음수가 되는 것은 캐릭터가 점프 후 중력에 의해 아래로 가속되는 정상적인 동작이다.

 

 

 

private void Die()
{
    animator.SetTrigger("Die");
    playerAudio.clip = deathClip;
    playerAudio.Play();

    //속도를 제로로 변경
    playerRigidbody.velocity = Vector2.zero;
    //사망 상태를 true로 변경
    isDead = true;
}

다른 Set계열 메서드와 달리 파라미터에 할당할 새로운 값은 입력하지 않는다.

SetTrigger()메서드는 '방아쇠''를 당길 뿐이다.

트리거 타입의 파라미터는 즉시 true가 되었다가 곧바로 false가 되기 때문에 별도의 값을 지정하지 않는다.

animator.SetTrigger("Die")가 실행되면 곧바로 Die상태러 전환되는 애니메이션 클립이 재생된다.

 

배경을 설정할 것인데 태그에다 이름을 지정하면 order in layer 처럼

레이어의 순서가 정해진다.

 

배경을 스크롤링 할것이므로 스크립트 추가

 

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

public class ScrollingObject : MonoBehaviour
{
    public float speed = 10f;
   

    void Update()
    {
        this.transform.Translate(Vector3.left * speed  * Time.deltaTime);
    }
}

배경과 발판에 스크립트를 부착해서 왼쪽으로 10f 속도로 이동하게 하였기 때문에

플레이어인 Toko는 지나가다 발판이 없어서 떨어진다.

 

Sky에 박스 콜라이더를 넣어주고 isTrigger를 체크하지 않으면

발판과 동시에 

인식을 제대로 하지 못하므로 isTrigger을 체크해야한다.

 

https://docs.unity3d.com/kr/2021.3/Manual/CollidersOverview.html

 

콜라이더 - Unity 매뉴얼

Collider 컴포넌트는 물리적 충돌을 위해 게임 오브젝트의 모양을 정의합니다. 보이지 않는 콜라이더는 게임 오브젝트의 메시와 완전히 똑같을 필요는 없습니다. 메시의 대략적인 근사치로도 효

docs.unity3d.com

 

배경을 무한 반복을 스크립트 추가

 

offset은 Vector2타입지만 transform.position은 Vector3타입이다.

그래서 (Vector2)로 형변환하여 사용

Vector2 값을 Vector3변수에 할당하는 것은 가능한데,,,

이 경우 z값이 0인 Vector3로 자동 형변환되어 할당된다.

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

public class BackgroudLoop : MonoBehaviour
{
    private float width; //배경의 가로 길이
    private void Awake()
    {
       BoxCollider2D col = GetComponent<BoxCollider2D>();
        this.width = col.size.x;
        Debug.Log(this.width);
    }

    void Update()
    {
        if(transform.position.x <= -this.width)
        {
            this.Reposition();
        }
    }

    void Reposition()
    {
        //현재 위치에서 오른쪽으로 가로 길이 * 2 만큼 이동
        Vector2 offset = new Vector2(this.width * 2f, 0);
        transform.position = (Vector2)this.transform.position + offset;
    }


}

sky를 복사해서 기존에 있는 스크린에 넣는다면

예를 들어 화면이 sky -> sky(1) -> sky -> sky(1)이런식으로 두배 간격으로 복사된다.

 

이제 발판을 더 만들 것인데 프리팹으로 만들었고

프리팹을 가져와서 생성된 게임오브젝트에 설정을 바꾸었다.

 

만들어진 발판 프리팹에 Obstacles를 추가할건데 이것을 추가할 때는

프리팹에서 만드는 것이 좋다.

 

그후 발판은 fore 방해물은 middle로 설정하였다.

 

발판 스크립트를 만들었고 방해물 3개가 각각 랜덤으로 나올수 있게 설정

그리고 방해물 회피 성공시 +1

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

public class Platform : MonoBehaviour
{
    public GameObject[] obstacles;
    private bool stepped = false;

    private void OnEnable()
    {
        stepped = false;
        for (int i = 0; i < obstacles.Length; i++)
        {
            if (Random.Range(0, 3) == 0)
            {
                obstacles[i].SetActive(true);
            }
            else
            {
                obstacles[i].SetActive(false);
            }
        }
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.collider.CompareTag("Player") && !stepped)
        {
            stepped = true;
            Debug.Log("점수 1점 추가");
        }
    }
}

 

그후 아까 했던 플랫폼의 모든 프리팹에 적용 되도록 Apply All

 

UI를 만들진 않았지만 UI라던지 게임을 관리할 게임 매니저를 만들었다.

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

public class GameManager : MonoBehaviour
{
    public static GameManager instance;
    public bool isGameover = false; //게임오버 상태
    public GameObject gameoverUI; //게임 오버 시 활성화 할 UI 게임 오브젝트 

    private int score = 0; //게임점수
    void Awake()
    {//싱글턴 변수 instance가 비어있는가?
        if (instance == null)
        {
            //instance가 비어있다면 그곳에 자기 자신을 할당
            instance = this;
        }
        else
        {
            Destroy(this.gameObject);
        }
    }
    private void Update()
    {
        if (isGameover && Input.GetMouseButtonDown(0))
        {
            //게임오버 상태에서 마우스 왼쪽 버튼 클릭하면 현재 씬 재시작
            SceneManager.LoadScene(SceneManager.GetActiveScene().name);
            //현재 활성화된 씬의 정보를Scene 타입의 오브젝트로 가져오는 메서드 씬의 이름을 변수 name으로 제공
        }
    }
    public void AddScore(int newScore)
    {
        //게임오버가 아니라면
        if (!isGameover)
        {
            //점수를 증가
            score += newScore;
            //scoreTect.text = "Score : " + score;
        }
    }
    public void OnPlayerDead()
    {
        isGameover = true;
        //  gameoverUI.SetActive(true);
    }

}

 

이제 platform이 랜덤으로 생성될 것이니 하이어라이키에 있는 플랫폼프리팹을 지워준후

많아질 데이터를 관리하기 위해 오브젝트 풀링사용하여

플랫폼 스폰서를 만들어서 복제된 sky(1)에 할당

오브젝트 풀링
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlatformSpawner : MonoBehaviour
{
    public GameObject platformPrefab;
    public int count = 3;
    private GameObject[] platforms;

    private float lastSpawnTime = 0;
    private float timeBetSpawn = 0;

    private int currentIndex = 0;

    private void Start()
    {
        platforms = new GameObject[count];

        //미리만들자 
        for (int i = 0; i < count; i++)
        {
            platforms[i] = Instantiate(platformPrefab, new Vector3(0, -25, 0), Quaternion.identity);
        }
    }

    private void Update()
    {
        if (Time.time >= lastSpawnTime + timeBetSpawn)
        {
            lastSpawnTime = Time.time;
            timeBetSpawn = Random.Range(1.25f, 2.25f);
            float yPos = Random.Range(-3.5f, 1.5f);

            //OnEnable호출을 위해 
            this.platforms[currentIndex].SetActive(false);
            this.platforms[currentIndex].SetActive(true);

            //재배치 
            this.platforms[currentIndex].transform.position = new Vector2(20, yPos);

            //인덱스 증가 
            currentIndex++;

            //마지막 순번에 도달 했다면 순번 리셋 
            if (currentIndex >= count)
            {
                currentIndex = 0;   //0, 1, 2, 0, 1, 2, 0, 1, 2....
            }
        }
    }
}



오브젝트 풀링 사용하는 이유

유니티에서 오브젝트를 생성하기 위해서는 Instantiate를 사용하고 삭제할 때는 Destroy를 사용

하지만 Instantiate, Destroy 이 두 함수는 무게가 상당히 크다.

Instantiate(오브젝트 생성)은 메모리를 새로 할당하고 리소스를 로드하는 등의 초기화 과정이 필요하고,
Destroy(오브젝트 파괴)는 파괴 이후에 발생하는 가비지 컬렉팅으로 인한 프레임 드랍이 발생할 수 있다.


쉽게 말해 "재사용" 이라고 볼 수 있다.


메모리를 할당 해두기 때문 메모리를 희생하여 성능을 높이는 것이지만,
실시간 작동하는 게임에서 프레임 때문에 선호된다.

--- Inspector ---

 

녹화_2024_03_05_22_52_50_643.mp4
2.89MB

'산대특 > 게임 클라이언트 프로그래밍' 카테고리의 다른 글

Zombie - Line Render  (0) 2024.03.07
Tank - Rigidbody.MovePosition  (0) 2024.03.06
Quaternion  (1) 2024.03.04
Dodge game  (0) 2024.03.04
코루틴 연습  (0) 2024.03.03

+ Recent posts