반응형

https://www.youtube.com/watch?v=4egDnkVkjv8 

 

Wall.cs

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

public class Wall : MonoBehaviour
{

    public Sprite dmgSprite;  // 플레이어가 벽을 한대 때렸을 때 보여줄 스프라이트  
    public int hp = 4; // 벽의 체력

    private SpriteRenderer spriteRenderer;

    // Start is called before the first frame update
    void Awake()
    {
        spriteRenderer = GetComponent<SpriteRenderer>();
    }

    public void DamageWall (int loss)
    {
        spriteRenderer.sprite = dmgSprite; // 플레이어가 성공적으로 벽을 공격했을떄 시각적인 변화를 줌
        hp -= loss;
        if (hp <= 0)
            gameObject.SetActive(false); //hp 0 이라면 게임오브젝트 비활성화
    }
}

 

Player.cs

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

public class Player : MovingObject
{
    public int wallDamage = 1; //플레이어가 가하는 공격력
    public int pointsPerFood = 10;
    public int pointsPerSoda = 20;
    public float restartLevelDelay = 1f;

    private Animator animator; //에니메이터 레퍼런스를 가져오기 위한 변수
    private int food;

    // Start is called before the first frame update
    protected override void Start() //MovingObject 에 있는 start 와 다르게 구현하기 위해 오버라이딩
    {
        animator = GetComponent<Animator>();
        food = GameManager1.instance.playerFoodPoints; // 해당레벨동안 음식점수를 관리할 수 있음, 레벨이 바뀔 때 마다 게임메니저에 저장 

        base.Start(); // 부모 클래스의 스타트 호출
    }

    private void OnDisable()
    {
        GameManager1.instance.playerFoodPoints = food;  //food 를 게임메니저에 저장하는 과정  
    }

    void Update()
    {
        if (!GameManager1.instance.playersTurn) return; //플레이어의 턴이 아니라면 종료

        int horizontal = 0;
        int vertical = 0;

        horizontal = (int)Input.GetAxisRaw("Horizontal");
        vertical = (int)Input.GetAxisRaw("Vertical");

        if (horizontal != 0)
            vertical = 0;//플레이어가 대각선으로 움직이는 것을 막음

        if (horizontal != 0 || vertical != 0)// 움직이려고 하면 
            AttemptMove<Wall>(horizontal, vertical);//벽에 부딪힐 때(움직이려는 방향으로)
    }

    protected override void AttemptMove <T> (int xDir, int yDir)
    {
        food--;//움직이면 푸드 -1

        base.AttemptMove<T>(xDir, yDir);

        RaycastHit2D hit; // 충돌여부 변수 

        CheckIfGameOver();

        GameManager1.instance.playersTurn = false;
    }

    private void OnTriggerEnter2D (Collider2D other)//출구, 소다 , 음식의 태그를 트리거로 설정했으므로 태그를 체크
    {
        if (other.tag == "Exit")
        {
            Invoke("Restart", restartLevelDelay);//restartLevelDelay 만큼 정지 후 함수 호출 
            enabled = false;
        }
        else if (other.tag == "Food")
        {
            food += pointsPerFood;
            other.gameObject.SetActive(false);//푸드, 소다 오브젝트를 비활성화
        }
        else if (other.tag == "Soda")
        {
            food += pointsPerSoda;
            other.gameObject.SetActive(false);
        }
    }

    protected override void OnCantMove <T> (T component)//wall 에 의해 block 되는 경우를 표현 
    {
        Wall hitWall = component as Wall;
        hitWall.DamageWall(wallDamage);//플레이어가 벽에 얼마나 데미지를 가하는지
        animator.SetTrigger("playerChop");//애니메이션 트리거
    }

    private void Restart()//Exit 오브젝트와 충돌시 발생하는 함수
    {
        SceneManager.GetActiveScene(); // 레벨을 다시 불러옴 , 이 게임에는 하나 밖에 없는 main 신을 불러옴, 많은 게임들이 이 함수를 이용해 다른 신을 불러온다
        //SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }

    public void LoseFood(int loss)
    {
        animator.SetTrigger("playerHit");
        food -= loss;
        CheckIfGameOver();
    }

    private void CheckIfGameOver()
    {
        if (food <= 0)
            GameManager1.instance.GameOver();
    }
}

 

새로 알게 된 내용

  •  SceneManager.GetActiveScene(); 
    • 많은 게임들이 이 함수를 이용해 신에서 신으로 넘어가는 작업을 한다
  • 1. Input.GetAxis(string name)
    • -1, 0, 1 세 가지 값 중 하나가 반환된다. 키보드 값을 눌렀을 때 즉시 반응해야한다면 GetAxisRaw를 사용하면 된다.  
  • 2. Input.GetAxisRaw(string name)
    • -1.0f 부터 1.0f 까지의 범위의 값을 반환한다. 즉, 부드러운 이동이 필요한 경우에 사용된다.
    • 키보드 입력을 위해 getAxisRaw 를 사용한다
  • RayCastHit
    • 원점에서 쏜 레이를 통해 충돌 여부를 파악
반응형

'Unity > 로그라이크 따라해보기' 카테고리의 다른 글

로그라이크 따라하기 6  (0) 2021.07.07
반응형

https://www.youtube.com/watch?v=LrpaW6VglVU 

 

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


//일반형(generic) 을 사용하는 이유 플레이어, 적, 벽 과의 상호작용에서 서로서로 hitComponent 의 종류를 알 수 없기 때문에 당장 OnCantMove 함수에 입력을 받고 각각의 상속한 클래스들의 구현에 따라 동작하게 할 수 있기 때문이다.
public abstract class MovingObject : MonoBehaviour
{
    public float moveTime = 0.1f;
    public LayerMask blockingLayer; //충돌여부 변수


    private BoxCollider2D boxCollider;
    private Rigidbody2D rb2D; //객체의 움직임 레퍼런스를 담을 변수
    private float inverseMoveTime; //움직임을 효율적이게 계산하기 위한 변수


    // Start is called before the first frame update
    protected virtual void Start()//자식 클래스가 덮어써서 재정의 하기 위해 start 를 다시 정의할 수도있어서
    {
        boxCollider = GetComponent<BoxCollider2D>();
        rb2D = GetComponent<Rigidbody2D>();
        inverseMoveTime = 1f / moveTime; //나누기 대신에 효율적인 곱하기를 사용하기 위해 움직임의 역수치를 저장
    }

    protected bool Move (int xDir, int yDir, out RaycastHit2D hit)//bool 리턴값, hit 리턴값 두개의 리턴값을 가짐
    {
        Vector2 start = transform.position; //현재위치를 받기위한 변수
        // position 은 기존 3차원 이지만 Vector 2에 저장함으로 z 값은 날라가게됨 
        Vector2 end = start + new Vector2(xDir, yDir);

        boxCollider.enabled = false;//자기자신과 부딛히지 않기 위해 boxCollider 해제
        hit = Physics2D.Linecast(start, end, blockingLayer); // 시작지점과 끝지점 까지의 라인을 가져오고 blockinglayer 와 충돌검사
        boxCollider.enabled = true; 

        if(hit.transform == null)//부딪힌게 없다면
        {
            StartCoroutine(SmoothMovement(end));//end 방향으로 이동
            return true;
        }
        return false;
    }


    protected IEnumerator SmoothMovement (Vector3 end)//객체를 움직이게 할 함수 end에 어디로 움직일지 저장
    {
        float sqrRemainingDistance = (transform.position - end).sqrMagnitude; // 현재위치 - end 벡터에 sqrMagnitude 로 거리계산 Magnitude : 벡터길이 , sqrMagnitude : 벡터길이 제곱 

        while (sqrRemainingDistance > float.Epsilon)
        {
            Vector3 newPosition = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime * Time.deltaTime); // epsilon (0에 가까운 아주작은 수 ) newposition 을 계산하여 end 값과 가장가까운 위치로 이동시키기 위한 변수
            //(현재위치(rigidpositon), 이동할위치, 시간(이 변수의 단위만틈 목적지로 가까워짐)) 
            rb2D.MovePosition(newPosition); // 새로운 위치로 이동
            sqrRemainingDistance = (transform.position - end).sqrMagnitude; // 움직인 후 남은 거리 계산 
            yield return null; //루프를 갱신하기 전에 다음 프레임을 기다림
        }
    }

    protected virtual void AttemptMove <T> (int xDir, int yDir)
        where T : Component
    {
        RaycastHit2D hit;
        bool canMove = Move(xDir, yDir, out hit);//이동하는데 성공하면 canMove = true else flase    

        if (hit.transform == null)//다른것과 부딪히지 않았다면 코드 종료
            return;

        T hitComponent = hit.transform.GetComponent<T>(); //충동할 레퍼런스를 T 타입 컴포넌트에 할당 

        if (!canMove && hitComponent != null)//움직임이 막힘 -> 충돌
            OnCantMove(hitComponent);
    }

    protected abstract void OnCantMove<T>(T component)
        where T : Component;// 자식 클래스에서 오버라이드 위해 남겨둠 
}

 

일반형(Generic) 타입을 사용하는 이유 

 

플레이어, 적, 벽 과의 상호작용에서 서로서로 hitComponent 의 종류를 알 수 없기 때문에 당장 OnCantMove 함수에 입력을 받고 각각의 상속한 클래스들의 구현에 따라 동작하게 할 수 있기 때문이다.

반응형

'Unity > 로그라이크 따라해보기' 카테고리의 다른 글

로그라이크 따라하기 7,8,9  (0) 2021.07.09

+ Recent posts