SKPhysicsJointFixed in SpriteKit and Swift - swift

I'm making a game in Sprite Kit and Swift and I have a Sprite at the bottom of the screen and falling Sprites from the top which I want to catch and stick to the Sprite at the bottom, so I'm trying to use SKPhysicsJointFixed but when the objects collide instead of the falling object sticking to the one at the bottom which is supposed to catch and have it attached, it seems as if the bottom Sprite adapts the physics of the falling sprite and then falls off the screen with it. Here's the code I have in my didBeginContact method. and skewer is the name of the Sprite at the bottom which should always be at the bottom and not disappear.
if contact.bodyA.node!.name == "Skewer"
{
let boundX = skewer.physicsBody?.node?.position.x
let fixedJoint = SKPhysicsJointFixed.jointWithBodyA(contact.bodyA.node!.physicsBody, bodyB: contact.bodyB.node!.physicsBody, anchor: CGPoint(x: boundX!, y: boundY))
physicsWorld.addJoint(fixedJoint)
// contact.bodyB.node!.removeFromParent()
}
else
{
contact.bodyA!.node!.removeFromParent()
}
and the physics for the bottom screen Sprite are here
func makeSkewer()
{
skewer.name = "Skewer"
skewer.position = CGPoint(x: size.width * 0.5, y: size.height * 0.244)
skewer.physicsBody = SKPhysicsBody(rectangleOfSize: skewer.size)
skewer.physicsBody?.affectedByGravity = false
skewer.physicsBody?.categoryBitMask = kSkewerCategory
skewer.physicsBody?.contactTestBitMask = kFoodCategory
skewer.physicsBody?.collisionBitMask = kSceneEdgeCategory
addChild(skewer)
}
and physics for the falling Sprites are here
func random() ->CGFloat
{
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(#min: CGFloat,max: CGFloat) -> CGFloat
{
return random() * (max - min) + min
}
func addFood()
{
let randomCatchIndex = Int(arc4random_uniform(UInt32(foods.count)))
let food = SKSpriteNode(imageNamed: foods[randomCatchIndex])
let actualX = random(min: food.size.width/2, max: size.width - food.size.width/2)
let actualDuration = random(min: CGFloat(1.5), max: CGFloat(8.0))
let actionMove = SKAction.moveTo(CGPoint(x: actualX, y: -food.size.height/2), duration: NSTimeInterval(actualDuration))
let actionMoveDone = SKAction.removeFromParent()
food.physicsBody = SKPhysicsBody(rectangleOfSize: food.size)
food.position = CGPoint(x: actualX, y: size.height + food.size.height/2)
food.physicsBody?.categoryBitMask = kFoodCategory
food.physicsBody?.contactTestBitMask = kSkewerCategory
food.physicsBody?.collisionBitMask = 0x0
food.physicsBody?.dynamic = true
food.runAction(SKAction.sequence([actionMove, actionMoveDone]))
addChild(food)
}

Set the skewer to not have dynamic physics. What you have currently is it not being affected by gravity, and as soon as it locks onto the food (which is traveling down and has momentum), the skewer moves with it.
In the creation of the skewer, run the following line:
skewer.physicsBody?.dynamic = false
You can also now ignore the affectedByGravity as that is something that only affects dynamic objects.

Related

How to place nodes on the ground?

I am creating a game with SpriteKit. The background is a png image, endlessly moving (parallax scroll):
func parallaxScroll(image: String, y: CGFloat, z: CGFloat, duration: Double, needsPhysics: Bool) {
for i in 0 ... 1 {
// position the first node on the left, and position second on the right
let node = SKSpriteNode(imageNamed: image)
node.position = CGPoint(x: 1023 * CGFloat(i), y: y)
node.zPosition = z
addChild(node)
if needsPhysics {
node.physicsBody = SKPhysicsBody(texture: node.texture!, size: node.texture!.size())
node.physicsBody?.isDynamic = false
node.physicsBody?.contactTestBitMask = 1
node.name = "ground"
}
// make this node move the width of the screen by whatever duration was passed in
let move = SKAction.moveBy(x: -1024, y: 0, duration: duration)
// make it jump back to the right edge
let wrap = SKAction.moveBy(x: 1024, y: 0, duration: 0)
// make these two as a sequence that loops forever
let sequence = SKAction.sequence([move, wrap])
let forever = SKAction.repeatForever(sequence)
// run the animations
node.run(forever)
}
}
The example function below places a box at random y position:
#objc func createObstacle() {
let obstacle = SKSpriteNode(imageNamed: "rectangle")
obstacle.zPosition = -2
obstacle.position.x = 768
addChild(obstacle)
obstacle.physicsBody = SKPhysicsBody(texture: obstacle.texture!, size: obstacle.texture!.size())
obstacle.physicsBody?.isDynamic = false
obstacle.physicsBody?.contactTestBitMask = 1
obstacle.name = "obstacle"
let rand = GKRandomDistribution(lowestValue: -200, highestValue: 350)
obstacle.position.y = CGFloat(rand.nextInt())
// make it move across the screen
let action = SKAction.moveTo(x: -768, duration: 9)
obstacle.run(action)
}
func playerHit(_ node: SKNode) {
if node.name == "obstacle" {
player.removeFromParent()
}
}
func didBegin(_ contact: SKPhysicsContact) {
guard let nodeA = contact.bodyA.node else { return }
guard let nodeB = contact.bodyB.node else { return }
if nodeA == player {
playerHit(nodeB)
} else if nodeB == player {
playerHit(nodeA)
}
}
Instead of placing it at random, I would like to place it on the "ground". The following image illustrates the current placement and the desired placement:
Does anyone know how to place an obstacle node (object) on the ground which is not flat instead of random y position?
You could cast a ray, at the box's intended x position, from the top of the screen to the bottom, and when the ray hits the ground, place the box directly above the hitpoint. See enumerateBodies(alongRayStart:end:using:). In your case you might want to insert into createObstacle something like:
let topOfScreen = size.height / 2
let bottomOfScreen = -size.height / 2
obstacle.position.x = -768
physicsWorld.enumerateBodies(alongRayStart: CGPoint(x: obstacle.position.x, y: topOfScreen), end: CGPoint(x: obstacle.position.x, y: bottomOfScreen)) { body, point, normal, stop in
if body.node?.name == "ground" {
// body is the ground's physics body. point is the point that an object
// falling from the sky would hit the ground.
// Assuming the obstacle's anchor point is at its center, its actual position
// would be slightly above this point
let positionAboveBody = point + (obstacle.size.height / 2)
obstacle.position.y = positionAboveBody
}
}

How to create realistic spinning wheel in SpriteKit

I am trying to create a spinning fortune wheel action via SKAction. I have a SKNode which used as wheel, this SKNode is a circle that divided to four quarters (each quarter in different color). also I set an SKAction (which is repeating for 10 counts) that spin the SKNode around fixed point (the node's center). The problem is that the action is running well but it stops suddenly and not slowing down - like a real wheel. I don't really have an idea how to set this animation, I mean to slow the spinning down before the action is stop.
Here is my code so far:
class GameScene: SKScene {
let colors = [SKColor.yellow, SKColor.red, SKColor.blue, SKColor.purple]
override func didMove(to view: SKView) {
createWheel()
let sq = CGRect(x: size.width/2, y: size.height/2, width: 300, height: 300)
let sqx = SKShapeNode(rect: sq)
sqx.lineWidth = 2
sqx.fillColor = .clear
sqx.setScale(1.0)
addChild(sqx)
}
func createWheel() {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 0, y: -200))
path.addArc(withCenter: CGPoint.zero,radius: 200,startAngle: CGFloat(0.0), endAngle: CGFloat(3.0 * Double.pi / 2),clockwise: false)
path.addLine(to: CGPoint(x: 200, y: 0))
let obstacle = obstacleByDuplicatingPath(path, clockwise: true)
obstacle.position = CGPoint(x: size.width/2, y: size.height/2)
addChild(obstacle)
let rotateAction = SKAction.rotate(byAngle: CGFloat((3.0 * CGFloat(Double.pi / 2)) - 90), duration: 0.5)
//obstacle.run(SKAction.repeatForever(rotateAction))
obstacle.run(SKAction.repeat(rotateAction, count: 10))
}
func obstacleByDuplicatingPath(_ path: UIBezierPath, clockwise: Bool) -> SKNode {
let container = SKNode()
var rotationFactor = CGFloat(Double.pi / 2)
if !clockwise {
rotationFactor *= -1
}
for i in 0...3 {
let section = SKShapeNode(path: path.cgPath)
section.fillColor = colors[i]
section.strokeColor = colors[i]
section.zRotation = rotationFactor * CGFloat(i);
let origin = CGPoint(x: 0.0, y: 0.0)
switch i {
case 0:
section.position = CGPoint(x: (origin.x + 10), y: (origin.y - 10))
case 1:
section.position = CGPoint(x: (origin.x + 10), y: (origin.y + 10))
case 2:
section.position = CGPoint(x: (origin.x - 10), y: (origin.y + 10))
case 3:
section.position = CGPoint(x: (origin.x - 10), y: (origin.y - 10))
default:
print("bolbol")
}
container.addChild(section)
}
return container
}
}
edit:
I was thinking about it and I tried to do it via SKAction, I set another action but this time I set their duration to a long one. first it run a action of duration 0.5, then of 2 and at end of 4. I looks pretty good but still not smooth as I want it to be.
here is my code:
let rotateAction = SKAction.rotate(byAngle: CGFloat(2.0 * CGFloat(M_PI)), duration: 0.5)
let rotateAction2 = SKAction.rotate(byAngle: CGFloat(2.0 * CGFloat(M_PI)), duration: 2)
let rotateAction3 = SKAction.rotate(byAngle: CGFloat(2.0 * CGFloat(M_PI)), duration: 4)
let wait = SKAction.wait(forDuration: 5)
let g1 = SKAction.repeat(rotateAction, count: 10)
let group = SKAction.group([wait, g1, rotateAction2, rotateAction3])
what do you think? there is any way to do it better??
edit 2:
Continued to #Ali Beadle answer, I tried to do it via physics body, the problem now is the when I drag finger on the screen the SKShapeNode (shape) in continue to rotate and never stops. can you detect what is wrong?
class GameScene: SKScene {
var start: CGPoint?
var end:CGPoint?
var startTime: TimeInterval?
let shape = SKShapeNode.init(rectOf: CGSize(width: 150, height: 150))
override func didMove(to view: SKView) {
self.physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
let sceneBody = SKPhysicsBody.init(edgeLoopFrom: self.frame)
sceneBody.friction = 0
self.physicsBody = sceneBody
shape.fillColor = SKColor.red
shape.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
shape.physicsBody = SKPhysicsBody.init(rectangleOf: CGSize(width: 50, height: 50))
shape.physicsBody?.affectedByGravity = false
shape.physicsBody?.isDynamic = true
addChild(shape)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {return}
self.start = touch.location(in: self)
self.startTime = touch.timestamp
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {return}
self.end = touch.location(in: self)
var dx = ((self.end?.x)! - (self.start?.x)!)
var dy = ((self.end?.y)! - (self.start?.y)!)
let magnitude:CGFloat = sqrt(dx*dx+dy*dy)
if(magnitude >= 25){
let dt:CGFloat = CGFloat(touch.timestamp - self.startTime!)
if dt > 0.1 {
let speed = magnitude / dt
dx = dx / magnitude
dy = dy / magnitude
print("dx: \(dx), dy: \(dy), speed: \(speed) ")
}
}
let touchPosition = touch.location(in: self)
if touchPosition.x < (self.frame.width / 2) {
self.shape.physicsBody?.angularVelocity = 10
self.shape.physicsBody?.applyAngularImpulse(-180)
} else {
self.shape.physicsBody?.angularVelocity = 10
self.shape.physicsBody?.applyAngularImpulse(180)
}
}}
I have created an open source prize spinning wheel in Spritekit that uses physics for realistic movement and flapper control. It also allows the user to drag the wheel to spin or generates a random spin by pushing the center of the wheel.
https://github.com/hsilived/SpinWheel
You can add realistic movement like this by using the built-in Physics simulation of SpriteKit. This will allow you to give your wheel a mass and friction and then use forces to rotate it. It will then slow down realistically.
In outline see Simulating Physics in the Apple Documentation:
To use physics in your game, you need to:
Attach physics bodies to nodes in the node tree and configure their physical properties. See SKPhysicsBody.
Define global characteristics of the scene’s physics simulation, such as gravity. See SKPhysicsWorld.
Where necessary to support your gameplay, set the velocity of physics bodies in the scene or apply forces or impulses to them. ...
The most appropriate method for your wheel is probably to make the wheel pinned to the scene and then rotate it with applyAngularImpulse.

How to keep a 30˚ distance between two lines anchored at a point

I am trying to create two lines that are anchored at a certain point (sprite) and rotate to form a 30 degree angle between them. Below is an image what I want to achieve.
This is what I've done so far:
extension Int {
var degreesToRadians: Double { return Double(self) * .pi / 180 }
}
extension FloatingPoint {
var degreesToRadians: Self { return self * .pi / 180 }
var radiansToDegrees: Self { return self * 180 / .pi }
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var anchorSprite = SKSpriteNode(imageNamed: "anchorSprite")
var armLeft = SKSpriteNode(imageNamed: "lineSprite")
var armRight = SKSpriteNode(imageNamed: "lineSprite")
override func didMove(to view: SKView) {
self.physicsWorld.gravity = CGVector(dx: 0, dy: -1.8)
self.physicsWorld.contactDelegate = self
var tealBg = SKSpriteNode(imageNamed: "tealBg")
tealBg.position = CGPoint(x: frame.midX, y: frame.midY)
tealBg.zPosition = 10
addChild(tealBg)
anchorSprite.position = CGPoint(x: frame.midX, y: frame.midY + frame.midY/2)
anchorSprite.zPosition = 20
anchorSprite.physicsBody = SKPhysicsBody(rectangleOf: anchorSprite.frame.size)
anchorSprite.physicsBody?.categoryBitMask = pinCategory
anchorSprite.physicsBody?.isDynamic = false
addChild(anchorSprite)
armRight.anchorPoint = CGPoint(x: 0.5, y: 1)
armRight.position = anchorSprite.position
armRight.zPosition = 20
armRight.physicsBody = SKPhysicsBody(rectangleOf: armRight.frame.size)
armRight.zRotation = CGFloat(Double(15).degreesToRadians)//CGFloat(Double.pi/6)
armRight.physicsBody!.isDynamic = true
addChild(armRight)
armLeft.anchorPoint = CGPoint(x: 0.5, y: 1)
armLeft.position = anchorSprite.position
armLeft.zPosition = 20
armLeft.physicsBody = SKPhysicsBody(rectangleOf: armRight.frame.size)
armLeft.zRotation = CGFloat(Double(-15).degreesToRadians)//CGFloat(-Double.pi/6)
armLeft.physicsBody!.isDynamic = true
addChild(armLeft)
//Pin joints
var pinAndRightArmJoint = SKPhysicsJointPin.joint(withBodyA: anchorSprite.physicsBody!, bodyB: armRight.physicsBody!, anchor: CGPoint(x: anchorSprite.position.x, y: self.armRight.frame.maxY))
self.physicsWorld.add(pinAndRightArmJoint)
var pinAndLeftArmJoint = SKPhysicsJointPin.joint(withBodyA: anchorSprite.physicsBody!, bodyB: armLeft.physicsBody!, anchor: CGPoint(x: anchorSprite.position.x, y: self.armLeft.frame.maxY))
self.physicsWorld.add(pinAndLeftArmJoint)
}
Below is an image from running the above code (they are close together).
How can I make sure the lines are always 30˚ apart and maintain 30˚ apart even when rotated?
To keep your two lines separated by exactly 30°, you can use an SKPhysicsJointFixed, which is just what it sounds like: it pins two physicsBodies together in a fixed position. Since you already have them positioned the way you want, just add this code where you have the other SKPhysicsJoints to hold them that way:
let fixArms = SKPhysicsJointFixed.joint(withBodyA: armLeft.physicsBody!, bodyB: armRight.physicsBody!, anchor: CGPoint.zero)
self.physicsWorld.add(fixArms)
Result:
If you make the line nodes children of the anchor sprite (instead of the scene), rotating the anchor sprite node will rotate all the lines along with it without doing anything special with physics. You just need to mind the anchor points so that they align properly (i.e. line's anchor at its extremity rather than center)

Getting a sprite kit object to move upwards

I have the following Sprite Kit Scene in my swift project. it moves a SKNode across the screen. The problem is that I would like the circle to move both upwards and downwards, but it only moves downwards or horizontally. I checked the value of the yMove to confirm that it was randomly generating positive changes in the y axis. It is generating those positive values, but the object still moves only horizontally and vertically.
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.whiteColor()
runAction(SKAction.repeatActionForever(
SKAction.sequence([
SKAction.runBlock(addKugel),
SKAction.waitForDuration(1.0)
])
))
}
func addKugel() {
// Create sprite
let kugel = SKShapeNode(circleOfRadius: 100 )
kugel.strokeColor = SKColor.blackColor()
kugel.glowWidth = 1.0
// Determine where to spawn the monster along the Y axis
let actualY = random(min: 200/2, max: size.height - 200/2)
// Position the monster slightly off-screen along the right edge,
// and along a random position along the Y axis as calculated above
kugel.position = CGPoint(x: size.width + 200/2, y: actualY)
// Add the monster to the scene
addChild(kugel)
// Determine speed of the monster
let actualDuration = random(min: CGFloat(2.0), max: CGFloat(4.0))
//randomly create coefficients for movement
let xVector = random(min: CGFloat(0.0), max: CGFloat(1.0))
var yVector = random(min: CGFloat(0.0), max: CGFloat(1.0))
//overly complicated way to make a number negative 50 percent of the time
var probNegative = random(min: CGFloat(0.0), max: CGFloat(1.0))
if (probNegative > 0.5){
yVector = 0.0 - yVector
}
debugPrint(yVector)
// Create the actions
let yMove = (200/2)*yVector
debugPrint(yMove)
let actionMove = SKAction.moveTo(CGPoint(x: -200/2*xVector, y: yMove), duration: NSTimeInterval(actualDuration))
let actionMoveDone = SKAction.removeFromParent()
kugel.runAction(SKAction.sequence([actionMove, actionMoveDone]))
}
}
So I was very mistaken in what moveTo was doing. all moveTo does is move a Sprite to a particular CGPoint. I was scaling the y coordinate by my sprite size, as opposed to randomly choosing a value between 0 and size.height.
Here is a simplified version of the above snippet:
func addKugel() {
let KUGELWIDTH = 50.0
let kugel = SKShapeNode(circleOfRadius: CGFloat(KUGELWIDTH)/2 )
kugel.strokeColor = SKColor.blackColor()
kugel.glowWidth = 1.0
let actualY = random(min: CGFloat(KUGELWIDTH)/2, max: (size.height - CGFloat(KUGELWIDTH)/2))
debugPrint("actualY: \(actualY)")
kugel.position = CGPoint(x: size.width + CGFloat(KUGELWIDTH / 2), y: actualY)
addChild(kugel)
let actualDuration = random(min: CGFloat(2.0), max: CGFloat(4.0))
let y = random(min: CGFloat(0.0), max: CGFloat(1.0))
let yPos = CGFloat(y)*size.height
let actionMove = SKAction.moveTo(CGPoint(x: 0.00, y: yPos), duration: NSTimeInterval(actualDuration))
let actionMoveDone = SKAction.removeFromParent()
kugel.runAction(SKAction.sequence([actionMove, actionMoveDone]))
}

Spawn Balls random position out of the screen

I would like to bring up enemy (var enemis) from outside the screen whether the top, bottom, left and right of the screen. And these enemy have a random direction in tranversant the screen. For the moment, my code do spawning enemy out of the screen top, bottom, left and right but with one direction only and I want make a random direction
func CreationEnnemis(){
let Enemis = SKSpriteNode(imageNamed: "Meteroites.png")
let choixDeCote = arc4random() % 4 + 1
switch choixDeCote {
case 1 : //Haut
let MinValue = self.size.width / 8
let MaxValue = self.size.width - 200
SpawnX = UInt32(MaxValue - MinValue)
SpawnX = arc4random_uniform(SpawnX)
SpawnY = UInt32(self.size.height)
break
case 2 ://Bas
let MinValue = self.size.width / 8
let MaxValue = self.size.width - 200
SpawnX = UInt32(MaxValue - MinValue)
SpawnX = arc4random_uniform(SpawnX)
SpawnY = UInt32(self.size.height) - UInt32(self.size.height)
break
case 3 : //Gauche
let MinValue = self.size.height / 8
let MaxValue = self.size.height - 200
SpawnX = 0
SpawnY = UInt32(MaxValue - MinValue)
SpawnY = arc4random_uniform(SpawnY)
break
case 4 ://Droite
let MinValue = self.size.height / 8
let MaxValue = self.size.height - 200
SpawnX = UInt32(self.size.width)
SpawnY = UInt32(MaxValue - MinValue)
SpawnY = arc4random_uniform(SpawnY)
break
default :
break
}
Enemis.position = CGPoint(x: CGFloat(SpawnX), y: CGFloat(SpawnY))
Enemis.setScale(4)
Enemis.physicsBody = SKPhysicsBody(rectangleOfSize: Enemis.size)
Enemis.physicsBody?.affectedByGravity = false
Enemis.physicsBody?.dynamic = true
let action = SKAction.moveTo(CGPoint(x: -50,y: -10),duration: 2.5)
let actionFini = SKAction.removeFromParent()
Enemis.runAction(SKAction.sequence([action, actionFini]))
Enemis.runAction(SKAction.repeatActionForever(action))
self.addChild(Enemis)
}
This is just an example to give you an idea how you can spawn enemies at random positions and move them in random directions. I don't use Swift extensively, and this is more like just to show you at which direction you can go, and how to solve the problem. I left to you to care about Swift 2 syntax :D Also, I am currently on outdated version of Swift, so not sure what works for me, will work for you, but the logic is the same.
Here you will see how you can:
spawn a node and move it to the opposite side of a screen
move a node to the random point of the opposite side of a screen
randomize duration of spawning
create a random point along the one of the screen's borders
create a random number between two numbers
using SKAction to do all this
One thing which is important here is how to use strong reference to self inside closure. Because of my Swift version, as I said, what works for me, probably will not work for you, but the logic is the same. Read more here about strong reference cycles if interested :
Shall we always use [unowned self] inside closure in Swift
Always pass weak reference of self into block in ARC?
What is the difference between a weak reference and an unowned reference?
Here is an code example:
import SpriteKit
class GameScene:SKScene, SKPhysicsContactDelegate{
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
createEnemies()
}
deinit{
print("deinit called")
}
func randomBetweenNumbers(firstNum: CGFloat, secondNum: CGFloat) -> CGFloat{
return CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(firstNum - secondNum) + min(firstNum, secondNum)
}
//Helper method for spawning a point along the screen borders. This will not work for diagonal lines.
func randomPointBetween(start:CGPoint, end:CGPoint)->CGPoint{
return CGPoint(x: randomBetweenNumbers(start.x, secondNum: end.x), y: randomBetweenNumbers(start.y, secondNum: end.y))
}
func createEnemies(){
//Randomize spawning time.
//This will create a node every 0.5 +/- 0.1 seconds, means between 0.4 and 0.6 sec
let wait = SKAction .waitForDuration(0.5, withRange: 0.2)
weak var weakSelf = self //Use weakSelf to break a possible strong reference cycle
let spawn = SKAction.runBlock({
var random = arc4random() % 4 + 1
var position = CGPoint()
var moveTo = CGPoint()
var offset:CGFloat = 40
println(random)
switch random {
//Top
case 1:
position = weakSelf!.randomPointBetween(CGPoint(x: 0, y: weakSelf!.frame.height), end: CGPoint(x: weakSelf!.frame.width, y: weakSelf!.frame.height))
//Move to opposite side
moveTo = weakSelf!.randomPointBetween(CGPoint(x: 0, y: 0), end: CGPoint(x:weakSelf!.frame.width, y:0))
break
//Bottom
case 2:
position = weakSelf!.randomPointBetween(CGPoint(x: 0, y: 0), end: CGPoint(x: weakSelf!.frame.width, y: 0))
//Move to opposite side
moveTo = weakSelf!.randomPointBetween(CGPoint(x: 0, y: weakSelf!.frame.height), end: CGPoint(x: weakSelf!.frame.width, y: weakSelf!.frame.height))
break
//Left
case 3:
position = weakSelf!.randomPointBetween(CGPoint(x: 0, y: 0), end: CGPoint(x: 0, y: weakSelf!.frame.height))
//Move to opposite side
moveTo = weakSelf!.randomPointBetween(CGPoint(x: weakSelf!.frame.width, y: 0), end: CGPoint(x: weakSelf!.frame.width, y: weakSelf!.frame.height))
break
//Right
case 4:
position = weakSelf!.randomPointBetween(CGPoint(x: weakSelf!.frame.width, y: 0), end: CGPoint(x: weakSelf!.frame.width, y: weakSelf!.frame.height))
//Move to opposite side
moveTo = weakSelf!.randomPointBetween(CGPoint(x: 0, y: 0), end: CGPoint(x: 0, y: weakSelf!.frame.height))
break
default:
break
}
weakSelf!.spawnEnemyAtPosition(position, moveTo: moveTo)
})
let spawning = SKAction.sequence([wait,spawn])
self.runAction(SKAction.repeatActionForever(spawning), withKey:"spawning")
}
func spawnEnemyAtPosition(position:CGPoint, moveTo:CGPoint){
let enemy = SKSpriteNode(color: SKColor.brownColor(), size: CGSize(width: 40, height: 40))
enemy.position = position
enemy.physicsBody = SKPhysicsBody(rectangleOfSize: enemy.size)
enemy.physicsBody?.affectedByGravity = false
enemy.physicsBody?.dynamic = true
enemy.physicsBody?.collisionBitMask = 0 // no collisions
//Here you can randomize the value of duration parameter to change the speed of a node
let move = SKAction.moveTo(moveTo,duration: 2.5)
let remove = SKAction.removeFromParent()
enemy.runAction(SKAction.sequence([move, remove]))
self.addChild(enemy)
}
func didBeginContact(contact: SKPhysicsContact) {
}
/*
Added for debugging purposes
override func touchesBegan(touches: NSSet, withEvent event: UIEvent?) {
//Just make a transition to the other scene, in order to check if deinit is called
//You have to make a new scene ... I named it WelcomeScene
var scene:WelcomeScene = WelcomeScene(fileNamed: "WelcomeScene.sks")
scene.scaleMode = .AspectFill
self.view?.presentScene(scene )
}
*/
}
And here is the result:
The important part is located in createEnemies() method:
//Top
case 1:
position = weakSelf!.randomPointBetween(CGPoint(x: 0, y: weakSelf!.frame.height), end: CGPoint(x: weakSelf!.frame.width, y: weakSelf!.frame.height))
//Move to opposite side
moveTo = weakSelf!.randomPointBetween(CGPoint(x: 0, y: 0), end: CGPoint(x:weakSelf!.frame.width, y:0))
break
Here you define spawning location, which can be any point along the top border. Or more precisely a little bit above top border. Nodes are spawned offscreen. And next, you create (randomize) a point where you would like to move a node, and that is an opposite side in compare to spawn location. So, that can be any random point along bottom border.
If you want to stop spawning, you will do this:
if(self.actionForKey("spawning") != nil){
self.removeActionForKey("spawning")
}
About your physics bodies setup... Note that I've set collisionBitMask of nodes to 0.
enemy.physicsBody?.collisionBitMask = 0 // no collisions
When moving nodes by actions in SpriteKit you are pulling them out of physics simulation and you can get unexpected behaviours if you are expecting to see realistic physics simulation. So, use actions only if you are not interested in collisions (or other sort of physics simulation), but rather just in contact detection. If you need collisions as well, use physics engine and move nodes by applying impulses or forces.
Hope this helps!
Thanks a lot !
I make a different version of your code because i found solution before your answer
func CreationMeteorites(){
let Meteorites = SKSpriteNode(imageNamed: "Meteroites.png")
let choixDeCote = arc4random() % 4 + 1
switch choixDeCote {
case 1 : //Haut
let MinValue = self.size.width / 8
let MaxValue = self.size.width - 200
SpawnX = UInt32(MaxValue - MinValue)
SpawnX = arc4random_uniform(SpawnX)
SpawnY = UInt32(self.size.height)
directionX = Int(arc4random()) % Int(self.frame.size.width)
directionY = 0
action = SKAction.moveTo(CGPoint(x: CGFloat(directionX),y: CGFloat(directionY)),duration: 4)
break
case 2 ://Bas
let MinValue = self.size.width / 8
let MaxValue = self.size.width - 200
SpawnX = UInt32(MaxValue - MinValue)
SpawnX = arc4random_uniform(SpawnX)
SpawnY = 0
directionX = Int(arc4random()) % Int(self.frame.size.width)
directionY = Int(self.frame.size.height)
action = SKAction.moveTo(CGPoint(x: CGFloat(directionX),y: CGFloat(directionY)),duration: 4)
break
case 3 : //Gauche
let MinValue = self.size.height / 8
let MaxValue = self.size.height - 200
SpawnX = 0
SpawnY = UInt32(MaxValue - MinValue)
SpawnY = arc4random_uniform(SpawnY)
directionY = Int(arc4random()) % Int(self.frame.size.height)
directionX = Int(self.frame.size.width)
action = SKAction.moveTo(CGPoint(x: CGFloat(directionX),y: CGFloat(directionY)),duration: 3)
break
case 4 ://Droite
let MinValue = self.size.height / 8
let MaxValue = self.size.height - 200
SpawnX = UInt32(self.size.width)
SpawnY = UInt32(MaxValue - MinValue)
SpawnY = arc4random_uniform(SpawnY)
directionY = Int(arc4random()) % Int(self.frame.size.height)
directionX = 0
action = SKAction.moveTo(CGPoint(x: CGFloat(directionX),y: CGFloat(directionY)),duration: 3)
break
default :
break
}
//Positioner les météorites
Meteorites.position = CGPoint(x: CGFloat(SpawnX), y: CGFloat(SpawnY))
Meteorites.setScale(4)
Meteorites.physicsBody = SKPhysicsBody(circleOfRadius: 30)
Meteorites.physicsBody?.affectedByGravity = false
Meteorites.physicsBody?.dynamic = true
Meteorites.physicsBody?.categoryBitMask = PhysicsCategories.Meteorites
Meteorites.physicsBody?.contactTestBitMask = PhysicsCategories.Meteorites
let actionFini = SKAction.removeFromParent()
Meteorites.runAction(SKAction.sequence([action, actionFini]))
Meteorites.runAction(SKAction.repeatActionForever(action))
self.addChild(Meteorites)
}
And about the collisions do you know a tutorial with a good explain because i don't understand how make collisions.
For anyone that is interested to do this in objective C inside GameScene:
-(void) randomSpawnPosition{
NSUInteger randPos = arc4random_uniform(4);
CGPoint spawnPosition;
CGFloat randFloatX = arc4random_uniform(self.frame.size.width + 10);
CGFloat randFloatY = arc4random_uniform(self.frame.size.height + 10);
switch (randPos) {
//top
case 1:
spawnPosition = CGPointMake(randFloatX, self.frame.size.height+10);
break;
//bottom
case 2:
spawnPosition = CGPointMake(randFloatX, 0-10);
break;
//left
case 3:
spawnPosition = CGPointMake(0 - 10, randFloatY);
break;
//right
case 4:
spawnPosition = CGPointMake(self.frame.size.width + 10, randFloatY);
break;
}
[self addEnemy:spawnPosition];
}