클래스 간 데이터와 종속성을 깨끗하고 우아하게 처리하는 방법


12

SFML 2에서 2D 하향식 게임을하고 있는데 모든 것이 작동하고 잘 어울리는 우아한 방법을 찾아야합니다.

설명해주세요. 모든 클래스에 그리기 메소드와 업데이트 메소드를 제공하는 추상 기반에서 상속되는 많은 클래스가 있습니다.

게임 루프에서 update를 호출 한 다음 각 클래스를 그립니다. 이것이 일반적인 접근법이라고 생각합니다. 타일, 충돌, 플레이어 및 모든 타일 / 이미지 / 텍스처를 포함하는 리소스 관리자에 대한 클래스가 있습니다. SFML에서 입력이 작동하는 방식으로 인해 업데이트 호출에서 각 클래스가 입력을 처리하도록 결정했습니다 (필요한 경우).

지금까지 필요에 따라 종속성을 전달했습니다. 예를 들어, 이동 키를 누를 때 플레이어 클래스에서 플레이어가 이동하려는 위치가 충돌인지 확인하기 위해 충돌 클래스의 메소드를 호출합니다. 충돌이없는 경우에만 플레이어를 움직입니다.

이것은 대부분 잘 작동하지만 더 잘 할 수 있다고 생각합니다. 어떻게 잘 모르겠습니다.

이제 구현해야하는 더 복잡한 작업이 있습니다. 예 : 플레이어가지면에있는 물체까지 걸어 갈 수 있으며, 키를 눌러 가져 와서 잃어버린 다음 인벤토리에 표시됩니다. 이것은 몇 가지 일이 발생해야 함을 의미합니다.

  • 키를 누를 때 플레이어가 찾기 쉬운 아이템 범위에 있는지 확인하십시오. 그렇지 않으면 진행하지 마십시오.
  • 항목을 찾으십시오.
  • 항목의 스프라이트 텍스처를 기본 텍스처에서 "풀린"텍스처로 업데이트합니다.
  • 항목의 충돌을 업데이트하십시오. 모양이 변경되었거나 완전히 제거되었을 수 있습니다.
  • 추가 된 품목으로 재고를 갱신해야합니다.

모든 것을 의사 소통하려면 어떻게해야합니까? 현재 시스템을 사용하면 클래스가 범위를 벗어나고 어디서나 메소드 호출이 완료됩니다. 하나의 큰 관리자로 모든 수업을 묶고 각 관리자에게 부모 관리자 수업에 대한 참조를 제공 할 수는 있지만 약간 더 나은 것 같습니다.

어떤 도움이나 조언이라도 대단히 감사하겠습니다! 불분명 한 것이 있으면 확장 해 드리겠습니다.


1
상속보다는 구성을 고려할 수 있습니다. 컴포지션 예제를 살펴보고 아이디어를 얻을 수 있습니다. pimpl 관용구도 물건을 정리하는 데 도움이 될 수 있습니다.
OriginalDaemon

5
구성에 캐논 제품 중 하나 : 진화 계층 구조
doppelgreener

너무 현지화 된 것 같습니다. Code Review SE를 사용해 보시겠습니까?
Anko

답변:


5

구성이 모든 문제를 해결하는지 확실하지 않습니다. 아마도 부분적으로 도움이 될 수 있습니다. 그러나 원하는 것이 클래스를 분리하는 것이라면 더 많은 이벤트 중심 논리를 살펴볼 것입니다. 이 방법은 예를 들어 플레이어 위치와 가장 가까운 곳을 찾을 수있는 전리품에 대한 정보가 필요한 OnLoot 기능이 있습니다. 그런 다음 함수는 약탈 된 항목으로 이벤트를 보냅니다. 이벤트 프로세스주기에서 전리품이이 이벤트를 처리하므로 항목 자체를 업데이트하는 방법 만 알아야합니다. OnLoot 기능은 플레이어 인벤토리를 업데이트하거나 항목 자체가 updateInventory / * OnLootSucess * 이벤트를 전송할 수 있으며 플레이어 / 인벤토리는 자체 프로세스 이벤트주기에서 처리합니다.

장점 : 수업 중 일부를 분리했습니다.

단점 : 이벤트 클래스 추가, 불필요한 코드 오버 헤드.

다음은 가능한 방법 중 하나 입니다.

case LOOT_KEY:
   OnLoot(PLayer->getPos(), &inventoryItems);
....

// note onLoot do not needs to know anything about InvItem class (forward decl in enough)
int onLoot(vec3 pos, InvItems& pitems)
{
    InvItem* pitem = findInRange(pos, pitems, LOOT_RANGE);
    if(pitem)
     EventManager::Instance->post( Event::makeLootEvent(pitem));
}
....

// knows only about EventManager
InvItem::processEvents()
{
    while(!events.empty())
    {
        Event* pev = events.pop();
        ...
        case LOOT_EVENT:
            // in case you broadcasted it, but better is to sort all posted/sent events and add them only if they addressed to particular item 
            if(pev->item == this && handleLoot((LootEvent)pev))
            {
                EventManager::Instance->post(Event::makeLootSuccessEvent(this));
            }
    }
}

int handleLoot(LootEvent* plootev)
{
    InvItem* pi = plootev->item;
    if(pi->canLoot())
    {
        updateTexture(pi->icon, LOOTED_ICON_RES);
        return true;
    }
    return false; 
}


...
// knows only LootSuccessEvent and player
Inventory::processEvents()
{
    while(!events.empty())
    {
        Event* pev = events.pop();
        ...
        case LOOT_SUCCESS_EVENT:
             player->GetInventory()->add( ((LootSuccessEvent*)pev)->item );
        ...
}

이것은 가능한 방법 중 하나 일뿐입니다. 아마 당신은 너무 많은 이벤트가 필요하지 않습니다. 그리고 나는 당신이 당신의 데이터를 더 잘 알 수 있다고 확신합니다. 이것은 많은 방법 중 하나 일뿐입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.