2016-11-09 6 views
3

Я пытаюсь научиться создавать класс типа GameManager и создавать индивидуальные классы для каждого из моих GameScenes ... возможно, это неправильно, но ради этого вопроса, пожалуйста, примите это как способ заниматься вещами.создавать подклассы SKScene программно, без информации о размере?

Моего GameManager выглядит так, имея ссылку на каждый из сцен, это статическое:

import SpriteKit 

class GM { 

    static let scene2 = SecondScene() 
    static let scene3 = ThirdScene() 
    static let home = SKScene(fileNamed: "GameScene") 
} 

Как создать SKScene программно, без информации размера, так как они находятся в подклассе SKScene и не имеют ни малейшего представления, что размер точка зрения, и я не хочу, чтобы нужно беспокоиться об этом:

Я делаю это, но получение EXC_BAD_ACCESS на convenience override init()

class SecondScene: SKScene { 

    override init(size: CGSize){ 
     super.init(size: size) 
    } 

    convenience override init(){ 
     self.init() 
     self.backgroundColor = SKColor.red 
     self.anchorPoint = CGPoint(x: 0.5, y: 0.5) 
    } 
} 
+1

Ваш метод init вызывает себя рекурсивно, что вызывает переполнение * стека * :) –

+0

Сравнить http://stackoverflow.com/questions/30798642/overriding-init-in-a-customuiview-crashes-app-exc-bad -access, который, похоже, касается одной и той же проблемы в подклассе UIView. –

+0

Хотелось бы, чтобы это было легко, для меня, чтобы понять. Если я изменю self.init() на super.init(), я получаю ошибку: «Инициализатор удобства для« SecondScene »должен делегировать с self.init, а не привязывать к суперклассу ... – Confused

ответ

6

Как я уже говорил, ваш вопрос немного расплывчатый, но позволяет сделать несколько примеров того, что может быть классом GameManager.

Перед тем, как начать позволяет различать вызова этого

let scene = StartScene(size: ...) 

и этот

let scene = SKScene(fileNamed: "StartScene") 

1-ый метод, с размером, когда вы создаете свои сцены все в коде, и вы не используете xCode визуальный редактор уровня.

Второй метод - это когда вы используете редактор уровня Xcode, поэтому вам нужно будет создать файл StartScene.sks. Его файл .sks, который он ищет в fileNamed.

Теперь, для примера некоторых игровых менеджеров, сначала представим себе, что у нас есть 3 SKScenes.

class StartScene: SKScene { 

     override func didMove(to view: SKView) { ... } 
} 

class GameScene: SKScene { 

     override func didMove(to view: SKView) { ... } 
} 

class GameOverScene: SKScene { 

     override func didMove(to view: SKView) { ... } 
} 

Допустим, вы хотите, чтобы перейти от StartScene к GameScene, вы бы добавить этот код в StartScene на правильном месте, когда например нажата кнопка воспроизведения. Это самый простой способ перехода от одного SKScene к другому, непосредственно из самого SKScene.

// Code only, no xCode level editor 
let gameScene = GameScene(size: CGSize(...)) 
let transition = SKTransition... 
gameScene.scaleMode = .aspectFill 
view?.presentScene(gameScene, transition: transition) 

// With xCode level editor (returns an optional so needs if let 
// This will need the GameScene.sks file with the correct custom class set up in the inspector 
// Returns optional 
if let gameScene = SKScene(fileNamed: "GameScene") { 
     let transition = SKTransition... 
     gameScene.scaleMode = .aspectFill 
     view?.presentScene(gameScene, transition: transition) 
} 

Теперь для некоторых реальных примеров GameManager, я уверен, что вы знаете о некоторых из них уже.

Пример 1

Допустим, мы хотим, чтобы менеджер загрузки сцены. Подход со статическими методами не будет работать, потому что новый экземпляр SKScene необходимо создать при переходе на один, иначе такие вещи, как враги и т. Д., Не будут сброшены. Ваш подход со статическими методами означает, что вы будете использовать один и тот же экземпляр каждый раз, и это не хорошо.

Я лично использую расширение протокола для этого. Создать новый файл .swift и назовите его SceneLoaderManager или что-то и добавить этот код

enum SceneIdentifier: String { 
    case start = "StartScene" 
    case game = "GameScene" 
    case gameOver = "GameOverScene" 
} 

private let sceneSize = CGSize(width: ..., height: ...) 

protocol SceneManager { } 
extension SceneManager where Self: SKScene { 

    // No xCode level editor 
    func loadScene(withIdentifier identifier: SceneIdentifier) { 

      let scene: SKScene 

      switch identifier { 

      case .start: 
       scene = StartScene(size: sceneSize) 
      case .game: 
       scene = GameScene(size: sceneSize) 
      case .gameOver: 
       scene = GameOverScene(size: sceneSize) 
      } 

      let transition = SKTransition...\ 
      scene.scaleMode = .aspectFill 
      view?.presentScene(scene, transition: transition) 
    } 

     // With xCode level editor 
    func loadScene(withIdentifier identifier: SceneIdentifier) { 

      guard let scene = SKScene(fileNamed: identifier.rawValue) else { return } 
      scene.scaleMode = .aspectFill 
      let transition = SKTransition... 
      view?.presentScene(scene, transition: transition) 
    } 
} 

В настоящее время в 3-х сценах соответствовать протоколу

class StartScene: SKScene, SceneManager { ... } 

и вызвать метод нагрузки, как это так, используя 1 из 3 перечислений в качестве идентификатора сцены.

loadScene(withIdentifier: .game) 

Пример 2

Позволяет сделать класс менеджера игра для игровых данных, используя подход Singleton.

class GameData { 

    static let shared = GameData() 

    private init() { } // Private singleton init 

    var highscore = 0 

    func updateHighscore(forScore score: Int) { 
     guard score > highscore else { return } 
     highscore = score 
     save() 
    } 

    func save() { 
     // Some code to save the highscore property e.g UserDefaults or by archiving the whole GameData class 
    } 
} 

Теперь в любом месте вашего проекта вы можете сказать

GameData.shared.updateHighscore(forScore: SOMESCORE) 

Вы склонны использовать Singleton для вещей, где вам нужно только 1 экземпляр класса. Хороший пример использования классов Singleton бы такие вещи, как вспомогательные классы для Game Center, InAppPurchases, GameData и т.д.

Примера 3

Помощников для хранения некоторых значений, которые могут понадобиться во всех сценах. Это использует метод статического метода, аналогичный тому, что вы пытались сделать. Мне нравится использовать это для таких вещей, как настройки игры, чтобы они были в хорошем централизованном месте.

class GameHelper { 

    static let enemySpawnTime: TimeInterval = 5 
    static let enemyBossHealth = 5 
    static let playerSpeed = ... 
} 

Используйте их, как это в ваших сценах

... = GameHelper.playerSpeed 

Пример 4

класс для управления SKSpriteNodes например врагов

class Enemy: SKSpriteNode { 

    var health = 5 

    init(imageNamed: String) { 
     let texture = SKTexture(imageNamed: imageNamed) 
     super.init(texture: texture, color: SKColor.clear, size: texture.size()) 
    } 

    func reduceHealth(by amount: Int) { 
     health -= amount 
    } 
} 

чем в вашей сцене, вы можете создать врагов, использующих этот вспомогательный класс и вызывающих методы и p на нем. Таким образом, вы можете добавить 10 врагов легко и индивидуально управлять своим здоровьем и т.д. например

let enemy1 = Enemy(imageNamed: "Enemy1") 
let enemy2 = Enemy(imageNamed: "Enemy2") 

enemy1.reduceHealth(by: 3) 
enemy2.reduceHealth(by: 1) 

Его массивный ответ, но я надеюсь, что это помогает.

+0

Это далеко не просто помогает. Это действительно проницательный материал. распаковка ... – Confused

+0

Добро пожаловать. Как я уже упоминал в другом комментарии, главное, что каждый раз, когда вы переходите на один, вам нужно создавать новый экземпляр SKScene, и поэтому вы не можете использовать статические или глобальные свойства для хранения ссылок на них. Вот почему ваш подход не будет работать. – crashoverride777

+0

Я действительно надеялся создать глобальные статические «экземпляры» сцен, чтобы не было необходимости загружать, поэтому я мог бы совершать сверхбыстрые переходы. DOH! – Confused