SpriteKit screen width not measuring properly - ios

I have this Sprite Kit game that passes some balls across the screen and the user has to tap them. If the user misses a ball and it goes off the screen then they lose.
The game works perfect when testing it on my iPhone but when I run it on my iPad it doesn't work as it's supposed to. It also doesn't work on iPhone X simulator. When the game first starts up it goes to the Start Scene, but when the user clicks "Start Game" it goes to the Game Scene and then automatically cuts to the game over scene without spawning any balls. After I hit "Play again" it will spawn the balls as it should but the measurements of detecting if the balls are out of bounds are not correct (which they are when testing on my iPhone 8 Plus).
Here are the areas where I think the problem may be coming. This is where the widths for things come from.
EnumerateChildNodes function to remove the balls once of the scene:
func searchChildNodes() {
self.enumerateChildNodes(withName: "BALL") { (node: SKNode, nil) in
if node.position.x < -25 || node.position.x > self.size.width + 25 {
print("balls Out")
node.removeFromParent()
let transition = SKTransition.fade(withDuration: 1)
self.gameScene = SKScene(fileNamed: "GameOverScene")
self.gameScene.scaleMode = .aspectFit
self.view?.presentScene(self.gameScene, transition: transition)
}
}
}
Specifically in the if statement:
if node.position.x < -25 || node.position.x > self.size.width + 25
If remove the part after the || operator then the game lets the balls do what they are supposed, only without detecting if the are out of bounds on the right side of the screen.
Update
Here is the code that sets the position of the balls when they are spawned:
if ballDirection == "right" {
player?.position.x = 0
moveRight()
}
else {
player?.position.x = (self.scene!.frame.size.height)
moveLeft()
}

Related

How to concisely control a spriteNode's movement with swipes

I am building a spriteKit game in Xcode 10.2.1 with swift 5, where the player has to reach the end of a large background (avoiding enemies, obstacles etc.) in order to complete the level. The camera is tied to the player spriteNode's position.x and the player can move the spriteNode in any direction with swipes.
I am using a cobbled together UIPanGestureRecognizer to enable this movement, and this seems to work reasonably well.
#objc func handlePan(recognizer: UIPanGestureRecognizer) {
let transformerX = 1024/self.view!.frame.size.width
let transformerY = 768/self.view!.frame.size.height
if recognizer.state == .began {
lastSwipeBeginningPoint = recognizer.location(in: self.view)
} else if (recognizer.state == .ended) {
if scrolling { // this is controlls whether player can be moved - scrolling is set to true once introductory elements have run
if playerDamage < 4 { //player cannot be controlled and falls off screen if it has 4 damage
let velocity = recognizer.velocity(in: self.view)
player.physicsBody?.applyImpulse(CGVector(dx: velocity.x * transformerX / 5.4, dy: velocity.y * transformerY * -1 / 5.4)) //-1 inverts y direction so it moves as expected
}
}
}
}
This does allow movement around the level, but not in as precise and controlled a way as I would like. The player moves off at the set speed but any further swipes (to change direction/avoid obstacles) seem to multiply the speed and generally make it difficult to control accurately.
Is there a way of choking off the speed, so that it starts off at the set speed but then starts to lose momentum, or is there a better way altogether of controlling multi-direction movement?

SKActions Doesn't works sometimes, but if minimise the game and reopen it, It Works. How that could happen?

I'm having problem with 2 actions both of the actions are move to actions, that moves the pieces to empty node's position(Empty sknodes are added from sks file for). The problem only happens some times not always, in first one when player reaches loc:25 means wins it moves to empty node's position but it some times doesn't moves to there but the game goes on and if I minimise the game and open it they are already there where they should have moved previously.
Second problem this one is more important because with this skaction player moves to next Position. But very few times it doesn't works everything works fine but when it comes to execute that skaction it Stops totally, only touches works. but if I just minimise the game and open it, it moves to position and then game works fine. No issue then, How can this Happen?
I run the second skaction with sequence with 2 other actions and first one with skspritenode.run() action. and I don't have iOS device so I'm testing it on simulator.
Here are the both of SKActions that I use just the same thing nothing new :
for node in children {
if (node.name == String(nextSpace)) {
let moveAction:SKAction = SKAction.move(to: node.position, duration: 0.5)
moveAction.timingMode = .easeOut
let wait:SKAction = SKAction.wait(forDuration: 0.1)
let runAction:SKAction = SKAction.run({
if (self.movesRemaining == 1) {
self.KillHim(self.whosTurn, nextspace: nextSpace)
}
self.movesRemaining = self.movesRemaining - 1
self.setThePlayerSpace(space: nextSpace, player:self.whosTurn)
self.movePiece()
})
if whosTurn == .Player1 {
touchedNode.run(SKAction.sequence([moveAction, wait, runAction]))
} else {
playerPiece.run(SKAction.sequence([moveAction, wait, runAction]))
}
}
}
code for moving when player won :
if (currentSpacePlayer1Piece1 == 25) {
let loc:SKNode = childNode(withName: "c1")!
Player1Piece1.run(SKAction.move(to: loc.position, duration: 0.2))
currentSpacePlayer1Piece1 = 26
OneMoreMove += 1
} else if (currentSpacePlayer1Piece2 == 25) {
let loc:SKNode = childNode(withName: "c2")!
Player1Piece2.run(SKAction.move(to: loc.position, duration: 0.2))
currentSpacePlayer1Piece2 = 26
OneMoreMove += 1
}
is it could be happening because of using it in simulator?
As knightOfDragon said the node was being paused with some logic some times after removing, it works perfectly. If you have same issue search isPaused if you are using it somewhere not intended.

SpriteKit move physics body on touch (air hockey game)

I'm trying to make an air hockey game using SpriteKit. I trying to make the pucks draggable but I can't make them continue to move after the touch has ended. Right now I am binding the touch and the puck and setting it's position when the touch moves.
using the physics system:
override func update(currentTime: NSTimeInterval) {
for (touch, node) in draggingNodes {
let targetPosition = touch.locationInNode(self)
let distance = hypot(node.position.x - targetPosition.x, node.position.y - targetPosition.y)
var damping = sqrt(distance * 100)
if (damping < 0) {
damping = 0.0
}
node.physicsBody!.linearDamping = damping
node.physicsBody!.angularDamping = damping
let translation = CGPointMake(targetPosition.x - node.position.x, targetPosition.y - node.position.y)
node.physicsBody!.velocity = CGVectorMake(translation.x * 100, translation.y * 100);
}
}
You're likely going to need to do a lot more reading. Either you'll use the physics system:
In which case you'll impart an impulse onto the puck on the touch end event, having calculated the speed based on a delta in position and delta in time from last frame to current frame (or some type of average over more than 1 frame).
https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Physics/Physics.html
[OR]
You'll manually set velocity on the puck (not using the physics system), and then manually update the puck's position per frame based on that velocity, then recalculate its vector when it comes into contact with another object based on angle of of incidence.

Prevent sprite rotation from changing gravity

I have a function in SpriteKit that spawns a sprite (which is a square) at the top of the screen, and then gravity pulls the sprite to the bottom of the screen. I'm trying to get the sprite to rotate smoothly for an indefinite amount of time until it is removed when it reaches the bottom of the screen. The following code is in the class for the sprite:
func rotate() {
var action = SKAction.rotateByAngle(CGFloat(M_PI_2), duration: NSTimeInterval(1.5))
var repeatAction = SKAction.repeatActionForever(action)
self.runAction(repeatAction)
}
The problem that I am having is that, as the sprite turns, the sprite travels in the direction of the bottom of itself, not towards the bottom of the screen (the direction gravity is supposed to be). To further clarify, the object rotates, but as it rotates to 90 degrees, it travels sideways instead of downwards. This doesn't make sense to me. This is the code I'm using to add gravity in the didMoveToView() function:
self.physicsWorld.gravity = CGVectorMake(0.0, -1.8)
and this is the code used to spawn the sprite (the rs.rotate() calls the rotate method that is listed above):
func spawnRedSquares() {
if !self.gameOver {
let rs = RedSquare()
var rsSpawnRange = randomNumberBetween(self.leftSideBar.position.x + rs.sprite.size.width / 2, secondNum: self.rightSideBar.position.x - rs.sprite.size.width / 2)
rs.position = CGPointMake(rsSpawnRange, CGRectGetMaxY(self.frame) + rs.sprite.size.height * 2)
rs.zPosition = 3
self.addChild(rs)
self.rsArray.append(rs)
rs.rotate()
let spawn = SKAction.runBlock(self.spawnRedSquares)
let delay = SKAction.waitForDuration(NSTimeInterval(timeBetweenRedSquares))
let spawnThenDelay = SKAction.sequence([delay, spawn])
self.runAction(spawnThenDelay)
}
}
How can I get the object to rotate, but still fall down as if it were affected by normal gravity?
It looks like you are adding the rotation action to 'self' which I would assume is your scene as opposed to your sprite. This is causing the entire scene to rotate, and therefore its gravity is rotating as well.
Add the rotating action to your sprite and that should solve the issue.
Example: assuming your square sprite is called squareSprite:
let action = SKAction.rotateByAngle(CGFloat(M_PI_2), duration: NSTimeInterval(2))
let repeatAction = SKAction.repeatActionForever(action)
squareSprite.runAction(repeatAction) //Add the repeatAction to your square sprite

Sprite Kit Collision without bouncing

I setup a hero and some platforms that are moving from the top downwards.
With these I have collisionBitMasks that detect when the hero lands on a platform if the hero comes from above (to let the hero jump through the platforms)
if (_hero.physicsBody.velocity.dy > 0) {
_hero.physicsBody.collisionBitMask = 0;
}
else {_hero.physicsBody.collisionBitMask = platformCategory;
}
Everything works fine, except that the hero keeps bouncing on the platform.
Is there a way to let him sit on it, while the platform is moving down?
I tried using physicsBody.resting and physicsBody.friction, but without any success.
Thanks for help Guys
Had the same issue just a minute ago. It works with setting the restitution but you need to set both restitutions. The one of the hero AND the one of the platform (or in my case the scene boundaries).
so:
hero.physicsBody.collisionBitMask = platformCategory
hero.physicsBody.restitution = 0.0
platform.physicsBody.restitution = 0.0
any_other_object_that_should_still_bounce.physicsBody.restitution = 1.0
will do it. All other objects on the screen still bounce on the platform as long as you set their restitution > 0
The trick is to avoid any physics behavior while the hero is on the platform by resetting the hero body's velocity and setting the hero's position to a fixed vertical offset from the platform's position.
In semi-pseudo-code:
-(void) didSimulatePhysics
{
if (<hero on platform>)
{
hero.physicsBody.velocity = CGPointZero;
hero.position = CGPointMake(hero.position.x,
platform.position.y + <offset as needed>);
}
}
Have you tried altering the restitution property of the nodes' physics bodies?
_hero.physicsbody.restitution = 0;
It controls the bounciness...
You can use the Collision category:
When the velocity.dy is < 0 the hero is falling.
func collisionWithPlayer(player: SKNode) {
// 1
if player.physicsBody?.velocity.dy < 0 && (player.position.y - player.frame.size.height / 2.0) >= (self.position.y){
// 2
player.physicsBody?.collisionBitMask = CollisionCategoryBitMask.Platform
}

Resources