20240105_함정 만들기

2024. 1. 8. 23:28IT/TIL

오늘의 TIL은 플랫포머 게임에서 빼놓을 수 없는 함정에 관한 내용이다.

 

 

플랫포머 게임의 느낌이 물씬 나는 이 맵은 점프와 가시 함정이 핵심인 맵인데,

 

가시 함정이 가져야되는 특징이라고 한다면

 

플레이어와 만났을 때 플레이어에게 대미지를 주어야하며,

 

대미지를 주는 경우에 플레이어에게 넉백을 주면서 피격했다는 표시를 해줘야 된다고 생각했다.

 

따라서 만든 스크립트는 아래와 같은데,

 

더보기
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Traps : MonoBehaviour
{
    public int trapDamage = 3;
    public float knockbackForce = 3f;
    public float invincibilityTime = 2f;
    public SpriteRenderer playerSprite;

    private bool isInvincible = false;

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Player") && !isInvincible)
        {
            Health playerHealth = collision.GetComponent<Health>();
            if (playerHealth != null)
            {
                playerHealth.TakeDamage(trapDamage);
                ApplyKnockback(collision.transform);
                StartCoroutine(InvincibilityTimer());
            }
        }
    }

    // Color 변환과 무적은 추후에 Player 스크립트나 Health 스크립트에서 처리하게 수정할 예정입니다.
    private IEnumerator InvincibilityTimer()
    {
        isInvincible = true;
        playerSprite.color = new Color(200, 0, 0, 100);

        yield return new WaitForSeconds(invincibilityTime);

        isInvincible = false;
        playerSprite.color = new Color(255, 255, 255, 255);
    }

    private void ApplyKnockback(Transform playertransform)
    {
        Vector2 knockbackDirection = playertransform.position - transform.position;
        Vector2 rotatedKnockbackDirection = Quaternion.Euler(0, 0, 180) * knockbackDirection;

        Rigidbody2D playerRigidbody = playertransform.GetComponent<Rigidbody2D>();
        playerRigidbody.AddForce(rotatedKnockbackDirection * knockbackForce, ForceMode2D.Impulse);
    }
}

 

 

이 스크립트를 분석해서 살펴보면

 

    public int trapDamage = 3;
    public float knockbackForce = 3f;
    public float invincibilityTime = 2f;
    public SpriteRenderer playerSprite;

    private bool isInvincible = false;

 

trap의 대미지, 플레이어를 넉백시키는 힘, 무적 시간을 정해주고,

플레이어 SpriteRenderer를 선언해준 후에,

 

플레이어가 무적 상태인지 판단하기 위한 bool 값 isInvincible을 설정해주었다.

 

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Player") && !isInvincible)
        {
            Health playerHealth = collision.GetComponent<Health>();
            if (playerHealth != null)
            {
                playerHealth.TakeDamage(trapDamage);
                ApplyKnockback(collision.transform);
                StartCoroutine(InvincibilityTimer());
            }
        }
    }

 

이후에 OnTriggerEnter2D를 이용하여 '함정'에 '플레이어'가 닿았을 경우 대미지를 줄 수 있게 코드를 만들었는데,

 

플레이어의 체력의 경우, 충돌 시에 그 충돌체의 Health를 가져오게 했다.

 

그 후에, 그 값이 null이 아닌 경우, TakeDamage(int damage)함수를 사용하여 대미지를 주고

 

아래서 설명할 두 함수 ApplyKnockback, InvincibilityTimer를 사용하였다.

 

(여기서 TakeDamage는 단순히 해당하는 damage를 체력에서 깍는 함수이다)

 

    private void ApplyKnockback(Transform playertransform)
    {
        Vector2 knockbackDirection = playertransform.position - transform.position;
        Vector2 rotatedKnockbackDirection = Quaternion.Euler(0, 0, 180) * knockbackDirection;

        Rigidbody2D playerRigidbody = playertransform.GetComponent<Rigidbody2D>();
        playerRigidbody.AddForce(rotatedKnockbackDirection * knockbackForce, ForceMode2D.Impulse);
    }

 

우선 피격 시, 넉백되는 효과를 주는 함수를 살펴보면,

 

knockbackDirection은 플레이어의 위치에서 함정의 위치를 빼준다.

 

(이 경우에 플레이어가 위에서 아래로 떨어지므로, 둘의 차이는 y축 값만 있게 된다)

 

그 후에 튕겨져 나가는 방향을 구하기 위해, Quaternion.Euler(0, 0, 180)을 곱해주는데,

 

이를 통해서 플레이어가 함정을 밟는 힘과 반대 방향으로 튕겨 나가게 된다.

 

즉, 다시 말해서 플레이어는 위에서 아래로 떨어지므로, rotatedKnockbackDirection은 이 180도인 위로 올라가는 방향이 된다.

 

이후에 playerRigidbody르 가져온 후에, 해당하는 방향으로 힘을 주어서 튕겨져 나가게 만들었다.

 

이 함수를 이용하여 플레이어가 함정을 밟은 경우, 위로 튕겨져 나가는 효과를 갖게 되었다.

 

    private IEnumerator InvincibilityTimer()
    {
        isInvincible = true;
        playerSprite.color = new Color(200, 0, 0, 100);

        yield return new WaitForSeconds(invincibilityTime);

        isInvincible = false;
        playerSprite.color = new Color(255, 255, 255, 255);
    }

 

이후에 플레이어의 피격 시, 무적과 피격 효과(색상 변화)를 주는 함수를 살펴보면,

 

이는 코루틴을 사용하여 구현하였는데, 이를 통해서 무적 시간을 줄 수 있게 하였다.

 

즉, 무적을 주는 효과는 isInvincible을 true와 false로 하면서

 

OnTriggerEnter의 if (collision.CompareTag("Player") && !isInvincible) 문으로 효과를 주었고,

 

이 무적 시간을 발동하는 것은 코루틴을 사용하여 구현하였다.

 

또, 플레이어 스프라이트의 색상을 변경함으로써

 

플레이어가 피격 당하여 무적 상태로 들어가고, 무적 상태가 끝난 것을 시각적으로 알 수 있게 만들었다.

 

 

오늘은 이렇게 미리 만들어둔 맵의 함정들에 플레이어를 대항하여 대미지를 주고, 피격을 연출하는 스크립트를 만들었는데,

 

실제로 이 코드에서의 대부분은 플레이어에 연관된 부분이므로,

 

이후에 리팩토링 과정을 거치면서 플레이어에 속하게 만들 예정이다.

 

그렇게 되면 이 스크립트엔 아래 정도의 코드만 남게되고,

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

public class Traps : MonoBehaviour
{
    public int trapDamage = 3;
   
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Player") && !isInvincible)
        {
            Health playerHealth = collision.GetComponent<Health>();
            Condition playerCondition = collision.GetComponent<Condition>();
            if (playerHealth != null)
            {
                playerHealth.TakeDamage(trapDamage);
                playerCondition.ApplyKnockback(collision.transform);
                StartCoroutine(playerCondition.InvincibilityTimer());
            }
        }
    }
}

 

 

playerCondition이라는 스크립트를 새로 만들어서

 

그 곳에서 player가 함정에 맞아서 체력이 깍이는 경우에 이 함수를 작동하도록 할 것으로 예정하고 있다.

 

 

 

오늘은 맵을 디자인하면서 플랫포머 게임에서 가장 중요하다고 할 수 있는 함정을 만드는 과정을 거쳤는데,

 

몬스터와 비교해서 함정은 조금은 다른 로직으로 작동해야되는 부분들이 있다는 점을 깨달았다.

 

특히, 플레이어가 넉백되는 방향과 무적 시간은 몬스터에게 피격 당했을 때와는 많이 다른데,

 

넉백되는 방향은 몬스터에게 피격 당했을 때와는 방향이 달라야 하는데,

 

보통 몬스터에게 피격 당하면 몬스터를 기준으로 x축을 움직여야되지만,

 

함정에 피격 당하는 경우에는 x축보다 y축으로 움직이는 것이 더 좋은 것으로 생각했기 때문이다.

 

또, 무적 시간이 짧으면 함정에서 계속해서 피격 당하며 바로 죽어버리게 되는 문제점이 있어

 

몬스터에게 피격 당했을 때와는 다르게, 조금 더 오랜 시간동안 무적 판정을 받아야된다고 생각했다.

'IT > TIL' 카테고리의 다른 글

20240109_C#에서의 순열  (0) 2024.01.09
20240108_맵의 상호작용 물체 만들기  (0) 2024.01.08
20230104_카메라 이동  (0) 2024.01.04
20240103_맵 만들기  (0) 2024.01.04
20240102_유니티 미니맵  (0) 2024.01.02