Jimmy rennt in seiner Welt!
Hier zeige ich mal eine Preview wie das Game später aussehen wird.
Jimmy rennt in einer Welt aus Plattformen und Fallen. Hier sammelt Jimmy Coins und Super Coins.
Wenn Jimmy alle Super Coins eines Levels einsammelt bekommt Jimmy ein Extra Leben geschenkt.
Jimmy hat ein wenig Parcours Style. Jimmy rennt, springt und performt double jumps um die Hindernisse zu Überwinden. Jimmy nutzt Springfedern um richtig hoch zu springen und schwing mit Seilen über größere Distanzen als er springen kann.
Super Jimmy.
Bis hierher war es ein schönes Stück Arbeit.
Jimmy lebt in einer simulierten Welt in der physikalische Gesetze mittels Physics Engine implementiert und berechnet werden. Coins können eigesammelt werden, Jimmy kann springen, double jumpen und mit einem Gleitschirm auch größere Distanzen überwinden.
Sowas muss alles animiert, programmiert und implementiert werden.
Die Plattformen werden über so genannte Tiles in Jimmy's Welt transferiert. Damit Jimmy nicht durch diese hindurch fällt, wir jedem Tile ein PhysicsBody verpasst. Dies geschieht beim einlesen der Tiles. Dabei werden mit Hilfe zweier Schleifen die Tiles in X-Richtung und Y-Richtung durchlaufen. Jedem Tile, dass über UserData als "ground" markiert wurde wird so ein Physics Body verpasst.
In SpriteKit kann man hierfür einen Editor nutzen. Die Tiles aus dem Editor werden dann als SKTileMapNode eingelesen.
Das schaut dann etwa so aus:
if let levelNode = SKNode.unarchiveFromFile(file: level) {
mapNode = levelNode
mapNode.zPosition = GameConstants.ZPositions.worldZ
addChild(mapNode)
}
//get tile map
if let groundTiles = mapNode.childNode(withName: GameConstants.StringConstants.groundTilesString) as? SKTileMapNode {
tileMap = groundTiles
tileMap.scale(to: frame.size, width: false, multiplier: 1.0)
/*add physicsBody to all ground tiles
The function iterates all tiles im Tilmap with userData Type of "ground"
and adds a physiscs Body */
PhysicsHelper.addPhysicsBody(to: tileMap, and: "ground")
Die Physics Helper Klasse erzeugt dann eine Physikalisch feste Linie zwischen zwei Koordinaten meiner Tiles:
static func addPhysicsBody(to tileMap: SKTileMapNode, and tileInfo: String) {
let tileSize = tileMap.tileSize
//determine type of tile in tilemap
//iterate all rows
for row in 0..<tileMap.numberOfRows {
var tiles = [Int]()
//iterate all columns
for column in 0..<tileMap.numberOfColumns {
let tiledefinition = tileMap.tileDefinition(atColumn: column, row: row)
let isUsedTile = tiledefinition?.userData?[tileInfo] as? Bool
if isUsedTile ?? false { tiles.append(1) }
else { tiles.append(0) }
}
if tiles.contains(1) {
var platform = [Int]()
for (index,tile) in tiles.enumerated() {
if tile == 1 && index < (tileMap.numberOfColumns - 1) { platform.append(index) }
else if !platform.isEmpty {
let x = CGFloat(platform[0]) * tileSize.width
let y = CGFloat(row) * tileSize.height
let tileNode = GroundNode(with: CGSize(width: tileSize.width * CGFloat(platform.count), height: tileSize.height))
tileNode.position = CGPoint(x: x, y: y)
tileNode.lightingBitMask = 1
tileNode.shadowCastBitMask = 1
tileNode.shadowedBitMask = 1
tileNode.anchorPoint = CGPoint.zero
tileMap.addChild(tileNode)
platform.removeAll()
}
}
}
}
}
Hier gibt es ein kleines Problem. Jimmy kann so nicht von unten auf die Platformen springen, da er sich hier immer den Kopf stossen würde.
Daher müssen wir der Welt erklären, dass die Physikalische feste Linie nur dann aktiv ist, wenn Jimmy sich oberhalb der Platform befindet.
Dies macht man am besten in der didSimulatePhysics Methode des GameLoops.
Hierbei handelt es sich um eine Methode die bei jedem Durchlauf eines GameLoops aufgerufen wird nachdem die PhysicsEngine Ihre Berechnungen in der simulierten Welt durchgeführt hat.
//MARK: - didSimulantePhysics
override func didSimulatePhysics() {
//activate tilemap platforms PhysicsBodys
for node in tileMap[GameConstants.StringConstants.groundNodeName] {
if let groundNode = node as? GroundNode {
let groundY = (groundNode.position.y + groundNode.size.height) * tileMap.yScale
let playerY = jimmy.position.y - jimmy.size.height / 3
groundNode.isActivatedBody = playerY > groundY
}
}