Cannot get SKActionmove to work reliably - ios

I'm trying to build a small spriteKitGame. I have been trying to use the function to move a couple of tank sprite nodes to a specific point.
Attached below is the code snippet.
let tankSpawn = CGPoint(x: self.size.width , y: 70);
tank.position = tankSpawn;
tank.zPosition = 3.0;
let targetPoint = CGPoint(x: -tank.size.width/2, y: tank.position.y);
let actionMove = SKAction.move(to: targetPoint, duration: TimeInterval(tankMoveDuration))
This is my result. They are spawning at the correct point ( 70units high ), but are going down as shown.
I want them to go in a straight line. I set the target points y as a constant. I have no idea why they are going to wards some bottom source.
I have similar code for planes that spawn above( which are working perfectly ).
let plane = SKSpriteNode(imageNamed: "SpaceShip");
let planeMoveDuration = 3.0
let planeSpawn = CGPoint(x: self.size.width , y: self.size.height/2);
plane.position = planeSpawn;
plane.zPosition = 3.0;
let actionMove = SKAction.move(to: CGPoint(x: -plane.size.width/2, y: plane.position.y), duration: TimeInterval(planeMoveDuration))
I have no idea what my mistake here is.
I have tried changing the y co ordinate of the target to tank.position.y, but it doesn't work.

Are they falling under gravity? A sprite-kit scene with physics bodies will have gravity and things will fall unless you take specific action to avoid this.
It's easily done - you're developing your game, you have basic elements on screen and movement etc is all working well, then you want to add some collision detection so you add physicsBodies and then whoosh - where'd my sprites go?

Related

Align a SceneKit Plane to Face of cube

I have created a scnbox in SceneKit and am trying to add a circular plane on the face that is touched by the user.
I can add the SCNPlane as a child node at the touch point using the hittest but I’m struggling to orient the plane to the face that was touched.
The localnormal vector provided as part of hit test seems to be what I need but I’m nit sure how to use it. Normally I would orient using the EulerAngles property but localnormal looks to be a vector. I tried Look(at:) which takes a vector3 but that didn’t seem to work.
Any suggestions would be gratefully received. Code sample below which is taken from touchesBegan. "result" is the SCNHitTestResult:
//Draw circular plane, double sided
let circle = SCNPlane(width: 0.1, height: 0.1) //SCNSphere(radius: 0.1)
circle.cornerRadius = 0.5
circle.materials.first?.diffuse.contents = UIColor.black
circle.materials.first?.isDoubleSided = true
let circleNode = SCNNode(geometry: circle)
//Set position to hit test
circleNode.position = result.localCoordinates
let lookAtPoint = SCNVector3(result.localNormal.x * 100, result.localNormal.y * 100, result.localNormal.z * 100)
//Align to far point on normal
circleNode.look(at: lookAtPoint)
//Add to touched node
result.node.addChildNode(circleNode)

Objects appear at bottom of screen in SpriteKit

I'm learning SpriteKit and Swift, and I'm trying to make 5 small images "spawn" at the top of the screen, and slide down to the bottom. This is what I got so far, added in the didMoveToView method.
var myArray = NSMutableArray()
myArray.addObject(NSNumber(int: 40))
myArray.addObject(NSNumber(int: 80))
myArray.addObject(NSNumber(int: 120))
myArray.addObject(NSNumber(int: 160))
myArray.addObject(NSNumber(int: 200))
for item in myArray
{
let location = CGPoint(x: CGFloat(item.floatValue), y: 1)
let sprite = SKSpriteNode(imageNamed: "myImage")
sprite.xScale = 0.5
sprite.yScale = 0.5
sprite.position = location
let action = SKAction.moveToY(-4, duration: 4.5)
sprite.runAction(action)
addChild(sprite)
}
But all the objects appear at the bottom. I've tried to change the y-position, with the same result..
Thanks!
You need to increase the y coordinate.
Think about the axis system as the left bottom corner is (0,0) and the top right is (self.frame.size.width,self.frame.size.height)
You may also want to read about anchor points - the default anchor point for an object is 0.5,0.5 so if you locate it on 1 half of it (+ 1 pixel) will appear from the top.
If you want it to pop in from out of the screen you need either to change the anchor point or the poisition
Try changing your location variable to this:
let location = CGPoint(x: CGFloat(item.floatValue) , y:self.size.height)
This will set the y-value to the top of the screen and move the nodes there.

How to "center" SKTexture in SKSpriteNode

I'm trying to make Jigsaw puzzle game in SpriteKit. To make things easier I using 9x9 squared tiles board. On each tile is one childNode with piece of image from it area.
But here's starts my problem. Piece of jigsaw puzzle isn't perfect square, and when I apply SKTexture to node it just place from anchorPoint = {0,0}. And result isn't pretty, actually its terrible.
https://www.dropbox.com/s/2di30hk5evdd5fr/IMG_0086.jpg?dl=0
I managed to fix those tiles with right and top "hooks", but left and bottom side doesn't care about anything.
var sprite = SKSpriteNode()
let originSize = frame.size
let textureSize = texture.size()
sprite.size = originSize
sprite.texture = texture
sprite.size = texture.size()
let x = (textureSize.width - originSize.width)
let widthRate = x / textureSize.width
let y = (textureSize.height - originSize.height)
let heightRate = y / textureSize.height
sprite.anchorPoint = CGPoint(x: 0.5 - (widthRate * 0.5), y: 0.5 - (heightRate * 0.5))
sprite.position = CGPoint(x: frame.width * 0.5, y: frame.height * 0.5)
addChild(sprite)
Can you give me some advice?
I don't see a way you can get placement right without knowing more about the piece texture you are using because they will all be different. Like if the piece has a nob on any of the sides and the width width/height the nob will add to the texture. Hard to tell in the pic but even if it doesn't have a nob and instead has an inset it might add varying sizes.
Without knowing anything about how the texture is created I am not able to offer help on that. But I do believe the issue starts with that. If it were me I would create a square texture with additional alpha to center the piece correctly. So the center of that texture would always be placed in the center of a square on the grid.
With all that being said I do know that adding that texture to a node and then adding that node to a SKNode will make your placement go smoother with the way you currently have it. The trick will then only be placing that textured piece correctly within the empty SKNode.
For example...
let piece = SKSpriteNode()
let texturedPiece = SKSpriteNode(texture: texture)
//positioning
//offset x needs to be calculated with additional info about the texture
//for example it has just a nob on the right
let offsetX : CGFloat = -nobWidth/2
//offset y needs to be calculated with additional info about the texture
//for example it has a nob on the top and bottom
let offsetY : CGFloat = 0.0
texturedPiece.position = CGPointMake(offsetX, offsetY)
piece.addChild(texturedPiece)
let squareWidth = size.width/2
//Now that the textured piece is placed correctly within a parent
//placing the parent is super easy and consistent without messing
//with anchor points. This will also make rotations nice.
piece.position = CGPoint(x: squareWidth/2, y: squareWidth/2)
addChild(piece)
Hopefully that makes sense and didn't confuse things further.

In SpriteKit on iOS, scaling a textured sprite produces an incorrect frame?

I'm learning SpriteKit game development for the fun of it & I've run across a seemingly simple problem that has me stumped.
Basically, after I scale a textured SKSpriteNode, the frame is NOT what I expect. I have figured out a few hacks to force it to what I want, but I'm trying to understand what is going on. Any ideas appreciated!
Here's my code WITHOUT SCALING:
func addSpaceship()
{
let spaceship = SKSpriteNode.init(imageNamed: "rocketship.png")
spaceship.name = "spaceship"
// spaceship.setScale(0.50)
let debugFrame = SKShapeNode.init(rect: spaceship.frame)
debugFrame.strokeColor = SKColor.greenColor()
spaceship.addChild(debugFrame)
spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - 150)
self.addChild(spaceship)
}
And my app looks like this:
Now, if I comment back in the line of code which scales it (spaceship.setScale(0.50)), I get this:
Notice that the spaceship is scaled down in the second image, but the frame is scaled even smaller. Why?
If I move the scaling line to after I add the spaceship to the scene, it does what I expect, but that seems wrong:
Here's the code with setScale called after adding the spaceship to the scene:
func addSpaceship()
{
let spaceship = SKSpriteNode.init(imageNamed: "rocketship.png")
spaceship.name = "spaceship"
let debugFrame = SKShapeNode.init(rect: spaceship.frame)
debugFrame.strokeColor = SKColor.greenColor()
spaceship.addChild(debugFrame)
spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - 150)
self.addChild(spaceship)
spaceship.setScale(0.50)
}
And here is what running my app looks like then:
So that works, but why is it necessary?
It has been suggested below, that this is a bug with SKShapeNode. But, replacing the SKShapeNode with an SKLabelNode has the same problem:
func addSpaceship()
{
let spaceship = SKSpriteNode.init(imageNamed: "rocketship.png")
spaceship.name = "spaceship"
spaceship.setScale(0.50)
let scoreNode = SKLabelNode(text: "100")
scoreNode.position = CGPointMake(CGRectGetMidX(spaceship.frame), CGRectGetMaxY(spaceship.frame))
scoreNode.fontColor = SKColor.redColor()
scoreNode.fontSize = 15.0
scoreNode.fontName = "Monaco"
scoreNode.zPosition = 10.0
spaceship.addChild(scoreNode)
spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - 150)
self.addChild(spaceship)
}
which gives us:
The intent is to have the score label (scoreNode) centered above the rocket, but as you can see it is on top of the top porthole. There is just something wrong with the spaceship's frame after I call spaceship.setScale.
I have made one additional discovery: the setScale call does not need to be after I add the spaceship to the scene. It just needs to be after I add the debugFrame/scoreNode to the spaceship. If I setScale AFTER that point, all is well:
func addSpaceship()
{
let spaceship = SKSpriteNode.init(imageNamed: "rocketship.png")
spaceship.name = "spaceship"
let scoreNode = SKLabelNode(text: "100")
scoreNode.position = CGPointMake(CGRectGetMidX(spaceship.frame), CGRectGetMaxY(spaceship.frame))
scoreNode.fontColor = SKColor.redColor()
scoreNode.fontSize = 15.0
scoreNode.fontName = "Monaco"
scoreNode.zPosition = 10.0
spaceship.addChild(scoreNode)
spaceship.setScale(0.50)
spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - 150)
self.addChild(spaceship)
}
which results in:
Your problem may be in the order of these two lines :
spaceship.setScale(0.50)
let debugFrame = SKShapeNode.init(rect: spaceship.frame)
You scaled down the spaceship and then calculate the size of the rectangle with the scaled spaceship. Then when rendering the rectangle is scaled down to half its size which is quarter of the original spaceship size.
If you swap the lines, it should work as expected.
In general, it is better to make compose in the real size and then scale the whole just before adding it to the scene.
First of all, let me preface with SKShapeNode is a really funky (maybe buggy class). At least it was in previous iterations of spritekit. If your goal is to add a debug rectangle for physics purposes. You can turn on showsPhysics on your SKView class inside of your GameViewController
Heres my experiment
let redbox = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 100, height: 100))
redbox.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
redbox.setScale(0.5)
let debugFrame = SKShapeNode(ellipseOfSize: redbox.size)
debugFrame.strokeColor = SKColor.greenColor()
self.addChild(redbox)
redbox.addChild(debugFrame)
looks same as yours. if i call setScale after i add the nodes then my circle fills up my red square.
Also if I keep everything the same, but I just add my debugframe to the scene directly it will be scaled the right way, weird huh??
ok another test. note I set greenbox to 50% of redboxes size so we can see the redbox beneath. If the bug was occuring here than greenbox would end up filling 25% of the redbox.
let redbox = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 100, height: 100))
redbox.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
redbox.setScale(0.5)
let greenbox = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 50, height: 50))
self.addChild(redbox)
redbox.addChild(greenbox)
Ok so i did the same thing using another SKSpriteNode, and it behaves the way we'd expect. So for whatever reason, when you use an SKShapeNode as a child.. setScale is being called twice on it; unless you set the scale after adding the nodes to the scene. But this doesnt happen with SKSpriteNode.
The answer is.. I don't think there's a good answer. It's probably a bug. SKShapeNode has a history of bugs. SpriteKit has a few bugs =/ Someone correct me if I'm wrong.

Random movements / turbulences - SWIFT

I'm developing a game on Iphone and Ipad like a space invaders.
Balloons to destroy are falling from the top of the screen in a straight line.
Here my codes to add them :
func addBalloonv(){
var balloonv:SKSpriteNode = SKSpriteNode (imageNamed: "ballonvert.png")
balloonv.physicsBody = SKPhysicsBody (circleOfRadius: balloonv.size.width/2)
balloonv.physicsBody.dynamic = true
balloonv.physicsBody.categoryBitMask = balloonCategory | greenCategory
balloonv.physicsBody.contactTestBitMask = flechetteCategory
balloonv.physicsBody.collisionBitMask = balloonCategory
balloonv.physicsBody.mass = 1
balloonv.physicsBody.restitution = 1
balloonv.physicsBody.allowsRotation = true
let minX = balloonv.size.width/2
let maxX = self.frame.size.width - balloonv.size.width/2
let rangeX = maxX - minX
let position:CGFloat = CGFloat(arc4random()) % CGFloat(rangeX) + CGFloat(minX)
balloonv.position = CGPointMake(position, self.frame.size.height+balloonv.size.height)
self.addChild(balloonv)
I have one func by balloon color.
So for the moment they move in straight line and I'm looking for random movements (with turbulences like balloon in air) from the top and both sides.
How can I do that?
Thank you very much !!
This is exactly what the new Physics Fields feature in SpriteKit (as of iOS 8 / OS X Yosemite) is for. These let you apply different kinds of forces to all physics bodies in region, like gravity, drag, and turbulence. See the SKFieldNode class docs for details.
Fields are a kind of node, so to get what you're after, you'd add one noise (or turbulence) field to your scene, covering the area that the balloons fall through, and it'll perturb the path of each balloon that passes. The simplest way to do it goes something like this:
let field = SKFieldNode.noiseFieldWithSmoothness(0.5, animationSpeed: 0.1)
scene.addChild(field)
You'll want to tweak the smoothness, animation speed, and field.strength till you get just the level of noise you want. You might also look into whether you want just a noise field, which applies random forces in random directions, or a turbulence field, which does the same thing, but with forces that get stronger when bodies are moving faster.
The above code gets you a field whose region of effect is infinite. You might want to limit it to a specific area (for example, so it doesn't keep knocking your balloons around after they land). I did this to make a field that covers only the top 3/4 of a 300x200 scene:
field.region = SKRegion(size: CGSize(width: 300, height: 100))
field.position = CGPoint(x: 150, y: 150)

Resources