I currently have two SKSpriteNodes that I have added SKPhysicsBodies to. When they have no SKJoint attached, they collide as expected. As soon as I add the SKPhysicsJoint, they just pass right through each other. Any joint I add functions properly, but the SKPhysicsJointLimit only limits the extent to which the nodes can travel apart from each other, not how close they can get. How can I fix this?
Here is code I am using for the joint:
let joint = SKPhysicsJointLimit.joint(withBodyA: object1.physicsBody!, bodyB: object2.physicsBody!, anchorA: CGPoint(x: object1.position.x + iconController.position.x, y: object1.position.y + iconController.position.y), anchorB: CGPoint(x: object2.position.x + iconController.position.x, y: object2.position.y + iconController.position.y))
joint.maxLength = screen.height * 0.4
physicsWorld.add(joint)
PhysicsBody of both nodes:
self.physicsBody = SKPhysicsBody(circleOfRadius: self.size.width / 2)
self.physicsBody?.allowsRotation = false
self.physicsBody?.friction = 0
self.physicsBody?.mass = 0.1
I have tested it with different values for the above modifications of the SKPhysicsBody and it performs the same.
An SKPhysicsJoint object connects two physics bodies so that they are simulated together by the physics world.
You can use also SKPhysicJointPin:
A pin joint allows the two bodies to independently rotate around the
anchor point as if pinned together.
If your objects work well before the SKPhysicsJoint addition with the physic engine so they fired the didBeginContact as you wish and as you have setted, I think your problem is simply a wrong anchor. Try to add:
let skView = self.view as! SKView
skView.showsPhysics = true
to your scene initialization code: you will see an outline of the physic bodies and maybe you'll see the issue immediatly.
To help you I'll try to make an example of elements configured to collide each other:
enum CollisionTypes: UInt32 {
case Boundaries = 1
case Element = 2
}
class GameScene: SKScene,SKPhysicsContactDelegate {
private var elements = [SKNode]()
override func didMoveToView(view: SKView) {
physicsWorld.gravity = CGVector(dx: 0, dy: 0)
self.physicsWorld.contactDelegate = self
let boundariesFrame = CGRectMake(20, 20, 200, 400)
let boundaries = SKShapeNode.init(rect: boundariesFrame)
boundaries.position = CGPointMake(350,150)
let boundariesBody = SKPhysicsBody.init(edgeLoopFromRect: boundariesFrame)
boundariesBody.dynamic = false
boundariesBody.categoryBitMask = CollisionTypes.Boundaries.rawValue
boundariesBody.contactTestBitMask = CollisionTypes.Element.rawValue
boundaries.physicsBody = boundariesBody
addChild(boundaries)
for index in 0..<5 {
let element = SKShapeNode(circleOfRadius: 10)
let body = SKPhysicsBody(circleOfRadius: 10)
body.linearDamping = 0
// body.mass = 0
body.dynamic = true
body.categoryBitMask = CollisionTypes.Element.rawValue
body.contactTestBitMask = CollisionTypes.Boundaries.rawValue | CollisionTypes.Element.rawValue
body.collisionBitMask = CollisionTypes.Boundaries.rawValue | CollisionTypes.Element.rawValue
element.physicsBody = body
element.position = CGPoint(x: size.width / 2, y: size.height / 2 - 30 * CGFloat(index))
elements.append(element)
addChild(element)
}
}
}
Hope it can help you to find your issue.
Related
I am making a platforming game in Swift with SpriteKit involving a main character that jumps around. However, once the level loads the player immediately falls through the ground. You can see it in action here.
I am using SKTilemapNode to create the ground, and looping through the tiles when a level loads to create an SKPhysicsBody on a child node of the tile map. This is very similar to what is demoed in the "What's new in SpriteKit" video at WWDC 2016:
So, here we've got a little platform that I built. A little guy that can run around. And you can see that I got the parallax scrolling going on in the background. And you'll note that I'm colliding with the tiles here. And I achieve this by leveraging custom user data that we can put on each of our tiles. Here, I'll show you in our tile set. Select one of the variants here.
And you can see that we have some user data over here. And I just have a value called edgeTile which is a Boolean, and I set to 1.
So, in code, I'm going through the tile map in our platform demo here, and I'm looking for all of these edge tiles.
And whenever I find one, I create some physics data to allow the player to collide with it.
My function to create a physics body based off of an SKTilemapNode is as follows:
extension SKTileMapNode {
//In order for this to work, edge tile definitions must have the "edge" property in user data
func createPhysicsBody() -> SKPhysicsBody {
var physicsBodies = [SKPhysicsBody]()
for row in 0 ..< self.numberOfRows {
for column in 0 ..< self.numberOfColumns {
if self.tileDefinition(atColumn: column, row: row)?.userData?["edge"] != nil {
physicsBodies.append(SKPhysicsBody(rectangleOf: self.tileSize, center: self.centerOfTile(atColumn: column, row: row)))
}
}
}
let body = SKPhysicsBody(bodies: physicsBodies)
body.affectedByGravity = false
body.isDynamic = false
body.allowsRotation = false
body.pinned = true
body.restitution = 0
body.collisionBitMask = 0b1111
body.categoryBitMask = 0b1111
body.contactTestBitMask = 0b1000
return body
}
func initializePhysicsBody() {
let node = SKNode()
node.name = "Tilemap"
node.physicsBody = createPhysicsBody()
addChild(node)
}
}
So, in my scene setup all I have to do is call tileMap.initializePhysicsBody() to do everything that I need.
The SKPhysicsBody for my player is as follows:
let rect = CGSize(width: 16 * xScale, height: 24 * yScale)
let physics = SKPhysicsBody(rectangleOf: rect)
physics.isDynamic = true
physics.allowsRotation = false
physics.pinned = false
physics.affectedByGravity = true
physics.friction = 0
physics.restitution = 0
physics.linearDamping = 0
physics.angularDamping = 0
physics.density = 100
physics.categoryBitMask = 0b0001
physics.collisionBitMask = 0b0001
physics.contactTestBitMask = 0b0011
physics.usesPreciseCollisionDetection = true
physicsBody = physics
I'm not sure what the problem is here, but if I set the SKTilemapNode's physics body to be dynamic, it works. This is how I had the game working up until this point, however, this creates a lot of jitter in the ground because it's moving as a result of the player hitting it. So, thanks for reading this far at least, and any suggestions would be appreciated.
EDIT.
I think the ERROR Here is not using UInt 32
body.categoryBitMask: UInt32 = 2
body.collisionBitMask: UInt32 = 1
body.contactTestBitMask: UInt32 = 1
And Player
physics.categoryBitMask: UInt32 = 1
physics.collisionBitMask: UInt32 = 2
physics.contactTestBitMask: UInt32 = 2
This should definitely Work
Also try this way for the tileMapNode (given below) rather than creating the extension. This was given in a apple developer forum by dontangg
self.tileMap = self.childNode(withName: "Tile Map") as? SKTileMapNode
guard let tileMap = self.tileMap else { fatalError("Missing tile map for the level") }
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?["edgeTile"] 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?.isDynamic = false
tileNode.physicsBody?.collisionBitMask = playerCollisionMask | wallCollisionMask
tileNode.physicsBody?.categoryBitMask = wallCollisionMask
tileMap.addChild(tileNode)
}
}
}
I've been asked to simplify this question, so that's what I'm doing.
I'm struggling in SpriteKit's physic joints (and possibly physic body properties). I tried every single subclass and many configurations but seams like nothing works or I'm doing something wrong.
I'm developing Snake game. User controls head of snake which should move at constant speed ahead and user can turn it clockwise or anticlockwise. All the remaining snake's pieces should follow the head - they should travel exactly the same path that head was some time ago.
I think for this game the Pin joint should be the answer, which anchor point is exactly in the centre between elements.
Unfortunately the result is not perfect. The structure should make the perfect circle, but it doesn't. I'm attaching the code, and gif showing the current effect. Is anyone experience enough to give me any suggestion what properties of physic body and or joints should are apply here for desired effect?
My code:
class GameScene: SKScene {
private var elements = [SKNode]()
override func didMove(to view: SKView) {
physicsWorld.gravity = CGVector(dx: 0, dy: 0)
let dummyTurnNode = SKNode()
dummyTurnNode.position = CGPoint(x: size.width / 2 - 50, y: size.height / 2)
let dummyTurnBody = SKPhysicsBody(circleOfRadius: 1)
dummyTurnBody.isDynamic = false
dummyTurnNode.physicsBody = dummyTurnBody
addChild(dummyTurnNode)
for index in 0..<5 {
let element = SKShapeNode(circleOfRadius: 10)
let body = SKPhysicsBody(circleOfRadius: 10)
body.linearDamping = 0
// body.mass = 0
element.physicsBody = body
element.position = CGPoint(x: size.width / 2, y: size.height / 2 - 30 * CGFloat(index))
elements.append(element)
addChild(element)
let label = SKLabelNode(text: "A")
label.fontSize = 10
label.fontName = "Helvetica-Bold"
element.addChild(label)
if index == 0 {
element.fillColor = UIColor.blue()
body.velocity = CGVector(dx: 0, dy: 30)
let dummyTurnJoint = SKPhysicsJointPin.joint(withBodyA: dummyTurnBody, bodyB: body, anchor: dummyTurnNode.position)
physicsWorld.add(dummyTurnJoint)
} else {
body.linearDamping = 1
element.fillColor = UIColor.red()
let previousElement = elements[index - 1]
let connectingJoint = SKPhysicsJointPin.joint(withBodyA: previousElement.physicsBody!, bodyB: body, anchor: CGPoint(x: size.width / 2, y: size.height / 2 - 30 * CGFloat(index) + CGFloat(15)))
physicsWorld.add(connectingJoint)
}
}
}
override func update(_ currentTime: TimeInterval) {
let head = elements.first!.physicsBody!
var velocity = head.velocity
velocity.normalize()
velocity.multiply(30)
head.velocity = velocity
}
}
extension CGVector {
var rwLength: CGFloat {
let xSq = pow(dx, 2)
let ySq = pow(dy, 2)
return sqrt(xSq + ySq)
}
mutating func normalize() {
dx /= rwLength
dy /= rwLength
}
mutating func multiply(_ factor: CGFloat) {
dx *= factor
dy *= factor
}
}
"All the remaining snake's pieces should follow the head - they should travel exactly the same path that head was some time ago."
You should note that with Physics joints you are likely going to have variance no matter what you do. Even if you have it close to perfect you'll have rounding errors under the hood making the path not exact.
If all the tail parts are equal you can also use a different approach, this is something I've done for a comet tail. Basically the idea is that you have an array of tail objects and per-frame move move the last tail-object always to the same position as the head-object. If the head-object has a higher z-position the tail is drawn below it.
If you need to keep your tail in order you could vary the approach by storing an array of head-positions (per-frame path) and then place the tail objects along that path in your per-frame update call to the snake.
See my code below for example:
These are you head-object variables:
var tails = [SKEmitterNode]()
var tailIndex = 0
In your head init function instantiate the tail objects:
for _ in 0...MAX_TAIL_INDEX
{
if let remnant = SKEmitterNode(fileNamed: "FireTail.sks")
{
p.tails.append(remnant)
}
}
Call the below per-frame:
func drawTail()
{
if tails.count > tailIndex
{
tails[tailIndex].resetSimulation()
tails[tailIndex].particleSpeed = velocity() / 4
tails[tailIndex].emissionAngle = zRotation - CGFloat(M_PI_2) // opposite direction
tails[tailIndex].position = position
tailIndex = tailIndex < MAX_TAIL_INDEX ? tailIndex + 1 : 0
}
}
The resulting effect is actually really smooth when you call it from the scene update() function.
I am generating Y position for SKSpriteNode. I need to generate y position, but it should not be in the position previously generated image, should not overlap. How would I be able to do this in Swift in SpriteKit?
for _ in 1...3 {
let lower : UInt32 = 100
let upper : UInt32 = UInt32(screenSize.height) - 100
let randomY = arc4random_uniform(upper - lower) + lower
obstaclesTexture = SKTexture(imageNamed: "levaPrekazka")
obstacle = SKSpriteNode(texture: obstaclesTexture)
prekazka = obstacle.copy() as! SKSpriteNode
prekazka.name = "prekazka"
prekazka.position = CGPoint(x: 0, y: Int(randomY))
prekazka.zPosition = 10;
prekazka.size = CGSize(width: screenSize.width / 7, height: screenSize.height / 7)
prekazka.physicsBody = SKPhysicsBody(texture: obstaclesTexture, size: obstacle.size)
prekazka.physicsBody?.dynamic = false
prekazka.physicsBody?.categoryBitMask = PhysicsCatagory.obstacle
prekazka.physicsBody?.contactTestBitMask = PhysicsCatagory.ball
prekazka.physicsBody?.contactTestBitMask = PhysicsCatagory.ball
self.addChild(prekazka)
}
To avoid both overlapping sprites and different positions you need to take the height of the sprite into consideration. Something like this perhaps:
let spriteHeight = 60 // or whatever
let lower = 100
let upper = Int(screenSize.height) - 100
let area = upper - lower
let ySpacing = area/spriteHeight
var allowedYs = Set<CGFloat>()
let numberOfSprites = 3
while allowedYs.count < numberOfSprites {
let random = arc4random_uniform(UInt32(spriteHeight))
let y = CGFloat(random) * CGFloat(ySpacing)
allowedYs.insert(y)
}
for y in allowedYs {
obstaclesTexture = SKTexture(imageNamed: "levaPrekazka")
obstacle = SKSpriteNode(texture: obstaclesTexture)
prekazka = obstacle.copy() as! SKSpriteNode
prekazka.name = "prekazka"
prekazka.position = CGPoint(x: 0, y: y)
// etc...
}
I would be tempted to generate the sprite's position and then loop over all existing sprites in the scene and see if their frame overlaps with the new sprites frame:
let lower : UInt32 = 100
let upper : UInt32 = UInt32(screenSize.height) - 100
obstaclesTexture = SKTexture(imageNamed: "levaPrekazka")
obstacle = SKSpriteNode(texture: obstaclesTexture)
// Generate a position for our new sprite. Repeat until the generated position cause the new sprite’s frame not to overlap with any existing sprite’s frame.
repeat {
let randomY = arc4random_uniform(upper - lower) + lower
prekazka.position = CGPoint(x: 0, y: Int(randomY))
} while checkForOverlap(prekazka) = true // If it overlaps, generate a new position and check again
//Function to see if an SkSpriteNode (passed as parameter node) overlaps with any existing node. Return True if it does, else False.
Func checkForOverlap(SKSpriteNode: node) ->> bool {
for existingNode in children { // for every existing node
if CGRectIntersectsRect(existingNode.frame, node.frame) {return True} // return True if frames overlap
}
return false //No overlap
}
(Writing this from memory, so may need a bit of fiddling to work).
Another approach would be to generate and position all the nodes with physics bodies, and set the scene up so that all nodes collide with all others. Then allow the physics engine to handle any collisions which would cause it to move the nodes around until none are overlapping. At this point, your could remove all the physics bodies.
I'm working on a Gameplaykit pathfinding proof-of-concept and I can't get GKObstacleGraph to find paths correctly.
In the following code snippet (it should work in an Xcode 7.2 playground), path2 is always an empty array if there is an obstacle provided when the graph is created. If I create the obGraph object with an empty array of obstacles the findPathFromNode returns a correct path.
The obstacle created should be a simple U shaped polygon with the end point being inside the U.
import UIKit
import GameplayKit
let pts = [vector_float2(2,2),
vector_float2(3,2),
vector_float2(3,6),
vector_float2(7,6),
vector_float2(7,2),
vector_float2(8,3),
vector_float2(8,7),
vector_float2(2,7),
vector_float2(2,2)]
let obstacle1 = GKPolygonObstacle(points: UnsafeMutablePointer(pts) ,
count: pts.count)
let obGraph = GKObstacleGraph(obstacles: [obstacle1], bufferRadius: 0)
let startPt = GKGraphNode2D(point: vector_float2(5,9))
let endPt = GKGraphNode2D(point: vector_float2(5,5))
let pt3 = GKGraphNode2D(point: vector_float2(0,0))
let pt4 = GKGraphNode2D(point: vector_float2(0,9))
let pt5 = GKGraphNode2D(point: vector_float2(5,0))
let pt6 = GKGraphNode2D(point: vector_float2(10,0))
obGraph.connectNodeUsingObstacles(startPt)
obGraph.connectNodeUsingObstacles(endPt)
obGraph.connectNodeUsingObstacles(pt3)
obGraph.connectNodeUsingObstacles(pt4)
obGraph.connectNodeUsingObstacles(pt5)
obGraph.connectNodeUsingObstacles(pt6)
startPt.connectedNodes
endPt.connectedNodes
pt3.connectedNodes
let path2 = obGraph.findPathFromNode(startPt, toNode: endPt)
print(path2)
I was having the same problem as Jack. I started with Will's code example and translated it to Swift 5.0 in Xcode 10.3. I added it into Xcode's Game project template. I still had the same result: an empty array from findPath(from:to:).
After playing around with the code, I realized that anything physics-related is going to impact pathing. The only way to show code that will work for everyone is to include the creation of SKScene and all SKNode instances. Notice I set gravity to 0 in SKPhysicsWorld and I add no SKPhysicsBody to anything.
Run this in a Playground. You activate the animation by tapping anywhere in the scene.
import PlaygroundSupport
import SpriteKit
import GameKit
class GameScene: SKScene {
let nodeToMove:SKShapeNode = {
let n = SKShapeNode(circleOfRadius: 10)
n.lineWidth = 2
n.strokeColor = UIColor.orange
n.position = CGPoint(x: -200, y: 150)
return n
}()
override func sceneDidLoad() {
addChild(nodeToMove)
let nodeToFind = SKShapeNode(circleOfRadius: 5)
nodeToFind.lineWidth = 2
nodeToFind.strokeColor = UIColor.red
addChild(nodeToFind)
nodeToFind.position = CGPoint(x: 200, y: -150)
let nodeToAvoid = SKShapeNode(rectOf: CGSize(width: 100, height: 100))
nodeToAvoid.lineWidth = 4
nodeToAvoid.strokeColor = UIColor.blue
addChild(nodeToAvoid)
nodeToAvoid.position = CGPoint.zero
let polygonObstacles = SKNode.obstacles(fromNodeBounds: [nodeToAvoid])
let graph = GKObstacleGraph(obstacles: polygonObstacles, bufferRadius: 10.0)
let end = GKGraphNode2D(point: vector2(Float(nodeToMove.position.x), Float(nodeToMove.position.y)))
let start = GKGraphNode2D(point: vector2(Float(nodeToFind.position.x), Float(nodeToFind.position.y)))
graph.connectUsingObstacles(node: end)
graph.connectUsingObstacles(node: start)
graphNodes = graph.findPath(from: end, to: start) as! [GKGraphNode2D]
print("graphNodes = \(graphNodes)")
}
var graphNodes = [GKGraphNode2D]()
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
touches.first.flatMap {_ in
let newActions: [SKAction] = graphNodes.map { n in
return SKAction.move(to: CGPoint(x: CGFloat(n.position.x), y: CGFloat(n.position.y)), duration: 2)
}
nodeToMove.run(SKAction.sequence(newActions))
}
}
}
let sceneView = SKView(frame: CGRect(x:0 , y:0, width: 640, height: 480))
let scene = GameScene(size: CGSize(width: 640, height: 480))
scene.scaleMode = .aspectFill
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
scene.backgroundColor = UIColor.purple
scene.physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
sceneView.presentScene(scene)
PlaygroundSupport.PlaygroundPage.current.liveView = sceneView
The output in the console is:
graphNodes = [GKGraphNode2D: {-200.00, 150.00}, GKGraphNode2D: {62.85, 62.85}, GKGraphNode2D: {200.00, -150.00}]
The start state:
The end state:
Warning: I do not know why it takes an entire second between the user tap and the start of the animation. Performance tuning is a separate topic.
(sorry in advance for obj-c not swift)
It is my impression that adding each vertex point as a connection is not necessary, merely telling the GKObstacleGraph about the GKPolygonObstacle will be enough for it to avoid the generated polgyon shape. I have used the following and received 3 nodes to create a pathway around my obstacle (with a buffer of 10.0f) seen here:
- (void)findPathWithNode:(SKNode *)nodeToFindPath {
NSMutableArray *obstaclesToAvoid = [NSMutableArray array];
for (SKNode *objectInScene in chapterScene.children) {
if ([objectInScene.name isEqualToString:#"innerMapBoundary"]) {
[obstaclesToAvoid addObject:objectInScene];
}
}
/* FYI: The objectInScene is just a black SKSpriteNode with a
square physics body 100 x 100 rotated at 45°
*/
NSArray *obstacles = [SKNode obstaclesFromNodePhysicsBodies:[NSArray arrayWithArray:obstaclesToAvoid]];
GKObstacleGraph *graph = [GKObstacleGraph graphWithObstacles:obstacles bufferRadius:10.0f];
GKGraphNode2D *end = [GKGraphNode2D nodeWithPoint:vector2((float)character.position.x, (float)character.position.y)];
GKGraphNode2D *start = [GKGraphNode2D nodeWithPoint:vector2((float)nodeToFindPath.position.x, (float)nodeToFindPath.position.y)];
[graph connectNodeUsingObstacles:end];
[graph connectNodeUsingObstacles:start];
NSArray *pathPointsFound = [graph findPathFromNode:enemy toNode:target];
NSLog(#"Path: %#", pathPointsFound);
GKPath *pathFound;
// Make sure that there were at least 2 points found before creating the path
if (pathPointsFound.count > 1) {
for (GKGraphNode2D *nodeFound in pathPointsFound) {
// This is just to create a visual for the points found
vector_float2 v = (vector_float2){(float)nodeFound.position.x, (float)nodeFound.position.y};
CGPoint p = CGPointMake(v.x, v.y);
SKShapeNode *shapetoadd = [SKShapeNode shapeNodeWithCircleOfRadius:4];
shapetoadd.name = #"shapeadded";
shapetoadd.fillColor = [UIColor redColor];
shapetoadd.position = p;
[chapterScene addChild:shapetoadd];
}
pathFound = [GKPath pathWithGraphNodes:pathPointsFound radius:10.0];
}
}
Hopefully this points you in the right direction!
I am searching for the most efficient way to Nodes with random SKSpriteNodes on given positions.
I have 10 Platforms, on each of them I want to have an Enemy (SKNode) that contains for 3 different SKSpriteNodes which will randomly spawn. These SKSpriteNodes all do other behaviors, thats why I can't simply change the Texture of the SKNode.
As with my Code, I wanted to spawn the same Node on different Platforms.
However, the code does not work properly, the nodes do spawn but only at 1 position(the position from the last SpawnEnemycode in didMoveToView), meaning 10 nodes at 1 position.
I also tried it with adding 10 different Enemy nodes, each given a Platform. But this just looks like a copy & paste code thats inefficient.
How can I fix my problem, or is there either another way to spawn them more efficiently?
My Code:
class GameScene: SKScene, SKPhysicsContactDelegate {
var Enemy: SKNode!
override func didMoveToView(view: SKView) {
/* Setup your scene here */
self.physicsWorld.contactDelegate = self
self.physicsBody?.velocity = CGVectorMake(0, 0)
self.anchorPoint = CGPoint(x: 0, y: 0.20)
backgroundColor = SKColor(red: 81.0/255.0, green: 192.0/255.0, blue: 201.0/255.0, alpha: 1.0)
self.Enemy1 = SKNode()
addChild(Enemy1)
SpawnPlayer()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform1.position.y + 15))
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform2.position.y + 15))
//Same code for all the other Platforms.
}
func SpawnEnemy(position: CGPoint!){
switch (arc4random_uniform(3)){
case 0:
Picture1.filteringMode = .Nearest
Picture2.filteringMode = .Nearest
Animation1 = SKAction.animateWithTextures([Picture1,Picture2], timePerFrame: 0.5)
AnimationRepeat1 = SKAction.repeatActionForever(Animation1)
Sprite1 = SKSpriteNode (imageNamed: "Sprite1.png")
Sprite1.size = CGSize(width: 64, height: 64)
Sprite1.zPosition = 2
Sprite1.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 45, height: 50))
Sprite1.physicsBody?.dynamic = true
Sprite1.physicsBody?.allowsRotation = false
Sprite1.physicsBody?.restitution = 0.0
Sprite1.physicsBody?.usesPreciseCollisionDetection = true
Sprite1.physicsBody?.categoryBitMask = EnemyCategory
Sprite1.physicsBody?.collisionBitMask = PlayerCategory | Platform1Category
Sprite1.physicsBody?.contactTestBitMask = Wall1Category | Wall2Category | PlayerCategory
Sprite1.runAction(AnimationRepeat1)
Enemy.position = position
Enemy.addChild(Sprite1)
case 1:
Picture3.filteringMode = .Nearest
Picture4.filteringMode = .Nearest
Animation1 = SKAction.animateWithTextures([Picture3,Picture4], timePerFrame: 0.5)
AnimationRepeat1 = SKAction.repeatActionForever(Animation1)
Sprite2 = SKSpriteNode (imageNamed: "Sprite2.png")
Sprite2.size = CGSize(width: 64, height: 64)
Sprite2.zPosition = 2
Sprite2.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 45, height: 50))
Sprite2.physicsBody?.dynamic = true
Sprite2.physicsBody?.allowsRotation = false
Sprite2.physicsBody?.restitution = 0.0
Sprite2.physicsBody?.usesPreciseCollisionDetection = true
Sprite2.physicsBody?.categoryBitMask = EnemyCategory
Sprite2.physicsBody?.collisionBitMask = PlayerCategory | Platform1Category
Sprite2.physicsBody?.contactTestBitMask = Wall1Category | Wall2Category | PlayerCategory
Sprite2.runAction(AnimationRepeat1)
Enemy.position = position
Enemy.addChild(Sprite2)
case 2:
Picture5.filteringMode = .Nearest
Picture6.filteringMode = .Nearest
Animation1 = SKAction.animateWithTextures([Picture5,Picture6], timePerFrame: 0.5)
AnimationRepeat1 = SKAction.repeatActionForever(Animation1)
Sprite3 = SKSpriteNode (imageNamed: "Sprite3.png")
Sprite3.size = CGSize(width: 64, height: 64)
Sprite3.zPosition = 2
Sprite3.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 45, height: 50))
Sprite3.physicsBody?.dynamic = true
Sprite3.physicsBody?.allowsRotation = false
Sprite3.physicsBody?.restitution = 0.0
Sprite3.physicsBody?.usesPreciseCollisionDetection = true
Sprite3.physicsBody?.categoryBitMask = EnemyCategory
Sprite3.physicsBody?.collisionBitMask = PlayerCategory | Platform1Category
Sprite3.physicsBody?.contactTestBitMask = Wall1Category | Wall2Category | PlayerCategory
Sprite3.runAction(AnimationRepeat1)
Enemy.position = position
Enemy.addChild(Sprite3)
default:
return
}
}
The reason you're only seeing one position is that you're only creating one "enemy" and moving it around with SpawnEnemy. Since your sprites are all added to the same "enemy", they will all show up wherever your enemy was moved to last.
Let's chop down what your code is to the relevant bits:
var Enemy: SKNode!
override func didMoveToView(view: SKView) {
self.Enemy1 = SKNode()
addChild(Enemy1)
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform1.position.y + 15))
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform2.position.y + 15))
}
func SpawnEnemy(position: CGPoint!){
switch (arc4random_uniform(3)){
case 0:
Enemy.position = position
Enemy.addChild(Sprite1)
case 1:
Enemy.position = position
Enemy.addChild(Sprite2)
case 2:
Enemy.position = position
Enemy.addChild(Sprite3)
default:
return
}
}
So, in didMoveToView(), you create one Enemy node. Then, as you call SpawnEnemy, you do a bunch of stuff to add other sprites, but as far as Enemy is concerned, you're just changing its position. Since you're adding the sprites to the same node, when you move that Enemy node, the child sprites move with it, too.
You have two options to fix this. Either add your sprites to some higher-level node (like the scene), or create new "enemy" nodes for each of the sprites you intend to add. Either should work, but the best choice will depend on what you're trying to accomplish with them - the scope of that discussion is beyond what you're talking about here.