I am working on a game where the character will be controled by a set of 4 buttons like in the old consoles. I used Tile Map in spritekit to create the map. Everything works fine (the players is moving when pressing the buttons, the scene follow the player) except for the fact that my 4 buttons also move when the character move. Up to the point that the 4 buttons move off screen and i have no control of it anymore. How can we anchor the 4 buttons to, let say, bottom right of the screen? Below is the code i used to create the controlled buttons
func controlButton() {
button = SKNode()
moveUpButton = SKSpriteNode(imageNamed: "moveup")
moveUpButton.alpha = 1
moveUpButton.setScale(1.5)
moveUpButton.position = CGPoint(x: 400 - self.frame.size.width/2, y: 0 - self.frame.size.height/2)
moveUpButton.zPosition = 2
moveLeftButton = SKSpriteNode(imageNamed: "moveleft")
...
moveRightButton = SKSpriteNode(imageNamed: "moveright")
...
moveDownButton = SKSpriteNode(imageNamed: "movedown")
...
button.addChild(moveUpButton)
button.addChild(moveLeftButton)
button.addChild(moveRightButton)
button.addChild(moveDownButton)
self.addChild(button)
}
and here is the code i used to create the tile map, adding node with physics body for tile containing wall:
func setUpSceneWithMap(map: SKTileMapNode) {
let tileMap = map
tileMap.setScale(1)
tileMap.position = CGPoint(x: 0 - self.frame.size.width/2, y: 0 - self.frame.size.height/2)
let tileSize = tileMap.tileSize
let halfWidth = CGFloat(tileMap.numberOfColumns) / 2.0 * tileSize.width
let halfHeight = CGFloat(tileMap.numberOfRows) / 2.0 * tileSize.height
for col in 0..<tileMap.numberOfColumns {
for row in 0..<tileMap.numberOfRows {
let tileDefinition = tileMap.tileDefinition(atColumn: col, row: row)
let isEdgeTile = tileDefinition?.userData?["isWalls"] as? Bool
if (isEdgeTile ?? false) {
let x = CGFloat(col) * tileSize.width - halfWidth
let y = CGFloat(row) * tileSize.height - halfHeight
let rect = CGRect(x: 0, y: 0, width: tileSize.width, height: tileSize.height)
let tileNode = SKShapeNode(rect: rect)
tileNode.position = CGPoint(x: x, y: y)
tileNode.physicsBody = SKPhysicsBody.init(rectangleOf: tileSize, center: CGPoint(x: tileSize.width / 2.0, y: tileSize.height / 2.0))
tileNode.physicsBody?.categoryBitMask = gamePhysics.Wall
tileNode.physicsBody?.collisionBitMask = gamePhysics.Player
tileNode.physicsBody?.contactTestBitMask = gamePhysics.Player
tileNode.physicsBody?.isDynamic = false
tileMap.addChild(tileNode)
}
}
}
}
Below is also the code where i add the map to scene:
func createScene() {
self.physicsWorld.contactDelegate = self
for node in self.children {
if (node is SKTileMapNode){
if let theMap:SKTileMapNode = node as? SKTileMapNode {
setUpSceneWithMap(map: theMap)
}
}
}
createPlayer()
createCamera()
}
Basically, SpriteKit subscribes to a child-parent node structure.
Let me use an image to illustrate.
Where each circle represents a node. Currently, you are moving the SKScene node (self). The position of the child nodes are relative to the parent node it is attached to. When you move SKScene node, all of the nodes attached to it follow suite because they are the child nodes.
For example, position a node at (0,0), and then attach a node to that node at position (10,0). Move the parent node to (10,0), and the child node at (10,0) will move to (20,0) because its origin point is relative to its parent, not the general scene.
To fix your issue, you need to create another level of nodes. Let me use another image to illustrate.
If you only apply movement to the Map Node, then only the children (nodes attached to Map Node) will move.
So in summary, your code would look something like this.
class GameScene: SKScene
{
let MapNode,
UINode = SKNode; //This is a fancy way to declare multiple variables of the same type
func controlButton()
{
... //Your other code here
UINode.addChild(button) //Instead of self.addChild(button)
}
func setUpSceneWithMap(map: SKTileMapNode)
{
//Your other code in here
MapNode.addChild(map)
// I don't know where you are adding your tile map to your scene
// However, it most likely is self.addChild(map) which needs to be changed to MapNode.addChild(map)
}
}
Attach all scene elements to MapNode, and all UI elements to UINode. Only apply positional change to MapNode. The UI elements will not move around unless you want them to.