I'm currently working on a game using Spritekit. The game has objects which spawn at the top of the screen and fall towards the player character, and the game ends when the player character collides with any of the objects. I am trying to find a way to gradually speed up gameplay over time to make the game more difficult (i.e. objects fall at normal speed when the game begins, after 5 seconds speed up 50%, after 5 more seconds speed up another 50%, ad infinitum.)
Would I need to use NSTimer to make a countdown to increase the gravity applied to the falling objects? Sorry if this is a basic thing, I'm kind of new to programming.
Thanks, Jake
EDIT:
My spawn method for enemies-
let spawn = SKAction.runBlock({() in self.spawnEnemy()})
let delay = SKAction.waitForDuration(NSTimeInterval(2.0))
let spawnThenDelay = SKAction.sequence([spawn, delay])
let spawnThenDelayForever = SKAction.repeatActionForever(spawnThenDelay)
self.runAction(spawnThenDelayForever)
And my method for making the enemies fall-
func spawnEnemy() {
let enemy = SKNode()
let x = arc4random()
fallSprite.physicsBody = SKPhysicsBody(rectangleOfSize: fallSprite.size)
fallSprite.physicsBody.dynamic = true
self.physicsWorld.gravity = CGVectorMake(0.0, -0.50)
enemy.addChild(fallSprite)
}
In spawnEnemy(), you set self.physicsWorld.gravity. Move this line to your update: method.
If you are not keeping track of the game's duration right now, you will want to implement that. You can use the parameter of the update: method to accomplish this.
You can then use the game duration to change the gravity.
For example,
override func update(currentTime: CFTimeInterval) {
if gameState == Playing{
//update "duration" using "currentTime"
self.physicsWorld.physicsBody = CGVectorMake(0.0, -0.50 * (duration / 10.0))
}
}
10.0 can be changed depending on how fast you want the gravity to increase. A higher number makes it change less drastically, and a smaller number makes the gravity increase quite rapidly.
Hopefully this answers your question.
Related
I am making a game with sprites that move and obstacles to learn SpriteKit. I want the sprites that move to collide with the obstacles and bounce off of them but I want the obstacles to stay fixed. How do I do this? I have tried the following with no success:
Setting obstacle.physicsBody?.isDynamic = true. This made the sprites go through the obstacle.
Fixing the movement and rotation of the object with SKConstraint. When I do this they just go through each other.
Setting the mass of the body to be really high as follows obstacle.physicsBody?.mass = CGPoint.maxFiniteMagnitude but this freezes the game. When I set it really high it doesn't seem to do anything.
Setting obstacle?.physicsBody.velocity = CGVector(dx: 0, dy: 0) when the objects collide. I know that the contact.bodyA and contact.bodyB are passed by value and not reference so I loop through an array with the obstacles and set the velocity this way. The obstacles are still pushed by the other sprites.
Update:
- Setting obstacle.physicsBody?.collisionBitMask = PhysicsCategory.none so the sprite collides with the obstacle but not the other way around.
The object is setup as follows, with fish being the other sprite:
obstacle.position = location
obstacle.physicsBody = SKPhysicsBody(rectangleOf: obstacle.size)
obstacle.physicsBody?.isDynamic = true
obstacle.physicsBody?.categoryBitMask = PhysicsCategory.obstacle
obstacle.physicsBody?.contactTestBitMask = PhysicsCategory.fish
obstacle.physicsBody?.collisionBitMask = PhysicsCategory.fish
obstacle.physicsBody?.usesPreciseCollisionDetection = true
self.obstacles.append(obstacle)
super.addChild(obstacle)
Please let me know if there is something I am doing wrong / misunderstanding. Thanks.
Found the answer here. One of the objects needs to have .physicsBody?.isDynamic = true. In this case I set the obstacle to false so it is stationary.
I have a square = SKSpriteNode() that turns that rotates 360° when you touch it. I want to stop the spinning when you touch it again.
Now I would make square rotate through the SKAction.rotate but how can I stop the spinning realisticly meaning that I want the sprite to spin slower and slower until it stands still.
Did you try and ease out curve for the timingMode? If you don't like this effect you can provide your own custom timingFunction. There are several websites where you can explore animation curves online. I like this one.
You may want to consider using a physicsbody and applying an angular force.
let square = SKSpriteNode(color:.white,size:CGSize(10,10))
if let physicsBody = SKPhysicsBody(rectangleOf:square.frame.size)
{
physicsBody.isDynamic = true
physicsBody.allowsRotation = true
physicsBody.affectedByGravity = false
physicsBody.angularDamping = 0.1 //Adjust this to speed up or slow down the resistance of the spin
square.physicsBody = physicsBody
}
...
When you need to spin:
square.physicsBody!.angularImpulse(1) //Adjust this to change the amount of force applied to the spin
I need to pause the balls for my game. I want them to stop in place but they are moving by impulse and if I make them non-dynamic to stop them, the impulse goes away. I'm trying to pause them and un-pause them, and they still keep going in the same direction. I tried
ball.paused = true
but that didn't work. Anyone know?
Instead of freezing the nodes, I froze the scene with
scene?.physicsWorld.speed = 0
because that freezes all of the nodes and not my time or score integers which is exactly what I needed.
Here is how I solved it in my game:
var ballVelocity = CGVector()
func pauseAction(ball: SKSpriteNode){
if ball.dynamic {
//Ball is moving. Save the current velocity of ball.
ballVelocity = ball.velocity
//Stop the ball
ball.physicsBody.dynamic = false
}else{
//Ball is paused.
//Resume ball movement.
ball.physicsBody.dynamic = true
//Add the saved velocity.
ball.velocity = ballVelocity
}
}
You can use ball.physicsBody.velocity = CGVectorMake(0, 0); but keep in mind that if the ball is affected by gravity, it will still drop.
sorry for the wall of text, I just wanted to give a good explanation of how my game is set up so you can understand my problem better.
I have a few questions about the game that I am currently making using swift.
I am making a game where a player has to dodge enemies. The player is an SKSpriteNode image which gets it’s physics body and is added to the scene here:
override func didMoveToView(view: SKView) {
player.position = CGPoint(x: size.width/2, y: size.height/2)
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width/2)
player.physicsBody?.dynamic = true
player.physicsBody?.categoryBitMask = PhysicsCategory.Player
player.physicsBody?.contactTestBitMask = PhysicsCategory.Enemy
player.physicsBody?.collisionBitMask = PhysicsCategory.None
player.physicsBody?.usesPreciseCollisionDetection = true
addChild(player)
}
This implementation seems to work great, I have collision working between the Player and Enemy.
The enemies are initialised as SkSpriteNodes too:
func addEnemy() {
let enemy = SKSpriteNode(imageNamed: “enemy”)
enemy.name = “enemy”
enemy.position = CGPoint(x: size.height/2, y: size.width/2)
self.addChild(enemy)
enemy.physicsBody = SKPhysicsBody(circleOfRadius: enemy.size.width/2.75)
enemy.physicsBody?.dynamic = true
enemy.physicsBody?.categoryBitMask = PhysicsCategory.Enemy
enemy.physicsBody?.contactTestBitMask = PhysicsCategory.Player
enemy.physicsBody?.collisionBitMask = PhysicsCategory.None
// code for making the enemies move here (including speed)
}
enemy.runAction(SKAction.sequence([moveAction,removeAction]))
}
and are constantly spawned every 0.7 seconds in didMoveToView(view: SKView) like this:
runAction(SKAction.repeatActionForever(
SKAction.sequence([
SKAction.runBlock(addEnemy),
SKAction.waitForDuration(0.7)
])
))
So my question is as follows. Is this the most appropriate way to do what I am trying to achieve? That is; spawning multiple different types of enemies constantly at different intervals?
I am struggling to figure out how I could (for example), make the enemy start spawning only after 10 seconds has passed, or how I could change the direction of the enemies with the press of a UIButton on screen? It seems since they are all called and created when the view loads, I can’t be making constant changes to things like direction, speed etc.
What is a better way of doing this, or how can I do what I need?
Thanks :)
It sounds like what you want to do is use the update method instead of using a repeating SKAction.
In update you calculate how much time has passed using currentTime and then decided on if you should spawn an enemy or not. This also gives you a chance to spawn an enemy any way you want based on the current state of your game.
As far as the button. It really depends on your game, but most games have some sort of count down or requires some sort of user interaction before game play begins.
EDIT
My swift is a bit rusty but it would be something like this...
var lastUpdateTime = 0.0;
var spawnTimer = 0.0;
var nextSpawnTime = 10.0;
override func update(currentTime: CFTimeInterval) {
if (lastUpdateTime == 0.0)
{
lastUpdateTime = currentTime
return
}
spawnTimer += currentTime-lastUpdateTime
//Do any other update logic here
if (spawnTimer >= nextSpawnTime)
{
spawnTimer = 0.0
spawn()
}
lastUpdateTime = currentTime
}
func spawn ()
{
//spawn and change nextSpawnTime if you wish
println("spawn")
}
Hopefully that helps and makes sense.
I am creating a very simple game using Swift and SpriteKit and I am moving a ball on the screen using the accelerometer data (acceleration x,y).
I would say the code works fine but I have noticed that sometimes (often right when I open the app) the accelerometer data is not correct and delayed for few seconds.
Why is that happening?
I am using the following code to read the accelerometer data:
if motionManager.accelerometerAvailable == true {
motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.currentQueue(), withHandler:{
data, error in
self.accX = CGFloat(data.acceleration.x)
self.accY = CGFloat(data.acceleration.y)
})
}
And the function update to apply some impulse to the ball:
override func update(currentTime: CFTimeInterval) {
var impulse = CGVectorMake(accX, accY)
var obj = childNodeWithName("ball") as SKSpriteNode
obj.physicsBody?.applyImpulse(impulse)
}
Am i missing something?
Thank you
With any accelerometer data, it is a good idea to run it through a filter to smooth out any irregular spikes. Here is my favorite:
double filteredAcceleration[3];
memset(filteredAcceleration, 0, sizeof(filteredAcceleration));
CMAccelerometerData *newestAccel = motionManager.accelerometerData;
filteredAcceleration[0] = (filteredAcceleration[0]*(1.0-alpha)) + (newestAccel.acceleration.x*alpha);
filteredAcceleration[1] = (filteredAcceleration[1]*(1.0-alpha)) + (newestAccel.acceleration.y*alpha);
filteredAcceleration[2] = (filteredAcceleration[2]*(1.0-alpha)) + (newestAccel.acceleration.z*alpha);
alpha can be any value from 0 to 1. The closer to 1 the more responsive it will be, the closer to zero the more smooth it will be. My favorite value on the iPhone is 0.2 It is a good compromise for smooth yet responsive for a game like doodle jump, or possibly moving a ball around.
I don't know why the accelerometer data is incorrect/delayed on startup, my guess would be that the hardware has to wake up and calibrate itself, but regardless of the why, if you implement a filter, it will smooth out these irregularities, and they won't be nearly as noticeable.
I have given priority to both functions and the issue seems fixed.
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
// do some task
dispatch_async(dispatch_get_main_queue()) {
// code with priority
}
}