[Unity] Knife Hit (3)

suhan0304·2024년 8월 23일

유니티 - Knife Hit

목록 보기
3/9
post-thumbnail

Sprite Editor (w. Polygon)

Target이 산산 조각나는 이펙트를 주고 싶은데, 실제 인 게임 영상을 보니 Target 오브젝트가 단순 원형 스프라이트가 아니라 3D 오브젝트였다.

내가 가지고 있는게 단순 원형 스프라이트라 이거를 3D 오브젝트로 바꾸려고 Blender를 켰지만 이거를 3D 오브젝트로 만드는 시간이 지금까지 프로젝트한 시간이랑 비슷하게 걸릴 것 같은 느낌이 들어서 그냥 2D 오브젝트를 깨지게 하는걸로 구현해보자고 방향을 틀었다.

원형 스프라이트를 쪼개는 것도 문제였는데 Unity Sprite Mode를 Polygon으로 하고 Editor에서 잘 만들어보자.

이런식을 Coustom Outline을 해준 다음에 원하는 모양으로잘라준 후에 저장해주었다.

아래와 같이 잘라줘서 일부러 조금 겹치게 잘라서 최대한 빈틈이 없도록 해주었다.

3개의 스프라이트를 이렇게 얻었다. 이제 Target을 바꿔보자.

근데 이렇게 하니깐 문제점이 flash 이펙트를 각각의 segment마다 줘야되서 그냥 이것도 흰색 스프라이트를 하나 더 얹어서 그 스프라이트의 alpha 값을 조절하는 것으로 해결해보자.

[Button("OnHit")]
public void OnHit() {
    transform.DOPunchPosition(Vector3.right * shakeStrength, shakeDuration, vibrato, randomness);
    FlashWhiteRenderer.DOFade(1f, flashDuration / 2)
        .OnComplete(() => FlashWhiteRenderer.DOFade(0f, flashDuration /2));

    if (GameManager.Instance.RemainKnives == 0) {
        Events.OnAllKnivesOnHit.Invoke();
    }
}

Destruction Effect

이제 오브젝트의 각 Segment가 사방으로 날라가야하는데 Rigidbody를 주고 파괴 시 AddForce를 해주는 것으로 구현해보자.

[TabGroup("Destruction Effect")] public Rigidbody2D[] rigidbodys;

일단 각 Segment에 Rigidbody를 넣어주고 Target.cs에서 참조해준다.

z축으로도 회전을 주고 싶어서 Rigidbody2D가 아니라 Rigidbody를 사용한다.

[Button("DestroyTarget")]
public void DestroyTarget() {
    transform.DOKill();

    ApplyForceToSegments();

    StartCoroutine(DestroyTargetCoroutine());
}

private void ApplyForceToSegments() {
    Vector3 parentPosition = transform.position;

    foreach(GameObject segement in Segements) {
        Rigidbody rb = segement.GetComponent<Rigidbody>();
        if (rb != null) {
            forceMagnitude = Random.Range(5f, 7.5f);
            Vector3 direction = ((segement.transform.position - parentPosition).normalized + Vector3.up * upwardForceMultiplier).normalized;

            Vector3 force = direction * forceMagnitude;

            rb.useGravity = true;
            rb.AddForce(force, ForceMode.Impulse);
        }
    }
}

useGravity로 Rigidbody의 중력 영향을 껐다 켰다 할 수 있다. 부서진 조각들이 바닥을 향해 떨어지기를 원해서 설정해줬다.

이제 좀 테스트 해보면서 적절한 forceMagnitude를 찾아보는 중에 문제가 발생했는데 조각 오브젝트와 부모 오브젝트의 포지션을 사용해서 각기 부모 오브젝트의 중앙으로부터 멀어지도록 구현을 하려고 했는데 자식 오브젝트도 기존의 부모 오브젝트의 스프라이트를 그대로 가져다가 사용하다 보니 position이 그대로여서 방향 벡터가 잘 구해지지 않는다. 고쳐보자.

Custom Pivot을 설정해서 중앙 점을 바꿔서 Position을 사용한 벡터 계산이 정상적으로 수행되게 바꿔보자.

당연히 포지션의 기준점인 Pivot의 위치가 바뀌어서.. 아래와 같이 못생겨지는데 잘 조절해주자.

회전시켜보니깐 z축 회전이 병행되서 너무 종잇장 같은 느낌이 들어서 z축 회전을 제거해줬다. (다시 RigidBody2D로 복귀)

이제 다시 magnitude 값을 조절하면서 적절한 힘을 설정해주면 아래와 같은 효과를 얻을 수 있다. 속도나 힘의 자잘한 수치는 진행하면서 자연스럽게 보이도록 수정해주었다.

완성된 함수 코드는 아래와 같다.

[Button("DestroyTarget")]
public void DestroyTarget() {
    StopCoroutine(rotateCoroutine);

    rotateTween?.Kill();

    ApplyForceToSegments();

    StartCoroutine(DestroyTargetCoroutine());
}

private void ApplyForceToSegments() {
    Vector3 parentPosition = transform.position;

    foreach(GameObject segement in Segements) {
        Rigidbody2D rb = segement.GetComponent<Rigidbody2D>();
        if (rb != null) {
            forceMagnitude = Random.Range(5f, 7.5f);
            Vector3 direction = ((segement.transform.position - parentPosition).normalized + Vector3.up * upwardForceMultiplier).normalized;

            Vector3 force = direction * forceMagnitude;
            
            rb.bodyType = RigidbodyType2D.Dynamic;
            rb.AddForce(force, ForceMode2D.Impulse);

        }       

        float randomTorque = Random.Range(-300f, 300f);
        rb.angularVelocity = randomTorque;
    }
}

IEnumerator DestroyTargetCoroutine() {
    yield return new WaitForSeconds(destructionDelay);
    Destroy(gameObject);
}

여기서 rotateCoroutinerotateTween이라는 변수를 추가적으로 선언해서 해당 코루틴과 DOTween을 변수에 저장해놓고 바로 필요할 때 중지시키도록 구현했다.

private Coroutine rotateCoroutine;
private Tween rotateTween;

UI Icon Bug

Target이 새로 Spawn 되면 기존의 UI Icon들을 모두 파괴해주어야한다.

UI Manager.cs

public void Initialize() {
    foreach (GameObject icon in KnifeIcons) {
        Destroy(icon);
    }
    KnifeIcons.Clear();
}

GameManager.cs

[Button("SpawnTarget")]
public void SpawnTarget() {
    GameObject currentTarget = Instantiate(target);
    RemainKnives = currentTarget.GetComponent<Target>().knivesToDestroy;
    UIManager.Instance.Initialize();
    UIManager.Instance.SpawnKnivesIcon(RemainKnives);
}

profile
Be Honest, Be Harder, Be Stronger

0개의 댓글