Swift 2: Removing Slowly increase of speed - swift

I am trying to effect the movement speed of a sprite in my app. When i apply velocity to the sprite it slowly increases in speed until it reaches a maximum speed. I am looking for a method to remove the smooth increase in speed and just have the exact same speed all the time (Until i change the velocity)
Feel free to add a comment if you have any questions.
enemy1 = SKSpriteNode(texture: enemyTexture)
enemy1.position = CGPoint(x: CGRectGetMidX(self.frame) - 300, y: CGRectGetMidY(self.frame) - 300)
enemy1.physicsBody = SKPhysicsBody(texture: enemyTexture, size: enemyTexture.size())
enemy1.physicsBody!.affectedByGravity = false
enemy1.physicsBody!.allowsRotation = false
self.addChild(enemy1)
This is the velocity i apply to the sprite. I am doing it in the update function for a continuing movement speed:
override func update(currentTime: NSTimeInterval) {
enemy1.physicsBody!.velocity = CGVectorMake(70, 0)
}

Instead of changing the velocity, have you tried to apply force vector for each update like this:
override func update(currentTime: NSTimeInterval) {
enemy1.physicsBody!.applyForce = CGVectorMake(70, 0)
}

I tried your code and it works correctly for me (sprite has a constant speed from the very beginning). It always worked like that. You can check this by pasting your code in an empty SpriteKit project. Currently, what you have described looks like you are actually using applyForce method rather than changing velocity vector directly.
The solution certainly is to change velocity property directly, like you are saying that you are doing right now. Probably you are overwriting this behaviour somewhere in your code accidentally, because what you have described isn't reproducible (with the code you've provided).

Related

Swift 5, Idea for a function that would save the highest value

Hello fellow programmers.
Im building my little swift game and I stumbled upon a problem.
I want my program to save the value of the highest Y coordinate that my ball ever acquired. So if a player went up and then down in 3d space, the Y value of the peak would be saved.
And my question isn't about implementation but an idea of a function that could solve my problem.
Keep in mind that coordinates update like 60 times per second and they aren't stored in any array or anything.
#edit
And to add to it, its not as simple as caching the change of a vector, like when the velocity is 0 because the ball is moving diagonally.. so even when it starts falling the velocity still isnt 0 it just slows down..
let ball = ballNode.presentation
let ballPosition = ball.position
let targetPosition = SCNVector3(x: ballPosition.x, y: ballPosition.y, z:ballPosition) //ball spawn point
Just create a maxY property initialize it with zero.
var maxY: CGFloat = .zero
As suggested in comment by vadian compare the last value with the current one. You can also extend Comparable protocol and implement a mutating method:
extension Comparable {
mutating func max(_ y: Self) {
self = Swift.max(self, y)
}
}
maxY.max(ballPosition.y)

Correct Way to use Accelerometer

Im using the accelerometer as the steering mechanism for my game. It works fine usually but occasionally it will act very odd. Randomly there is suddenly a large amount of input delay and every rotation I make isn't registered for a while. In some cases there might be a lot of delay in between commands entered right after each other leading to my character drifting in one direction for far too long. Is this due to game lag or is my code at fault? My code below.
actionMoveLeft = SKAction.moveBy(x: -3, y: 0, duration: 0.1)
actionMoveRight = SKAction.moveBy(x: 3, y: 0, duration: 0.1)
self.addChild(ship)
if motionManager.isAccelerometerAvailable == true {
motionManager.startAccelerometerUpdates(to: OperationQueue.current!, withHandler:{
data, error in
if (data!.acceleration.y) < -0.05 {
self.ship.run(self.actionMoveLeft)
}
else if data!.acceleration.y > 0.05 {
self.ship.run(self.actionMoveRight)
}
})
}
It's not guaranteed that the accelerometer will give you updates with regular intervals of 0.1 seconds (assuming that the deviceMotionUpdateInterval property of your CMMotionManager instance is set to 0.1). SKActions work fine when every action is executed at a discrete interval of time. But since the accelerometer gives you irregular interval updates, you may end up executing more actions at the same time.
An easy fix for that would be to remove the previous action every time:
if (data!.acceleration.y) < -0.05 {
self.ship.removeAction(forKey: "Move")
self.ship.run(self.actionMoveLeft, withKey: "Move")
}
But I still not recommend to use this approach, because the movement still doesn't look smooth, but it looks like your ship is moving jerkily. I suggest to use a SKPhysicsBody and directly manipulate its velocity property. Something like this:
// Inside the update method, assuming you have saved the acceleration vector
// and you have a deltaTime variable that holds the difference of time
// elapsed from the previous update
self.ship.physicsBody.velocity = CGVector(dx: self.ship.physicsBody.velocity.dx + CGFloat(acceleration.x * deltaTime), dy: self.ship.physicsBody.velocity.dy + CGFloat(acceleration.y * deltaTime))
If you don't want to use a physics body because you're just handling physics using your custom functions, then I suggest to compute the position of the ship manually at every frame.

Swift 3 (SpriteKit): Locking the x axis of a SKSpriteNode and its physicsBody

I really need to know how to lock the x axis of an SKSpriteNode and its physicsBody. I need to keep the SKSpriteNode dynamic and affectedByGravity. The node is on a slope, so this is why it's x axis is moved due to gravity. However, I don't want the x axis of this SKSpriteNode to move due to gravity. Is there a way to lock the x axis in order to achieve this?
Thanks for any help :D
Edit: I have tried to apply a constraint to the x value like this:
let xConstraint = SKConstraint.positionX(SKRange(constantValue: 195))
node.constraints?.append(xConstraint)
However this doesn't work and I'm not sure why and how to fix it. Anyone know of a solution?
Edit 2: SKPhysicsJointPin is actually looking more promising. In the comments of the first response/answer to this question, I have been trying to figure how to properly use it in my situation.
An example of my code:
let node = SKSpriteNode(imageNamed: "node")
enum collisionType: UInt32 {
case node = 1
case ground = 2
case other = 4 //the other node is unrelated to the ground and node
}
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMove(to view: SKView) {
//Setup node physicsBody
node.physicsBody = SKPhysicsBody(rectangleOf: node.size)
node.physicsBody?.categoryBitMask = collisionType.node.rawValue
node.physicsBody?.collisionBitMask = //[other node that isn't the ground or the node]
node.physicsBody?.contactTestBitMask = //[other node that isn't the ground or the node]
node.physicsBody?.isDynamic = true
node.physicsBody?.affectedByGravity = true
addChild(node)
//Physics Setup
physicsWorld.contactDelegate = self
}
The node is on top of the ground, and the ground is composed of various SKSpriteNode lines that have a volumeBased physicsBody. The ground keeps adding new lines at the front, and removing the ones at the back, and changing the x value of each line by a negative (so the ground appears to moving - this process is performed in an SKAction). These lines (the parts of the ground) are on an angle which is why the node's x axis moves. I want the node to always be at the front of the ground (e.g. always on top of the newly created line). Currently, setting the position of the node like this locks the x axis (solving my issue):
override func didSimulatePhysics() {
//Manage node position
node.position.x = 195
node.position.y = CGFloat([yPosition of the first line of the ground - the yPosition keeps changing])
}
Note: ^This^ function is inside the GameScene class
The x axis actually stays the same like this. However, the issue is that now the physicsBody of the node is lower than the centre of the node (which didn't happen before).
A node's constraints property is nil by default. You'll need to create an array of one or more constraints and assign it to the property. For example
let xConstraint = SKConstraint.positionX(SKRange(constantValue: 195))
node.constraints = [xConstraint]
Update
You may want to use a camera node instead of moving the ground in the scene. With a camera node, you move the main character and the camera instead of the ground.
I think you could set the linearDamping property to 0.0
The linearDamping is a property that reduces the body’s linear velocity.
This property is used to simulate fluid or air friction forces on the
body. The property must be a value between 0.0 and 1.0. The default
value is 0.1. If the value is 0.0, no linear damping is applied to the
object.
You should pay attention also to the other forces applied to your SKSpriteNode. The gravitational force applied by the physics world for example where dx value, as you request , should be setted to 0.0:
CGVector(dx:0.0, dy:-4.9)
Remember also that when you apply other forces vectors like velocity you should maintain the dx property to 0.0 as constant if you want to block the x axis.
You could find more details to the official docs
Update (after your details to the comments below):
You could also anchored your sprite to the ground with an SKPhysicsJoint (I don't know your project so this is only for example):
var myJoint = SKPhysicsJointPin.joint(withBodyA: yourSprite.physicsBody!, bodyB: yourGround.physicsBody!, anchor: CGPoint(x: yourSprite.frame.minX, y: yourGround.frame.minY))
self.physicsWorld.add(myJoint)
You can work with the anchor property to create a good joint as you wish or adding more joints.

Check when an SKSpriteNode passes a certain point or line

In my game, there is an SKSpriteNode, which we will call a "gate," that is moving upwards in the scene. When the gate reaches the top of the screen, it moves back down to the bottom and begins traveling upwards again.
The way the player loses is if the character, which is located at a constant y position towards the top of the scene, collides with the gate. I have the collision and 'death' all worked out, but now I need to check if the character successfully passed 'through' the gate (meaning the gate moved past the character without hitting him). Basically, I need to know how to check when one moving node crosses a certain y position (in this case, that position is the character's y position). If this occurs, I want to increase the score by one.
Here is a rough sketch of the situation:
(click here for the rough sketch)
One thing I have tried is in the update method, if the gate's y position ever equals the character's y position, I know the two nodes have crossed, so I should increase the score. It looks like this:
override func update(currentTime: CFTimeInterval) {
if gate.position.y == character.position.y {
print("score")
score++
}
}
but doesn't seem to work. Is there any other way to check when the gate crosses the character's y position?
Thanks!
What may be happening is that the y coordinates aren't the same. For example if gate.position.y = 100, and character. position.y = 101, it won't work. Try this:
override func update(currentTime: CFTimeInterval) {
let minY = gate.position.y - 10
let maxY = gate.position.y + 10
if character.position.y > minY && character.position.y < maxY {
print("score")
score++
}
}
You can adjust the number 10 to see what works best. Hope this helps

Giving a physicsBody a constant force

I have a ball bouncing up and down. I've accomplished this by setting it's physics body up like this:
linearDamping = 0
friction = 0
restitution = 1
And then just applyForce(CGVectorMake(0, 10)) in the setup call. The ball is now bouncing up and down, up and down with not interruption.
Now I want to add player control. The player can move the ball sideways. I could do this by just setting the balls velocity when the controls are pressed. BUT by doing that I lose all the stuff the physics engine gives me.
Here's a short snippet of code I have right now:
override func update(currentTime: CFTimeInterval) {
/* Apply damping only in x */
if !movingLeft && !movingRight {
let dx = ball!.physicsBody!.velocity.dx * xAlpha
let dy = ball!.physicsBody!.velocity.dy
ball!.physicsBody?.velocity = CGVectorMake(dx, dy)
}
if movingRight {
ball!.physicsBody?.applyImpulse(CGVectorMake(ballVelocityX, 0))
}
if movingLeft {
ball!.physicsBody?.applyImpulse(CGVectorMake(-ballVelocityX, 0))
}
}
The problem, as you might imagine, is that applying impulse upon impulse on each frame like this makes the ball go superfast, which is not what I want, when the controls are held down. But if I apply the force only on keyDown the ball will stop moving in that direction once the ball hits something. And also the keyDown event works wonky on OS X (because of key repeat settings).
So what I want to do is apply an impulse in the update function in a way that makes the force of the ball not exceed my limit but also not go away completely if the ball hits something.