Win32API RigidBody

Win32API 강의 64화. Win32API RigidBody (1)

https://www.youtube.com/watch?v=5i6rbcpZUTg&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=64

 

< 배우게 된 내용 >

2D에서 사용 할 수 있는 기본적인 물리 

모든 물체가 다 물리 적용을 받는것은 아님 ( Component로 구현 )

 

간단하게 힘을 줬을때 이전 속도를 잃지 않은 상태로 움직이는 물리 구현

 

 

#pragma once

class CObject;

class CRigidBody
{
private:
    CObject* m_pOwner;

    Vec2 m_vForce; // 크기, 방향
    Vec2 m_vAccel; // 가속도
    float m_fMass; // 질량
    Vec2 m_vVelocity; // 속도

    // F = M * A;
    // V += A * DT -> 현재 속도

public:
    void finalupdate();

public:
    void AddForce(Vec2 _vF)
    {
        // 힘 누적
        m_vForce += _vF;
    }

    void SetMass(float _fMass)
    {
        m_fMass = _fMass;
    }

    float GetMass()
    {
        return m_fMass;
    }

private:
    void Move();
    
public:
    CRigidBody();
    ~CRigidBody();

    friend class CObject;
};

 

#include "pch.h"
#include "CRigidBody.h"

#include "CObject.h"
#include "CTimeMgr.h"

CRigidBody::CRigidBody()
    : m_pOwner(nullptr)
    , m_fMass(1.f)
{
}

CRigidBody::~CRigidBody()
{
}

void CRigidBody::finalupdate()
{
    // 힘의 크기
    float fForce = m_vForce.Length();

    if (0.f != fForce)
    {
        // 힘의 방향
        m_vForce.Normalize();

        // 가속도의 크기
        float m_fAccel = fForce / m_fMass;

        // 가속도
        m_vAccel = m_vForce * m_fAccel;

        // 속도
        m_vVelocity += m_vAccel * fDT;
    }

    // 속도에 따른 이동
    Move();

    // 누적된 힘을 0으로
    m_vForce = Vec2(0.f, 0.f);
}

void CRigidBody::Move()
{
    // 이동 속력
    float fSpeed = m_vVelocity.Length();

    if (0.f != fSpeed)
    {
        // 이동 방향
        Vec2 vDir = m_vVelocity;
        vDir.Normalize();

        Vec2 vPos = m_pOwner->GetPos();

        vPos += m_vVelocity * fDT;

        m_pOwner->SetPos(vPos);
    }
}

 

+ 이 가속도 속도 계산에서 마찰이 필요함

 


Win32API 강의 65화. Win32API RigidBody (2)

https://www.youtube.com/watch?v=vJnkF3BNGgo&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=65

 

< 배우게 된 내용 >

플레이어의 최대 속도 제한 걸기

 

스스로 멈출수 있도록 마찰력 추가 ( 속도의 반대 방향으로 )

 

void CRigidBody::finalupdate()
{
    // 힘의 크기
    float fForce = m_vForce.Length();

    if (0.f != fForce)
    {
        // 힘의 방향
        m_vForce.Normalize();

        // 가속도의 크기
        float m_fAccel = fForce / m_fMass;

        // 가속도
        m_vAccel = m_vForce * m_fAccel;

        // 속도
        m_vVelocity += m_vAccel * fDT;
    }

    // 마찰력에 의한 반대방향으로의 가속도 적용
    if (!m_vVelocity.IsZero())
    {
        Vec2 vFricDir = -m_vVelocity;
        vFricDir.Normalize();

        Vec2 vFriction = vFricDir * m_fFricCoeff * fDT;

        if (m_vVelocity.Length() <= vFriction.Length())
        {
            // 마찰 가속도가 본래 속도보다 큰 경우
            m_vVelocity = Vec2(0.f, 0.f);
        }
        else
        {
            m_vVelocity += vFriction;
        }
    }

    // 속도 제한 검사
    if (m_fMaxSpeed <= m_vVelocity.Length())
    {
        m_vVelocity.Normalize();
        m_vVelocity *= m_fMaxSpeed;
    }

    // 속도에 따른 이동
    Move();

    // 누적된 힘을 0으로
    m_vForce = Vec2(0.f, 0.f);
}

 

플레이어의 이동방식을 힘을 이용해서 변경

( TAP키를 넣은 이유는 초기 스타트 속도를 한번에 주기 위해서 )

 

void CPlayer::update()
{
    CRigidBody* pRigid = GetRigidBody();

    // 힘을 이용해서 좌표를 수정

    if(KEY_HOLD(KEY::W))
    {
        pRigid->AddForce(Vec2(0.f, -200.f));
    }

    if(KEY_HOLD(KEY::S))
    {
        pRigid->AddForce(Vec2(0.f, 200.f));
    }

    if(KEY_HOLD(KEY::A))
    {
        pRigid->AddForce(Vec2(-200.f,0.f));
    }

    if(KEY_HOLD(KEY::D))
    {
        pRigid->AddForce(Vec2(200.f, 0.f));
    }

    // 초기속도 지정
    if (KEY_TAP(KEY::W))
    {
        pRigid->AddVelocity(Vec2(0.f, -100.f));
    }

    if (KEY_TAP(KEY::S))
    {
        pRigid->AddVelocity(Vec2(0.f, 100.f));
    }

    if (KEY_TAP(KEY::A))
    {
        pRigid->AddVelocity(Vec2(-100.f, 0.f));
    }

    if (KEY_TAP(KEY::D))
    {
        pRigid->AddVelocity(Vec2(100.f, 0.f));
    }

    if(KEY_TAP(KEY::SPACE))
    {
        CreateMissile();
    }

    GetAnimator()->update();
}

 


Win32API 강의 66화. Win32API RigidBody (3)

https://www.youtube.com/watch?v=6E-iTAn-k5I&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=66

 

 

< 배우게 된 내용 >

 

마우스 클릭한 곳에서 원형으로 힘이 발동 ( 플레이어와 몬스터에게 힘을 줘서 반대방향으로 밀리게 )

 

void CScene_Start::update()
{
    if (KEY_HOLD(KEY::LBTN))
    {
        m_bUseForce = true;
        CreateForce();
    }
    else
    {
        m_bUseForce = false;
    }

    for (UINT i = 0; i < (UINT)GROUP_TYPE::END; ++i)
    {
        const vector<CObject*>& vecObj = GetGroupObject((GROUP_TYPE)i);

        for (size_t j = 0; j < vecObj.size(); ++j)
        {
            if (!vecObj[j]->IsDead())
            {
                if (m_bUseForce && vecObj[j]->GetRigidBody())
                {
                    Vec2 vDiff = vecObj[j]->GetPos() - m_vForcePos;
                    float fLen = vDiff.Length();

                    if (fLen < m_fForceRadius)
                    {
                        // 가까울때 힘을 높게
                        float fRatio = 1.f - (fLen / m_fForceRadius);
                        float fForce = m_fForce* fRatio;

                        vecObj[j]->GetRigidBody()->AddForce(vDiff.Normalize() * fForce);
                    }
                }

                vecObj[j]->update();
            }
        }
    }

    if (KEY_TAP(KEY::ENTER)) // tool scene 전환
    {
        ChangeScene(SCENE_TYPE::TOOL); // 이벤트 등록
    }
}

 


Win32API 강의 67화. Win32API RigidBody (4)

https://www.youtube.com/watch?v=jgxTQIaOaAI&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=67

 

 

< 오늘 배운 내용 >

애니메이션 전환에 따른 내부 로직 개편 ( 대략적인 흐름만, 구체적 구현 X )

#pragma once
#include "CObject.h"

enum class PLAYER_STATE
{
    IDLE,
    WALK,
    ATTACK,
    DEAD,
};

enum class PLAYER_ATTACK_STATE
{
    NORMAL_ATT_1,
    NORMAL_ATT_2,
    NORMAL_ATT_3,

    SKILL_ATT_1,
    // ....
};


class CTexture;

class CPlayer :
    public CObject
{
private:
    vector<CObject*> m_vecColObj;

    PLAYER_STATE m_eCurState;
    PLAYER_STATE m_ePrevState;
    int m_iDir;

public :
    virtual void update();
    virtual void render(HDC _dc);

private:
    void CreateMissile();
    void update_state();
    void update_move();
    void update_animation();

    CLONE(CPlayer);

public:
    CPlayer();
    ~CPlayer();
};

 

 

void CPlayer::update()
{
    update_move();

    update_state();

    update_animation();


    if(KEY_TAP(KEY::SPACE))
    {
        CreateMissile();
    }

    GetAnimator()->update();
    
    m_ePrevState = m_eCurState;
}

 

void CPlayer::update_animation()
{
    if (m_ePrevState == m_eCurState)
        return;

    switch (m_eCurState)
    {
    case PLAYER_STATE::IDLE:
        break;
    case PLAYER_STATE::WALK:
        break;
    case PLAYER_STATE::ATTACK:
        break;
    case PLAYER_STATE::DEAD:
        break;
    default:
        break;
    }
}

 


Win32API 강의 68화. Win32API RigidBody (5)

https://www.youtube.com/watch?v=ST7OjCNy0Z0&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=68

 

 

< 오늘 배운 내용 >

Walk와 Idle 상태 전환 애니메이션, 스프라이트는 방향별로 따로 2개씩 둠

CPlayer::CPlayer()
    : m_eCurState(PLAYER_STATE::IDLE)
    , m_ePrevState(PLAYER_STATE::WALK)
    , m_iDir(1)
    , m_iPrevDir(1)
{
    //// Texture 로딩하기
    //m_pTex = CResMgr::GetInst()->LoadTexture(L"PlayerTex", L"texture\\Player.bmp");

    CreateCollider();
    GetCollider()->SetOffsetPos(Vec2(0.f, 12.f));
    GetCollider()->SetScale(Vec2(20.f, 40.f));

    CreateRigidBody();

    // Texture 로딩하기
    CTexture* pLeftTex = CResMgr::GetInst()->LoadTexture(L"PlayerLeft", L"texture\\Player01_L.bmp");
    CTexture* pRightTex = CResMgr::GetInst()->LoadTexture(L"PlayerRight", L"texture\\Player01_R.bmp");

    CreateAnimator();

    GetAnimator()->CreateAnimation(L"IDLE_LEFT", pLeftTex, Vec2(0.f, 0.f), Vec2(50.f, 100.f), Vec2(50.f, 0.f), 0.1f, 9);
    GetAnimator()->CreateAnimation(L"IDLE_RIGHT", pRightTex, Vec2(0.f, 0.f), Vec2(50.f, 100.f), Vec2(50.f, 0.f), 0.1f, 9);

    GetAnimator()->CreateAnimation(L"WALK_LEFT", pLeftTex, Vec2(0.f, 225.f), Vec2(50.f, 100.f), Vec2(50.f, 0.f), 0.1f, 4);
    GetAnimator()->CreateAnimation(L"WALK_RIGHT", pRightTex, Vec2(0.f, 225.f), Vec2(50.f, 100.f), Vec2(50.f, 0.f), 0.1f, 4);

    GetAnimator()->Play(L"IDLE_RIGHT", true);
   
}

 

 

Rigidbody에서 속도를 가져와 0이 됐을때 Idle 설정

 

void CPlayer::update_state()
{
    if (KEY_TAP(KEY::A))
    {
        m_iDir = -1;
        m_eCurState = PLAYER_STATE::WALK;
    }

    if (KEY_TAP(KEY::D))
    {
        m_iDir = 1;
        m_eCurState = PLAYER_STATE::WALK;
    }

    if (0.f == GetRigidBody()->GetSpeed())
    {
        m_eCurState = PLAYER_STATE::IDLE;
    }
}

void CPlayer::update_move()
{
    CRigidBody* pRigid = GetRigidBody();

    // 힘을 이용해서 좌표를 수정
    if (KEY_HOLD(KEY::A))
    {
        pRigid->AddForce(Vec2(-200.f, 0.f));
    }

    if (KEY_HOLD(KEY::D))
    {
        pRigid->AddForce(Vec2(200.f, 0.f));
    }

    // 초기속도 지정
    if (KEY_TAP(KEY::A))
    {
        pRigid->AddVelocity(Vec2(-100.f, 0.f));
    }

    if (KEY_TAP(KEY::D))
    {
        pRigid->AddVelocity(Vec2(100.f, 0.f));
    }
}

void CPlayer::update_animation()
{
    if (m_ePrevState == m_eCurState && m_iPrevDir == m_iDir)
        return;

    switch (m_eCurState)
    {
    case PLAYER_STATE::IDLE:
    {
        if(m_iDir == -1)
            GetAnimator()->Play(L"IDLE_LEFT", true);
        else
            GetAnimator()->Play(L"IDLE_RIGHT", true);
    }
        break;
    case PLAYER_STATE::WALK:
    {
        if (m_iDir == -1)
            GetAnimator()->Play(L"WALK_LEFT", true);
        else
            GetAnimator()->Play(L"WALK_RIGHT", true);
    }
        break;
    case PLAYER_STATE::ATTACK:
        break;
    case PLAYER_STATE::DEAD:
        break;
    default:
        break;
    }
}

 

 

중력을 어떻게 구현?

1. 중력을 Scene에서 관리한다

2. 중력 자체를 component로 관리 또는 manager로 관리

 

일단 임시로 Player에서만 중력을 적용!

 

항상 아래쪽으로 힘이 적용

( 땅에 있을때는 중력 적용 X , 현실에서는 적용하지만, 게임에서는 중력 계산 X )

점프해서 땅위에서 떨어질때만 중력을 적용한다! && 중력의 가속도는 9.8 ( 인간의 질량은 거의 영향을 미치지 못함 )

 

 


Win32API 강의 69화. Win32API RigidBody (6)

https://www.youtube.com/watch?v=ne9Cyu9a6C0&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=69

 

 

< 배우게 된 내용 >

 

-> ground에 있는지를 체크

CGravity를 component로 구현

CGravity의 역할은 땅이 부딪혔는지 확인과 중력 적용

 

class CObject;

class CGravity
{
private:
    CObject* m_pOwner;

    bool m_bGround;

public:
    void SetGround(bool _b) { m_bGround = _b; }
public:
    void finalupdate();

public:
    CGravity();
    ~CGravity();

    friend class CObject;
};

 

 

땅 CObject를 상속받는 CGround 클래스 구현

땅과 player가 collider가 부딪히면 땅이라고 판단

CGround가 CGravity에게 땅에 닿았음을 알려줌

 

CGround::CGround()
{
    CreateCollider();

    GetCollider()->SetScale(Vec2(GetScale()));
}

CGround::~CGround()
{
}

void CGround::start()
{
    GetCollider()->SetScale(Vec2(GetScale()));
}

void CGround::update()
{
}

void CGround::OnCollisionEnter(CCollider* _pOther)
{
    CObject* pOtherObj = _pOther->GetObj();
    if (pOtherObj->GetName() == L"Player")
    {
        pOtherObj->GetGravity()->SetGround(true);
    }
}

 

 

생성자는 객체가 만들어지는 그 순간에 호출되는게 생성자

생성자뒤에 객체가 만들어지고 Scene이 시작하기 직전에 초기작업 해주는 함수 필요 -> Start() 함수

( Enter 함수에서 맨 끝에 start() 함수를 호출 )

ex) 콜라이더 크기를 오브젝트 크기와 맞추기 위한 시점

 


Win32API 강의 70화. Win32API RigidBody (7)

https://www.youtube.com/watch?v=QtUHWOwbNcs&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=70

 

< 배우게 된 내용 >

땅과 캐릭터가 닿을때 DT만큼 Player가 땅속 안으로 들어갈수 있음

이 차이 만큼을 Player가 올라가야함

( 아래 코드는 위의 문제점을 해결하지만 옆에서 진입하면 바로 위로 올라가버려지는 문제점이 있다 )

 

void CGround::OnCollision(CCollider* _pOther)
{
    CObject* pOtherObj = _pOther->GetObj();
    if (pOtherObj->GetName() == L"Player")
    {
        pOtherObj->GetGravity()->SetGround(true);

        Vec2 vObjPos = _pOther->GetFinalPos();
        Vec2 vObjScale = _pOther->GetScale();

        Vec2 vPos = GetCollider()->GetFinalPos();
        Vec2 vScale = GetCollider()->GetScale();

        float fLen = abs(vObjPos.y - vPos.y);
        float fValue = (vObjScale.y / 2.f + vScale.y / 2.f) - fLen;

        vObjPos = pOtherObj->GetPos();
        vObjPos.y -= (fValue - 1);
        
        pOtherObj->SetPos(vObjPos);
    }
}

 

땅에서 벗어나면 ( OnCollisionExit )  땅위에 있다는 bool 값을 false로 변경

 

 

중력을 구현할때 

원래 물체가 적용받고 있던 가속도에 추가 가속도를 더해주는 개념으로 적용시켜야 함

( m_vAccelA는 추가 가속도 )

 

 

 

땅 위에 있을때는 x 방향은 속도 유지 y 방향은 0으로

 

 

중력 적용은 y축으로 800f 정도로 함

 

 


Win32API 강의 71화. Win32API RigidBody (8)

https://www.youtube.com/watch?v=X_8ArldKwJw&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=71

 

 

< 배우게 된 내용 >

점프 만들기 + 애니메이션 실행

Y축으로 추가속도를 덧셈해주기

void CPlayer::update_state()
{
    if (KEY_TAP(KEY::A))
    {
        m_iDir = -1;
        m_eCurState = PLAYER_STATE::WALK;
    }

    if (KEY_TAP(KEY::D))
    {
        m_iDir = 1;
        m_eCurState = PLAYER_STATE::WALK;
    }

    if (0.f == GetRigidBody()->GetSpeed())
    {
        m_eCurState = PLAYER_STATE::IDLE;
    }

    if (KEY_TAP(KEY::SPACE))
    {
        m_eCurState = PLAYER_STATE::JUMP;

        if (GetRigidBody())
        {
            GetRigidBody()->AddVelocity(Vec2(0.f, -200.f));
        }
    }
}

 

void CPlayer::update_animation()
{
    if (m_ePrevState == m_eCurState && m_iPrevDir == m_iDir)
        return;

    switch (m_eCurState)
    {
    case PLAYER_STATE::IDLE:
    {
        if(m_iDir == -1)
            GetAnimator()->Play(L"IDLE_LEFT", true);
        else
            GetAnimator()->Play(L"IDLE_RIGHT", true);
    }
        break;
    case PLAYER_STATE::WALK:
    {
        if (m_iDir == -1)
            GetAnimator()->Play(L"WALK_LEFT", true);
        else
            GetAnimator()->Play(L"WALK_RIGHT", true);
    }
        break;
    case PLAYER_STATE::JUMP:
        if (m_iDir == -1)
            GetAnimator()->Play(L"JUMP_LEFT", true);
        else
            GetAnimator()->Play(L"JUMP_RIGHT", true);
        break;
    case PLAYER_STATE::ATTACK:
        break;
    case PLAYER_STATE::DEAD:
        break;
    default:
        break;
    }
}

 


Win32API 강의 72화. Win32API RigidBody (9)

https://www.youtube.com/watch?v=nrRXX0DyXp8&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=72

 

< 배우게 된 내용 >

애니메이션 전환 문제점들 수정

점프 했을때 Walk 애니메이션으로 전환 X

점프 중에서 끝에 속도가 0일때 Idle로 전환 X

 

void CPlayer::update_state()
{
    if (KEY_HOLD(KEY::A))
    {
        m_iDir = -1;

        if(PLAYER_STATE:: JUMP != m_eCurState)
            m_eCurState = PLAYER_STATE::WALK;
    }

    if (KEY_HOLD(KEY::D))
    {
        m_iDir = 1;
        if (PLAYER_STATE::JUMP != m_eCurState)
            m_eCurState = PLAYER_STATE::WALK;
    }

    if (0.f == GetRigidBody()->GetSpeed() && PLAYER_STATE::JUMP != m_eCurState)
    {
        m_eCurState = PLAYER_STATE::IDLE;
    }

    if (KEY_TAP(KEY::SPACE))
    {
        m_eCurState = PLAYER_STATE::JUMP;

        if (GetRigidBody())
        {
            GetRigidBody()->SetVelocity(Vec2(GetRigidBody()->GetVelocity().x, -300.f));
        }
    }
}

 

 

Player가 땅에 닿을때 진입시 IDLE 애니메이션으로 전환

 

 

 

 

'Win32API' 카테고리의 다른 글

Win32API Sound  (0) 2025.01.31
Win32API FSM ( Finite State Machine )  (0) 2025.01.27
Win32API 렌더링 최적화  (0) 2025.01.27
Win32API Alpha Blend  (0) 2025.01.27
Win32API 파일 입출력  (0) 2025.01.26
  Comments