2016-09-23 1 views
3

내 Sprite-Kit 게임에서 내 게임은 다양한 장면을 통해 진행되며, 여기를 통해 특정 변수를 사용할 수 있습니다. 다음 그림을 참조하십시오 스프라이트 키트 게임 라이프 사이클, 속성 및 전역 변수?

enter image description here

그래서 타이틀 화면은 메인 게임 화면을로드합니다. '물결'이 끝나면 (모든 객관적인 완료 또는 모든 생명의 손실) 최종 장면에 몇 가지 통계가 표시됩니다. 그런 다음 플레이어의 생명이 남아있는 경우 기본 게임이 다시로드되고 그렇지 않으면 제목 화면이 다시 표시됩니다.

메인 게임 장면과 마지막 장면에서 사용할 수있는 플레이어의 점수와 라이프 수를 원합니다. 높은 점수는 모든 장면에서 유지되어야합니다.

대부분 변수는 클래스에 정의 된 속성이지만 전역 변수를 사용하고 있습니다.

높은 점수 또는 여러 생명을 추가하기 전에 처음으로이 작업을 수행해야했습니다. 다음 장면에 대해 PresentScene을 수행 할 때 모든 장면에서 점수 속성을 사용하고 장면 사이를 전달했습니다. 그러나 그것은 특히 변수를 더 추가해야 할 때 어색해 보였습니다.

글로벌 변수를 사용할지 여부를 결정하는 것이 가장 좋습니다. 그렇지 않은 경우 속성을 처리하는 올바른 방법이 있습니다. 속성을 초기화하는 위치에서 제어 할 수 있습니다. init에서 didMoveToView과 비교하여 장면이 다시 표시 될 때 다시 초기화되는지 확인합니다.

편집 :

내가 다시 다시 초기화 할 수있는 메인 화면의 원인이되는 메인 게임 화면에 다시 끝 웨이브 장면에서 갈 때 내가 '잘못'일을하고 있습니까? 그것을 보존하는 방법이 있나요 (그것의 모든 속성과 함께) 그래서 그것은 완전히 다시 초기화되지는 않았지만 didMoveToView가 다시 호출 되었습니까?

+0

https://thatthinginswift.com/singletons/ – 0x141E

+2

NSCoding 및 iCloud를 사용하기 때문에 sharedIstance의 클래스를 사용합니다. –

+0

여전히 이러한 답변 및 의견을 통해 무엇을 해결하려고 노력하고 있습니까? 나는 일을 해요! –

답변

4

일반적인 경험 법 : 글로벌 변수를 피할 수 있으면 그렇게하십시오.

이제 모든 노드에는 userData이라는 변경 가능한 사전이 있습니다. 이것은 노드에 데이터를 저장하는 데 사용됩니다.

새 장면을 만들 때 이전 장면에서 필요한 모든 데이터를 전송합니다.

let scene = SKScene(fileNamed:....) 
scene.userData["highscore"] = self.highscore 
view.presentScene(scene) 

이제 새로운 장면에서 didMove(view:) 방법, 당신은 0x141E으로 userData

는 지적했다 읽고 당신이 기회는 당신이 사용자 정의 클래스를 가지고 있기 때문에, 당신이 할 수있는, userData를 초기화해야 기억 귀하의 init 메소드에서, 당신이 편리한 메소드에 접근 할 수 있도록 모든 지정된 inits를 오버라이드하는 것을 기억하십시오. 회오리 바람 사이의 의견에 자신을 언급 한 바와 같이

또한, SKNode 당신이 가서 당신의 정보를 저장할 때, 당신은 전체 SKNode을 저장하고 userData는 그것과 함께 갈 것입니다, 준수 NSCoding입니다.

이것은 노드를 저장 /로드하기 위해 SKNode의 확장을 작성하면 다른 클래스를 관리하는 것을 걱정하지 않아도된다는 것을 의미합니다.

+1

얼마나 많은 사람들이이 속성에 대해 실제로 사용하는지/알고 있는지 궁금합니다 ...이 속성은 중요하지 않은 데이터에 대해 노드마다 확실히 사용될 수 있기 때문에 내 upvote를 얻었습니다. 이 속성을 사용하는 Iirc는 디스크의 데이터를 plist 파일에 저장하는 것과 같습니다. 맞습니까? – Whirlwind

+0

@Whirlwind 그것은 단지 일반적인 NSMutableDictionary이지만 SKNode는 NSCoding과 호환되므로 디스크에 SKNode를 저장할 때 userData가 함께 따라갑니다. 정말 멋집니다. – Knight0fDragon

+0

아 좋아, 디스크에 저장할 수 있다는 것을 알았지 만 자동 저장이 아닌 것을 잊어 버렸습니다. – Whirlwind

4

게임을 실행하는 동안 많은 일이 발생할 수 있습니다. 게임에 크래시가 발생하고 플레이어가 앱을 종료하고 인터넷이 중지되면 노드가 부모로부터 일시 중지 될 수 있습니다 (장면이 액션을 처리 할 때 건너 뜁니다) . 이런 이유로 저는 Simone Pistecchia의 의견에 동의합니다. 플레이어 통계를로드하고 저장하고, NSCoding을 사용하여 선언하고 초기화하고, 공유 인스턴스 클래스를 사용하여 프로젝트 전체에서 처리하도록 "저장 관리자"클래스를 준비 할 수 있습니다. 이런 식으로 당신이 필요로 할 때 당신의로드를로드하고 저장할 수 있습니다. 게임에서 빠져 나올 수도 있습니다. (메인 메뉴에서 "하이 스코어"장면을 원하는 곳 어디에서나 볼 수 있습니다.)

+1

Ray Wenderlich도이 인수를 여기에서 설명합니다 https://www.raywenderlich.com/63235/how-to-save-your-game-data-tutorial-part-1-of-2 –

+0

계속 추적하기에 과잉이라고 생각하지 않습니까? 플레이어의 현재 점수 중? –

+0

모든 요소가있는 intere 장면이나 노드보다 하나의 var를 파일에 저장하는 것이 더 좋으며 저장소로 데이터로드 시간이 향상되고 텍스처와 같은 불필요한 데이터를 저장하지 않으므로 프로젝트를 기반으로 전략을 선택하십시오. –

5

많은 사람들이 일하는 것을 알고 있습니다. "전역 사용 안 함"또는 "단독 사용은 악"과 같은 일반적인 규칙이 적용됩니다. 이러한 지혜의 단어는 때때로 적용될 수 있지만, 게임을 끝내는 데있어보다 효율적으로 사용할 수있는 방법으로 보는 것이 좋습니다.

전역/싱글 톤이 유용 할 때가 있습니다. 게임 및 임베디드 시스템은 기존 응용 프로그램과 다른 규칙으로 실행됩니다. 이들은 일반적으로 자원 요구 사항과 결합 된 자원 제약 "응용 프로그램"입니다. 지구본은 각각의 생각보다 더 일반적입니다. 싱글 톤 또한 일반적으로 사용됩니다. 필자는 임베디드 시스템과 게임 모두에서 작업했으며, 내가 작업 한 모든 프로젝트에서 전역, 싱글 톤 또는 둘 다를 사용했다고 말할 수 있습니다.

실제로는 전역에 대해 싱글 톤을 사용합니다. 주로 전역 파일을 어디에 밀어 넣을지 걱정할 필요가 없으며, 헤더 파일이 속한 파일이 무엇인지 걱정할 필요가 없기 때문에 주로 그 이유에 대한 근거가 모든 Swifties와 다를 것입니다. 나에게이 물건은 C++에서 약간의 Obj-C와 스위프트 (Swift)의 흩어져있는 부분에서 처리된다.

게임 라이프 사이클을 관리해야하는 필요성은 SpriteKit과 별개입니다. 내가보고있는 SpriteKit 게시물 중 상당수는 개발자 또는 씬 (scene)에 게임 상태를 포함시키는 데 사용됩니다. 그들은 보통 상황을 다음 장면으로 옮기는 방법에 대한 몇 가지 딜레마에 직면 해 있습니다. 이 방법이 "로컬 상태"(즉, 현재 화면, 스테이지 등의 상태)에 좋을 수도 있지만 글로벌 게임 상태에는 좋지 않습니다. 예를 들어 기본 화면에서 옵션 화면으로 전환 한 다음 다시 메인 화면으로 돌아간 다음 게임에서 변경 사항을 추적하는 경우 (예 : 게임 난이도 변경)? 그렇습니다. 사전, 구조체 또는 주위에있는 것들을 확실히 전달할 수 있습니다. 그러나 어떤 시점에서, 당신은 하나의 공통적이고 편리한 투기장을 갖는 것이 더 편리하다는 것을 발견하게 될 것입니다. 이 "덤핑 그라운드"는 글로벌/싱글 톤이 될 것입니다.

이제 나에게 소리 지르기 시작하기 전에 전역과 싱글 톤에 대한이 모든 미친 이야기를 포착 할 수 있습니다. 나는 전역/싱글 톤을 많이 만들지는 않는다. 오히려 글로벌/싱글 톤을 사용하는 경우 제어 할 수 있습니다.

저는 'gazillion'이라고 말하지 않습니다. 나는 하나처럼 이야기하고있다. (나는 보통 소수만 사용한다.) 우리의 허구의 친구, GameManager에 들어가십시오.

공간 사수의 아주 간단한 시나리오를 살펴 보겠습니다. 제목 화면, 기본 화면, 게임 화면 및 끝 파도 화면과 같은 여러 화면이 있다고 가정 해보십시오. 여러 화면으로 전달되는 데 유용한 정보가 있습니다. 예를 들어 높은 점수 또는 현재 점수. 이러한 값은 다양한 화면/장면에 표시하려는 값일 수 있습니다. 그럼 점수는 어디에 저장합니까? 그들 사이에 점수를 어떻게 전달합니까? 예를 들어, 모든 화면에 마지막으로 높은 점수가 표시되면 어디에서 그 값을 유지해야합니까?

글로벌 인 경우 범위를 벗어나는 인스턴스의 전역 정의가 있습니다.

:처럼 : 당신이 점수를 UDPATE 할 경우는 같이 보일 것이다 점수를 업데이트 싱글이라면

var theGameManager : GameManager

그래서, 당신은

theGameManager.score += 100

을 할 거라고 GameManager.sharedInstance.score += 100

좋아요. 싱글 톤 구문은 조금 길지만 아마 이것이 어디서 무엇인지에 관해서는 세계보다 덜 암시 적이다.

하지만 이제 여기에 더 많은 힘을 줄 수 있습니다. 점수를 추가 할 때마다 100000의 배수가 당신에게 여분의 점수를 부여한다고 가정 해 봅시다. 나는 이제 세터를 쉽게 활용하고 그 세터를 사용하여 여분의 생명에 보답 할 수 있습니다. 예를 들어이 들어

:

GameManager.sharedInstance.score += 100 // the magic of the 100000 for extra life happens automagically in the setter

이 같은 가능성이 뭔가 대 :이 같은

myScore += 100 

// Do logic to find out if we get an extra life. 
// Is this done in a method and if so where does this method live? 
// Or is it straight up code which will be duplicated? 

코드 글로벌 게임 상태를 관리하는 데 도움이 모든 깔끔하게 GameManager 내에 포함되어 있습니다. 즉, GameManager은 값을 유지 관리하는 것이 아니라 이러한 값을 중심으로 기능을 캡슐화하는 방법을 제공합니다.

흠, 여기는 currentScene 인 것 같습니다. 왜 그랬을까요? GameManager은 장면 전환을 관리하는 훌륭한 방법이기도합니다. 게임 상태를 통해 할 가능성이 더 큽니다. 같은 아마 뭔가 : 장면 뒤에

GameManager.sharedInstance.gameState = GameOverState

gameState 세터 장면을 교환의 마법을 할 다음 수 있습니다.

이러한 방법의 실제적인 예가 될 수 있습니다.

로딩/저장에 관한 이야기는 원하는 경우 GameManager을 통해 수행 할 수도 있습니다. 나 자신을 위해, 나는 일반적으로 GameManager 내의 일부 데이터를 기반으로 독립적으로 취급합니다. 그리고 이것은 또 다른 구별을 불러옵니다. 일반적으로 GameManager 내에 아이디어를 캡슐화합니다. 예를 들어, 나는 아마 Player있을 것하고는 GameManager 모습과 같이 할 것 :

class GameManager : NSObject { 
    // Overall game state, GameState is probably some enum 
    var state:GameState 
    var lastState:GameState 

    // Sometimes tracking scenes is useful 
    var currentScene:SKScene? 
    var nextScene:SKScene? // Sometimes helpful if you need to construct the next scene and it takes a non-trivial amount of time 

    // These are the globals we need to share to different "parts" of the game 
    // Note player state is done through this object. This way I can pass around only player when needed. 
    var player: Player 

    var stage: Uint 

    var highscores:[Uint] // A list of all the high score for the user. You would probably want HS based on other users too 

    // Egads a singleton! 
    static let sharedInstance = GameManager() 
} 

은 당신이 말하는 기다립니다; "하지만이 글로벌/싱글 톤 재즈가 마음에 들지 않습니다. 왜이 곡을 필요로하는 모든 사람들에게 전달하지 않는 것이 좋을까요?" 대답은 "할 수있다"입니다. 그러나 실제로, 당신은 이것이 지루 해지는 것을 발견하기 시작할 것입니다. GameManager이 필요하다는 것을 알았지 만 발신자에게 GameManager이 전달 된 적이 없었습니다. 이것은 이제이 객체를 통과시키는 몇 가지 방법을 재 작업한다는 것을 의미합니다.

Soooo는 (는) 글로벌/싱글 톤입니다. 당신은 당신 자신의 판사가 될 수 있습니다. 누구나 선택할 수있는 해결책이 있습니다. 그리고 그것이 당신을 위해 작동한다면, 그것을 사용하십시오. 나를 위해, 그것은 생각할 필요가 없다.구현을 통해 전역 게임 상태에 액세스/관리 할 때 일관성있는 사용 용이성에 대한 이점은 확실한 선택입니다.

에 대한 이미 긴 대답을 위해 여기에 조금 더 많은 정보를 추가하려고합니다. 몇 가지를 명확히 해보십시오.

싱글 톤 또는 글로벌 선택은 사용법, 플랫폼 및 언어에 따라 다릅니다. 또한 게임 코드 기반에 따라 달라집니다. 그것은 단지 어떤 데이터의 투기장이되는 것이 아닙니다. 활용할 경우 내용을 신중하게 고려해야하며 가능한 경우 내용에 다른 용기를 사용하십시오 (예 : 수업에서 포장하십시오). 따라서 첫 번째 예제에 플레이어 정보가 매달려 있지만 (실제로 아이디어를 전달하기 쉽도록) 실제로는 PlayerState에 플레이어 데이터가 포함되어 있습니다. 전체적으로 플레이어가 아닐 수도 있지만 플레이어의 게임 수명을 넘어서 살아야하는 일부 공유 정보 일 수 있습니다.

class PlayerState { 
    var score:Uint 
    var highscore:Uint 
    var lives:Uint 
} 

// Simplified GameManager 
class GameManager { 
    var state:GameState 
    var lastState:GameState 

    var player:PlayerState 

    // Egads a singleton! 
    static let sharedInstance = GameManager() 
} 

또한, 나는 항상 전역/싱글을 통해 그것을 잡기 위해 코드를 기대 대 PlayerState 예를 주위에 전달 끝날 것입니다.

그래서 예를 들어 내가 이런 짓을 했을까 :

func doSomething(playerState:PlayerState) { 
    var score = playerState.score 

    // Blah blah blah 
} 

베루스
func doSomething() { 
    // Whee I have singleton 
    var score = GameManager.player.score 

    // Blah blah blah 
} 

효과적으로 제가 데이터에 액세스 할 수 앵커 포인트로 싱글 톤을 사용하고 있어요 것은 객체 더 쉽게 다음 필요한 경우 전달하십시오. 다시 말하면, 그들이 고용되어있는 동안, 나는 또한 코드가 GameManager.sharedInstance으로 가득 차는 것을 원하지 않는다.

앞서 언급했듯이이 접근법에는 확실한 단점이 있습니다. 하나는 동시성입니다. 그러나 글로벌/싱글 톤이 아닐지라도 동시성은 여전히 ​​데이터에서 발생할 수 있다고 지적 할 것입니다.

이 추가 사항은 게임 개발사 (일반적으로 코딩)가 어느 정도의 실용성이 있다는 것입니다. 게임을 발표 할 때 시스템 리소스와 균형을 맞추는 것뿐만 아니라 사람들이 게임을 할 수 있도록 게임을 만들고있는 것 같습니다. 그리고 그 일이 일어나려면 그 일을 끝내야합니다. 모두가 완벽한 눈송이를 만들고 싶어하지만 그 완벽을위한 시간은 무엇입니까? 대답의 시작 부분에서 나는이 기법이 도구라는 것을 지적했다. 전역/싱글 톤은 모든 사람에게 적합한 것은 아닙니다. 동시에, 디자인 진언을 맹목적으로 따르지 않아야합니다. 그것이 당신을위한 효과적인 도구가 될 수 있고 당신이 당신의 게임을 더 빨리 끝낼 수 있다면, 그 일은 가치있는 일입니다. 항상 다른 게임을 쓸 것이므로, 경험에 기반하여 무엇이 효과적인지, 그렇지 않은지를 결정할 수 있습니다.

+0

모든 대답의 어머니. 지혜로도! 고맙습니다!!! 싱글 톤과 전역을 사용하고자하는 욕구에 대해 감히 대답 할 때마다이 내용을 인쇄하고 가리키고 있습니다. 지금 ... 만약 내가 그 차이를 알았다면 ... – Confused

+1

당신이 도움이 되었기 때문에 기쁩니다. 항상 diff 도구를 가능한 도구로보고 모든 사람이 적합하다고 말하는 것에 기초하여 무릎 덩어리 반응을 시도해 보는 것이 좋습니다. 자주 잃어버린 항목은 프로젝트를 완료하고 수행해야 할 필요가 있으므로 모든 사람이 상반 관계에 직면합니다. 때로는 이러한 상충 관계로 인해 사람들이 싫증 나게됩니다. 괜찮아. 당분간, 내가 너라면, 아마도 싱글 톤의 첫 번째 대 전역을 생각할 것이다. 가장 좋은 방법은 가능한 한 많이 만들고, 마음을 열고, 실수 나 다른 사람들로부터 배우는 것입니다. –

+0

@MobileBen, 20 년 전이 아니기 때문에 "프로젝트 완료"가 디자인 할 때 고려해야 할 유일한 것이 아니기 때문에 패턴과 관행이 필요한 이유입니다. 코드가 살아 있고 업데이트 중이며 누구나 유지 관리 할 수 ​​있도록 디자인해야합니다 (영원히 살지 않을 것입니다). 그렇지 않으면 수정 및 업데이트로 시간을 낭비하게됩니다. 그래서 싱글 톤이나 글로벌과 같은 절충안을 보게되면 사람들은 자신을 상대 할 때 얼마나 많은 시간을 낭비하게되는지 깨닫기 때문에 사람들이 싫어하는 이유입니다. – Knight0fDragon