게임 아키텍처 / 디자인 패턴에 대한 조언


16

나는 잠시 동안 2d RPG에서 일하고 있었고, 나는 나쁜 디자인 결정을 내렸다는 것을 알게되었습니다. 특히 나에게 문제를 일으키는 몇 가지가 있으므로 다른 사람들이 어떤 디자인을 사용하여 극복하거나 사용할 것인지 궁금했습니다.

약간의 배경을 위해 작년 여름 자유 시간에 작업을 시작했습니다. 처음에 C #으로 게임을 만들었지 만 약 3 개월 전에 C ++로 전환하기로 결정했습니다. C ++을 많이 사용한 이후로 오래 되었기 때문에 C ++을 잘 다루고 싶었고 이와 같은 흥미로운 프로젝트가 좋은 동기 부여자가 될 것이라고 생각했습니다. 나는 부스트 라이브러리를 광범위하게 사용했으며 그래픽에는 SFML을 사용하고 오디오에는 FMOD를 사용하고 있습니다.

나는 약간의 코드를 작성했지만 그것을 폐기하고 다시 시작하는 것을 고려하고 있습니다.

여기 내가 관심을 갖는 주요 영역이 있으며 다른 사람들이 해결하거나 해결할 방법에 대한 의견을 얻고 자했습니다.

1. 순환 종속성 C #에서 게임을 할 때 문제가되지 않기 때문에 걱정할 필요가 없었습니다. C ++로 넘어 가면서 이것은 상당히 중대한 문제가되었고 내가 잘못 설계했을 수도 있다고 생각하게 만들었습니다. 나는 수업을 분리하고 여전히 내가 원하는 것을하도록하는 방법을 상상할 수 없다. 다음은 종속성 체인의 몇 가지 예입니다.

상태 효과 클래스가 있습니다. 이 클래스에는 캐릭터에 대한 효과를 적용하기위한 많은 메소드 (적용 / 적용 해제, 틱 등)가 있습니다. 예를 들어

virtual void TickCharacter(Character::BaseCharacter* character, Battles::BattleField *field, int ticks = 1);

이 기능은 상태 효과가 적용된 캐릭터가 회전 할 때마다 호출됩니다. Regen, Poison 등과 같은 효과를 구현하는 데 사용됩니다. 그러나 BaseCharacter 클래스와 BattleField 클래스에 대한 종속성도 소개합니다. 당연히 BaseCharacter 클래스는 주기적으로 의존하는 상태 효과가 현재 어떤 상태에 있는지 추적해야합니다. 배틀 필드는 전투 당사자를 추적해야하며, 파티 클래스에는 또 다른 주기적 종속성을 소개하는 BaseCharacters 목록이 있습니다.

2-이벤트

C #에서는 캐릭터, 전장 등의 이벤트에 참여하기 위해 대리자를 광범위하게 사용했습니다. )) 전장 / 그래픽 구성 요소가 해당 대의원에게 연결되어 효과를 강화합니다. C ++에서 비슷한 것을했습니다. 분명히 C # 대리자와 직접적으로 동등한 것은 없으므로 대신 다음과 같이 만들었습니다.

typedef boost::function<void(BaseCharacter*, int oldvalue, int newvalue)> StatChangeFunction;

내 캐릭터 수업에서

std::map<std::string, StatChangeFunction> StatChangeEventHandlers;

캐릭터의 스탯이 변경 될 때마다 맵에서 모든 StatChangeFunction을 반복해서 호출합니다. 그것이 작동하는 동안, 이것이 일을하는 데 나쁜 접근법이라고 걱정합니다.

3-그래픽

이것은 큰 것입니다. 내가 사용하는 그래픽 라이브러리와 관련이 없지만 개념적인 것입니다. C #에서는 그래픽이 많은 클래스와 결합되어있어 끔찍한 아이디어라고 생각합니다. 이번에는 그것을 분리하고 싶었고 다른 접근법을 시도했습니다.

그래픽을 구현하기 위해 게임과 관련된 모든 그래픽을 일련의 화면으로 상상했습니다. 즉, 타이틀 화면, 캐릭터 상태 화면, 맵 화면, 인벤토리 화면, 전투 화면, 전투 GUI 화면이 있으며 기본적으로 게임 그래픽을 만드는 데 필요한 화면을 서로 겹쳐서 쌓을 수 있습니다. 활성 화면이 무엇이든 게임 입력을 소유합니다.

사용자 입력에 따라 화면을 푸시하고 팝업하는 화면 관리자를 설계했습니다.

예를 들어,지도 화면 (타일 맵의 입력 처리기 / 시각화 장치)에 있고 시작 버튼을 누른 경우 화면 관리자에게 전화를 걸어 주 메뉴 화면을지도 화면 위로 밀고지도를 표시했습니다. 화면이 그리거나 업데이트되지 않습니다. 플레이어는 메뉴 주위를 탐색하여 새 화면을 화면 스택에 푸시하고 사용자가 화면 / 취소를 변경할 때 팝업을 표시하기 위해 화면 관리자에게 더 많은 명령을 발행합니다. 마지막으로 플레이어가 기본 메뉴를 종료하면 팝업 메뉴에서 다시지도 화면으로 돌아와서 그리기 / 업데이트 한 후 다시 표시합니다.

전투 화면은 더 복잡 할 것입니다. 배경으로 작동하는 화면, 전투에서 각 파티를 시각화하는 화면 및 전투의 UI를 시각화하는 화면이 있습니다. UI는 캐릭터 이벤트에 연결하고이를 사용하여 UI 구성 요소를 업데이트 / 다시 그릴시기를 결정합니다. 마지막으로, 사용 가능한 애니메이션 스크립트가있는 모든 공격은 화면 스택을 시작하기 전에 추가 레이어를 호출하여 자체 애니메이션을 적용합니다. 이 경우 모든 레이어는 일관되게 드로어 블 및 업데이트 가능으로 표시되며 전투 그래픽을 처리하는 화면 스택을 얻습니다.

화면 관리자가 아직 완벽하게 작동하지는 못했지만 시간이 좀 걸릴 수 있다고 생각합니다. 그것에 대한 내 질문은, 이것이 가치있는 접근법입니까? 그것이 나쁜 디자인이라면 지금 필요한 모든 화면을 만드는 데 너무 많은 시간을 투자하기 전에 알고 싶습니다. 게임 그래픽을 어떻게 구축합니까?

답변:


15

전반적으로 나는 당신이 나열된 어떤 것도 당신이 시스템을 폐기하고 다시 시작하게 만들 것이라고 말하지 않을 것입니다. 이것은 모든 프로그래머가 작업중 인 프로젝트를 통해 약 50-75 %의 방법을 원하지만 결코 끝나지 않는 개발주기와 끝나지 않습니다. 따라서이를 위해 각 섹션에서 피드백이 발생합니다.

  1. 이것은 문제가 될 수 있지만 일반적으로 다른 것보다 더 성가신 것입니다. #pragma를 한 번 또는 #ifndef MY_HEADER_FILE_H를 사용하고 있습니까? 이런 식으로 .h 파일은 각 범위 내에서 한 번만 존재합니까? 그렇다면 내 권장 사항은 모든 #include 문을 제거하고 컴파일하여 게임을 다시 컴파일하는 데 필요한 문장을 추가합니다.

  2. 나는 이러한 유형의 시스템을 좋아하며 아무런 문제가 없습니다. C #의 이벤트는 일반적으로 이벤트 시스템 또는 메시징 시스템으로 대체됩니다 (자세한 정보를 찾으려면 여기에서 질문을 검색 할 수 있음). 여기서 핵심은 상황이 발생할 때이를 최소화하는 것입니다. 이미하고있는 것처럼 들리므로 걱정할 필요가 없습니다.

  3. 이것은 또한 나에게 올바른 길로 보이고 개인적으로나 전문적으로 내 엔진을 위해하는 일입니다. 이렇게하면 설정 방법에 따라 메뉴 시스템이 루트 메뉴 (게임 시작 전) 또는 플레이어 루트가 '루트'화면으로 표시되는 상태 시스템이됩니다.

요약하자면, 당신이 겪고있는 것에 가치있는 재시작이 없습니다. 더 공식적인 이벤트 시스템 교체를 원할 수 있지만 시간이 지날 것입니다. 순환 포함은 모든 C / C ++ 프로그래머들이 끊임없이 뛰어 넘어야하는 장애물이며, 그래픽을 분리하기 위해 노력하는 것은 모두 논리적 '다음 단계'처럼 보입니다.

도움이 되었기를 바랍니다!


#ifdef는 순환 포함 문제에 도움이되지 않습니다.
공산주의 오리

순환 포함을 추적하기 전에 거기에있을 것으로 기대하면서 내 기초를 덮고있었습니다. 자신을 포함하는 파일을 포함해야하는 파일과 달리 여러 개의 기호로 정의 된 경우 완전히 다른 물고기 주전자가 될 수 있습니다. (그것이 포함이 .CPP 파일에 있고 .H 파일이 아니라면 그가 기술 한 것으로부터 그는 서로에 대해 알고있는 두 개의 기본 객체로 확인해야합니다)
James

조언 주셔서 감사합니다 :) 내가 올바른 길을 가고 있음을 알게되어 기쁘다
user127817

4

헤더 파일에서 할 수있는 클래스를 선언하고 실제로 .cpp (또는 무엇이든) 파일에 클래스를 포함 시키면 순환 종속성은 문제가되지 않습니다.

이벤트 시스템의 경우 두 가지 제안 :

1) 지금 사용하는 패턴을 유지하려면 std :: map 대신 boost :: unordered_map으로 전환하십시오. 문자열로 키를 사용하여 매핑하는 것은 느립니다. 특히 .NET은 속도를 높이기 위해 멋진 일을하기 때문에 특히 그렇습니다. unorder_map을 사용하면 문자열이 해시되므로 일반적으로 비교가 더 빠릅니다.

2) boost :: signals와 같은 더 강력한 것으로 전환하는 것을 고려하십시오. 그렇게하면 boost :: signals :: trackable에서 파생시켜 게임 객체를 추적 가능하게 만드는 것과 같은 좋은 일을 할 수 있으며 소멸자가 이벤트 시스템에서 수동으로 등록을 취소하는 대신 모든 것을 정리하도록 할 수 있습니다. 또한이 일을 매우 유사하므로 (반대, 내가 정확한 명칭을 기억하지 않습니다 또는 그) 각 슬롯을 가리키는 여러 신호를 가질 수 +=A의 delegateC #으로합니다. boost :: signals의 가장 큰 문제는 헤더가 아니라 컴파일되어야한다는 것입니다. 따라서 플랫폼에 따라 시작 및 실행이 어려울 수 있습니다.

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