I've been trying to code this for days now, without success.
My game is a 2D endless runner. My hero is locked onto a specific point on the X-axis. The obstacles are randomly spawned (height and distance) buildings.
I'd like to add a chasing enemy to the game. Currently for visuals only.
chasingCopNode = CopSprite.newInstance(position: CGPoint(x: -90, y: 0))
let follow = SKAction.moveTo(x: thiefNode.position.x - 50, duration: 5)
let wait = SKAction.wait(forDuration: 2)
let fallBehind = SKAction.moveTo(x: -90, duration: 4)
let chasingSequence = SKAction.sequence([follow, wait, fallBehind])
chasingCopNode.run(chasingSequence)
chasing = true
self.addChild(chasingCopNode)
I created this to move tha chasing sprite along the X-axis.
In my update function, I simply update the Y position of the enemy with the hero's Y position.
That is working, but not quite the thing I need. Since the Y positions are the same, the enemy sprite is flying sometimes (because there is no building under it).
I couldn't come up with any working ideas for the movement on the Y-axis.
I was thinking maybe add a function that calculates the needed Y impulse for the enemy to land on the next building, but I was not able to do that correctly.
thank you for your help!
Related
I have a moving background which is 1500 x 600 pixels and constantly moves vertically down the screen using this code:
let bgTexture = SKTexture(imageNamed: "bg.png")
let moveBGanimation = SKAction.move(by: CGVector(dx: 0, dy: -bgTexture.size().height), duration: 4)
let shiftBGAnimation = SKAction.move(by: CGVector(dx: 0, dy: bgTexture.size().height), duration: 0)
let moveBGForever = SKAction.repeatForever(SKAction.sequence([moveBGanimation, shiftBGAnimation]))
var i: CGFloat = 0
while i < 3 {
bg = SKSpriteNode(texture: bgTexture)
bg.position = CGPoint(x: self.frame.midX, y: bgTexture.size().height * i)
bg.size.width = self.frame.width
bg.zPosition = -2
bg.run(moveBGForever)
self.addChild(bg)
i += 1
}
I now want a new background to come onto the screen after x amount of time to give the feel the player is moving into a different part of the game.
Could I put this code into a function and trigger it with NSTimer after say 20 seconds but change the start position of the new bg to be off screen?
The trouble with repeatForever actions is you don't know where they are at a certain moment. NSTimers are not as precise as you'd like, so using a timer may miss the right time or jump in too early depending on rendering speeds and frame rate.
I would suggest replacing your moveBGForever with a bgAnimation as a sequence of your move & shift actions. Then, when you run bgAnimation action, you run it with a completion block of { self.cycleComplete = true }. cycleComplete would be a boolean variable that indicates whether the action sequence is done or not. In your scene update method you can check if this variable is true and if it is, you can run the sequence action once again. Don't forget to reset the cycleComplete var to false.
Perhaps it sounds more complex but gives you control of whether you want to run one more cycle or not. If not, then you can change the texture and run the cycle again.
Alternatively you may leave it as it is and only change the texture(s) after making sure the sprite is outside the visible area, e.g. its Y position is > view size height.
In SpriteKit you can use wait actions with completion blocks. This is more straightforward than using a NSTimer.
So, to answer your question - when using actions for moving the sprites on-screen, you should not change the sprite's position at any time - this is what the actions do. You only need to make sure that you update the texture when the position is off-screen. When the time comes, obviously some of the sprites will be displayed, so you can't change the texture of all 3 at the same time. For that you may need a helper variable to check in your update cycle (as I suggested above) and replace the textures when the time is right (sprite Y pos is off-screen).
I have a simple game, where after my score the ball gets more speed with
ball.physicsBody?.applyImpulse(CGVector(dx: 1, dy: -1))
So now i want to slow down after he loses. I tried:
ball.pause = 0
and many other codes, that i found on the web. Can you help me?
// Sorry if this is not a advanced Question, I'm not that long in swift.
If you want to stop the ball completely, you can run this line:
ball.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
This sets the velocity to nothing, effectively stopping the ball, without pausing it.
If you only want to slow it down, you can try doing something like this:
ball.physicsBody?.velocity = CGVector(dx: ball.physicsBody.velocity.dx - 5, dy: ball.physicsBody.velocity.dy + 5)
This brings the velocity closer to nothing (designed for your example), rather than completely stopping it.
Alternatively, you could apply an impulse in the opposite direction, that would slow down its movement in the original direction:
ball.physicsBody?.applyImpulse(CGVector(dx: -1, dy: 1))
Note that if you're trying to completely pause the ball (not just slow down or stop it, but pause it's actions) you would run this: ball.isPaused = true
I'm trying to make a game where the sprite will always move to the right when hit by an object. However since the Sprite rotates constantly and the zero radians rotates with the Sprite causes my calculated magnitude to go the opposite direction if the sprite is facing left and hits the object. Is there a way to keep the direction of the magnitude always pointing to the right even if the zero is facing left?
// referencePoint = upper right corner of the frame
let rightTriangleFinalPoint:CGPoint = CGPoint(x: referencePoint.x, y: theSprite.position.y)
let theSpriteToReferenceDistance = distanceBetweenCGPoints(theSprite.position, b: referencePoint)
let theSpriteToFinalPointDistance = distanceBetweenCGPoints(theSprite.position, b: rightTriangleFinalPoint)
let arcCosineValue = theSpriteToFinalPointDistance / theSpriteToReferenceDistance
let angle = Double(acos(arcCosineValue))
let xMagnitude = magnitude * cos(angle)
let yMagnitude = (magnitude * sin(angle)) / 1.5
Not sure if this works for you:
I would use an orientation constraint to rotate the sprite. The movement can be done independent from the orientation in that case.
I made an tutorial some time ago: http://stefansdevplayground.blogspot.de/2014/09/howto-implement-targeting-or-follow.html
So I figured out what was going on.
It seems like the angle doesn't rotate with the Sprite like I originally thought and the vector that I am making is working with the code above. THE problem that I had was that I also set the collision bit for the objects which is wrong. If I only set the contact bit for the objects against the sprite the my desired outcome comes true.
I am making a fist person camera in Scenekit and the character is not moving relative to the rotation. Instead it is moving relative to the world axis. I want my game to be so when the player swipes forward, the character moves forward on its own x axis. Not along the x axis of the world. I tried to compensate of this with sine and cosine but it did not work. Here is my code:
func lookGestureRecognized(gesture: UIPanGestureRecognizer) {
let velocity = gesture.velocityInView(sceneView)
let rotationAngle = heroNode.presentationNode().rotation.w * heroNode.presentationNode().rotation.y
var impulse = SCNVector3Make(Float(velocity.x)/50, 0, Float(velocity.y)/50)
impulse = SCNVector3(x: impulse.x * cos(rotationAngle), y: 0, z: impulse.z * sin(rotationAngle))
heroNode.physicsBody?.applyForce(impulse, impulse: true)
}
This is still making the character move in the wrong direction of the swipe. Does anyone know how to make the character move relative to its rotation?
Thanks, please ask for clarification if needed
the rotation property is made of a rotation axis and a rotation angle. If you want to retrieve the rotation along the x axis you can use the eulerAngles property.
I need to fire a "shot" from a static SKSpriteNode towards another dynamic node. I've created a shot as a sprite node as the shot and moved it to by SKAction to the main sprite node, like so:
let node = info["node"] as SKSpriteNode // The static node
let shot = SKSpriteNode(color: UIColor.blueColor(), size: CGSize(width:node.frame.width / 3, height: node.frame.height / 1.2))
shot.physicsBody = SKPhysicsBody(edgeLoopFromRect: shot.frame)
shot.position = node.position
shot.physicsBody?.categoryBitMask = shotMask
shot.physicsBody?.contactTestBitMask = spriteMask
shot.physicsBody?.collisionBitMask = 0
shot.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(shot)
shot.runAction(SKAction.moveTo(sprite.position, duration: 1.0))
My problem is that the shot stops after it reaches the point. Is there a way to make the shot continue moving in it's direction, or is there another way of doing what I need? Thanks!
Don't use a SKAction to move the shot. Instead change the shot's position by directly modifying it's position.
shot.position = CGPointMake(shot.position.x+1, shot.position.y);
Run the above in your update method. To speed up the shot, change the +1 to whatever value you need. Obviously you will need to remove the shot node from parent once it leaves the screen, hits the target, etc...
You'll need to calculate the vector from the shot's origin to it's destination. Then extend the vector until the shot will be off screen, possibly by finding the unit vector (a vector of length 1) and multiplying it by 3000 or so. As you have a definite endpoint now, you could still use an SKAction.