Vary speed of object moving through SkAction.followPath (iOS) - ios

Is it possible to vary the speed of an object moving because of SKAction.followPath? For instance, let's say we use the code below to have a ball follow a rectangular path. The code will use a constant speed throughout the path. But what if we want to vary the speed of the object along the rectangle?
let goPath = SKAction.followPath(ballPath!.CGPath, duration: 2.5)
movingBall.runAction(goPath)
Is the only option to effectively have the ball follow a rectangular path built of separate lines with different speeds (as opposed to one path)?
Thanks!

two ways of accomplishing this.
FIRST WAY:
You can use SKAction.speedBy. You'd group SKAction.speedBy with your followPath. example:
movingBall.runAction(SKAction.group([
SKAction.followPath(ballPath!.CGPath, duration: 2.5)
SKAction.speedBy(4, duration: 2.5)
]))
Now the ball is going to reach 4 times the speed over 2.5 seconds. NOTE: now we're not going to be honoring the followPath duration. As time passes we're increasing the speed of the sprite. It's going to reach its destination in less than 2.5 seconds.
SECOND WAY:
The other way is to use a timing function. This is probably the better way to go because we have a lot more control over how fast the ball is going to move during the animation.
example:
let goPath = SKAction.followPath(ballPath!.CGPath, duration: 2.5)
goPath.timingFunction = {
t in
return powf(t, 3)
}
movingBall.runAction(goPath)
The way a timing function works is that it gives you a block that has the current elapsed time as a float. it's your job to use some kind of computation to modify the value that comes in, and then return it.
I'm just cubing whatever time comes into the function and returning it. This makes it so the ball starts off very slow, and then accelerates very quickly towards the end of the animation.
You can get really creative and use sin waves to make things bounce, etc. Just note that any returned values <0 are ignored.

With the followPath SKAction this isn't possible.
What you could do though is use the update method of the node to update its position manually.
This will take a bit of work to make it follow a path but you can do a lot more than is available with SKAction.

Related

Applying rotation to group of nodes sometimes flip the nodes

I am trying to rotate a group of objects in the scene by applying Rotation to each node. The rotation part of the code is as simple as:
for node in sceneView.scene.rootNode.childNodes {
node.localRotate(by: SCNVector4(0,1,0, Double.pi/180))
}
The expected result is the resulting node will rotate by 1 degree every time it is triggered. However, I have noticed sometimes the resulting image have reversed y values (for example, the rotation vector was -1 in the previous frame but the next frame it suddenly becomes +1), resulting in something looks like an illusion. The frequency looks like it happens every other frame or so. I tried with different axis but the same thing also occurs. I have tried both localRotate and Rotate, the problem still exists. Is this suppose to happen?
This is a video link for demonstrating the problem:
https://res.cloudinary.com/df7kpyhrg/video/upload/v1616634641/RPReplay_Final1616634008_ohvg1k.mp4
I am still not 100% sure, but I think the problem was I need to use the simd version of the ar modification. I need to use simdRotate instead of rotate. I believe the current flip in the video is the change in the angel which will look like reflection. To give a clearer example, suppose the current angel is 0 and it increments in the scale of 0.1 radian, the values will be 0, -0.1, 0.1, -0.2, 0.2 and so on. Therefore the flip results are the intermediate results to be executed. This stack overflow posts explains the difference between simd and non-simd methods nicely
What does SIMD mean?

How to use timing function for slowly speed up scaling function with SpriteKit

I have a sprite that I'm trying to scale out. I'm using the scaleTo skaction to do this... I want it to slowly ease in. And this was my initial solution:
let scal = SKAction.scale(by: 100, duration: 10)
scal.timingMode = SKActionTimingMode.easeIn
The problem is that as it scales out, since it is scaling out so much it appears to slow down. So I need to use the timeingFunction in order to write a custom easeIn for the action.
https://youtu.be/CE-B27gSXJI
In the video, you can see that it appears that it starts off fast and slows down. It only appears this way because I'm going a scale, and the bigger you go the slower it will appear...
Problem: I have no clue how to do this with the timing function, and I haven't been able to find a good source to use as a reference?
Any help would be appreciated and thank you!!!
Have a look at the Sprite Kit Utils from Ray Wenderlich for an example on how one can write an easing function: https://github.com/raywenderlich/SKTUtils Specifically, look at SKTTimingFunctions.swift and SKTEffects.swift.
There are some neat functions to give you better control of your easing. You might even want to use the easing functions that are defined there without changes, most of them work pretty well. For reference on how each easing function behaves, you can look at http://easings.net
Hope this helps!
I'm going to mark the answer above correct but this ended up being my solution and I wanted to share as it is simple to implement.
I don't fully understand how this works but when you specify the function for the timingFunction you have to include "t in". Then you can use this t to manipulate the timing of the animation.
So if you look at the links above, you can get the desired affect by using what is contained in the functions in Ray's SKTTimingFunctions.swift.
The below code shows how I used it; my timing function starts of slow and then slowly picks up speed... Again see the links above and you can get an idea.
let scal = SKAction.scale(by: 100, duration: 10)
scal.timingFunction = { t in
return t*t*t*t*t
}

SpriteKit Jumping and moving issues

I'm fairly new to swift, and have been working on a game for fun, and i'm running into something I can't quite get my head around.
When the run button is pressed, the character moves forward with the following function
func move(dt: CGFloat) {
position.x += moveRate * dt
}
And when the jump button is pressed, the character jumps with the following function
func jump() {
physicsBody?.applyImpulse(CGVector(dx: 0, dy: jumpRate))
run(jumpAnimation!)
}
both work fine, but consider this senario. The player is running, and then they jump while still moving. While in the air, the player releases the move button and the player's x position stops dead. This obviously feels very unnatural, and i would like the player's x position to ease out.
Now i have also played with moving the character with physicsBody?.applyForce(CGVector(dx: 1000, dy: 0)) which would give that effect, but he seems to just gain more and more speed and you don't get a constant rate or "max speed" so to speak.
Could anybody share some insight with me? I'd love to learn anything I can about spritekit and game development in general. Thanks!
You should try to set the velocity instead of setting the X position. When setting the position you bypass all the physics behaviors.
You should also try to set it only when you actually press a button.
func move(dt: CGFloat) {
if Math.abs(moveRate) > 0.1 { // If player initiates movement. You can increase the value 0.1 if you want to filter move values
velocity = CGVector(dx: moveRate, dy: veloxity.dy)
}
}
It your character moves indefinitely like in space, linearDamping will be useful. it's used to simulate air friction, so values closer to 1 means more friction and values closer to 0 means less friction.
linearDamping = 0.85
Also, this way, moveRate isn't dt dependent but it should be lowered.
Try it, I haven't tested it yet, but that's basically how I would do it.
There are two schools of thought on platformer game "physics".
Don't use physics, do everything with positional incrementation.
Do everything with physics, since positional changes mess up physics
Since you're using physics for jumping, and physics jumping is fun:
There are three ways to create movement in a physics system:
Set the velocity as and when required. This is what Crazyrems is suggesting
Apply impulses as needed to increase and decrease rates of movement
Apply forces over time that increase or decrease rates of movement
Use fields to induce motion (too complex for this, and messy, but fun)
What you're attempting, with your physicsBody?.applyForce(CGVector(dx: 1000, dy: 0)) is the application of force over time. Number 3 in the list above. The longer you continue applying this force the faster the character moves.
Each of these techniques can be made to work, but they all need compensation for their various limitations and methodologies of simulation.
In the case of your approach, you need monitor speed and to set a maximum speed. Having reached maximum speed, if the player is still holding the button, only provide enough force to maintain speed (assuming you're using some form of constant resistance to slow the character).
Monitoring speed combined with force-over-time creates interesting acceleration trait possibilities - like very strong initial acceleration and then taper off acceleration as the player nears their maximum speed.
Slowing down when the player releases the button is the next focus. In all these approaches you need something that slows the player. This can be the application of opposing forces, or the use of friction or damping, as provided by the physics engine.

Filtering collisions in Chipmunk for trapped bodies

Background
I'm making an iOS app for kids where you can use your finger to drag balls around on a screen.
I'm using Chipmunk 7.0.0 for the physics simulation.
I've adapted the Chipmunk demo code to implement the dragging functionality.
I'm using 2 ms fixed time step. (It's not like my app has anything better to do...)
Issue
I've recently added code to play a sound whenever the balls collide with each other or with a wall, which I'm doing inside a postSolve callback:
static void postSolve(cpArbiter *arb, cpSpace *space, cpDataPointer userData)
{
GameLayer *layer = (__bridge GameLayer *)userData;
[layer collisionHandler:arb];
}
-(void) collisionHandler:(cpArbiter*)arb
{
if(cpArbiterIsFirstContact(arb)) {
[[SimpleAudioEngine sharedEngine] playEffect:kCollisionEffectFilename];
}
}
Here's the problem... When I drag a ball into a wall, it generates a very large number of collisions, even when filtering on cpArbiterIsFirstContact, and it sounds terrible. It appears that the ball is bouncing off the wall, being driven back into wall by the constraint, rinse, and repeat. I'd like to play the collision sound only once or twice in this scenario.
Things I've tried that don't seem to work...
Filtering using cpArbiterTotalKE, cpArbiterTotalImpulse, or relative velocity: Impulse, kinetic energy, and relative velocity are all in the range of typical collisions.
Using a separate callback: The ball really is bouncing off the wall multiple times.
Reducing the step size: The physics engine actually takes longer to converge.
Rate limiting the sound effects: Better, but the ball still makes noise even after it looks like it's stationary.
Question
Is there a way to filter collisions for trapped bodies?
A simple and effective solution is to avoid playing the sound in quick succession.
When the ball contacts with the wall, check if the ball's "last contact" timer is lower than the current time. If so, play the sound and set the ball's "last contact" time to the current time plus x, where x is the timeout duration the sound shouldn't play again (ie 0.5 seconds).
In cocos2d-iphone you can add up an update method's deltaTime to keep track of time. There are also a number of ways to get system time, for example [NSDate date].timeIntervalSince1970 gives you the current number of seconds since 1970. I know, seems ridiculous, but if you get that number again sometime later, and subtract the previous number of seconds you get the difference in seconds, that's all that counts.
So you are using a constraint to drag the ball around, and the ball is elastic correct? Have you tried tweaking the constraint parameters at all? If the constraint has a lower maxForce, the chance for oscillations would drop significantly.
Another thing that can help is to increase the space's collision slop (how much shapes are allowed to overlap). The default value is 0.1 (in whatever scale you are using), but increasing it to a pixel or so can help this sort of thing significantly without being very visible.

Cocos2D Realistic Gravity?

I have tried many different techniques of applying a realistic looking gravity feature in my game but have had no luck so far. I plan on offering a 100 point bounty on this for someone who can show me or (share) some code that applies gravity to a CCSprite in Cocos2D.
What I have done so far has just been ugly or unrealistic and I have asked in many different places on what the best approach is but I just have not found any good looking gravity techniques yet.
Anyway, can anyone offer some tips/ideas or their approach only applying gravity to a CCSprite in Cocos2D without using a physics engine?
Thanks!
A effective approach without having to explicitly use any physics engine is to step the velocity and position of your sprite manually in your update loop. This is essentially Euler Integration.
// define your gravity value
#define GRAVITY -0.1
// define a velocity variable in the header of your Game class/CCSprite Subclass (neater)
float velocity_y;
-(void) update: (ccTime) dt
{
// Step the y-velocity by your acceleration value (gravity value in this case)
velocity_y += GRAVITY *dt; // drop the dt if you don't want to use it
// Step the position values and update the sprite position accordingly
sprite.position.y += velocity_y* dt; // same here
}
In the code snippet above, I defined a velocity_y variable to keep track of my sprite's current velocity along the y-direction. Remember to initialize this value to 0 in your init method.
Next comes the crux of Euler. At every time step:
Advance your velocity by your acceleration (which is your gravity) multiplied by dt to find your new velocity.
Advance your position by your newly computed velocity value multiplied by dt to find your new position.
You can experiment whether using delta-time or not (see LearnCocos2D's excellent comment on the cons of using it) works for your game. While multiplying by delta-time allows your object motion to more accurately take into account varying framerates, as LearnCocos2d pointed out, it might be wise to avoid using it in a real-time game where system interrupts (new mail, low battery, ads pop-out) can occur and subsequently cause the game simulation to jump forward in an attempt to make up.
So if you are dropping the dt, remember to tweak (scale down) your GRAVITY value accordingly to retain the right feel, since this value is no longer multiplied by the value of delta-time (which is ~ 1/60).
Aditional Tip: To apply an impulse to move the sprite (say via a swipe), you can affect the velocities directly by changing the values of velocity_x (if you have this) and velocity_y.
I have used this approach in my games, and it works very well. hope it helps.
This isn't trivial matter, you should try to see other posts. I'm sure other poeple already had this issue. Try to look at :
How to simulate a gravity for one sprite in cocos2d without a physics engine?
Cocos2D Gravity?
Or try our good friend google :
http://www.gamedev.net/page/resources/ -> got to Math and Physics and then A Verlet based approach for 2D game physics
http://www.rodedev.com/tutorials/gamephysics/
In any case here are some tips :
Step 1, calculate the effective direction vectors
Step 2, calculate velocity
Step 3, project this velocity onto the two effective directions.
Step 4, generate an equal and opposite force for the two direction and call this the “response force”.

Resources