글로벌 / 싱글 렛이 게임 개발에 유용한 경우가 있습니까? [닫은]


11

전역 변수 또는 싱글 톤 클래스를 사용하면 테스트 / 관리가 어려울 수있는 사례가 생성되며 코드에서 이러한 패턴을 사용하는 데 어려움을 겪었지만 종종 배를 타야합니다.

전역 변수 또는 싱글 톤이 실제로 게임 개발에 유용한 경우가 있습니까?

답변:


30

이것들은 항상 유용 할 수 있습니다 . 그것이 가장 예쁘거나 안전한 솔루션인지 여부는 또 다른 문제이지만, 게임 개발에는 어느 정도의 실용주의가 필요하다고 생각합니다.


내 진언에 매우 가깝습니다.
Ólafur Waage

15

완전하지 않은 목록이지만 여기에 간다.

단점

  • 평생 관리 . 단일 시스템과 전역은 설정 방법에 따라 키 시스템 (예 : 힙)이 초기화되기 전에 시작하기를 원할 수 있습니다. 누출 추적을 수행하려는 경우 (예 : 누출 추적을 수행하는 경우 유용) 분해 순서에주의를 기울이거나 피닉스 싱글 톤 과 같은 일을 시작해야 합니다. ( 정적 초기화 순서 fiasco 참조 )
  • 액세스 제어 . 렌더링은 게임 데이터에 대한 const 액세스 권한 만 있어야하고 업데이트는 일관성이 없습니까? 싱글 톤 및 글로벌에서는 적용하기가 더 어려울 수 있습니다.
  • . Kaj가 지적했듯이 단일 공유를 비공유 메모리 아키텍처에서 작동하도록 기능적으로 분해하고 변환하는 것이 더 어렵습니다. 또한 다른 유형의 NUMA 시스템 (로컬이 아닌 메모리에 액세스)에 잠재적 인 성능 영향을 미칩니다 . 싱글 톤은 일반적으로 중앙 집중식 상태를 나타내므로 순도에 의해 쉽게 변형되는 변형의 대립입니다.

찬반 양론

  • 동시성 . 동시 환경에서 싱글 톤은 고통 (데이터 레이스 / 재진입 문제를 고려해야 함) 또는 축복 (중심화 및 잠금 및 리소스 관리에 대한 추론) 일 수 있습니다. 스레드 로컬 스토리지와 같은 것을 영리하게 사용하면 잠재적 인 문제를 다소 완화 할 수 있지만 일반적으로 쉬운 문제는 아닙니다.
  • Codegen (컴파일러, 아키텍처 등에 따라 다름) : 순진한 최초 사용 싱글 톤 구현의 경우 각 액세스에 대해 추가 조건부 분기를 평가할 수 있습니다. 함수에 사용되는 여러 전역이 리터럴 풀을 부 풀릴 수 있습니다 . "전역의 구조체"접근 방식은 리터럴 풀의 공간을 절약 할 수 있습니다. 구조체의베이스에 하나의 항목 만로드 한 다음로드 명령어로 인코딩 된 오프셋입니다. 마지막으로 모든 비용으로 글로벌 및 싱글 톤을 피하려면 일반적으로 포인터, 참조 또는 복사본을 전달하기 위해 최소한 추가 메모리 (레지스터, 스택 또는 힙)를 사용해야합니다.

찬성

  • 단순성 . 위에 나열된 단점이 그다지 영향을 미치지 않는다는 것을 알고 있다면 (예를 들어 단일 CPU 핸드 헬드 플랫폼의 단일 스레드 환경에서 작업하는 경우) 위에서 언급 한 인수 전달과 같은 일부 아키텍처를 피하십시오. 경험이 적은 코더에게는 싱글 톤과 글로벌을 이해하기가 더 쉽습니다 (잘못 사용하는 것이 더 쉽지만).
  • 평생 관리 (다시). "글로벌의 구조체"또는 처음 생성시 요청 이외의 메커니즘을 사용하는 경우 초기화 및 삭제 순서를 쉽게 읽고 세밀하게 제어 할 수 있습니다. 이를 어느 정도 자동화하거나 수동으로 관리합니다 (전역 / 단일 수 및 상호 종속성의 수에 따라이를 관리하는 것은 장단점이 될 수 있습니다).

우리는 핸드 헬드 타이틀에서 "글로벌 싱글 톤의 구조체"를 많이 사용합니다. PC와 콘솔 타이틀은 덜 의존하는 경향이 있습니다. 우리는 이벤트 중심 / 메시징 아키텍처로 더 전환 할 것입니다. PC / 콘솔 타이틀은 여전히 ​​중앙 TextureManager를 사용합니다. 일반적으로 단일 공유 리소스 (텍스처 메모리)를 래핑하기 때문에 이것은 우리에게 의미가 있습니다.

API를 비교적 깨끗하게 유지하면 필요할 때 싱글 톤 패턴에서 리팩토링 (또는 리팩토링)하기가 너무 어렵지 않을 수 있습니다 ...


훌륭한 목록. 개인적으로 나는 공유 된 (돌릴 수있는) 상태에서 오는 문제 때문에 싱글 톤과 글로벌에서 부정적인 가치 만 찾습니다. "codegen"문제는 문제가되지 않습니다. 레지스터를 통해 포인터를 전달하거나 그것을 얻기 위해 일련의 작업을 수행해야합니다. 코드와 데이터 크기를 눈에 띄게 변경한다고 생각하지만 합계는 거의 같습니다.
dash-tom-bang

그렇습니다. 코드 젠을 다시 살펴보면 실제로 실제로 본 유일한 차이는 개별 전역에서 전역 테이블로 이동하는 것입니다. 리터럴 풀을 축소하여 .text 섹션 크기를 몇 퍼센트 줄였습니다. 작은 시스템에서는 아주 좋은 것입니다. =) 리터럴에 대해 dcache를 너무 많이 치지 않는 성능 이점은 단지 (대부분의 경우 작지만) 부수적 인 이점이었습니다. 글로벌 테이블로 이동하는 또 다른 장점은 더 빠른 메모리에있는 섹션으로 테이블을 쉽게 이동할 수 있다는 것입니다.
leander

4

그것들은 특히 프로토 타이핑 또는 실험적 구현 중에 매우 유용 할 수 있지만 일반적으로 관리자와 같은 구조에 대한 참조를 전달하는 것이 좋습니다. 글로벌 및 싱글 톤 (내 의견으로는)과 관련하여 가장 큰 문제는 멀티 스레드에 적합하지 않으며 SPU와 같은 비 공유 메모리로 이식하는 것이 훨씬 어렵다는 것입니다.


2

싱글 톤 디자인 자체는 전혀 유용하지 않다고 말하고 싶습니다. 전역 변수는 분명히 유용 할 수 있지만 잘 작성된 인터페이스 뒤에 숨겨져있어 자신의 존재를 알지 못합니다. 싱글 톤을 사용하면 자신의 존재를 확실하게 알 수 있습니다.

나는 종종 엔진 전체를 통해 액세스가 필요한 것들에 전역 변수를 사용합니다. 내 퍼포먼스 툴은 엔진 전체에서 호출하는 좋은 예입니다. 전화는 간단합니다. ProbeRegister (), ProbeHit () 및 ProbeScoped (). 그들의 실제 접근은 조금 더 까다 롭고 일부에 전역 변수를 사용합니다.


2

글로벌의 주요 문제, 잘못 구현 된 싱글 톤은 모호한 구성 및 해체 버그입니다.

따라서 이러한 문제가 없거나 포인터로 문제를 잘 알고있는 프리미티브로 작업하는 경우. 그러면 안전하게 사용할 수 있습니다.

지구본은 자신의 위치를 ​​가지고 있으며, 고토 스와 동일하며 손을 떼지 말고 조심스럽게 사용해야합니다.

Google C ++ 스타일 가이드 에 좋은 설명이 있습니다.


나는 이것이 주요 문제라는 것에 동의하지 않는다. "글로벌을 사용하지 마십시오"는 존재 생성자보다 더 나아갑니다. 나는 그들에게 두 가지 큰 문제가 있다고 말할 것입니다. 첫째, 그들은 프로그램에 대한 추론을 복잡하게 만듭니다. 전역에 액세스하는 함수가있는 경우 해당 함수의 동작은 자체 인수뿐만 아니라 전역에 액세스하는 다른 모든 함수에 따라 다릅니다. 생각해야 할 것이 훨씬 더 많습니다. 둘째, 모든 것을 머리에 담을 수는 있지만 (더 이상 할 수는 없지만) 더 복잡한 동시성 문제가 발생합니다.

@Joe의 핵심은 "종속성"입니다. 전역 (또는 싱글 톤 또는 다른 공유 상태)에 액세스하는 함수는 이러한 모든 것에 암묵적인 의존성을 갖습니다. 모든 종속 항목이 명시 적이면 전체 코드 목록이 인수 목록에 문서화되어있을 때 얻을 수있는 코드의 일부를 추론하는 것이 훨씬 쉽습니다.
dash-tom-bang

1

전역 함수는 함수 호출 사이에 상태가 필요한 시스템을 빠르게 프로토 타이핑하는 동안 유용합니다. 시스템이 작동한다는 것을 확인하면 상태를 클래스로 옮기고 함수를 해당 클래스의 메소드로 만듭니다.

싱글 톤은 자신과 타인에게 문제를 일으키는 데 유용합니다. 더 많은 전역 상태를 도입할수록 코드 정확성, 유지 관리, 확장 성, 동시성 등과 관련하여 더 많은 문제가 발생합니다.하지 마십시오.


1

"단일 인스턴스"수명 관리자를 사용하는 경우에도 싱글 톤 대신 사용자 지정 수명 관리 기능이있는 일종의 DI / IoC 컨테이너를 사용하는 것이 좋습니다. 그런 다음 테스트를 용이하게하기 위해 구현을 쉽게 교체 할 수 있습니다.


모르는 분들을 위해 DI는 "종속성 주입"이고 IoC는 "제어의 반전"입니다. 링크 : martinfowler.com/articles/injection.html (이전에 읽었지만 약어를 보지 못했기 때문에 약간의 검색이 필요했습니다.) +1이 훌륭한 변형을 언급했습니다.
leander


0

싱글 톤은 초기 프로토 타입에 공유 상태를 저장하는 좋은 방법입니다.

그것들은 은색 총알이 아니며 몇 가지 문제가 있지만 특정 UI / 논리 상태에 매우 유용한 패턴입니다.

예를 들어 iOS에서는 [UIApplication sharedApplication]을 가져 오는 데 싱글 톤을 사용하고 cocos2d에서는 [CCNotifications sharedManager]와 같은 특정 객체에 대한 참조를 가져 오는 데 사용할 수 있으며 개인적으로는 보통 [Game sharedGame] 싱글 톤으로 시작할 수 있습니다. 다양한 구성 요소간에 공유되는 저장 상태.


0

와우, 나는 개인적으로 싱글 톤 패턴에 문제가 없었기 때문에 나에게 흥미 롭습니다. 현재 진행중인 프로젝트는 Nintendo DS의 C ++ 게임 엔진이며, 많은 하드웨어 액세스 유틸리티 (예 : VRAM Banks, Wifi, 두 개의 그래픽 엔진)를 단일 인스턴스로 구현하고 있습니다. 기본 라이브러리의 함수.


내 질문은 왜 싱글 톤을 사용합니까? 사람들은 정적 함수로만 구성된 단일 톤 또는 클래스로 API를 래핑하는 것을 좋아하지만 왜 클래스를 전혀 신경 쓰지 않습니까? 함수 호출 (원하는대로 랩핑)을 갖는 것이 더 쉬운 것처럼 보이지만 내부에서 "전역"상태에 액세스합니다. 예를 들어 Log :: GetInstance ()-> LogError (...)는 클라이언트 코드를 알 필요없이 전역 상태에 내부적으로 액세스하는 LogError (...) 일 수도 있습니다.
dash-tom-bang

0

컨트롤러가 하나만 있지만 여러 모듈에서 처리하는 항목이있는 경우에만 해당됩니다.

예를 들어, 마우스 인터페이스. 또는 조이스틱 인터페이스. 또는 음악 플레이어. 또는 사운드 플레이어. 또는 화면. 또는 파일 저장 관리자.


-1

지구본이 훨씬 빠릅니다! 따라서 게임과 같은 성능 집약적 응용 프로그램에 완벽하게 적합합니다.

싱글 톤은 더 나은 글로벌 IMO이므로 올바른 도구입니다.

드물게 사용하십시오!


무엇 보다 훨씬 빠릅니다 . 전역은 포인터 인 경우 임의의 메모리 페이지에 있고 값은 고정이지만 먼 메모리 페이지가 될 것입니다. 컴파일러는 유용한 들여다 보는 구멍 최적화를 사용할 수 없습니다. 내가 생각할 수있는 모든 조치에 따르면 세계는 느리다 .

getter 및 setter보다 빠르며 참조를 전달하는 것보다 빠릅니다. 그러나 별로는 아닙니다. 크기를 줄이는 데 도움이되므로 일부 시스템에서는 도움이됩니다. 나는 '많이'라고 말할 때 아마도 과장했지만, 당신이 무언가를 사용해서는 안된다고 말하는 사람은 매우 회의적입니다. 상식 만 사용하면됩니다. 결국 싱글 톤은 정적 클래스 멤버에 지나지 않으며 전역입니다.
jacmoe

그러나 글로벌과의 동기화 문제를 해결하려면 게터 / 세터가 필요하므로 실제로는 관련이 없습니다. 전역 상태의 문제점 (및 드문 이점)은 인터페이스의 속도 또는 (효과적으로) 구문 측면에 대한 염려가 아니라 전역 상태라는 것입니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.