본문 바로가기

Unity 개발 공부

[내배캠] 8일차 게임속 상호작용 분석하기 (25.04.01)

 

 

1.선택한 게임의 이름은 무엇인가요?
 
• 다크소울, 몬스터헌터, 젤다 야생의숨결
 
2. 선택한 게임의 장르는 무엇인가요?
 
 소울라이크, 액션 롤플레잉, 액션 어드벤쳐
 
3. 선택한 게임의 어떤 시스템에 집중하셨나요?
 
• 프레임회피와 저스트 회피에 대해서 알아봤다.

 
4. 해당 시스템이 동작하는 구조의 시작부터 과정을 자세하게 분석, 나열해봅시다.

 

1. 개념적 차이

일반 회피 (Frame Dodge)

  • 목적:
    • 플레이어가 회피 버튼을 누르면 바로 회피 동작이 시작되고, 일정 시간 동안(예를 들어 0.5초) 무적 상태가 됩니다.
  • 특징:
    • 고정된 무적 시간: 버튼 입력 시 정해진 시간 동안 무적 상태를 부여하여, 충돌 판정을 단순하게 무시합니다.
    • 예측 가능한 동작: 모든 회피 시 동일한 효과가 발생하여, 플레이어가 딱 맞는 타이밍을 신경쓰지 않아도 됩니다.
  • 구현 포인트:
    • 애니메이션 동기화: 애니메이션의 특정 프레임에서 무적 상태를 시작하고 종료하도록 맞추면 자연스러운 회피 동작을 구현할 수 있습니다.
    • 단순성: 복잡한 타이밍 계산 없이, 버튼 입력과 함께 고정 시간 동안 상태를 변경합니다.

저스트 회피 (Just Dodge)

  • 목적:
    • 회피 입력의 정확한 타이밍에 따라 추가 보너스(예: 추가 무적 시간, 카운터 기회 등)를 부여하는 방식입니다.
  • 특징:
    • 타이밍 평가: 회피 버튼 입력 시점과 애니메이션 혹은 무적 시작 시점의 이상적인 타이밍(예를 들어, 애니메이션의 특정 프레임)을 비교합니다.
    • 보상 시스템: 입력 타이밍이 정확할 경우 일반 회피보다 더 긴 무적 시간이나 추가 효과를 부여하여, 플레이어의 기술 숙련도를 반영합니다.
  • 구현 포인트:
    • 시간 측정: 입력이 발생한 시점과 이상적인 타이밍 간의 차이를 측정합니다.
    • 허용 오차: 일정 범위 내에 들어오면 “저스트” 성공으로 판단합니다.
    • 보너스 효과: 성공 시 추가적인 무적 시간이나 기타 효과를 적용합니다.

2. 구현 예제

아래 예제에서는 두 가지 회피 동작을 각각 MonoBehaviour 스크립트로 구현합니다.
(※ Unity 엔진 내에서 동작하는 스크립트 예제입니다.)

 

2.1 일반 회피 (Frame Dodge) 예제

using UnityEngine;
using System.Collections;

public class FrameDodge : MonoBehaviour
{
    public Animator animator;        // Unity에서 애니메이션을 관리하는 컴포넌트
    public Rigidbody rb;             // 물리 기반 이동을 위한 컴포넌트
    public float invincibilityDuration = 0.5f;  // 무적 상태 지속 시간 (초)
    public float dodgeSpeed = 5f;    // 회피 동작 중 적용할 이동 속도

    private bool isDodging = false;

    void Update()
    {
        // 스페이스바를 회피 입력으로 사용
        if (Input.GetKeyDown(KeyCode.Space) && !isDodging)
        {
            StartCoroutine(PerformDodge());
        }
    }

    IEnumerator PerformDodge()
    {
        isDodging = true;
        animator.SetTrigger("Dodge"); // 애니메이터에서 "Dodge" 트리거가 설정되어 있어야 함

        // 무적 상태 시작
        SetInvincible(true);

        // 전방으로 이동 (간단하게 rb.velocity로 처리)
        Vector3 dodgeDirection = transform.forward;
        rb.velocity = dodgeDirection * dodgeSpeed;

        // 고정된 시간 동안 무적 유지
        yield return new WaitForSeconds(invincibilityDuration);

        // 무적 해제 및 이동 속도 초기화
        SetInvincible(false);
        rb.velocity = Vector3.zero;
        isDodging = false;
    }

    void SetInvincible(bool state)
    {
        // 실제 게임에서는 Collider의 레이어 변경이나 무적 플래그 처리 등으로 구현
        Debug.Log("Invincibility set to: " + state);
    }
}
 
2.2 저스트 회피 (Just Dodge) 예제
using UnityEngine;
using System.Collections;

public class JustDodge : MonoBehaviour
{
    public Animator animator;
    public Rigidbody rb;
    
    public float invincibilityDuration = 0.5f; // 기본 무적 시간
    public float justBonusDuration = 0.3f;       // 저스트 성공 시 추가 무적 시간
    public float dodgeSpeed = 5f;

    // 이상적인 무적 시작 시점(애니메이션의 특정 프레임에 해당하는 시간, 초 단위)
    public float idealDodgeTime = 0.2f;
    // 입력 타이밍 허용 오차 (초)
    public float justTimingTolerance = 0.1f;
    
    private bool isDodging = false;
    private float dodgeStartTime;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space) && !isDodging)
        {
            dodgeStartTime = Time.time;
            StartCoroutine(PerformJustDodge());
        }
    }

    IEnumerator PerformJustDodge()
    {
        isDodging = true;
        animator.SetTrigger("Dodge");
        SetInvincible(true);
        rb.velocity = transform.forward * dodgeSpeed;

        // 이상적인 시점까지 대기 (예: 애니메이션에서 무적 시작 시점)
        yield return new WaitForSeconds(idealDodgeTime);

        // 실제 입력 타이밍과 이상적인 시간 간의 차이 측정
        float inputDelay = Time.time - dodgeStartTime;
        bool isJustSuccessful = Mathf.Abs(inputDelay - idealDodgeTime) <= justTimingTolerance;

        if (isJustSuccessful)
        {
            Debug.Log("Just Dodge 성공! 추가 보너스 적용");
            // 추가 보너스 무적 효과 적용
            yield return new WaitForSeconds(justBonusDuration);
        }
        else
        {
            Debug.Log("일반 회피로 처리");
        }

        SetInvincible(false);
        rb.velocity = Vector3.zero;
        isDodging = false;
    }

    void SetInvincible(bool state)
    {
        Debug.Log("Invincibility set to: " + state);
    }
}
 

3. 결론

  • 일반 회피(프레임 회피):
    단순하게 버튼 입력과 동시에 고정 시간 동안 무적 상태를 부여합니다. 구현이 단순하며, 예측 가능한 효과를 줍니다.
  • 저스트 회피:
    회피 입력의 정확성을 평가하여, 입력 타이밍이 정확하면 추가 보너스 효과를 부여합니다. 이를 통해 플레이어의 숙련도와 기술적 도전을 반영할 수 있습니다.

추가적인 공부

1. 왜 코루틴(열거자와 yield)을 썼을까?

코루틴 사용 이유

  • 비동기 작업 처리: (순서대로 동기화되어 처리된게 아닌 비동기, 따라서 특정 동작이 실행될때, 같이 병렬구조로 실행가능한상황.)
    코루틴은 특정 작업(예: 시간 지연, 여러 프레임에 걸친 연산)을 처리할 때, 한 프레임에서 멈추고 다음 프레임에 이어서 실행할 수 있습니다.
    예를 들어, 회피 동작에서 0.5초 동안 무적 상태를 유지하는 동안 다른 로직이 계속 실행되도록 하기 위해 사용합니다.
  • 메인 스레드 블로킹 방지:
    코루틴은 Unity의 메인 스레드를 블로킹하지 않고, 비동기적으로 동작할 수 있게 해줍니다.
    이로 인해 애니메이션이나 물리 연산 등 다른 게임 루프가 원활하게 돌아갑니다.

열거자(IEnumerator)와 yield의 역할

  • IEnumerator:
    코루틴은 C#의 열거자(이터레이터) 기능을 사용하여 구현됩니다.
    메서드가 IEnumerator를 반환하면, Unity는 이 메서드 내부의 yield return 문을 만나면 실행을 일시 중지했다가 다음 프레임 또는 조건이 만족될 때 다시 실행합니다.
  • yield return:
    이 키워드는 현재 실행 중인 코루틴을 “일시 중지” 상태로 만들고, 특정 조건(예: 일정 시간 대기) 후에 재개하게 합니다.
    덕분에 복잡한 타이밍 제어나 딜레이 로직을 간단하게 작성할 수 있습니다.

 

 

 

5. 직접 분석해본 내용 중 가장 핵심이 되는 구성 요소는 무엇이라 생각하나요?
 
 

일반 회피와 저스트 회피 모두 기본적으로 입력 처리, 애니메이션/이동 처리, 무적 상태 부여라는 공통 구성요소를 갖습니다. 그런데 이 둘을 구분하는 가장 핵심적인 요소는 바로 타이밍 평가입니다.

  • 일반 회피:
    입력이 발생하면 바로 정해진 시간 동안 무적 상태로 전환되고, 단순한 애니메이션과 이동 처리가 진행됩니다.
    핵심: 일정한 무적 상태와 그에 따른 단순한 애니메이션/물리 효과
  • 저스트 회피:
    입력 시점과 애니메이션 또는 무적 시작 시점 사이의 정확한 타이밍 평가가 추가됩니다. 입력 타이밍이 이상적인 범위 내에 있으면 추가 보너스(예: 추가 무적 시간, 카운터 기회 등)를 제공함으로써 플레이어의 숙련도를 보상합니다.
    핵심: 입력 타이밍의 정밀한 평가 및 그 결과에 따른 보너스 효과

즉, 일반 회피는 일관된 효과(예: 고정 무적 시간)가 중심이라면, 저스트 회피는 정확한 타이밍 판정이 핵심 구성요소라고 볼 수 있습니다.

 

정리하자면, 저스트 회피는(  저스트 액션)은 기존의 회피시스템에서 더 나아가 유저들에게 숙련도에 따라 리턴에대한 재미를 더 줄 수 있는 하이 리스크 하이 리턴 시스템입니다.