게임 루프, 조건을 한 번 확인하고 무언가를 한 다음 다시하지 않는 방법


13

예를 들어, 나는 Game 클래스를 가지고 있으며 int플레이어의 삶을 추적하는 클래스를 유지합니다 . 나는 조건부

if ( mLives < 1 ) {
    // Do some work.
}

그러나이 상태는 계속 발생하며 작업은 반복적으로 수행됩니다. 예를 들어, 타이머를 설정하여 5 초 안에 게임을 종료하려고합니다. 현재는 매 프레임마다 5 초로 설정되어 있으며 게임은 끝나지 않습니다.

이것은 하나의 예일 뿐이며 게임의 여러 영역에서 동일한 문제가 있습니다. 조건을 확인한 다음 한 번만 한 번 수행 한 다음 if 문에서 코드를 다시 확인하거나 수행하지 않습니다. 생각 나는 몇 가지 가능한 해결책 bool은 각 조건에 대해 bool있고 조건이 발생 하는 시기를 설정하는 것 입니다. 그러나 실제로 이것은 bools클래스 필드로 저장되거나 메소드 자체 내에 정적으로 저장되어야하기 때문에 많은 처리가 매우 어려워 집니다.

이것에 대한 올바른 해결책은 무엇입니까 (내 예가 아니라 문제 영역에 대한)? 게임에서 어떻게 하시겠습니까?


답변 주셔서 감사합니다, 내 솔루션은 이제 ktodisco와 crancran 답변의 조합입니다.
EddieV223

답변:


13

가능한 코드 경로를보다 신중하게 제어 하여이 문제를 해결할 수 있다고 생각합니다. 예를 들어, 플레이어의 생명수가 1 이하로 떨어졌는지 확인하는 경우, 모든 프레임 대신 플레이어가 생명을 잃을 때만 확인하지 않겠습니까?

void subtractPlayerLife() {
    // Generic life losing stuff, such as mLives -= 1
    if (mLives < 1) {
        // Do specific stuff.
    }
}

이는 subtractPlayerLife특정 상황에서만 호출되는 것으로 가정하며 , 이는 모든 프레임 (충돌)을 확인 해야하는 조건으로 인해 발생할 수 있습니다 .

코드 실행 방식을 신중하게 제어하면 정적 부울과 같은 지저분한 솔루션을 피하고 단일 프레임에서 실행되는 코드 양을 비트 단위로 줄일 수 있습니다.

리팩토링이 불가능한 것으로 보이는 경우 실제로 한 번만 확인하면 상태 ( enum) 또는 정적 부울 값을 사용할 수 있습니다. 정적 선언을 직접 피하지 않으려면 다음 트릭을 사용하십시오.

#define ONE_TIME_IF(condition, statement) \
    static bool once##__LINE__##__FILE__; \
    if(!once##__LINE__##__FILE__ && condition) \
    { \
        once##__LINE__##__FILE__ = true; \
        statement \
    }

다음 코드를 작성합니다.

while (true) {
    ONE_TIME_IF(1 > 0,
        printf("something\n");
        printf("something else\n");
    )
}

인쇄 somethingsomething else한 번만. 가장 잘 보이는 코드를 생성하지는 않지만 작동합니다. 또한 고유 한 변수 이름을 더 잘 확보하여 개선 방법이 있다고 확신합니다. 이것은 #define런타임 동안 한 번만 작동합니다. 재설정 할 방법이 없으며 저장할 방법이 없습니다.

트릭에도 불구하고 코드 흐름을 먼저 제어 #define하고이를 최후의 수단으로 사용하거나 디버그 목적으로 만 사용하는 것이 좋습니다 .


한 번 해결하면 좋은 +1. 지저분하지만 시원합니다.
Kender

1
ONE_TIME_IF에 큰 문제가 있습니다. 다시 발생시킬 수 없습니다. 예를 들어 플레이어는 주 메뉴로 돌아가서 나중에 게임 장면으로 돌아갑니다. 그 진술은 다시 발사되지 않습니다. 이미 획득 한 트로피 목록과 같이 응용 프로그램 실행간에 유지해야하는 조건이 있습니다. 플레이어가 두 개의 다른 응용 프로그램 실행에서 트로피를 얻는 경우 방법은 트로피를 두 번 보상합니다.
Ali1S232

1
@Gajoo 사실, 조건을 재설정하거나 저장해야하는 경우 문제가됩니다. 나는 그것을 명확히하기 위해 편집했다. 이것이 내가 최후의 수단으로 또는 디버깅을 위해 추천 한 이유입니다.
kevintodisco

관용구 ({) while (0)를 제안해도 되겠습니까? 나는 개인적으로 무언가를 망치고 내가하지 말아야 할 곳에 세미콜론을 넣었다는 것을 알고 있습니다. 멋진 매크로, 브라보.
michael.bartnett

5

이것은 상태 패턴을 사용하는 전형적인 경우처럼 들립니다. 한 상태에서 수명 <1 조건을 확인합니다. 해당 상태가되면 지연 상태로 전환됩니다. 이 지연 상태는 지정된 지속 시간을 기다렸다가 종료 상태로 전환합니다.

가장 사소한 접근 방식에서 :

switch(mCurrentState) {
  case LIVES_GREATER_THAN_0:
    if(lives < 1) mCurrentState = LIVES_LESS_THAN_1;
    break;
  case LIVES_LESS_THAN_1:
    timer.expires(5000); // expire in 5 seconds
    mCurrentState = TIMER_WAIT;
    break;
  case TIMER_WAIT:
    if(timer.expired()) mCurrentState = EXIT;
    break;
  case EXIT:
    mQuit = true;
    break;
}

State 문 / StateMachine과 함께 State 클래스를 사용하여 switch 문 대신 상태 간의 변경 / 푸시 / 전환을 처리 할 수 ​​있습니다.

여러 활성 상태, 일부 계층 구조를 사용하는 활성 자식 상태가 많은 단일 활성 부모 상태 등 상태 관리 솔루션을 원하는대로 정교하게 만들 수 있습니다. 상태 패턴 솔루션을 사용하면 코드를 훨씬 더 재사용 가능하게 유지하고 쉽게 유지 관리하고 따를 수 있습니다.


1

성능이 걱정되는 경우 여러 번 확인하는 성능은 일반적으로 중요하지 않습니다. 부울 조건이있는 경우

다른 것에 관심이 있다면 널 디자인 패턴과 비슷한 것을 사용하여이 문제를 해결할 수 있습니다.

이 경우 foo플레이어에 생명이 남아 있지 않은 경우 메서드를 호출한다고 가정합니다 . 이것을 두 개의 클래스로 구현할 것입니다. 하나는를 호출 foo하고 다른 하나는 아무것도하지 않습니다. 그들에게 전화 수 있습니다 DoNothingCallFoo과 같이 :

class SomeLevel {
  int numLives = 3;
  FooClass behaviour = new DoNothing();
  ...
  void tick() { 
    if (numLives < 1) {
      behaviour = new CallFoo();
    }

    behaviour.execute();
  }
}

이것은 "원시"예제입니다. 핵심 사항은 다음과 같습니다.

  • 몇 가지 예를 추적 유지 FooClass될 수있는 DoNothing또는 CallFoo. 기본 클래스, 추상 클래스 또는 인터페이스 일 수 있습니다.
  • 항상 execute해당 클래스 의 메소드를 호출하십시오 .
  • 기본적으로 클래스는 아무 것도 수행하지 않습니다 ( DoNothing인스턴스).
  • 수명이 다하면 인스턴스는 실제로 무언가 ( CallFoo인스턴스) 를 수행하는 인스턴스로 교체됩니다 .

이것은 본질적으로 전략과 널 패턴의 혼합과 같습니다.


그러나 각 프레임마다 새로운 CallFoo ()를 계속 작성하므로 새로 만들기 전에 삭제해야하며 문제가 해결되지 않습니다. 예를 들어 CallFoo ()가 타이머를 몇 초 안에 실행하도록 설정하는 메소드를 호출하면 해당 타이머는 매 프레임마다 동일한 시간으로 설정됩니다. 내가 알 수있는 한 귀하의 솔루션은 if (numLives <1) {// do stuff} else {// empty}와 동일합니다
EddieV223

흠, 당신이 무슨 말을하는지 봅니다. 해결 방법은 if체크를 FooClass인스턴스 자체 로 옮기는 것이므로 한 번만 실행되고 인스턴스화하기 위해 ditto입니다 CallFoo.
ashes999
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.