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 try to build 2D - top down game, and I have player (SKSpriteNode) he can move and rotate, and I want to shoot two bullets from him.
I use this code to shoot:
func setBullet(player: Player, bullet: Int)
{
let weaponPosition = scene!.convertPoint(player.weapon.position, fromNode: player)
var xPos, yPos: CGFloat!
let sinus = sin(player.zRotation)
let cosinus = cos(player.zRotation)
if bullet == 1
{
xPos = converted.x - sinus * player.size.height / 2
yPos = converted.y + cosinus * player.size.height / 2
}
else if bullet == 2
{
xPos = weaponPosition.x - sinus * player.size.height / 2
yPos = weaponPosition.y + cosinus * player.size.height / 2
}
position = CGPoint(x: xPos, y: yPos)
physicsBody!.applyImpulse(CGVector(dx: -sinus * normalSpeed, dy: cosinus * normalSpeed))
}
But, i do not know how to correctly set the position...
I try to make something like this
(Green dots - this is a bullets). Can anyone help me please!
Shooting multiple bullets in the same direction is fairly straightforward. The key is to determine the bullets' initial positions and direction vectors when the character is rotated.
You can calculate a bullet's initial position within the scene by
let point = node.convertPoint(weapon.position, toNode: self)
where node is the character, weapon.position is non-rotated position of a gun, and self is the scene.
Typically, a bullet moves to the right, CGVector(dx:1, dy:0), or up, CGVector (dx:0, dy:1), when the character is not rotated. You can calculate the direction of the impulse to apply to the bullet's physics body by rotating the vector by the character's zRotation with
func rotate(vector:CGVector, angle:CGFloat) -> CGVector {
let rotatedX = vector.dx * cos(angle) - vector.dy * sin(angle)
let rotatedY = vector.dx * sin(angle) + vector.dy * cos(angle)
return CGVector(dx: rotatedX, dy: rotatedY)
}
Here's an example of how to shoot two bullets from a rotated character:
struct Weapon {
var position:CGPoint
}
class GameScene: SKScene {
let sprite = SKSpriteNode(imageNamed:"Spaceship")
let dualGuns = [Weapon(position: CGPoint(x: -15, y: 15)), Weapon(position: CGPoint(x: 15, y: 15))]
let singleGun = [Weapon(position: CGPoint(x: 0, y: 15))]
let numGuns = 1
// If your character faces up where zRotation == 0, offset by pi
let rotationOffset = CGFloat(M_PI_2)
override func didMoveToView(view: SKView) {
scaleMode = .ResizeFill
sprite.position = view.center
sprite.size = CGSizeMake(25, 25)
self.addChild(sprite)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let _ = touches.first {
let action = SKAction.rotateByAngle(CGFloat(M_PI_4/2), duration:1)
sprite.runAction(action) {
[weak self] in
if let scene = self {
switch (scene.numGuns) {
case 1:
for weapon in scene.singleGun {
scene.shoot(weapon: weapon, from: scene.sprite)
}
case 2:
for weapon in scene.dualGuns {
scene.shoot(weapon: weapon, from: scene.sprite)
}
default:
break
}
}
}
}
}
func shoot(weapon weapon:Weapon, from node:SKNode) {
// Convert the position from the character's coordinate system to scene coodinates
let converted = node.convertPoint(weapon.position, toNode: self)
// Determine the direction of the bullet based on the character's rotation
let vector = rotate(CGVector(dx: 0.25, dy: 0), angle:node.zRotation+rotationOffset)
// Create a bullet with a physics body
let bullet = SKSpriteNode(color: SKColor.blueColor(), size: CGSizeMake(4,4))
bullet.physicsBody = SKPhysicsBody(circleOfRadius: 2)
bullet.physicsBody?.affectedByGravity = false
bullet.position = CGPointMake(converted.x, converted.y)
addChild(bullet)
bullet.physicsBody?.applyImpulse(vector)
}
// Rotates a point (or vector) about the z-axis
func rotate(vector:CGVector, angle:CGFloat) -> CGVector {
let rotatedX = vector.dx * cos(angle) - vector.dy * sin(angle)
let rotatedY = vector.dx * sin(angle) + vector.dy * cos(angle)
return CGVector(dx: rotatedX, dy: rotatedY)
}
}
Suppose your player is a circle maked with SKShapeNode or SKSpriteNode.
Both of them have the frame property:
let f = player.frame
So, the first bullet can be in this position:
let firstBullet.position = CGPointMake(player.position.x-(f.width/2),player.position.y)
let secondBullet.position = CGPointMake(player.position.x+(f.width/2),player.position.y)
To know it during rotation do:
let firstBulletXPos = firstBullet.position.x - sinus * bullet.size.height / 2
let firstBulletYPos = firstBullet.position.y + cosinus * bullet.size.height / 2
let secondBulletXPos = secondBullet.position.x - sinus * bullet.size.height / 2
let secondBulletYPos = secondBullet.position.y + cosinus * bullet.size.height / 2
I have tons of the same node in game all generated randomly, and when I run the project by itself, it runs very smoothly. However, when I use this physics body (or any) it slows down tremendously:
circle.physicsBody = SKPhysicsBody(circleOfRadius: 25)
How do I fix this? Will post more code if necessary.
Edit:
func createCirclesOnLine(line: CGFloat) {
var currentY : CGFloat = -110000
let maxY = self.size.width * 15
let spacing : CGFloat = 120
while currentY < maxY {
let circle = SKSpriteNode(imageNamed: "first#2x")
circle.position = CGPointMake(line, currentY)
circle.physicsBody = SKPhysicsBody(rectangleOfSize: circle.size)
circle.physicsBody?.dynamic = false
circle.physicsBody?.restitution = 1
circle.size = CGSizeMake(75, 75)
let up = SKAction.moveByX(0, y: 9000, duration: 90)
circle.runAction(up)
foregroundNode.addChild(circle)
currentY += CGFloat((random() % 400) + 70)
}
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.