I want to make it so my node (ball) always ends up resetting above my other node block1 based on where block 1 ends up. However, I could not think of any code that would help me do that. I was thinking of making a function saying something ball.position = block1 + 20 but I have no ideas of how to go about that. Thanks for any help!
Code:
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) + min
}
func resetScene (){
let ball = childNode(withName: BallCategoryName) as! SKSpriteNode
ball.removeFromParent()
ball.physicsBody?.velocity = CGVector( dx: 0, dy: 0 )
ball.physicsBody?.collisionBitMask = BallCategory
ball.physicsBody?.collisionBitMask = BorderCategory | PaddleCategory
ball.zRotation = 0.0
addChild(ball)
let block1 = childNode(withName: Block1Name) as! SKSpriteNode
block1.removeFromParent()
let actualX = random(min:85, max: 300)
block1.position = CGPoint(x: actualX, y: 190)
addChild(block1)
//ball.position = CGPoint(x: , y: )
//This is the line i would like to change
canRestart = false
}
assuming you are just trying to set the Y position of your ball you can use
ball.position.y = block1.position.y + 20
or if you need to set the x and the y
ball.position = CGPoint(x: block1.position.x, y: block1.position.y + 20)
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]))
}
Im a beginner slowly learning how swift and Xcode function, as of now I still have issues with simple things like this:
func addMonster() {
// Create sprite
let monster = SKSpriteNode(imageNamed: "Enemy")
// Determine where to spawn the monster along the Y axis
let actualY = random(min: monster.size.height/2, max: size.height - monster.size.height/2)
// Position the monster slightly off-screen along the right edge,
// and along a random position along the Y axis as calculated above
monster.position = CGPoint(x: size.width + monster.size.width/2, y: actualY)
// Add the monster to the scene
addChild(monster)
// Determine speed of the monster
let actualDuration = random(min: CGFloat(2.0), max: CGFloat(4.0))
// Create the actions
let actionMove = SKAction.moveTo(CGPoint(x: -monster.size.width/2, y: actualY), duration: NSTimeInterval(actualDuration))
let actionMoveDone = SKAction.removeFromParent()
monster.runAction(SKAction.sequence([actionMove, actionMoveDone]))
}
please help me move my monster vertically rather than horizontally
Mainly change "width" to "height" and "height" to "width".And define a actualX
func addMonster() {
// Create sprite
let monster = SKSpriteNode(imageNamed: "Enemy")
// Determine where to spawn the monster along the Y axis
let actualX = random(min: monster.size.width/2, max: size.width - monster.size.width/2)
// Position the monster slightly off-screen along the right edge,
// and along a random position along the Y axis as calculated above
monster.position = CGPoint(x: actualX, y: size.height + monster.size.height/2)
// Add the monster to the scene
addChild(monster)
// Determine speed of the monster
let actualDuration = random(min: CGFloat(2.0), max: CGFloat(4.0))
// Create the actions
let actionMove = SKAction.moveTo(CGPoint(x:actualX, y: -monster.size.height/2), duration: NSTimeInterval(actualDuration))
let actionMoveDone = SKAction.removeFromParent()
monster.runAction(SKAction.sequence([actionMove, actionMoveDone]))
}
Keep unchange the x-coordinate and change the y-coordinate
let actionMove = SKAction.moveTo(CGPoint(x: actualX, y: -monster.size.height/2), duration: NSTimeInterval(actualDuration)
let actionMoveDone = SKAction.removeFromParent() monster.runAction(SKAction.sequence([actionMove, actionMoveDone]))
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];
}
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.