I'm having difficulty getting the Chipmunk physics engine to do what I want. The only solution that appears to work requires some heavy vector math. Before diving into that rabbit hole for the other components of my game, I was hoping someone could fill me in on a better way to go about this. The desired gameplay is as follows:
A character moves around a finite space in a top-down view
Movement is always a constant velocity in whatever direction the character faces
The player taps on the screen, which causes the character to 'turn' towards the touched location
The basic idea is like driving a car. You cannot immediately turn around, but instead must first perform a u-turn. That car must also maintain a constant speed. How might I do this? Bonus question: how can you override whatever method chipmunk calls to update a body's position, and is this a good idea?
There is this tutorial on how to do top down controls using specially configure joints:
http://chipmunk-physics.net/tutorials/ChipmunkTileDemo/
It's based on Chipmunk Pro, but the stuff about controlling the character is easily adapted to vanilla Chipmunk. The "Tank" demo that comes with the non-Pro Chipmunk source implements pretty much the same thing if you want to see some C code for it.
You basically want to rotate the orientation of the player more gradual. You could do this at a constant rate, so when you tap the screen it will start rotating at a constant rate until it has reached the right orientation. This would give a circular turn circle. This will however affect your position, so you would have to keep turning until you would be on a collision course with the position you tapped.
The path you would travel would be similar to that of the game Achtung die kurve.
So you would have to save the location and orientation of the player (x, y and phi coordinates). And to determine whether to stop turning you could do something like this:
dx = playerx - tapx;
dy = playery - tapy;
targetAngle = atan2(dy,dx);
if (phi > targetAngle)
{
if (phi - targetAngle > PI) omega = rotate;
else omega = -rotate;
}
else if (phi < targetAngle)
{
if (targetAngle - phi > PI) omega = -rotate;
else omega = rotate;
}
else omega = 0;
Related
I'm currently building a simplified Reaction Control System for a Satellite game, and need a way to use the system to align the satellite to a given unit direction in world-space coordinates. Because this is a game simulation, I am faking the system and just applying a torque force around the objects epicenter.
This is difficult because in my case, the Torque cannot be varied in strength, it is either on or off. It's either full force or no force. Calculating the direction that the torque needs to be applied in is relatively easy, but I'm having trouble getting it to align perfectly without spinning out of control and getting stuck in a logical loop. it needs to apply the opposing force at precisely the right 'time' to land on the target orientation with zero angular velocity.
What I've determined so far is that I need to calculate the 'time' it will take to reach zero velocity based on my current angular velocity and the angle between the two vectors. If that exceeds the time until I reach angle zero, then it needs to apply the opposing torque. In theory this will also prevent it from 'bouncing' around the axis too much. I almost have it working, but in some cases it seems to get stuck applying force in one direction, so I'm hoping somebody can check the logic. My simulation does NOT take mass into account at the moment, so you can ignore the Inertia Tensor (unless it makes the calculation easier!)
For one axis, I'm currently doing it this way, but I figure someone will have a far more elegant solution that can actually compute both Yaw and Pitch axes at once (Roll is invalid).
Omega = Angular Velocity in Local-Space (Degrees Per Second)
Force = Strength of the Thrusters
// Calculate Time Variables
float Angle = AcosD(DotProduct(ForwardVector, DirectionVector));
float Time1 = Abs(Angle / Omega.Z); // Time taken to reach angle 0 at current velocity
float Time2 = Abs(DeltaTime * (Omega.Z / Force); // Time it will take to reach Zero velocity based on force strength.
// Calculate Direction we need to apply the force to rotate toward the target direction. Note that if we are at perfect opposites, this will be zero!
float AngleSign = Sign(DotProduct(RightVector, DirectionVector));
float Torque.Z = 0;
if (Time1 < Time2)
{
Torque.Z = AngleSign * Force;
}
else
{
Torque.Z = AngleSign * Force * -1.0f
}
// Torque is applied to object as a change in acceleration (no mass) and modified by DeltaSeconds for frame-rate independent force.
This is far from elegant and there are definitely some sign issues. Do you folks know a better way to achieve this?
EDIT:
If anybody understands Unreal Engine's Blueprint system, this is how I'm currently prototyping it before I move it to C++
Beginning from the "Calculate Direction" line, you could instead directly compute the correction torque vector in 3D, then modify its sign if you know that the previous correction is about to overshoot:
// Calculate Direction we need to apply the force to rotate toward the target direction
Torque = CrossProduct(DirectionVector, ForwardVector)
Torque = Normalize(Torque) * Force
if (Time2 < Time1)
{
Torque = -Torque
}
But you should handle the problematic cases:
// Calculate Direction we need to apply the force to rotate toward the target direction
Torque = CrossProduct(DirectionVector, ForwardVector)
if (Angle < 0.1 degrees)
{
// Avoid divide by zero in Normalize
Torque = {0, 0, 0}
}
else
{
// Handle case of exactly opposite direction (where CrossProduct is zero)
if (Angle > 179.9 degrees)
{
Torque = {0, 0, 1}
}
Torque = Normalize(Torque) * Force
if (Time2 < Time1)
{
Torque = -Torque
}
}
Okay well what i take from the pseudocode above is that you want to start braking when the time needed to break exceeds the time left till angle 0 is reached. Have you tried to slowly start breaking (in short steps because of the constant torque) BEFORE the time to break exceeds the time till angle 0?
When you do so and your satellite is near angle 0 and the velocity very low, you can just set velocity and angle to 0 so it doesn't wobble around anymore.
Did you ever figure this out? I'm working on a similar problem in UE4. I also have a constant force. I'm rotating to a new forward vector. I've realized time can't be predicted. Take for example you're rotating on Z axis at 100 degrees/second and a reverse force in exactly .015 seconds will nail your desired rotation and velocity but the next frame takes .016 seconds to render and you've just overshot it since you aren't changing your force. I think the solution is something like cheating by manually setting the forward vector once velocity is zeroed out.
I looked at a bunch of similar questions, and I cannot seem to find one that particularly answers my question. I am coding a simple 3d game, and I am trying to allow the player to pick up and move entities around my map. I essentially want to get a velocity vector that will "push" the physics object a distance from the player's eyes, wherever they are looking. Here's an example of this being done in another game (the player is holding a chair entity in front of his eyes).
To do this, I find out the player's eye angles, then get the forward vector from the angles, then calculate the velocity of the object. Here is my working code:
void Player::PickupOtherEntity( Entity& HoldingEntity )
{
QAngle eyeAngles = this->GetPlayerEyeAngles();
Vector3 vecPos = this->GetEyePosition();
Vector3 vecDir = eyeAngles.Forward();
Vector3 holdingEntPos = HoldingEntity.GetLocation();
// update object by holding it a distance away
vecPos.x += vecDir.x * DISTANCE_TO_HOLD;
vecPos.y += vecDir.y * DISTANCE_TO_HOLD;
vecPos.z += vecDir.z * DISTANCE_TO_HOLD;
Vector3 vecVel = vecPos - holdingEntPos;
vecVel = vecVel.Scale(OBJECT_SPEED_TO_MOVE);
// set the entity's velocity as to "push" it to be in front of the player's eyes
// at a distance of DISTANCE_TO_HOLD away
HoldingEntity.SetVelocity(vecVel);
}
All that is great, but I want to convert my math so that I can apply an impulse. Instead of setting a completely new velocity to the object, I want to "add" some velocity to its existing velocity. So supposing I have its current velocity, what kind of math do I need to "add" velocity? This is essentially a game physics question. Thank you!
A very simple implementation could be like this:
velocity(t+delta) = velocity(t) + delta * acceleration(t)
acceleration(t) = force(t) / mass of the object
velocity, acceleration and force are vectors. t, delta and mass scalars.
This only works reasonably well for small and equally spaced deltas. What you are essentially trying to achieve with this is a simulation of bodies using classical mechanics.
An Impulse is technically F∆t for a constant F. Here we might want to assume a∆t instead because mass is irrelevant. If you want to animate an impulse you have to decide what the change in velocity should be and how long it needs to take. It gets complicated real fast.
To be honest an impulse isn't the correct thing to do. Instead it would be preferable to set a constant pick_up_velocity (people don't tend to pick things up using an impulse), and refresh the position each time the object rises up velocity.y, until it reaches the correct level:
while(entPos.y < holdingEntPos.y)
{
entPos.y += pickupVel.y;
//some sort of short delay
}
And as for floating in front of the player's eyes, set an EyeMovementEvent of some sort that also sends the correct change in position to any entity the player is holding.
And if I missed something and that's what you are already doing, remember that when humans apply an impulse, it is generally really high acceleration for a really short time, much less than a frame. You wouldn't see it in-game anyways.
basic Newtonian/D'Alembert physics dictate:
derivate(position)=velocity
derivate(velocity)=acceleration
and also backwards:
integrate(acceleration)=velocity
integrate(velocity)=position
so for your engine you can use:
rectangle summation instead of integration (numerical solution of integral). Define time constant dt [seconds] which is the interval between updates (timer or 1/fps). So the update code (must be periodically called every dt:
vx+=ax*dt;
vy+=ay*dt;
vz+=az*dt;
x+=vx*dt;
y+=vy*dt;
z+=vz*dt;
where:
a{x,y,z} [m/s^2] is actual acceleration (in your case direction vector scaled to a=Force/mass)
v{x,y,z} [m/s] is actual velocity
x,y,z [m] is actual position
These values have to be initialized a,v to zero and x,y,z to init position
all objects/players... have their own variables
full stop is done by v=0; a=0;
driving of objects is done only by change of a
in case of collision mirror v vector by collision normal
and maybe multiply by some k<1.0 (0.95 for example) to account energy loss on impact
You can add gravity or any other force field by adding g vector:
vx+=ax*dt+gx*dt;
vy+=ay*dt+gy*dt;
vz+=az*dt+gz*dt;
also You can add friction and anything else you need
PS. the same goes for angles just use angle/omega/epsilon/I instead of x/a/v/m
to be clear by angles I mean rotation (pitch,yaw,roll) around mass center
I haven't been able to find this after scavenging the forums. I would like to implement something like this ... the main character always moves in the direction it's facing. When the player touches the screen, the character will turn to face that touch location, which should cause the body to move in a different direction.
I can get the character to face a touch location as follows:
CGPoint diff = ccpSub(location, self.position);
CGFloat targetAngle = atan2f(diff.y, diff.x);
self.body->a = targetAngle;
I want something along these lines. Get the current angle the character is facing. Turn that angle into a unit vector. Multiply that unit vector by a max_velocity, and apply it to the character. This should should (theoretically) move the character in the direction it is facing at a constant velocity?
This seems to give me what I want:
cpVect rotatedVel = cpvmult(ccpForAngle(self.body->a), MAX_VELOCITY);
self.body->v = cpvlerpconst(self.body->v, rotatedVel, ACCELERATION * dt);
Now all I need is a way to rotate the character's direction slowly over time. How might I do that?
Sounds like you want to do something like this from Chipmunk's Tank demo:
// turn the control body based on the angle relative to the actual body
cpVect mouseDelta = cpvsub(ChipmunkDemoMouse, cpBodyGetPos(tankBody));
cpFloat turn = cpvtoangle(cpvunrotate(cpBodyGetRot(tankBody), mouseDelta));
cpBodySetAngle(tankControlBody, cpBodyGetAngle(tankBody) - turn);
'turn' is calculated relative to the body's current rotation by transforming the direction vector relative to the body's current rotation. The demo smooths out the rotation using constraints (which you might want to consider here too), but you could also just get away with using cpflerpconst() on 'turn' to get a maximum angular velocity too.
What about using the cpBodySetTorque to set object torque to make it spin/rotate?
For the past days, I've been trying to make a ping pong like game. I have 2 paddles and a ball. All dynamic sprites. Everything's been working well except for one issue I'm having. The ball tends to bounce on the same angle at some point. So there would be times when the player can simply move the paddle on a specific part and the game can go on for a while or might be forever, since the ball doesn't change its angular velocity regardless of which part of the paddle it hits. I'm using a combination of linear and angular velocity to keep the ball moving like so:
if(_isPaused == FALSE)
{
_world->Step(dt, 10, 10);
for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *)b->GetUserData();
if(sprite.tag == 2)
{
b2Vec2 dir = b->GetLinearVelocity();
dir.Normalize();
float currentSpeed = dir.Length();
int maxSpeed = 60;
float accelerate = vel;
if(currentSpeed <= maxSpeed)
{
b->SetLinearVelocity(accelerate * dir);
}
sprite.position = ccp(b->GetPosition().x * PTM_RATIO,
b->GetPosition().y * PTM_RATIO);
sprite.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
//Keep sprite from bouncing in a straight angle
b->SetAngularVelocity(_body->GetAngle());
}}}
So my question is, how can I manipulate the angular velocity to keep the ball bouncing on different angles everytime it collides with my paddle? I'm thinking something like getting the current angular velocity then multiplying it with some value but I'm not sure if that's the right way to approach the issue I'm having. Any suggestions or ideas would be greatly appreciated. Thanks in advanced.
The way I see it, you have two options:
Check the location of a collision. If it's close to the top/bottom edge of the paddle, deflect the outgoing velocity by an angular amount proportional to the surface "curvature" at that point. Of course, this is cheating, but if the artwork and code are in agreement, it looks correct. And graphics is "the art of cheating without getting caught".
You could take into account the current velocity of the paddle as well as that of the ball. Eg: if the ball is moving downwards and to the right, and the paddle is moving down, then you can compute the outgoing direction using conservation of linear momentum. Just make sure you restrict the paddle's change in momentum along the horizontal axis to be zero.
Finally, you could combine the above techniques, but now you'd have to use accurate collision detection (not the hack I described in (1) above).
Hope that helps!
A few pointers, you should use SetLinearVelocity() and SetAngularVelocity() rarely. Each alters a property of the body definition, which could make you run into problems later on when things get more complex. It would be better to use ApplyForceToCenter() or ApplyLinearImpulse() in the place of SetLinearVelocity() as these two are much more versatile functions and are a bit more coder-friendly. In my opinion, I don't think you should use b->SetAngularVelocity(_body->GetAngle()); If you wanted to change the angular velocity each time it collided, you could, in your beginContact method, write that every time the body collides with the paddle body, a random angular impulse is applied to the ball, using ApplyAngularImpulse().Hope that helps.
I'm making a game which uses very simple collision detection. I'm not using box 2D because it's an overkill. Basically, it's a mix of Pong and fooseball. As the ball gains speed and has a very high velocity it ends up going through the wall it's supposed to collide with. The code works with slow and regular speeds, but not with very fast motion.
This is a snipet of my code:
pos.x is a vector which holds the x position of my ball.
if (pos.x - radius < wallLeft)
{
pos.x = wallLeft + radius;
vel.x *= -1;
}
What could i do to improve this?
thanks
Try increasing wallLeft a bit, so that the balls speed is never greater than wallLeft, it seems that after your ball goes below 0 it glitches (or you have some code for that that I don't know), not familiar with the framework or how the rest of your code works, but that's the easiest way to solve it. If you don't want to do that, there's probably a code somewhere that does something if the ball's x is less than 0, and you'll have to make that a bit more lenient, maybe make it so that if the ball's x is less than -50, or something like that (play around with the number until it works)
Arguably if (pos.x - radius) == wallLeft then the ball is already touching the wall and its velocity can be reversed; if you add this as an additional test in the loop does it help?
The only idea I am having is that the speed is so high that you get an overflow when adding it to the position, making pos.x > wallLeft + radius again.