How do I wrap x & y axis in SpriteKit? - ios

So in my GameScene I have these lines in the beginning of func didMoveToView
let border = SKPhysicsBody(edgeLoopFromRect: self.frame)
border.friction = 0
self.physicsBody = border
self.physicsWorld.contactDelegate = self
It works fine for preventing my player from going outside the screen; However, I want my player, or anything else in the scene, to go to the other side of the screen when touching the border. In other words: If i kept going right i would keep going right until i hit the border then my player would appear from the left border and continue going right in an endless loop.
Same goes for y axis.
How is this possible in code?

Try this code:
if player.position.x + player.size.width / 2 > size.width {
player.position.x = -player.size.width
}
else if player.position.x + player.size.width / 2 < 0 {
player.position.x = size.width + player.size.width / 2
Same logic for Y axis.

For whoever is interested to reproduce this, I had to delete the following code:
let border = SKPhysicsBody(edgeLoopFromRect: self.frame)
border.friction = 0
self.physicsBody = border
self.physicsWorld.contactDelegate = self
Then, in touchesMoved I added:
if Player.position.x + Player.size.width / 16 > size.width {
Player.position.x = Player.size.width / 16
}
else if Player.position.x + Player.size.width / 16 < 0 {
Player.position.x = size.width - Player.size.width / 16
}
for the x axis. & the same can be done for the Y axis except we will change the x to y & the width to height.

Related

Sprite physics showing up but not visually showing up in Swift (SpriteKit)

I set up the sprite's physics body, zPosition, color, size, etc., but it won't appear visually. (It has the highest zPosition of my sprites, and YES I did import SpriteKit)
let wall = SKSpriteNode()
wall.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
//Takes screen size / width to make each piece one/(value set by "maze" at top) of the screen
let width: CGFloat = (UIScreen.main.bounds.width) / CGFloat(mazeSize)
let height: CGFloat = (UIScreen.main.bounds.height - 134) / CGFloat(mazeSize) //subtract so there's extra room on top+bottom of screen
wall.color = UIColor(ciColor: .black)
wall.isHidden = false
wall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: width, height: height))
wall.physicsBody?.restitution = 0
wall.physicsBody?.friction = 0
wall.physicsBody?.isDynamic = false
wall.zPosition = 4
//sets up x+y location based on row count and which row this is on
//locations (ONLY positive)
let xLoc: CGFloat = ((UIScreen.main.bounds.width) / CGFloat((mazeSize/2))) * CGFloat(i/mazeSize)
let yLoc: CGFloat = ((UIScreen.main.bounds.height - 134) / CGFloat(mazeSize/2)) * CGFloat(i/mazeSize)
if((i/mazeSize) > (mazeSize/2)){ //if positive on x
wall.position.x = xLoc
}
else{ //if negative on x
wall.position.x = -xLoc
}
if(i > ((mazeSize^2)/2)){ //if positive on y
wall.position.x = yLoc
}
else{ //if negative on y
wall.position.y = yLoc
}
I'm using this to set up walls in a maze (Yes I know the x+y positions are wrong, but they still "appear" clumped in the middle), but only the physics of these nodes show up. Is there a way I can make these black walls appear?
The geometry of the sprite node and the geometry of the physics body don't have to be related, and in this case you've made a rectangular physics body and basically an empty sprite node. Try something like
let wall = SKSpriteNode(color: .black, size: CGSize(width: 100, height: 100)
or using the SKSpriteNode(texture:) initializer.

Shoot Arrow in the direction that the Bow is facing

I am creating a iOS Game where a player has a bow and arrow that spins in a 360 degree circle, and the player must shoot the bow at the right time to hit the target. Right now I am having trouble getting the arrow to shoot in the direction the bow is facing, as well as getting the arrow to shoot at the right angle towards that direction.
let bullet = SKSpriteNode(fileNamed: "Bullet")
bullet?.size = CGSize(width: 100, height: 100)
bullet.zPosition = -5
bullet.position = CGPointMake(player.position.x, player.position.y)
bullet.zRotation = player.zRotation
let action = SKAction.moveToY(self.size.height + 30, duration: 0.8)
let actionDone = SKAction.removeFromParent()
bullet.runAction(SKAction.sequence([action, actionDone]))
bullet.physicsBody = SKPhysicsBody(rectangleOfSize: bullet.size)
bullet.physicsBody?.affectedByGravity = false
bullet.physicsBody?.dynamic = false
self.addChild(bullet)
The player refers the the bow for reference.
Instead of moving the bullet to self.size.height + 30 in the Y direction and 0 pixels in the X direction, you can rotate that movement direction by the zRotation using trigonometry.
let amount = self.size.height + 30
let action = SKAction.moveTo(CGPointMake(bullet.position.x + amount * sin(bullet.zRotation), bullet.position.y + amount * cos(bullet.zRotation)), duration: 0.8)
You can get the behavior you are looking for by calculating a force vector from the bullet's zRotation and then use it to apply a force to the bullet's physicsBody.
To do this we will use trigonometry.
//adjust rotation by pi/2 radians to match spriteKits rotation system
let adjustedRotation = bullet.zRotation + .pi/2
//intensity scalar
let intensity:CGFloat = 4000 //adjust this value
//find x and y components using adjustedRotation and scale by intensity
let vx = intensity * cos(adjustedRotation)
let vy = intensity * sin(adjustedRotation)
//make vector using vx and vy components
let forceVector = CGVector(dx:vx, dy: vy)
//apply force to physicsBody
bullet.physicsBody?.applyForce(forceVector)

Show direction to CGPoint SpriteKit

I need an arrow (white circle) that shows the direction to CGPoint while user move his icon and camera.
I mean that arrow (white circle) needs to take position on the edge of visible screen and shows the way that helps user to return to followed CGPoint.
Demo gif
You are expecting for two things:
place the arrow on the correct screen side given the target CGPoint position
Orient the arrow towards the target CGPoint
In your touchesMoved(_:) method, you can update the arrow rotation and position, not tested but the principle should work :
private func placeArrow(at sourceNode: SKNode, for targetNode: SKNode) {
//do not display arrow if already on screen
guard targetNode.position.x > cameraNode.position.x - screenSizeX/2
&& targetNode.position.x < cameraNode.position.x + screenSizeX/2
&& targetNode.position.y > cameraNode.position.y - screenSizeY/2
&& targetNode.position.y < cameraNode.position.y + screenSizeY/2
{
arrowNode.isHidden = true
return
}
//find arrow position, if on the left place to the left side, else on the right
//place at the medium y between the 2 points
let screenSizeX = UIScreen.main.bounds.width
let screenSizeY = UIScreen.main.bounds.height
let ymin = cameraNode.position.y - screenSizeY/2 + 10
let ymax = cameraNode.position.y + screenSizeY/2 - 10
let midY = (sourceNode.position.y + targetNode.position.y)/2
var clampedMidY = midY
if midY > ymax {
clampedMidY = ymax
} else if midY < ymin {
clampedMidY = ymin
}
arrowNode.position = CGPoint(x: (targetNode.position.x < sourceNode.position.x) ? cameraNode.position.x - screenSizeX/2 : cameraNode.position.x + screenSizeX/2, y: clampedMidY)
//find arrow orientation
//see https://stackoverflow.com/questions/38411494/rotating-a-sprite-towards-a-point-and-move-it-towards-it-with-a-duration
let v1 = CGVector(dx:0, dy:1)
let v2 = CGVector(dx: targetNode.position.x - sourceNode.position.x, dy: targetNode.position.y - sourceNode.position.y)
arrowNode.zRotation = atan2(v2.dy, v2.dx) - atan2(v1.dy, v1.dx)
}

Why is there is a gap between my looping images in my SpriteKit scene?

At the beginning I have declared the nodes.
//Declaration of the ground nodes.
var groundImage: SKSpriteNode = SKSpriteNode()
var groundImage2: SKSpriteNode = SKSpriteNode()
In the viewDidLoad I have:
//Creates an instance of both sprites that are of the same image that are to be lined up against each other in the x axis creating the illution of continuity.
groundImage = SKSpriteNode(imageNamed: "Ground.png")
groundImage2 = SKSpriteNode(imageNamed: "Ground.png")
//Specifies the Z position of the images.
groundImage.zPosition = 3
groundImage2.zPosition = 3
//Scales the images to the correct size of the screen.
groundImage.size.width = self.frame.width
groundImage.size.height = self.groundImage.size.height / 2
groundImage2.size.width = self.frame.width
groundImage2.size.height = self.groundImage2.size.height / 2
//Specicies the x position of the images. By offsetting the second you create the illution of a long, continuous image.
groundImage.position.x = view.bounds.size.width * 0.5
groundImage2.position.x = view.bounds.size.width * 1.5
//Specifies the y postion of the images, obviously these are the same as they are not to be offset at any time.
groundImage.position.y = (view.bounds.size.height - view.bounds.size.height) + self.groundImage.size.height / 2
groundImage2.position.y = (view.bounds.size.height - view.bounds.size.height) + self.groundImage2.size.height / 2
//Not sure what this does yet.
groundImage.texture?.filteringMode = SKTextureFilteringMode.Nearest
groundImage2.texture?.filteringMode = SKTextureFilteringMode.Nearest
//Adds instances of the sprites to the scene.
self.addChild(groundImage)
self.addChild(groundImage2)
In the update method I have:
//This is how the image is moved relative the number specified. The number in the variable is how many pixels the frame is being moved each frame refresh.
groundImage.position.x -= gameSpeed
groundImage2.position.x -= gameSpeed
if (groundImage.position.x <= -self.view!.bounds.size.width / 2)
{
groundImage.position.x = self.view!.bounds.size.width * 1.5 // - 2
}
if (groundImage2.position.x <= -self.view!.bounds.size.width / 2)
{
groundImage2.position.x = self.view!.bounds.size.width * 1.5 // - 2
}
Any yet there is a slight gap between the two images when they are looping. This gap increases as I increase the speed they are looped at using a game speed variable.
Can anyone explain to me what I have done wrong please?
I have checked the images themselves are not causing the issue.
Thanks,
Steven
The gap is probably because the update method called about 60 times in a second (defaults is 60 fps if I am not wrong),
you should use SKAction to simply flip your images,
it will be much more efficient. Here is your starting point:
https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/AddingActionstoSprites/AddingActionstoSprites.html

Using anchorPoint in spritekit to each node

Im wondering at my setting ball.position function, the point is to set ball's point to other spritenode(in left/middle/right side), i tried to write my own function, but it was full of bugs, (it's commented), next after review developer lib's. I found anchorPoint to parent position, but it's not working, i dont know exactly how to set current parent for ball. I'll be really grateful for some advices.
if (ball.position.y < block.position.y +10 && ball.position.y > block.position.y -10) {
midX = CGRectGetMidX(self.frame);
side = block1.size.width/2;
if (emitter.position.x < midX - side+5) {
// fixedEmitterPos = emitter.position.x+side;
ball.anchorPoint = CGPointMake(0,0.5);
}
if (emitter.position.x > midX + side-5){
// fixedEmitterPos = emitter.position.x-side;
ball.anchorPoint = CGPointMake(1,0.5);
}
if (emitter.position.x == 160) {
// fixedEmitterPos = emitter.position.x;
ball.anchorPoint = CGPointMake(0.5,0.5);
}
}
I don't know what exactly is your question but to change your anchor point you don't change anything in parent node. For example if anchor point is
ball.anchorPoint = CGPointMake(0.5,0.5);
the anchor point in exactly in the middle and if you change position of the ball like:
ball.position == CGPointMake(50,50);
the ball center will be exactly in that point (50, 50).
but if the anchor point will be:
ball.anchorPoint = CGPointMake(1, 1);
and you change the position to :
ball.position == CGPointMake(50,50);
the ball centre will be in
X = 50 - (ball width / 2)
Y = 50 - (ball height / 2)
If you want to set up ball position base on other sprite node you can do something like that:
//Attach left centre side of ball to other sprite:
ball.anchorPoint = CGPointMake(0, 0.5);
ball.position.x == otherSprit.position.x + otherSprit.size.with;
ball.position.y == otherSprit.position.y;
//Attach right centre side of ball to other sprite:
ball.anchorPoint = CGPointMake(1, 0.5);
ball.position.x == otherSprit.position.x;
ball.position.y == otherSprit.position.y;
Hope this is what you are about.

Resources