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.
Related
I have made a game using SpriteKit and Swift 3 and have figured out all aspects of the game except the speed of the ball node in the game. Im confused with the different function applyImpulse() and ball.physicsBody.velocity, as I have tested both and don't seem to really understand what the speed I'm actually programatically settings is. Any clarification on what I should be using would be great?
Also whilst testing (by printing the ball's velocity to the console every collision) I would see sometimes the ball's speed would simply go to some long and random decimal value when it hit other nodes such as a paddle which I hadn't specifically coded anything to happen with the ball's speed in the case of a collision with it.
In summary I would appreciate:
Just general clarification regarding speed of the ball in SpriteKit and how I should approach it (what method/function I should use)
How I would make it so the ball's speed doesn't got to these very long random decimals
Thanks
In regards to the values, there is not really a set rule of what the values are for impulses and forces. It depends on how big your sprites physics body are etc. An impulse of 80 might be a perfect jump value for 1 sprite size, but than make it half the size and that 80 is suddenly way to high. There are also factors such as gravity, mass etc than can have an effect on this.
So you usually just play around with the values until you get the desired result.
In regards to the collision with the paddle, you need to check your bit mask values and your dynamic properties. SpriteKit by default sets collisions to all objects, so if you dont specifically tell your paddle/ball to ignore each other they will collide.
There are also things such as restitution, friction, damping etc that can have an effect on how you sprites behave when colliding.
There are loads of tutorials on google about SpritKit physic/collisions or read the apple documentation.
In regards to the difference between velocity and impulses/forces, as per apples documentation
"First, you can control a physics body’s velocity directly, by setting its velocity and angularVelocity properties. As with many other properties, you often set these properties once when the physics body is first created and then let the physics simulation adjust them as necessary. For example, assume for a moment you are making a space-based game where a rocket ship can fire missiles. When the ship fires a missile, the missile should have a starting velocity of the ship plus an additional vector in the direction of the launch.
When a body is in the simulation, it is more common for the velocity to be adjusted based on forces applied to the body. Another source of velocity changes, collisions, is discussed later."
https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Physics/Physics.html
So basically the general rule of thumb is this:
1) Only set the velocity property when you create the physics body. I never needed to do this for my games yet.
The only time I really use the velocity property is for things such as double jumping where I need to set it to 0 to have a consistent double jump
...velocity.dy = 0
...applyImpulse(
2) When you are playing the game already than
a) If you are trying to continuously move your ball you should use
applyForce...
in something like the update method of your SKScene.
b) If you want to make your ball jump, so basically a short 1 time thing, you should use
applyImpulse...
Hope this helps
I really hope someone understands what the effect is that I am asking for. Have you ever played the games where a character is sliding one way and when you try and change the characters direction it is not immediate as they need to slow down in the initial direction before they can start sliding the other way? The new game on the App Store 'Swing Copters' by the maker of Flappy Bird is exactly the effect I am talking about. Can someone please help me create this effect in SpriteKit. I have already tried achieving it by applying different forces but I am either not doing it correctly or the effect isn't possible with forces.
Thanks in advance!
Maybe you want to try working with some acceleration-stuff.
Let's think about the gravity on the earth (a = 9.81 metres/s²). If you throw a ball straight to the sky, the Ball is starting with a velocity of: lets say 5metres per second(positive velocity).
After a short time, the gravity is pulling the velocity of the ball down to 0, so the ball can't get any higher. Right after this, the gravity pulls the ball down until it hits the ground. (Negative velocity)
If we're talking about this in a game, where the ball doesn't move up or down but from left to right, you can use something like this. The moment when you throw the ball, is the moment in the game where you send the command to change the direction. The ball keeps going in the direction where it used to go, gets slower, stops, changes the direction and finally gets faster and faster until you send another command to change the direction again(Or it hits the Wall/the ground). In that case you have to inverse the acceleration you want to use, so the whole thing repeats in the other way.
If the ball should move to the right, positive acceleration,
if the ball should move to the left, negative acceleration.
As formula you can use something like
v = (int) (a * t + v0);
v0 = v;
v is the next velocity, a is the acceleration you want to use, t is the spent time and v0 is the current velocity. t should count the nano-time since the last direction-change. (int) casts the whole thing to an integer, so you can use this directly to move the ball/graphics on the screen.
Repeat this each frame.
For the direction change you can use
t = 0;
a = a * (-1);
t has to be 0 again, otherwise it gets buggy.
I hope this was helpful.
I'm developing a game where the character bounces much like the "Flappy Bird" character.
I have it working well, but I'm handing the bouncing effect with a touch event and when the user double touches really quick it basically doubles the force behind the player.
Also, take for example if the player is dropping from a higher height then the gravity seems to be too much and I have to touch many more times to bring the player back up, and I just can't seem to figure out how to make every touch consistent with the amount the character bounces.
Here is my function for the bounce effect:
function flyUp(event)
if event.phase == "began" then
if gameStarted == false then
player.bodyType = "dynamic"
instructions.alpha = 0
tb.alpha = 1
addColumnTimer = timer.performWithDelay(1000, addColumns, -1)
moveColumnTimer = timer.performWithDelay(2, moveColumns, -1)
gameStarted = true
player:applyForce(0, -300, player.x, player.y)
else
player:applyForce(0, -460, player.x, player.y)
end
end
end
...and here is where my player is defined as a physics body:
physics.addBody(player, "static",
{ density=.106, bounce=.1, friction=1, radius = 30 })
any help would be extremely appreciated, i just want the player to always move up the same amount no matter how many taps and how much he has dropped.
Thanks
You're probably forgetting that force determines acceleration, not velocity. So if you have an object moving downwards at speed V (V is positive value, since downward is positive in Corona) then applyForce() applies a momentary upward force (duration: one time frame) to the object, so the object's downward velocity will decrease a little bit (decelerate as a result of the upward force), but gravity is constant so you'll have to press multiple times to get enough deceleration.
What you probably want is to directly setLinearVelocity() to a negative value, thus making it look as though the object "bounced" upwards upon touch; gravity will then give it a parabolic trajectory. Also, if the object is always given the same linear velocity, it doesn't matter how many taps occur, although each tap will counter the effect of gravity with same upwards velocity.
Two ideas:
To avoid the double-touch/double-bounce, apply a cooling off period. When the user touches, ignore additional touches for some given amount of time.
Strictly speaking, you are getting the same amount of bounce (or upward force) per touch. It sounds like you want a variable bounce depending on the current velocity toward the ground. This shouldn't be too hard. Try applying more upward force in player:applyForce depending on the velocity on the y axis. If the player isn't falling, a smaller amount of force is needed. If they are falling at terminal velocity, a much larger force is needed.
It sounds like you recognize that "perfect" physics are less fun in your game. That's often the case. You'll likely need to experiment until something feels right.
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.
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”.