Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

1vdlrwnsv1 님의 블로그

유니티 슬롯머신 개발 중 문제 & 보스 문제 수정 본문

Unity

유니티 슬롯머신 개발 중 문제 & 보스 문제 수정

1vdlrwnsv1 2025. 2. 28. 21:13

슬롯머신 개발 일지

1. 슬롯머신 기능 구현

  1. 슬롯이 돌아감
  2. 일정 시간이 지나면 멈춤
  3. 멈춘 결과를 저장하고 버튼을 활성화
  4. 사용자가 슬롯을 선택하면 해당 결과를 적용

2. NullReferenceException 문제

발생 원인

  • OnSlotButtonClick에서 sprite.name을 가져오려고 했는데, sprite가 null이라서 에러 발생.
  • SlowDown에서 finalSprite를 OnSlotButtonClick으로 넘길 때 null이 전달된 것 같음.

해결 방법

  • Sprite 객체를 넘기는 대신, SkillSprite 리스트의 인덱스를 넘기도록 수정.
  • 버튼 클릭 시 인덱스를 이용해 SkillSprite에서 직접 Sprite를 가져오도록 변경.
void OnSlotButtonClick(int slotIndex, int spriteIndex)
{
    if (spriteIndex < 0 || spriteIndex >= SkillSprite.Length)
    {
        Debug.LogError($"잘못된 인덱스: {spriteIndex}");
        return;
    }
    
    Sprite sprite = SkillSprite[spriteIndex];
    string upgradeMessage = "";

    switch (spriteIndex)
    {
        case 0:
            SkillManager.Instance.ChangeNumberOfBackwardProjectiles(1);
            upgradeMessage = "후방 발사 화살이 1개 증가했습니다!";
            break;
        case 1:
            SkillManager.Instance.HealPlayer(10);
            upgradeMessage = "플레이어의 체력이 10 회복되었습니다!";
            break;
        case 2:
            SkillManager.Instance.ChangeMaxHealth(10);
            upgradeMessage = "플레이어의 최대 체력이 10 증가했습니다!";
            break;
        case 3:
            SkillManager.Instance.ChangeNumberOfForwardProjectiles(1);
            upgradeMessage = "전방 발사 화살이 1개 증가했습니다!";
            break;
        case 4:
            SkillManager.Instance.ChangePower(1);
            upgradeMessage = "화살의 공격력이 1 증가했습니다!";
            break;
        default:
            upgradeMessage = "잘못된 선택입니다.";
            break;
    }
    
    upgradeTxt.text = upgradeMessage;
    upgradeFrame.SetActive(true);
    DisableOtherSlots(slotIndex);
}

3. 슬롯이 멈춘 후 버튼에 이벤트 추가

SlowDown 코루틴에서 슬롯이 멈추면 slotFinalResults에 결과를 저장하고 버튼 클릭 시 실행할 이벤트를 설정하도록 수정.

IEnumerator SlowDown(int slotIndex, GameObject slotObject)
{
    float currentY = slotObject.transform.localPosition.y;
    float targetY = Mathf.Round(currentY / 100) * 100;
    targetY = Mathf.Clamp(targetY, 0, 300);

    while (Mathf.Abs(slotObject.transform.localPosition.y - targetY) > 1f)
    {
        slotObject.transform.localPosition = Vector3.Lerp(
            slotObject.transform.localPosition,
            new Vector3(slotObject.transform.localPosition.x, targetY, 0),
            0.2f);
        yield return new WaitForSeconds(0.05f);
    }
    slotObject.transform.localPosition = new Vector3(slotObject.transform.localPosition.x, targetY, 0);

    int resultIndex = Mathf.RoundToInt(targetY / 100) % ItemCnt;
    int spriteIndex = StartList[resultIndex];

    slotFinalResults[slotIndex] = spriteIndex;

    Slot[slotIndex].onClick.RemoveAllListeners();
    Slot[slotIndex].onClick.AddListener(() => OnSlotButtonClick(slotIndex, spriteIndex));
    Slot[slotIndex].interactable = true;
}

 

EnemyManager 몬스터 리스트 0이면 ClearStage 호출

EnemyManager에서 몬스터가 전부 죽으면 클리어 판정 내리려고 했는데 안 됨. 확인해 보니 monsterList에서 몬스터를 삭제하는 코드가 없었음.

수정 코드

public void RemoveEnemyOnDeath(GameObject enemy)
{
    monsterList.Remove(enemy);
    if (monsterList.Count == 0)
    {
        isClear = true;
        skillUpSlot.SetActive(true);
    }
}

 이제 몬스터 다 죽으면 skillUpSlot 활성화됨.


보스가 밀리는 문제

보스를 Rigidbody2D Kinematic으로 해도 밀림. isKinematic = true 하면 물리 영향 안 받을 줄 알았는데 그래도 밀림. MovePosition으로 이동하는 게 문제였음. transform.position 직접 수정하니까 해결.

수정 코드

void FixedUpdate()
{
    transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.fixedDeltaTime);
}

ㅇㅋ 이제 안 밀림.


장애물 피해서 이동

A* 알고리즘 쓰기엔 너무 오버임. NavMeshAgent는 2D에서 지원 안 함. 그냥 Raycast 써서 길막 체크하고 우회하도록 구현.

예제 코드

RaycastHit2D hit = Physics2D.Raycast(transform.position, moveDirection, checkDistance, obstacleLayer);
if (hit.collider != null)
{
    // 장애물 있음 -> 방향 바꿈
    moveDirection = Vector2.Perpendicular(moveDirection);
}
transform.position += (Vector3)moveDirection * speed * Time.deltaTime;

이제 벽에 안 걸리고 돌아감.


포탈 진입 시 스킬 업 UI 활성화

플레이어가 포탈에 들어가면 enemyManager.monsterList.Count 확인해서 몬스터 다 죽었으면 스킬 업 UI 띄우게 했는데, NullReferenceException 터짐. EnemyManager.Instance가 초기화 안 된 상태에서 호출돼서 그런 듯.

수정 코드

private void Start()
{
    gameManager = GameManager.Instance;
    enemyManager = FindObjectOfType<EnemyManager>();
}

이제 문제 없음.


버튼 클릭 시 포탈 이동

포탈에서 버튼 눌렀을 때 PortalContoroll() 실행 안 됨. _potal이 null이었음. FindObjectOfType<PotalController>()로 초기화해줌.

수정 코드

private void Start()
{
    _potal = FindObjectOfType<PotalController>();
}

딱알았다. 이제 버튼 누르면 포탈 이동됨.

1. 보스 체력 감소 기능 구현

  • 보스 캐릭터에 currBossHp 추가. ResourceController에서 실시간으로 체력을 받아올 수 있도록 수정함.
  • ChangeHealth 메서드를 사용하여 마우스 클릭 시 체력이 10 감소하도록 함.
    • if (Input.GetMouseButtonDown(0)) { _resourceController.ChangeHealth(-10); }로 구현.

2. 보스 AI - 이동 및 공격

  • 보스가 플레이어를 추적하는 FollowPlayer 메서드 추가.
  • 플레이어와의 거리가 3m 이하일 경우 공격 상태로 변경, 공격 이펙트 활성화. DelayedAttackStart와 AttackPlayerRepeatedly 코루틴으로 공격 딜레이와 반복 공격 구현.

3. 보스 HP 바 및 UI 업데이트

  • 보스 체력바 UI 추가. 체력 수치가 실시간으로 업데이트되고 애니메이션이 적용되도록 수정.
    • fillHealthBar.fillAmount와 healthText.text로 체력바와 체력 수치를 UI에 반영.

4. ResourceController 추상화

  • ResourceController 클래스를 추상 클래스로 리팩토링.
  • 기본적인 체력 변화 로직을 CalculateCalChangeHealth 메서드로 분리. Death() 메서드 추가하여 죽음 처리.
  • 각 캐릭터에서 ChangeHealth 호출 시 공통적인 로직을 처리하도록 설정. 

 

내가 공부한 내용 복붙해서 지피티한테 요약 맡겼는데 이 꼴 남 다음부터 그냥 내가 쓰는게 낫겠다


 

'Unity' 카테고리의 다른 글

유니티 체력바 만들기, 보스 만들기  (0) 2025.02.25