오늘 학습 키워드

최종 팀 프로젝트

오늘 학습 한 내용을 나만의 언어로 정리하기

튜터님의 피드백

  • 점프 어색함
  • 벽점은 더 어색함
  • HandleInput 중복 너무 많음. 상위로 올리던지 하셈
    • enum flag를 써보셈

피드백대로 고쳐보기

1. 점프 어색한 문제

  • 좀 주관적이라서 어떻게 해야할 지 모르겠음.
  • 그래서 기획이랑 좀 더 얘기를 해봤음
  • 바닥이 너무 잡아 끄는 느낌이라고 했음. 그래서 그래비티를 조절해봄
  • 원래 중력 배수 4였던거 3으로 줄임
  • 점프 힘힘 조절함

2. 벽점 어색한 문제

  • 이건 인정
  • 지금 물리 처리 안하고 그냥 애니메이션 커브 써서 하고있어서 그런듯
  1. 일단 물리를 쓰던 이전 버전으로 돌리자
public override void LogicUpdate(PlayerController controller)  
{  
    animRunningTime += Time.deltaTime;  
      
    t = animRunningTime / wallJumpAnimationLength;  
      
    newPos = Vector2.Lerp(startPos, targetPos, t);  
      
    curPos = controller.transform.position;  
      
      
    // 현재 위치에서 이동할 위치만큼 선 하나 그어서, 그게 벽에 닿으면 벽 끝에까지만 가고 상태 바뀌게함  
    Vector2 direction = (newPos - curPos).normalized;  
    float distance = Vector2.Distance(curPos, newPos);  
      
    RaycastHit2D hit =  
        Physics2D.Raycast(controller.transform.position, direction, distance, controller.Move.groundMask);  
      
    if (hit.collider != null)  
    {  
        controller.Move.rb.MovePosition(hit.point - direction * 0.01f);  
        if (controller.Move.isGrounded) controller.ChangeState<IdleState>();  
        else controller.ChangeState<FallState>();  
        return;  
    }  
      
      
    controller.Move.rb.MovePosition(newPos);  
  
      
    if (Vector2.Distance(newPos, targetPos) < 0.01f)  
    {  
        if (controller.Move.isGrounded)  
        {  
            controller.ChangeState<IdleState>();  
            return;  
        }  
        controller.ChangeState<FallState>();  
          
    }    
      
    if (controller.Move.isGrounded)  
    {  
        controller.ChangeState<IdleState>();  
        return;  
    }  
      
}
  • 이랬더니 너무 갑자기 크게 움직임
  1. Lerp 대신 SmoothDamp를 써보자
  • SmoothDamp : current 에서 target까지 부드럽게 움직임. smoothTime동안 목적지에 (거의?) 도착하도록 처리함. (완전히 정확한 시간은 아님)
  • 참고 자료
  • 해봤는데 무한하게 계속 이동함…
  1. 원점으로 돌아가서, rigidbody의 velocity를 조정하자
public void WallJump()  
{  
    Vector2 jumpDir = new Vector2(lastWallIsLeft ? 1 : -1, 1).normalized;  
      
    float jumpPower = 20f;  
    float jumpSidePower = 12f;  
      
    rb.velocity = Vector2.zero;  
  
      
    // AddForce로 점프 적용  
    /*rb.AddForce(new Vector2(jumpDir.x * jumpSidePower, jumpDir.y * jumpPower), ForceMode2D.Impulse);*/  
    rb.velocity = new Vector2(  
        (lastWallIsLeft ? 1 : -1) * jumpSidePower,  
        jumpPower  
    );  
}
 
  • 현재까지 스테이지에서 벽을 활용한 기믹이 없었을 뿐더러 벽 점프에 필요 이상으로 시간이 소요되고 있었다는 점, 그리고 벽 점프 때문에 중력 관련 값을 조정하는데도 시간이 걸린다는 점 때문에 삭제하기로 함
  • 아쉬우니까 아카이빙
// WallJumpState.cs
public class WallJumpState : AirSubState  
{  
    private float wallJumpStartTime;  
    private float wallHoldAbleTime = 0.5f;  
    private float startFallTime = 0.2f;  
      
    private float animRunningTime = 0f;  
    private float wallJumpAnimationLength;  
    private float wallJumpSpeed = 5f;  
    private float wallJumpDistance = 5f;  
      
    private Vector2 wallJumpDirection;  
    private Vector2 targetPos;  
    private Vector2 newPos;  
    private Vector2 curPos;  
  
    private Vector2 startPos;  
    private Vector2 nextPos;  
    /*private float wallJumpDirection;*/  
    private float wallJumpMoveX = 8f;  
    private float wallJumpMoveY = 3f;  
  
    private Vector2 smoothSpeed;  
    private float smoothTime = 0.1f;  
      
    private float t;  
  
    public override void Enter(PlayerController controller)  
    {  
        if (!controller.Condition.TryUseStamina(controller.Data.wallJumpStamina))  
        {  
            if (controller.Move.isGrounded)  
            {  
                controller.ChangeState<IdleState>();  
                return;  
            }  
            else  
            {  
                controller.ChangeState<FallState>();  
                return;  
            }  
        }  
        base.Enter(controller);  
        // 벽점할 때에는 벽 반대방향 봐야됨  
        controller.Move.ForceLook(!controller.Move.lastWallIsLeft);  
        controller.isLookLocked = true;  
        // 벽점했으니까 강제로 벽 터치 취소  
        controller.Animator.ClearBool(); // WallHold 끄려고  
        controller.Move.rb.velocity = Vector2.zero;  
        controller.Move.isWallTouched = false;  
        controller.Animator.SetTriggerAnimation(AnimatorHash.PlayerAnimation.WallJump);  
        controller.Move.isWallJumped = true;  
          
        wallJumpStartTime = Time.time;  
          
        animRunningTime = 0f;  
        wallJumpAnimationLength =  
            controller.Animator.animator.runtimeAnimatorController  
                .animationClips.First(c => c.name == "StartWallJump").length  
            + controller.Animator.animator.runtimeAnimatorController  
                .animationClips.First(c => c.name == "WhileWallJump").length * 2f;  
          
        wallJumpDirection = new Vector2((controller.Move.lastWallIsLeft ?  1.5f : -1.5f),1f).normalized;  
          
        startPos = controller.transform.position;  
        targetPos = startPos + (wallJumpDirection * 2f);  
        // 시도하려는 것 : 포물선 운동을 강제로 만들기  
        controller.Move.wallJumpStartY = startPos.y;  
  
  
        controller.Move.WallJump();  
        /*wallJumpDirection = controller.Move.lastWallIsLeft ? 1 : -1;*/  
    }  
  
    public override void HandleInput(PlayerController controller)   
    {  
  
        var moveInputs = controller.Inputs.Player.Move.ReadValue<Vector2>();  
  
        if (controller.Inputs.Player.Jump.triggered && !controller.Move.isDoubleJump)  
        {  
            controller.ChangeState<DoubleJumpState>();  
            return;  
        }  
  
        if(Time.time - wallJumpStartTime > wallHoldAbleTime && controller.Move.isWallTouched)  
        {  
            controller.ChangeState<WallHoldState>();  
            return;  
        }  
        else  
        {  
            controller.Move.isWallTouched = false;  
        }  
          
        if (controller.Inputs.Player.NormalAttack.triggered && moveInputs.y < 0)  
        {  
            controller.isLookLocked = true;  
            controller.ChangeState<DownAttackState>();  
            return;  
        }  
          
        if (controller.Inputs.Player.NormalAttack.triggered && !controller.Attack.HasJumpAttack)  
        {  
            controller.isLookLocked = true;  
            controller.ChangeState<NormalJumpAttackState>();  
            return;  
        }  
          
        if (controller.Inputs.Player.SpecialAttack.triggered)  
        {  
            controller.isLookLocked = false;  
            controller.ChangeState<SpecialAttackState>();  
            return;  
        }  
        if (controller.Inputs.Player.Dodge.triggered)  
        {  
            controller.ChangeState<DodgeState>();  
            return;  
        }  
        if (controller.Inputs.Player.AdditionalAttack.triggered)  
        {  
            controller.ChangeState<AdditionalAttackState>();  
            return;  
        }  
          
          
  
    }  
  
    public override void LogicUpdate(PlayerController controller)  
    {  
          
        curPos = controller.transform.position;  
          
        if (controller.Move.rb.velocity.y <= 0 || Vector2.Distance(startPos, curPos) >= wallJumpDistance)  
        {  
            Debug.Log("[Wall Jump] 플레이어가 낙하 중 or 초기 위치와 멀어짐");  
            if (controller.Move.isGrounded) controller.ChangeState<IdleState>();  
            else controller.ChangeState<FallState>();  
            return;  
        }  
  
        if (controller.Move.isWallTouched)  
        {  
            controller.ChangeState<FallState>();  
            return;  
        }  
          
        /*animRunningTime += Time.deltaTime;  
                t = animRunningTime / wallJumpAnimationLength;  
                /*newPos = Vector2.Lerp(startPos, targetPos, t);#1#  
                newPos = Vector2.SmoothDamp(  
            controller.transform.position,            targetPos,            ref smoothSpeed,            smoothTime        );                curPos = controller.transform.position;  
                  
// 현재 위치에서 이동할 위치만큼 선 하나 그어서, 그게 벽에 닿으면 벽 끝에까지만 가고 상태 바뀌게함  
        Vector2 direction = (newPos - curPos).normalized;        float distance = Vector2.Distance(curPos, newPos);                RaycastHit2D hit =  
            Physics2D.Raycast(controller.transform.position, direction, distance, controller.Move.groundMask);                if (hit.collider != null)  
        {            controller.Move.rb.MovePosition(hit.point - direction * 0.01f);            if (controller.Move.isGrounded) controller.ChangeState<IdleState>();            else controller.ChangeState<FallState>();            return;        }                  
        controller.Move.rb.MovePosition(newPos);  
  
                if (Vector2.Distance(newPos, targetPos) < 0.01f)  
        {            if (controller.Move.isGrounded)            {                controller.ChangeState<IdleState>();                return;            }            controller.ChangeState<FallState>();                    }    
          
        if (controller.Move.isGrounded)  
        {            controller.ChangeState<IdleState>();            return;        }*/                /*float x = startPos.x + controller.Move.jumpXCurve.Evaluate(t) * wallJumpDirection * wallJumpMoveX;  
        float y = startPos.y + controller.Move.jumpYCurve.Evaluate(t) * wallJumpMoveY;  
        nextPos = new Vector2(x, y);  
        Vector2 direction = (nextPos - (Vector2)controller.transform.position).normalized;        float distance = Vector2.Distance(controller.transform.position, nextPos);  
        RaycastHit2D hit =            Physics2D.Raycast(controller.transform.position, direction, distance, controller.Move.groundMask);  
        if (hit.collider != null)        {            if (!hit.collider.CompareTag("Platform"))            {                controller.Move.rb.MovePosition(hit.point - direction * 0.01f);                if (controller.Move.isGrounded) controller.ChangeState<IdleState>();                else controller.ChangeState<FallState>();                return;            }        }  
        controller.transform.position = nextPos;  
        if (t >= 1f)        {            if (controller.Move.isGrounded)            {                controller.ChangeState<IdleState>();                return;            }            else            {                controller.ChangeState<FallState>();                return;            }        }*/    }  
  
    public override void Exit(PlayerController controller)   
    {  
        base.Exit(controller);  
        controller.isLookLocked = false;  
        controller.Move.rb.velocity = new Vector2(controller.Move.rb.velocity.x, 0);  
    }  
}
// WallHoldState.cs
public class WallHoldState : AirSubState  
{  
    public override void Enter(PlayerController controller)  
    {  
        base.Enter(controller);  
        controller.Move.ForceLook(!controller.Move.lastWallIsLeft);  
        controller.Attack.ClearAttackCount();  
        controller.isLookLocked = true;  
        controller.Move.rb.velocity = Vector2.zero;  
    }  
  
    public override void Exit(PlayerController controller)  
    {  
        base.Exit(controller);  
        controller.isLookLocked = false;  
        controller.Condition.canStaminaRecovery.Value = true;  
        // player.PlayerAnimator.ClearBool();  
    }  
  
    public override void HandleInput(PlayerController controller)  
    {  
        var moveInput = controller.Inputs.Player.Move.ReadValue<Vector2>();  
        if (!controller.Move.isWallTouched)  
        {  
            controller.ChangeState<FallState>();  
            return;  
        }  
  
        // 벽이 있는 방향으로 입력이 들어왔을 때  
        if (((moveInput.x < 0 && controller.Move.lastWallIsLeft)   
            || moveInput.x > 0 && !controller.Move.lastWallIsLeft) )  
        {  
            // 점프 키가 눌림 and 벽점 가능함  
            if(controller.Inputs.Player.Jump.triggered && controller.Move.CanWallJump())  
            {  
                // Debug.Log("벽점으로");  
                controller.ChangeState<WallJumpState>();  
                return;  
            }  
            if (controller.Move.isWallTouched)  
            {  
                // Debug.Log("중력 감소");  
                controller.Move.ChangeGravity(true);  
                return;  
            }  
        }  
  
        if (moveInput.x == 0)  
        {  
            controller.ChangeState<FallState>();  
            return;  
        }  
        if (controller.Move.isGrounded)  
        {  
            controller.ChangeState<IdleState>();  
            return;  
        }  
          
        if (controller.Inputs.Player.SpecialAttack.triggered)  
        {  
            controller.isLookLocked = false;  
            controller.ChangeState<SpecialAttackState>();  
            return;  
        }  
        if (controller.Inputs.Player.Dodge.triggered)  
        {  
            controller.ChangeState<DodgeState>();  
            return;  
        }  
        if (controller.Inputs.Player.AdditionalAttack.triggered)  
        {  
            controller.ChangeState<AdditionalAttackState>();  
            return;  
        }  
          
    }  
  
    public override void LogicUpdate(PlayerController controller)  
    {  
        controller.Move.ApplyWallSlideClamp();  
        controller.Move.ForceLook(!controller.Move.lastWallIsLeft);  
          
        if (controller.Move.rb.velocity.y < 0)  
        {  
            controller.Animator.SetBoolAnimation(AnimatorHash.PlayerAnimation.WallHold);  
        }  
          
        if (controller.Move.keyboardLeft != controller.Move.lastWallIsLeft)  
        {  
            controller.Move.Move();  
            return;  
        }  
    }  
}
// PlayerMove.cs
public void WallJump()  
{  
    float jumpPower = 12f;    float jumpSidePower = 20f;        rb.velocity = Vector2.zero;  
  
    rb.AddForce(new Vector2((lastWallIsLeft ? 0.1f : -0.1f), 0), ForceMode2D.Impulse); // 벽에서 약간 밀어내기  
          
    // AddForce로 점프 적용  
    // rb.AddForce(new Vector2(jumpDir.x * jumpSidePower, jumpDir.y * jumpPower), ForceMode2D.Impulse);    rb.velocity = new Vector2(        (lastWallIsLeft ? 1 : -1) * jumpSidePower,        jumpPower    );    StartCoroutine(Controller.IgnoreInputInTime(0.5f));}  
  
  
  
public bool CanWallJump()  
{  
    // 벽점 당시에 왼쪽 벽인지 아닌지 확인한 다음에 벽 체크  
    rightWallCheckPos = (Vector2)transform.position + Vector2.right * checkDistance;    
    leftWallCheckPos = (Vector2)transform.position + Vector2.left * checkDistance;  
    Collider2D hit = Physics2D.OverlapBox(keyboardLeft? leftWallCheckPos : rightWallCheckPos, wallCheckBoxSize, 0f, groundMask);    if(hit != curWall)    {        return true;    }    return false;}

3. HandleInput 중복

  • 해결할 예정임
  • 근데 enum flag가 뭔지 궁금해서 찾아봄
  • 참고 자료
  • 원래는 enum이 값을 하나만 가질 수 있는데, flag 속성을 붙이면 여러 개를 담을 수 있음!
[Flags]  
public enum eTransitionType  
{  
    None = 0,  
    IdleState = 1 << 0,  
    MoveState = 1 << 1,  
    JumpState = 1 << 2,  
    DoubleJumpState = 1 << 3,  
    FallState = 1 << 4,  
    NormalAttackState = 1 << 5,  
    NormalJumpAttackState = 1 << 6,  
    DownAttackState = 1 << 7,  
    SpecialAttackState = 1 << 8,  
    DodgeState = 1 << 9,  
    StartParryState = 1 << 10,  
    SuccessParryState = 1 << 11,  
    DamagedState = 1 << 12,  
    DieState = 1 << 13,  
    PotionState = 1 << 14,  
    AdditionalAttackState = 1 << 15,  
}
  • 원래 인터페이스였던 IPlayerState를 BasePlayerState라는 이름으로 추상클래스로 변경 (프로퍼티때매)
public abstract class BasePlayerState  
{  
    public abstract eTransitionType ChangableStates { get; }  
      
    public abstract void Enter(PlayerController controller); // 상태 변화했을 때 돌아감   
public abstract void HandleInput(PlayerController controller); // 입력에 따른 상태 전환 등을 처리  
    public abstract void LogicUpdate(PlayerController controller); // 실제 로직이 돌아가는 부분  
    public abstract void Exit(PlayerController controller); // 상태에서 나갈 때 돌아감  
    public bool CanChangeTo(eTransitionType next)  
    {  
        return ChangableStates.HasFlag(next);  
    }  
  
    public void TryChangeState(eTransitionType next, PlayerController controller)  
    {  
        if (ChangableStates.HasFlag(next))  
        {  
            System.Type type = controller.stringStateTypes[next.ToString()];  
            controller.ChangeState(type);  
        }  
    }  
}
  • 각 상태들은 어떤 상태로 변화 가능한지를 ChangableState에 가지고 있을 거임.

플레이어 상호작용 만들기

public void TryInteract()  
{  
    RaycastHit2D hit = Physics2D.Raycast(transform.position,  Vector2.up, 0.1f, interactableMask);  
    if (hit.collider != null)  
    {  
        if (hit.collider.TryGetComponent(out InteractableObject interactable))  
        {  
            interactable.Interact();  
              
            Controller.PlayerInputDisable();  
        }  
    }  
}
  • 상호작용 가능한 오브젝트는 레이어가 Interactable로 되어있고, abstract class 인 InteractableObject를 상속받아 구성되어있음.

모의 면접 대비 공부

  • Q1. CSV/JSON 등 데이터 저장 포맷에 대해 설명하고, 활용에 적절한 상황을 설명해주세요.

  • A1. CSV는 행은 줄넘김, 열은 콤마로 나눠져있습니다. JSON은 딕셔너리 형태와 유사합니다. XML은 마크다운 언어인 HTML과 비슷하게 태그를 사용해서 데이터를 정의합니다. YAML은 공백으로 데이터를 구분합니다. CSV는 표 형식의 데이터를 작성하는데 용이합니다. Json은 복잡한 구조를 가진 데이터에 유용합니다. XML은 명확한 구조 정의가 필요한 문서에, YAML은 사람이 쉽게 읽을 수 있어야 하는 데이터에 적합합니다.

  • Q2. 월드 스페이스 (World Space) 와 로컬 스페이스 (Local Space)의 차이에 대해 설명해주세요.

  • A2. 월드 스페이스는 게임 전체의 절대 좌표계를 의미하고, 로컬 스페이스는 각각 개별적인 게임 오브젝트가 자기 자신을 기준으로 하는 상대 좌표계를 의미합니다.

  • Q3. 3D 공간에 있는 오브젝트들이 화면에 표현되는 픽셀로 표시되기까지의 과정을 설명해보세요.

  • A3. 3D 모델을 2차원에 투영하는 렌더링 과정을 렌더링 파이프라인이라고 합니다.

      1. Input Assembler (입력 조립) : Vertex Specification이라고도 하는데, CPU에서 렌더링을 수행할 도형의 정점(vertex) 정보를 정점 버퍼라는 자료구조에 담아서 GPU로 보냄.
      • 정점 버퍼는 위치, 노말, 색상, uv 값들을 가지고 있고, 구조체가 아닌 직렬화된 형태로 담겨있음
      • GPU는 정점 버퍼를 받아서 정점 데이터(구조체)로 변경함
      • 조립하는 과정에서, 삼각형과 같은 기본적인 도형(프리미티브)으로 조립해줌.
      • 조립된 프리미티브는 정점 쉐이더에 입력됨
      1. Vertex Shader : 1단계에서 받은 정점 데이터의 local space 좌표를 world space 좌표로 변겅하고, 카메라가 담는 view space로 변환한 뒤, 투영을 통해 투영 공간인 clip space로 바꿈
      • 이 단계에서 행렬 연산이 진행되는데, Model - View - Projection 순서로 진행함.
      • M, V, P는 순서 이름이기도 하지만 변환 행렬의 이름이기도 함.
      • P (View space Clip Space) 단계에서, 컬링 (카메라 절두체 외에 있는 부분을 지움)과 클리핑 (정규 좌표에 의해 계산된 절두체에 걸쳐있는 부분)을 진행함
      1. Tesselation : 모델의 정점을 쪼개서 디테일한 표현 가능 (LOD)
      1. Geometry Shader : 2단계에서 생성되지 않은 임의의 정점을 추가하거나 삭제해 모델을 수정하는 셰이더. Tesselation이나 그림자 효과, 큐브 맵을 한번의 처리로 렌더링하는데 사용
      1. Rasterization : 정점 정보가 확정된 도형을 2D로 표현하기 위해 픽셀 데이터로 변환하는 단계.
      • Viewport Transformation : 3차원 정규좌표 (NDC공간) 상의 좌표를 2D 스크린 좌표로 변환
      • Scan Transformation : 프리미티브를 통해 픽셀 데이터(프래그먼트)를 생성하고, 여기를 채우는 픽셀을 찾아냄. 각 픽셀마다 정점 데이터를 보간해 할당
      1. Pixel Shader : (Fragment Shader) 렌더링 될 각각의 픽셀들의 색을 계산. Rasterizer가 전달한 픽셀 개수만큼 실행되며, 투명도 처리, 조명 처리, 그림자 처리, 텍스쳐에 색상 입히기 모두 Pixel Shader의 역할. 각 픽셀의 색상과 깊이를 출력으로 전달하는데, 색상은 컬러버퍼, 깊이는 Z버퍼에 저장됨. 이 버퍼를 통칭 스크린 버퍼라고 부름.
      1. Output Merge : 투명도와 깊이 값 등을 통해 픽셀끼리 경쟁해 색을 정하거나 합쳐서 최종적으로 화면에 그려질 픽셀을 정함. Z-Test, Stencil Test, Alpha Blending 등의 연산을 수행함.
  • 축약본 :

    • 렌더링 파이프라인은 크게 Input Assembler, Vertex Shader, Rasterization, Pixel Shader, Output Merge의 과정을 거칩니다.
    • Input Assembler에서는 CPU가 GPU에게 정점 데이터를 보내줍니다.
    • Vertex Shader에서는 GPU가 받은 정점 데이터를 토대로 local space에서 world space > view space > clip space로 좌표변환을 합니다.
    • Rasterization에서는 정점 데이터가 확정된 도형을 2D로 바꾸기 위해 3D 공간상의 좌표를 2D 스크린상의 좌표로 바꾸고, 픽셀 데이터를 생성합니다.
    • Pixel Shader에서는 렌더링 될 픽셀의 색을 결정합니다.
    • Output merge에서는 픽셀들의 투명도, 깊이 값 등을 보고 화면에 어떻게 그려질지 정해서 출력합니다.

참고자료

  • Q4. Tree의 순회(Traversal) 방법에 대해 설명해주세요.

  • A4. 트리 순회는 노드를 어떤 순서로 방문할지를 정하는 방식입니다. 전위는 루트를 먼저, 중위는 루트를 중간에, 후위는 루트를 마지막에 방문하는 차이가 있습니다.

  • Q4-2. 순회를 구현하는 방법들

  • A4-2. 트리의 순회는 재귀로 구현할 수 있음. 예를 들어 전위 순회의 경우 현재 노드를 방문하고 왼쪽 자식을, 그 후에 오른쪽 자식을 재귀적으로 순회하면 됨. 다만 깊은 트리의 경우 스택 오버플로우 위험이 있을 수 있어서, 스택으로 구현하는 방식을 고려해 보아야 함.

  • Q5. 각 길찾기 알고리즘의 차이점은 무엇인가요?

  • A5. DFS, BFS, 다익스트라, A*, 벨만-포드, 플로이드-워셜 등이 있음.

    • DFS : 깊이 우선 탐색. 그래프를 최대한 깊게 탐색한 후에 다른 경로를 찾음
    • BFS : 너비 우선 탐색. 시작지점부터 가까운 순서대로 탐색함. 가중치가 없는 그래프에서 최단경로를 찾음.
    • 다익스트라 : BFS와 유사, 음의 가중치를 허용하지 않는 가중 그래프에서 최단경로를 찾음.
    • A* : 다익스트라의 확장판. 휴리스틱 함수를 사용해 경로의 예상 비용을 고려함.
    • 벨만-포드 : 음의 가중치가 있는 그래프에서도 최단경로를 찾음.
    • 플로이드-워셜 : 모든 노드 쌍 간의 최단 거리를 한 번에 계산함.
  • Q6. A* 알고리즘에 대해 설명해주세요.

  • A6. 시작 지점과 목적지를 알고 있을 때, 최단 거리를 효율적으로 찾는 방법임. 다음에 방문할 정점을 선택할 때 최단 거리 경로를 유지할 수 있는 정점과, 목적지에 얼마나 가까운 정점인지 두 개를 동시에 고려해서 선정함.

  • Q7. 해당 알고리즘들을 프로젝트에 적용해본 경험이 있나요?

  • A7. 로봇 원격 제어 시뮬레이션을 제작할 때 다익스트라 알고리즘을 사용한 경험이 있습니다.

내일 학습 할 것은 무엇인지

  • 다른 서브스테이트도 다 변경해줘야됨