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”.
Related
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.
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 faced with some problem which I can't find solution for. I have swinging platform (body1) and hero (body2) standing on it. Platform is swinging so hero moving right and left on the platform.
In the beginning I want hero to just stand on the platform without changing it's position relatively to platform. I achieved that by adding SKPhysicsJointFixed joining the platform and the hero.
Then in some moment in the game I want hero to run along the platform which means that Y should remain the same relatively to platform and X should grow. The only solution that I found is to make hero body static and change it's position in the didSimulatePhysics. The problem with that approach is that position not changing accurately. Hero position lags to one frame in comparison with platform position.
After hero reaches the right end of the platform he should stop moving and again just stand on the platform. So I make hero body static and add fixed joint again. And because hero position changes with lags hero position relative to platform is not correct. Sometimes it stops some pixels below the surface of the platform and sometimes some pixels above the surface.
So is there some other approach to move the hero along the platform, hero body should remain dynamic all the time.
I have a game with moving floating platforms. The simplest and most dynamic approach to achieve correct relative velocity is to simply make adjustments to the velocity. Here is a simplified version of what I do to achieve this.
In my game loop I have the following:
if heroOnBridge {
hero.extraMotion = floatingBridge.physicsBody!.velocity
} else {
hero.extraMotion = CGVector(dx:0,dy:0)
}
Here what I do is constantly look to see if my hero is on a moving bridge, if so then I set my hero's "extra motion" property to the velocity of the bridge that I am on. If I am not on a bridge then I don't add on any extra velocity. Note that I don't show how heroOnBridge is calculated because it really depends on how you define whether you are on a platform. In my case I assume you are on a platform if you intersect the platform frame and are on the ground.
Then in the area where I calculate my hero's velocity depending on his direction (left, right, jumping etc), I simply add on the extraMotion to the hero's velocity.
If you don't already I suggest you have an area in the game loop for processing the motion of you characters. It will allow you to make changes to the velocity of your hero depending on certain factors (in the air, jumping, moving left/right, on a platform, etc.)
If you need more help with calculating the velocity part let me know and I will post more of my code. You may also want to read my answer here for smoothly and dynamically setting the velocity.
Remember, for the most part you never want to set the position of anything directly, always try to set the necessary velocity to achieve that position over some period of time. This prevents very jittery and unpredictable motion.
I'm pretty new to Box2D and cocos2d. I'm trying to do something which I thought would be pretty simple, but it is turning out to be more difficult than I had expected and I cannot find a working answer anywhere.
Basically, I want to move a b2body and rotate it to a certain point with animation.
I can get it to the correct position and rotation with:
targetBody->SetTransform(b2Vec2(10.0f,1.0f),10);
But I have no idea how to animate it there over time. I tried using cocos2d animation on the sprite used for the body, but that doesn't do anything. Any ideas?
There are a couple of ways you could do this.
You could use SetTransform every time step to update the position of the body gradually over time, in effect performing the animation yourself. The drawback with this method is that the body is 'teleporting' to the new position rather than moving, so it has no momentum, which means you can get odd results if it hits anything along the way. Still, if you know it will not hit anything or does not need to react to a collision this would be ok.
The other way is to SetLinearVelocity and SetAngularVelocity to give the body proper movement. This will keep the results of a collision realistic, and you don't need to keep updating anything every timestep. On the other hand, you will need to detect when the body has arrived at the desired position and then set the velocities back to zero, otherwise it will just keep moving. For dynamic bodies you will also need to counter gravity somehow, either by setting the gravity scale of the body to zero, or by applying an upwards force to balance gravity, or by changing the body type to kinematic during the move.
In general, you use Box2D to simulate the physical behavior of objects in relation to each other. The rules of mechanics implemented by Box2D dictate how your cocos2d CCSprites move if you continuously update the translation and rotation of your sprites according to their corresponding Box2d b2Body. You will have some kind of repeatedly invoked tick: method in which you step your Box2d world along in time, and in which you update your sprite positions according to simulation results of Box2d.
This pattern corresponds to b2Bodys of type b2_dynamicBody. Physical laws dictate the motion of the body in this case, and your sprites will follow these simulation results. This is why setting a conflicting position of a sprite by means of a CCAction or even directly will be undone almost instantaneously with the next tick:.
Solution 1: kinematic body type
However, there do exist other modes for a b2Body, and one of these is b2_kinematicBody in which the translation is no longer governed by the world but by the velocities or angular speeds you dictate through setters. So it would be one solution to work with body type b2_kinematicBody as in:
b2BodyDef bdef;
bdef.type = b2_kinematicBody;
With this you would manipulate the velocity and angular speed of a b2Body explicitly. In this case, Box2d is still responsible for moving bodies around, but according the velocities you dictate, and without any force effects of collision or gravity applied to this particular body. Also with this strategy, your CCSprites will follow b2Bodys. Setting conflicting positions for your sprites directly or by applying a CCAction would not make sense for the same reason as described above.
Solution 2: decouple sprites from Box2d
An alternative way to animating sprites would be to fully decouple those sprites from Box2d. In this case, you would simply not have any b2Body that governs the position of the sprite you are going to animate. Now, in this case, only you will dictate the position and rotation of your CCSprite, i.e. directly either through its position property or by applying a CCAction.
In my app before, I was using box2D methods to move my bodies around but now I changed it so that I am controlling my CCSprites directly. Anyway I have this code:
myBody->ApplyForce(b2Vec2(AccelPoint.x, -40.0f), myBody->GetPosition());
myBody was a b2Body. And AccelPoint.x was the x value so my body could moves around controlled by the UIAccelerometer only on the X axis.
Also, the -40 was a constant for gravity.
So, what would be the equivalent in Cocos2D code?
Thanks!
You will need to store a velocity for the sprite, and every time step add a little bit to that velocity in the direction of the force. Then just move the sprite by the velocity each time step.