2014-10-07 5 views
1

배경 정보 : 여러 개의 "바닥"이있는 SpriteKit 2D 플랫폼 스타일 게임을 만들고 있습니다. 사용자가 "포털"에 입장하면 다른 층 (하나 또는 둘 중 하나)으로 운송됩니다. 사용자가 가장 최근의 스폰 지점과 다른 층에서 사망하면 스폰 지점으로 다시 이송됩니다.SKNodes 추가, 제거 및 캐싱

문제점 : iOS8로 업그레이드 한 후이 시나리오를 실행하면 예외/오류 EXC_BAD_ACCESS이 발생하여 게임이 중단됩니다. 이러한 오류를 디버깅하는 방법에 대한 여러 자습서를 거친 후에도 문제를 찾을 수없는 것 같습니다.

누군가 내 코드를 살펴보고 내가하고있는 일을하는 더 좋은 방법을 볼 수 있다면 게임을 멈추지 않는 것이 좋습니다.

게임 레이아웃 : 나는 레이 Wenderlich의 책 자습서으로 아이폰 OS 게임에서 같은 클래스를 기반으로 사용자 정의 클래스 TileMapLayer 있습니다. 기본적으로 여러 개의 32x32p 타일을 포함하는 사용자 정의 SKNode 클래스로, 내 게임의 배경을 만듭니다.

게임 시작시 적절한 층을 적재하고 사용자가 위/아래로 이동할 때 다른 층을 불러 오라고합니다. 이 시나리오에서는 현재 층을 NSDictionary에 먼저 캐시하므로 사용자가이 층으로 돌아 오면 나중에 액세스 할 수 있습니다.

// Cache the current floor 
[_allFloors setObject:_bgLayer forKey:[NSString stringWithFormat:@"floor_%tu", (unsigned)_currentFloor]]; 

// Remove the current floor 
[_bgLayer removeFromParent]; 

// Get the cached (next) floor if it exists, if not create it 
TileMapLayer *cachedFloor = [_allFloors objectForKey:[NSString stringWithFormat:@"floor_%tu", (unsigned)(_currentFloor + 1)]]; 
if (!cachedFloor) { 
    _bgLayer = [self createScenery:[NSNumber numberWithInteger:(_currentFloor + 1)]]; 
    NSLog(@"creating new floor"); 
} else { 
    _bgLayer = cachedFloor; 
    NSLog(@"getting cached floor"); 
} 

// Display the new floor 
[_worldNode addChild:_bgLayer]; 

// Increment the floor number 
_currentFloor++; 

(나는 바닥을 내려 가기위한 유사한 방법을가집니다). iOS8로 업그레이드하기 전후에 완벽하게 작동합니다.

사용자가 사망하면 마지막 스폰 지점으로 다시 이동합니다. 마지막 스폰 지점이 다른 층에 있으면 층도 적절하게 변경됩니다. 내가 애니메이션의 일환으로 볼 자체에이 사용자 정의 SKAction 전화 :

SKAction *changeFloor = [SKAction runBlock:^{ 
    if (self.spawnFloor != _currentFloor) { 
    [_bgLayer removeFromParent]; 
    _bgLayer = [_allFloors objectForKey:[NSString stringWithFormat:@"floor_%tu", (unsigned)self.spawnFloor]]; 
    [_worldNode addChild:_bgLayer]; 
    _currentFloor = self.spawnFloor; 
    NSLog(@"Made it here"); 
    } 
}]; 

당신은 큰 차이가없는 볼 수 있듯이

. Made it here은 콘솔에 기록되지만 게임은 즉시 멈 춥니 다. 공을 올바른 위치로 이동시키기 위해 내가 호출하는 다음 메서드는 전혀 실행되지 않습니다.

그냥 재미를 위해 나는과 같이 부모로부터 제거하기 전에 _bgLayer 캐싱을 시도 :

if (self.spawnFloor != _currentFloor) { 
    [_allFloors setObject:_bgLayer forKey:[NSString stringWithFormat:@"floor_%tu", (unsigned)_currentFloor]]; 
    ... 

그리고 나는이 작업을 수행 할 때 게임이 정지하지 않는 몇 가지 이유. 그러나 바닥은 마치 _bgLayer이 부모로부터 절대 제거되지 않은 것처럼 섞여 버립니다. (참고 : "오래된"타일은 더 이상 물리 시뮬레이션에 반응하지 않으며 배경이없는 상태입니다.)

[_bgLayer removeFromParent]; 
[_bgLayer removeAllChildren]; 

을하지만 내가 respawning 후이 바닥에 반환 할 때 _bgLayer가 비어 원인 :

는 또한 바로 부모로부터 제거한 후 (개별 타일을 제거) [_bgLayer removeAllChildren]을 시도했다. 마치 사전에 저장하기 전에 모든 노드를 제거한 것처럼 말입니다. 내 생각에 사전은 콘텐츠 자체가 아닌 _bgLayer의 위치 만 참조합니다. 그래서 화면에서 모든 아이들을 제거 할 때 캐시에서 모든 아이들을 효과적으로 제거합니다.

요약 : 이 질문은 오랜 질문이며, 작성해 주신 것에 대해 감사드립니다. 질문이 있으시면 아래 의견에 주저하지 마십시오. 궁극적으로, 제가 알고 싶은 것은 이것입니다 : 게임이 멈추지 않도록 내 문제를 어떻게 해결할 수 있습니까? 메모리 문제없이 바닥을 캐싱하는 가장 좋은 방법은 무엇입니까? 화면에서 노드를 제거하고 추가하는 가장 좋은 방법은 무엇입니까? 항상 현재 바닥 (화면에있는 바닥)을 _bgLayer이라고 말하면 어떨까요?

다시 한 번 감사드립니다.

+0

예외 중단 점을 추가 했습니까? 충돌이 발생할 때 Xcode가 강조하는 라인은 무엇입니까? 호출 스택이란 무엇입니까? 어떤 종류의 오류 메시지가 로그에 인쇄됩니까? – LearnCocos2D

+0

예외 중단 점을 추가하려고 시도했지만 xcode는 @autoreleasepool을 제외한 모든 행을 강조 표시하지 않습니다. 콘솔에 아무 것도 인쇄되지 않습니다 ... :( – Aleksander

답변

2

iOS8은 블록 내부의 노드를 제거 할 때와 다른 것처럼 보입니다. 백그라운드 스레드에서 수행하려고 시도한 것처럼 가끔 이상한 충돌이 발생합니다. 그것은 버그일지도 모르지만 그때까지 나는 두 가지를 제안 할 것입니다.

1. 제거 할 노드를 블록 내에서 표시하지만 update: 루프에서 수행하십시오. 어떤 차이가 있는지 눈치 채지 못할 것입니다.

2.- removeFromParent가 주 스레드에서 발생하는지 확인하십시오. 그러나 이것이 문제를 해결할 지 확신하지 못합니다.

__strong typeof(_bgLayer) strongLayer = _bgLayer; 
dispatch_async(dispatch_get_main_queue(), ^{ 
     [strongLayer removeFromParent]; 
    }); 
+0

안녕하세요, 감사합니다! 삭제를 표시하고 업데이트 루프에서 실행) 실제로 어제 밤에 마음에 들었으므로 시도하고 작동했습니다. 조금 더 많은 작업이 있습니다. 이전보다 더 빨리 수정해야 겠지만, 지금은 괜찮습니다. 정말 고마워요! :) – Aleksander