어드벤쳐 게임에서 다른 주 코딩


12

모험 게임을 계획 중이며 스토리 진행 상태에 따라 레벨의 동작을 구현하는 올바른 방법을 알 수 없습니다.

내 싱글 플레이어 게임은 플레이어가 게임의 다양한 지점에서 마을 사람들과 상호 작용해야하는 거대한 세상을 특징으로합니다. 그러나 스토리 진행에 따라 플레이어에게 다른 것들이 제공 될 것입니다. 예를 들어 길드 리더는 도시 광장에서 도시 주변의 다양한 위치로 위치를 변경합니다. 문은 특정 루틴을 마친 후 하루 중 특정 시간에만 잠금을 해제합니다. 다른 컷 스크린 / 트리거 이벤트는 특정 마일스톤에 도달 한 후에 만 ​​발생합니다.

나는 처음에 switch {} 문을 사용하여 NPC가 무엇을 말해야하는지, 무엇을 찾을 수 있는지를 결정하고 전역 game_state 변수의 상태를 확인한 후에 만 ​​퀘스트 목표를 상호 작용할 수있게 만드는 것을 생각했습니다. 그러나 객체의 동작을 변경하기 위해 많은 다른 게임 상태와 스위치 케이스를 빠르게 실행할 수 있다는 것을 깨달았습니다. 이 switch 문은 디버깅하기가 매우 어려우므로 레벨 편집기에서 사용하기가 어려울 수도 있습니다.

따라서 여러 상태의 단일 객체를 갖는 대신 단일 상태의 동일한 객체의 여러 인스턴스가 있어야한다고 생각했습니다. 이런 식으로 레벨 에디터와 같은 것을 사용하면 NPC 인스턴스를 다른 모든 위치에 배치 할 수 있으며 각 대화 상태에 대한 인스턴스도 배치 할 수 있습니다. 그러나 그것은 레벨 주변에 떠 다니는 비활성의 보이지 않는 게임 객체가 많이 있다는 것을 의미합니다. 메모리에 문제가 있거나 레벨 에디터에서보기가 어려울 수 있습니다.

또는 단순히 각 게임 상태에 대해 동일하지만 별도의 수준을 만듭니다. 이것은 가장 깨끗하고 버그가없는 방식으로 작업을 수행하지만 레벨의 각 버전이 실제로 동일한 지 확인하는 대규모 수동 작업처럼 느껴집니다.

내 모든 방법은 비효율적이므로 내 질문을 요약하면 스토리 진행 상태에 따라 레벨의 동작을 구현하는 더 좋고 표준화 된 방법이 있습니까?

추신 : 아직 레벨 편집기가 없습니다 .JME SDK와 같은 것을 사용하거나 직접 만들 생각입니다.

답변:


9

이 경우에 필요한 것은 State Design Pattern 입니다. 각 게임 오브젝트의 여러 인스턴스를 갖는 대신 단일 인스턴스를 작성하지만 별도의 클래스에서 해당 동작 을 캡슐화하십시오 . 가능한 각 동작마다 하나씩 여러 클래스를 작성하고 모든 클래스에 동일한 인터페이스를 제공하십시오. 하나를 게임 오브젝트 (초기 상태)에 연결하고 조건이 변경 될 때 (마일스톤에 도달하거나 시간이 경과하는 등) 해당 오브젝트의 상태를 전환합니다 (즉, 게임 로직에 따라 다른 오브젝트와 연관) 해당되는 경우 속성을 업데이트하십시오.

상태 인터페이스가 어떻게 보일지에 대한 한 가지 예 (완전히 구성-이 체계가 제공하는 제어 수준을 설명하기 위해) :

interface NPCState {
    Scene whereAmI(NPC o);
    String saySomething(NPC o);
}

그리고 두 가지 구현 클래스 :

class Busy implements NPCState {
    Scene whereAmI(NPC o) {
        return o.getWorkScene();
    }
    String saySomething(NPC o) {
        return "Can't talk now, I'm busy!";
    }
}

class Available implements NPCState {
    Scene whereAmI(NPC o) {
        return TAVERN;
    }
    String saySomething(NPC o) {
        String[] choices = o.getRandomChat();
        return choices[RANDOM.getInt(choices.length)];
    }
}

그리고 스위칭 상태 :

// The time of day passed from "afternoon" to "evening"
NPCState available = new Available();
for ( NPC o : list ) {
    Scene oldScene = o.state.whereAmI(o);
    o.state = available;
    Scene newScene = o.state.whereAmI(o);
    moveGameObject(o, oldScene, newScene);
    ...

중요한 NPC는 자신 만의 커스텀 상태를 가질 수 있고, 상태 선택 로직은 더욱 커스터마이징이 가능하며, 게임의 다양한 측면에 대해 다른 상태를 가질 수 있습니다 (이 예제에서는 단일 클래스를 사용하여 위치와 채팅을 모두 구분할 수 있지만 그들과 많은 조합을 수행).

레벨 에디터에서도 잘 작동합니다 : 레벨의 "글로벌"상태를 전환하는 간단한 콤보 상자를 가지고 있고, 그 상태에 나타나길 원하는대로 게임 오브젝트를 추가하고 재배치 할 수 있습니다. 게임 엔진은 올바른 상태에있을 때 실제로 해당 오브젝트를 장면에 "추가"하는 것만 담당하지만 매개 변수는 여전히 사용자 친화적 인 방식으로 편집 할 수 있습니다.

(면책 조항 : 게임 편집자에 대한 실제 경험이 거의 없으므로 전문 편집자가 어떻게 작동하는지에 대해 확신 할 수 있지만 상태 패턴에 대한 요점은 여전히 ​​유지됩니다.이 방법으로 코드를 정리하면 깨끗하고 유지 보수가 가능해야하며 낭비되지 않아야합니다. 자원.)


이 State 디자인 패턴을 앞서 설명한 연관 배열과 결합 할 수 있습니다. 여기에 설명 된대로 상태 객체를 코딩 한 다음 제안 된 연관 배열을 사용하여 다른 상태 객체 중에서 선택할 수 있습니다.
jhocking 2016 년

게임을 엔진에서 분리하는 것이 좋으며 게임 로직을 하드 코딩하면 이들 사이의 연결이 강화됩니다 (재사용 가능성 감소). 의도 한 동작의 복잡성에 따라 "소프트 코드"를 시도하는 것은 불필요한 혼란을 초래할 수 있기 때문에 트레이드 오프가 있습니다 . 이 경우 혼합 접근 방식이 바람직 할 수 있습니다 (예 : "일반적인"상태 전이 로직을 갖지만 사용자 정의 코드도 통합 할 수 있음)
mgibsonbr

내가 이해 한대로, 일대일 매핑 btwn NPCState와 GameState가 있습니다. 그런 다음 NPC를 배열에 넣고 반복하여 게임 상태 변경이 관찰되면 새 NPCState를 할당합니다. NPCState는 자신에게 전송 된 모든 diff NPC를 처리하는 방법을 알 수 있어야하므로 기본적으로 NPCState는 주어진 상태에 대한 모든 NPC의 동작을 포함합니까? 모든 비헤이비어가 단일 NPCState에 깔끔하게 저장되어 게임 에디터 구현에 깔끔하게 매핑되는 것을 좋아하지만 NPCState를 꽤 크게 만듭니다.
Cardin

오, 나는 당신의 대답을 오해했다고 생각합니다. 관찰자를 포함하도록 약간 변경했습니다. 따라서 상태를 공유 할 수있는 Crowd NPC와 같은 슈퍼 제네릭을 제외하고는 모든 diff NPC마다 하나의 diff NPCState입니다. 각 게임 상태에 대해 NPC는 자신과 NPCState를 Observer에 등록합니다. 따라서 Observer는 어떤 NPC가 어떤 게임 상태에서 동작을 변경하기 위해 등록되어 있는지 정확히 알고이를 간단히 반복합니다. 게임 에디터 측에서 게임 에디터는 전체 레벨의 상태를 변경하기 위해 Observer에 신호를 전달하면됩니다.
Cardin

1
네, 그 아이디어입니다! 중요한 NPC에는 많은 주가 있으며 주 전환은 완료된 이정표에 따라 달라집니다. 일반 NPC는 때때로 이정표에 반응 할 수 있으며 내부 속성에 따라 선택한 상태를 가질 수도 있습니다 (모든 NPC가 기본 초기 상태를 가지고 있다고 가정하고 처음으로 이야기 할 때 자신을 소개 한 다음 정상적인 상태 전환주기).
mgibsonbr

2

내가 고려한 선택은 개별 개체가 다른 게임 상태에 반응하도록하거나 다른 게임 상태에서 다른 수준을 제공하는 것입니다. 이 두 가지 사이의 선택은 게임에서 정확히 무엇을하려고하는지에 달려 있습니다 (다른 상태는 무엇입니까? 게임은 상태간에 어떻게 전환됩니까? 등)

그러나 어느 쪽이든 나는 상태를 게임 코드에 하드 코딩하여 그렇게하지 않을 것입니다. NPC 객체의 대규모 switch 문 대신 데이터 파일에서 연관 배열에로드 된 NPC 동작 대신 다음과 같은 연관 배열을 사용하여 관련 상태에 대해 다른 동작을 실행합니다.

if (state in behaviors) {
  behaviors[state]();
}

해당 데이터 파일이 일종의 스크립팅 언어입니까? 나는 평범한 텍스트 데이터 파일이 행동을 설명하기에 충분하지 않을 것이라고 생각합니다. 어쨌든 동적으로로드되어야합니다. 유효한 Java 코드를 생성하기 위해 게임 편집기를 사용하는 것을 생각할 수는 없지만 확실히 파싱해야합니다.
Cardin

1
글쎄, 그것은 내 초기 생각이지만 mgibsonbr의 대답을 본 후에 다양한 행동을 별도의 클래스로 코딩 할 수 있다는 것을 깨달았고 데이터 파일에서 어떤 행동 클래스가 어떤 상태에 속하는지 말해줍니다. 런타임시 해당 데이터를 연관 배열에로드하십시오.
jhocking 2016 년

아 .. 정말 간단합니다! : D Lua haha와 같은 것을 포함하는 시나리오와 비교 ..
Cardin

2

마일스톤 변화를 찾기 위해 관찰자 패턴을 사용하는 것은 어떻습니까? 변경이 발생하면 일부 클래스는이를 인식하고 예를 들어 npc에 수행해야하는 변경을 처리합니다.

언급 된 상태 디자인 패턴 대신 전략 패턴을 사용합니다.

npc가 자신이있을 수있는 캐릭터 및 m 위치와 상호 작용할 수있는 방법이 n 개인 경우, 설계해야하는 최대 (m * n) +1 개의 클래스가 있습니다. 전략 패턴을 사용하면 n + m + 1 클래스로 끝나지만 다른 npcs에서도이 전략을 사용할 수 있습니다.

따라서 이정표를 처리하는 클래스와이 클래스를 관찰하고 npc 또는 적 또는 변경해야 할 사항을 처리하는 클래스가있을 수 있습니다. 관찰자가 업데이트되면 자신이 지배하는 인스턴스로 무언가를 변경해야하는지 결정합니다. 예를 들어 NPC 클래스는 생성자에서 NPC-Manager에게 업데이트해야 할 때와 업데이트해야 할 사항을 알려줍니다.


관찰자 패턴이 흥미로운 것 같습니다. NPC는 주정부 관찰자 자신을 등록해야 할 모든 책임을 분명히 맡길 수 있다고 생각합니다. Strategy 패턴은 Unity Engine의 Trigger 및 AI 동작과 매우 흡사합니다.이 동작은 btwn diff 게임 오브젝트의 동작을 공유하는 데 사용됩니다 (생각합니다). 실현 가능하게 들립니다. 나는 지금 장단점이 무엇인지 확실하지 않지만, Unity도 같은 방법을 사용한다는 점이 다소 안심입니다 ..
Cardin

나는이 두 가지 패턴을 몇 번 사용하여 단점에 대해 이야기 할 수 없었습니다 .- / 그러나 단일 책임과 모든 전략을 테스트 할 수있는 경우에는 좋을 것이라고 생각합니다. :) 다양한 클래스에서 전략을 사용하고 전략을 사용하는 모든 클래스를 찾고 싶습니다.
TOAOGG

0

주어진 모든 접근법이 유효합니다. 주어진 순간의 상황에 따라 다릅니다. 많은 모험이나 MMO는 이들을 조합하여 사용합니다.

예를 들어 중추적 사건이 레벨의 큰 부분을 바꾸는 경우 (예 : 부채 수집가가 아파트를 청소하고 그 안에있는 모든 사람이 체포되는 경우) 일반적으로 전체 방을 비슷한 모양의 두 번째 방으로 교체하는 것이 더 쉽습니다.

OTOH, 캐릭터가지도를 돌아 다니면서 다른 장소에서 다른 일을하는 경우, 종종 다른 행동 오브젝트를 통해 회전하는 단일 액터가 있습니다 (예 : 똑바로 걷기 / 대화하지 않고 vs. 목적이 달성되면 "숨겨 짐".

즉, 수동으로 만든 객체의 복제본이 있으면 아무런 문제가 발생하지 않습니다. 얼마나 많은 개체를 만들 수 있습니까? 게임이 반복 할 수있는 것보다 더 많은 객체를 만들 수있는 경우 "숨겨진"속성을보고 건너 뛰면 엔진 속도가 너무 느립니다. 그래서 나는 그것에 대해 너무 걱정하지 않을 것입니다. 많은 온라인 게임이 실제로이 작업을 수행합니다. 특정 캐릭터 나 아이템은 항상 존재하지만 해당 미션이없는 캐릭터에게는 표시되지 않습니다.

접근 방식을 결합 할 수도 있습니다. 아파트 건물에 두 개의 문이 있습니다. 하나는 "채무자 수령 전"아파트로, 다른 하나는 아파트로 연결됩니다. 복도에 들어가면 실제로 스토리 진행에 적용되는 복도 만 표시됩니다. 이렇게하면 "현재 스토리에서 항목을 볼 수 있습니다"라는 단일 메커니즘과 단일 대상이있는 문을 가질 수 있습니다. 또는 교체 할 수있는 동작을 가질 수있는 더 복잡한 문을 만들 수 있으며 그 중 하나는 "전체 아파트로 이동", 다른 하나는 "빈 아파트로 이동"입니다. 실제로 문의 목적지 만 바뀌면 외관이 무의미 해 보일 수 있지만 외관이 바뀌는 경우 (예 : 문 앞의 큰 자물쇠가 먼저 깨져야 함),

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