Carrot
본문 바로가기
Unity/멋쟁이사자처럼 부트캠프

[멋쟁이사자처럼부트캠프] 유니티 게임 개발 5기(111일차/Final Project) - 부적 연동 & 스포너 로직 수정

by 독기품은토끼 2025. 11. 5.
✅ 오늘의 백로그
1. 천우인 부적 연동
2. 스포너 로직 수정
- 스폰 포인트 지정
- 벽 스폰
3. 귀신 탐지 아이템 멀티 연동

1. 천우인 부적 연동

부적 관련 스크립트는 이미 팀원분께서 구현을 해주셨기에

나는 그 스크립트에 맞게 적용만 해주면 된다.

 

 

스크립트를 살펴보니

외부 함수를 호출하고 있고

 

천우인은 ApplyRalisman 함수가 정의되어 있는 (=IExorcisableByTalisman을 상속받는)

ParanormalPhenomenonBase 의 자식으로 만들었다.

 

 

ParanormalPhenomenonBase 스크립트를 살펴보면

ApplyRalisman 내에서 EAbnormalState.Dead를 호출하고 있고

Update문에서 이상현상의 상태가 Dead면 Disappear() 함수를 호출하고 있다.

 

 

Disappear 함수에서 이상현상을 없애는 로직이 있기 때문에

천우인에 Layer만 달아주면 부적 연동이 끝나지만~!!

 

천우인은 오브젝트풀을 사용하고 있기 때문에

바로 삭제를 해버리면 이미 뿌려놓은 풀이 반납되지 않은채 사라지기 때문에

다음에 다시 스폰될 때 풀 재사용/상태가 꼬일 염려가 있다.

 

protected override void Disappear()
{
    // 스폰 루프 정리
    StopAllCoroutines();

    // 활성 드롭 전부 풀에 반납
    for (int i = _actives.Count - 1; i >= 0; i--)
        Return(i);
    _actives.Clear();

    base.Disappear();
}

 

그래서 천우인 스크립트 내에서 Disappear() 함수를 오버라이드 해주었다!

 

 

천우인도 잘 사라지고 비 떨어지는 것도 잘 없어지는지 확인!

 

2. 스포너 로직 수정

팀장님과 이야기를 해보다가 결정된 사안인데

 

1. 이상현상은 NavMesh에서 소환되는 거 보다 Spawn Point를 지정해서 소환하자

이유 : 별로 무섭지 않은 공간에서 스폰되는 거 방지

 

2. 천우인의 경우 바닥 스폰보다는 벽에 스폰되는 게 좋을 것 같다

이유 : 티가 잘 안남

 

나는 솔직히 1번의 경우 갑자기 퐉 나타나는 게 더 무서울 거라 생각하고 이렇게 구현했던 건데

확실히 맵의 구석이나 플레이어가 자주 가지 않는 곳에 스폰이 되어버리면 애써 만든게 말짱도루묵이니까 수정 필요성을 느꼈다.

 

1. 스폰 포인트 지정

[SerializeField] Transform[] spawnPoints; // 스폰 포인트
[SerializeField] float safeRadius = 1.0f; // 자리 비었는지 검사할 반경
[SerializeField] LayerMask occupyMask; // Player/Ghost/Obstacle/Abnormal 등 점유 판정용 레이어

 

우선 스폰 포인터를 담을 Transform 리스트를 만들어주고

혹시라도 플레이어나 귀신, 다른 이상현상이 해당 스폰 포인트 위에 있을 때

겹침 현상을 없애주기 위해서 Layer도 체크해주었다.

 

bool AbnormalSetPosition(out Vector3 pos)
{
    pos = default;

    if (spawnPoints == null || spawnPoints.Length == 0)
        return false; // 스폰포인트가 하나도 없으면 실패

    // 유효한 위치 나올 때까지 재시도
    for (int t = 0; t < Mathf.Max(1, navSampleMaxTries); t++)
    {
        var sp = spawnPoints[Random.Range(0, spawnPoints.Length)];
        if (!sp) continue;

        // 스폰포인트 위치를 NavMesh에 스냅
        if (!NavMesh.SamplePosition(sp.position, out var hit, 3f, NavMesh.AllAreas))
            continue;

        var candidate = hit.position;

        // 자리 점유 검사: 비어있을 때만 통과
        var hits = Physics.OverlapSphere(candidate, safeRadius, occupyMask, QueryTriggerInteraction.Ignore);
        if (hits != null && hits.Length > 0)
        {
            // 스폰 취소 로그 (누가 막았는지 함께 출력)
            for (int i = 0; i < hits.Length; i++)
            {
                var c = hits[i];
                Debug.Log($"[AbnormalSpawner] {c.name}로 인해 스폰 실패");
            }
            continue;
        }

        // 스폰 성공 직전 로그
        Debug.Log($"[AbnormalSpawner] 스폰 완료");
        pos = candidate;
        return true;
    }

    return false; // 유효 좌표 못 찾음
}

 

스폰 포인트를 랜덤으로 갖고와서

그 포인트에 Layer 대상이 점유하고 있는지 체크하고

있으면 다른 스폰 포인트를 찾고 없으면 그 자리에 스폰하는 로직을 만들어주었다.

 

Physics.OverlapSphere(검사 위치, 검사 반경, 레이어 감지, 트리거 취급 방식)

트리거 취급 방식에서 Ignore은 일반 콜라이더를 감지하고

만약 is Trigger가 true인 오브젝트까지 감지하길 원한다면 Collide로 변경해주면 된다.

 

 

정상적으로 작동되는 것을 확인!

 

2. 천우인 벽에 스폰

하아 이부분이 참 난감한데..

여태 만들어온 스폰매니저는 천우인 뿐만 아니라 다른 이상현상들까지 같이 관리하기 때문에

천우인만 스크립트를 따로 빼서 스폰하는 건 Solid 원칙 위반이라 생각된다..

 

그래서

Abnormal Data에 Ground용 / Wall 용 타입을 넣어주어서

Ground일 때는 바닥에 스폰되게 하고

Wall일 때에는 벽에 스폰되도록 로직을 바꿔주려고 한다.

 

그러면 두가지 방법이 있는데

  1. wall 전용 spawn point도 만들어주기
  2. 모든 벽에는 wall 태그를 붙이고 그 wall 태그가 붙은 정 중앙에 스폰되도록 하기

만약 벽 앞에 액자나 거울같은 오브젝트들이 있을 때에는

또 Physics 함수를 호출해서 처리하면 되지 않을까..? 생각든다..

 

using UnityEngine;

[CreateAssetMenu(menuName = "Abnormal/AbnormalData")]
public class AbnormalData : ScriptableObject
{
    public EAbnormal type;
    public GameObject prefab;
    public bool spawnWall = false; // true면 벽 스폰
}

 

우선 bool 값에 따라 바닥/벽에 스폰하도록 Abnormal Data를 세팅해주었고

 

2번 방법으로 구현을 해보려고 했으나!

내 생각엔 벽 외벽, 내벽도 구분해주어야 외벽에 스폰되는 걸 막고

벽은 또 동서남북 모든 위치에 존재할테니까 이 값에 따라서 천우인의 Rotation 값을 수정해야하지 않을까? 라는 생각이 들었다.

 

 

이걸 어캐 계산하지?

 

 

 

[멋쟁이사자처럼부트캠프] 유니티 게임 개발 5기(81일차) - [팀플] Animator Mirror 활용 및 표정 변화

1. 이벤트 애니메이션 (오목판 던지기)캐릭터 커스터마이즈는 구현이 끝났고팀원들과 이야기하다보니 이벤트 발생시 발동할 애니메이션 제작이 필요하다는 의견이 나오게 되었다. 그부분을 내

toxicbunny.tistory.com

 

저번 팀플 때 플레이어 위치에 따라 팔 뻗는 방향을 다르게 해준 적이 있었는데

이때 좀 많이 헤맸던 기억이 있어서 이 방법에 정이 안 간다...

 

 

그래서~! 어차피 스폰 포인트를 만들어주니까

스폰 포인트의 앞 방향을 벽쪽으로 해서

벽으로 레이캐스트 쏴서 벽이 있으믄 거기다 스폰을 하게 하는.. 그런식으로!!! 구현을 해주려고 한다.

 

몰라 ㅠㅠ 이게 정답이 아닐 수 있음 근데 나는 이렇게밖에 생각이 안남!! 우액!!

 

if (!data.spawnWall)
{
    // Ground 스폰 ...
}
else
{
    // Wall 스폰
    if (!Physics.Raycast(sp.position, sp.forward, out var hit, wallRayDistance, wallMask, QueryTriggerInteraction.Ignore))
        continue;

    // 벽 표면에서 살짝 띄워 붙이기
    var candidate = hit.point + hit.normal * surfaceOffset;

    // 벽 앞 액자/거울/플레이어 등 간섭물 확인
    var hits = Physics.OverlapSphere(candidate, safeRadius, occupyMask, QueryTriggerInteraction.Collide);
    if (hits != null && hits.Length > 0)
    {
        // 디버그(선택)
        // foreach (var c in hits) Debug.Log($"[AbnormalSpawner] Wall 차단: {c.name}");
        continue;
    }

    pos = candidate;
    return true;
}

 

 

레이를 쏴서 벽 오브젝트가 있으면 벽 쪽에 스폰되는 것을 확인하였으나 몇가지 문제가 있었다.

 

1. 천우인 Rotation이 벽에 등지고 부착되지 않고 있다는 점

2. 벽 앞에 장애물이 있으면 OverlapSphere 점유 자리 체크 과정에서 계속 저 장애물과 겹쳐서 결국 스폰이 안됨 (저게 왜 됐는지도 모르겠음ㅋㅋㅋㅋ)

 

else
{
    // 여러 방향으로 레이 쏘기
    Vector3[] dirs =
    {
        sp.forward, -sp.forward, sp.right, -sp.right,
        (sp.forward + sp.right).normalized, (sp.forward - sp.right).normalized
    };

    // Wall 스폰
    foreach (var dir in dirs)
    {
        if (!Physics.Raycast(sp.position, dir, out var hit, wallRayDistance, wallMask, QueryTriggerInteraction.Ignore))
            continue;

        // 벽 표면에서 살짝 띄워 붙이기
        var candidate = hit.point + hit.normal * surfaceOffset;

        // 벽 앞 액자/거울/플레이어 등 간섭물 확인
        var hits = Physics.OverlapSphere(candidate, safeRadius, occupyMask, QueryTriggerInteraction.Ignore);
        if (hits == null || hits.Length == 0)
        {
            pos = candidate;
            return true;
        }
    }
}

 

우선 2번 문제부터 해결해주었다.

ray를 forword 방향으로만 쏘던걸 모든 방향 다 쏘도록 바꿔주었다

이러면 나중에 스폰포인트 위치시킬 때도 방향을 안맞춰줘도 돼서 오히려 잘 됐다!

 

이제 1번이 문제인데..

회전값을 넘겨서 스폰해주면 천우인 말고도 흑기, 어둑시니도 회전돼서 스폰될텐데 이를 어쩐담 ㅠ_ㅠ

아 어차피 else문으로 처리하고 있으니까 wall 타입인 경우에만 회전값을 넘겨주면 되려나?

동서남북은 어캐 따지지 😭

백문이불여일견 일단 시도해보겠다 ㅎ..ㅠㅠ

 

bool AbnormalSetPosition(AbnormalData data, out Vector3 pos, out Quaternion rot) // 파라미터 추가
{
    pos = default;
    rot = Quaternion.identity; // 추가

    // ... 생략
        if (!data.spawnWall)
        {
            // Ground 스폰 ...
            rot = Quaternion.identity; // 추가
        }
        else
        {
            // 여러 방향으로 레이 쏘기
            Vector3[] dirs =
            {
                sp.forward, -sp.forward, sp.right, -sp.right,
                (sp.forward + sp.right).normalized, (sp.forward - sp.right).normalized
            };
            // Wall 스폰
            foreach (var dir in dirs)
            {
                // 생략..
                candidate.y += 2f; // 벽 중앙에 위치하도록 y값 수정

                // 벽 앞 액자/거울/플레이어 등 간섭물 확인
                var hits = Physics.OverlapSphere(candidate, safeRadius, occupyMask, QueryTriggerInteraction.Ignore);
                if (hits == null || hits.Length == 0)
                {
                    pos = candidate;
                    rot = Quaternion.LookRotation(-hit.normal, Vector3.up); // 벽을 등지도록 회전
                    return true;
                }
            }
        }
    }
    return false; // 유효 좌표 못 찾음
}

 

회전값은 Ground 스폰용은 그냥 기본값으로 넘겨주었고

Wall 스폰용만 내가 값을 넣어서 넘겨주었다.

 

다행히 동서남북은 Ray를 쏴서 위치를 맞춰주고 있었던 덕분에 생각보다 구현이 어렵지 않았다.

 

Physics.Raycast가 무언가에 맞으면 RaycastHit hit 안에 정보가 들어오고

그 중 normal이라는 값이 있는데

이 값이 표면에 수직인(World-space) 단위 벡터이다.

 

따라서

  • 바닥을 향해 쏘면 hit.normal ≈ (0,1,0)
  • 동쪽 벽을 맞추면 hit.normal ≈ (1,0,0)
  • 북쪽 벽 ≈ (0,0,1)
  • 서쪽 벽 ≈ (-1,0,0)

이런식으로 표면이 바라보는 바깥 방향을 가르키는 정보가 있었다! 그래서 이 값을 기준으로 회전값을 주면 벽을 등지고 스폰될 수 있도록 할 수 있다는 뜻!

 

그리고 벽 중간에 스폰될 수 있도록 y값도 넣어주었다.

 

 

 

우히히 이제 벽이 어느 방향에 있던 천우인이 벽을 등지고 스폰된다~!

 

 

멀티도 동일하게 벽에 스폰되는지 확인해줬다

(부적은 다른 팀원분 담당이라! 아직 멀티가 구현되지 않았다)

 

3. 탐지 아이템 멀티 연동

 

[멋쟁이사자처럼부트캠프] 유니티 게임 개발 5기(104일차/Final Project) - 책 넘기는 연출 / 귀신 아이

 

toxicbunny.tistory.com

 

이 날 만들었던 탐지 아이템들을 모두 이제 멀티에서도 동작되도록 구현해주어야 한다..

 

1. 씬 통합

우선 최종 씬에서 제대로 동작되는지 확인해야하니까

기존 씬에서의 테스트는 그만하고

최종씬에 내가 작업한 내용들을 통합해주려고 한다.

 

테스트 할 것들
1. 무당방울 > Ghost Item (ex. 천우인) > 소리 연동되는지 체크
2. 꽃신 > 씬에 설치 > 움직임&소리 체크
3. 복숭아 나무가지 > 씬에 설치 > 사라지는지 체크
4. 가이드북 체크
5. 이상현상 > 씬에 소환 > 천우인 = 비 내리기 > 멀티 체크

 

 

우선 다른 분들이 씬을 복제하실 수 있응게

이상현상부터 씬에 넣어주었다.

 

탐지 아이템은 오피스 씬에서부터 들고가기 때문에.. 이 씬에 당장 없어도 되기 때문!!

 

1. 기본 동작 확인

이제 무당 방울을 멀티로 연동해주겠다.

 

근데 멀티가 아니더라도 동작을 해야하는데 왜 갑자기 전에 다 구현된게 동작을 안하지..?ㅠㅠㅠ

내 씬에서는 잘 되는데

왜 통합 씬에서는 안되지..? 무당 방울 말고도 지금 꽃신도 동작을 안하고

복숭아나무 가지는 왜인지 자꾸 땅 끝으로 떨어지고.. 이상하다..

계속 해결해주려고 잡고있는데 해결이 되지 않는다ㅠㅠ

원인이 뭔지 좀 더 찾아봐야겠다..