카테고리 없음

2025.05.14 6주차 수요일

mochoa 2025. 5. 14. 21:13

오늘의 학습 키워드

1. 필수과제 진행

 

오늘은 튜토리얼 만들었다.

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

public enum TutorialStep
{
    Jump,
    Slide,
    CollectCoin,
    TakeDamage,
    Complete
}

public class TutorialManager : MonoBehaviour
{

    public bool IsStepLocked { get; private set; } = true;

    public static TutorialManager Instance;

    public TutorialStep currentStep;

    private void Awake()
    {
        if (Instance == null) Instance = this;
        else Destroy(gameObject);
    }

    private void Start()
    {
        Time.timeScale = 1f; // 게임 흐름은 정상 유지
        ShowCurrentStep(); // 시작 시 첫 말풍선 출력
    }

    public void AdvanceStep()
    {
        currentStep++;
        IsStepLocked = true;
        ShowCurrentStep();
        if (currentStep == TutorialStep.Complete)
        {
            OnTutorialComplete();
        }
    }

    public bool IsCurrentStep(TutorialStep step)
    {
        return currentStep == step;
    }
    public void UnlockStep()
    {
        IsStepLocked = false;
        Time.timeScale = 1f; // 게임 다시 재생
    }
    public void ShowCurrentStep()
    {
        Debug.Log("튜토리얼 다음 단계로: " + currentStep);
        TutorialUI.Instance?.ShowStep(currentStep);
    }
    private void OnTutorialComplete()
    {
        GameManager.Instance.PauseGame();
        TutorialUI.Instance?.gameObject.SetActive(false); // 말풍선 숨김
        FindObjectOfType<UIManager>().SetGameOver();       // GameOverUI 표시
    }
}

 

이런식으로 만들었다.

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

public class PlayerController : Unit
{
    private TutorialManager tutorialManager;

    private bool isGodMode = false;
    private float godModeTimer = 0f;
    private bool isDead = false;

    [SerializeField] private int maxJumpCount = 2; // 최대 점프 횟수
    private int currentJumpCount = 0;

    public float hitDamage = 20f; // 충돌 시 감소할 체력
    public float damageByTime = 5f; //시간에 따른 감소 체력
    private float limitPosY = -20f;
    private bool wasGroundedLastFrame = false;

    public float CurrentHp => Hp;

    private float defaultSpeed;
    private Coroutine speedCoroutine;

    private void Fall()
    {
        if (transform.position.y < limitPosY)
        {
            Hp = 0f;
            isDead = true;
            GameManager.Instance.GameOver();
        }
    }

    protected override float Hp
    {
        get { return hp; }
        set
        {
            hp = Mathf.Clamp(value, 0, fullHP);
        }
    }

    protected override float JumpForce
    {
        get { return jumpForce; }
        set
        {
            jumpForce = Mathf.Max(0, value);
        }
    }

    public override float Speed
    {
        get { return speed; }
        set
        {
            speed = Mathf.Max(0, value);
        }
    }

    private void Awake()
    {
        base.Awake();
        Hp = 100f;
        JumpForce = 6f;
        Speed = 4f;
        defaultSpeed = Speed;

        tutorialManager = FindObjectOfType<TutorialManager>();
    }

    private void Update()
    {
        DecreaseHpByTime();
        Fall();     //낙하 게임오버

        //땅에 오브젝트가 닿았는지 확인
        isGrounded = Physics2D.Raycast(transform.position, Vector2.down, groundRayLength, groundLayer);
        Debug.DrawRay(transform.position, Vector2.down * groundRayLength, Color.red);

        if (isGrounded && !wasGroundedLastFrame)
        {
            currentJumpCount = 0;
        }
        wasGroundedLastFrame = isGrounded;
        //점프
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // 튜토리얼 단계 확인
                Jump();
                if (tutorialManager != null && tutorialManager.IsCurrentStep(TutorialStep.Jump))
                {
                    tutorialManager.AdvanceStep();
                }
            
        }

        animCtrl.JumpAnim(isGrounded);

        //슬라이드
        // 현재 LeftShift 키 상태 체크
        PressedShift = Input.GetKey(KeyCode.LeftShift) || GameUI.Instance?.IsSlidePressed() == true;
        Slide(PressedShift);

        if (PressedShift && tutorialManager != null && tutorialManager.IsCurrentStep(TutorialStep.Slide))
        {
            tutorialManager.AdvanceStep();
        }



        // 무적 시간 감소
        if (isGodMode)
        {
            godModeTimer -= Time.deltaTime;
            if (godModeTimer <= 0f)
            {
                isGodMode = false;
                GetComponentInChildren<SpriteRenderer>().color = Color.white; // 색 복원
            }
        }
    }
    private void FixedUpdate()
    {
        Move();
    }

    public override void Move()
    {
        tr.Translate(Vector3.right * Speed * Time.fixedDeltaTime, Space.World);
    }

    public override void Jump()
    {
        if (currentJumpCount >= maxJumpCount)
        {
            Debug.Log("점프 불가: currentJumpCount = " + currentJumpCount);
            return;
        }

        SFXManager.Instance.JumpSFX();
        rb.velocity = new Vector2(rb.velocity.x, 0f);
        rb.AddForce(Vector2.up * JumpForce, ForceMode2D.Impulse);
        currentJumpCount++;

        Debug.Log("점프! currentJumpCount = " + currentJumpCount);
    }

    public override void Slide(bool PressedShift)
    {
        animCtrl.SlideAnim(PressedShift);
    }

    public void ActivateGodMode(float duration)
    {
        isGodMode = true;
        godModeTimer = duration;
        StartCoroutine(FlashWhileInvincible());
    }

    private IEnumerator FlashWhileInvincible()
    {
        SpriteRenderer sr = GetComponentInChildren<SpriteRenderer>();
        while (isGodMode)
        {
            sr.color = new Color(1, 1, 1, 0.5f); // 반투명
            yield return new WaitForSeconds(0.1f);
            sr.color = Color.white;
            yield return new WaitForSeconds(0.1f);
        }
    }
    public override void DecreaseHpByTime()
    {
        //시간에 따른 체력 감소
        Hp -= damageByTime * Time.deltaTime;
        
    }
    public void Heal(float amount)
    {
        if (100f - Hp < amount)
        {
            Hp += amount - (100f - Hp);
        }
        else
        {
            Hp += amount;
        }

    }

    public void SpeedChangeTemporary(float amount, float duration = 2f)
    {
        if (speedCoroutine != null)
            StopCoroutine(speedCoroutine);

        speedCoroutine = StartCoroutine(TemporarySpeedChange(amount, duration));
    }

    private IEnumerator TemporarySpeedChange(float amount, float duration)
    {
        Speed = defaultSpeed + amount;

        yield return new WaitForSeconds(duration);

        Speed = defaultSpeed;
    }

    public void Damaged(float amount)
    {
        if (isGodMode || isDead) return;

        Hp -= amount;

        if (Hp <= 0)
        {
            Hp = 0;
            isDead = true;
            GameManager.Instance.GameOver();
            return;
        }

        ActivateGodMode(GameManager.Instance.playerGodMode);

        // 튜토리얼 단계 처리 추가
        if (TutorialManager.Instance != null &&
            TutorialManager.Instance.IsCurrentStep(TutorialStep.TakeDamage))
        {
            TutorialManager.Instance.AdvanceStep();
        }

        Debug.Log($"체력: {Hp} (무적 모드 활성화됨)");
    }

}

 

그리고 여기에서 튜토리얼 때만, 작동하도록 튜토리얼 단계 처리를 추가해주면 끝이다.

 

그리고 이제

    public void ApplyEffect(PlayerController player)
    {
        player.Damaged(_data.value);
    }

ObstarcleObject.cs 안에있는 이부분이 적용이 안돼었었는데

data 필드를 선언해줘서 됐다.

 private GroundObjectData data;
 private GroundObjectData _data
 {
     get { return data; }
     set { data = value; }
 }

 

이런식으로