I'm making a sniper shooter arcade style game in Gamemaker Studio 2 and I want the position of targets outside of the viewport to be pointed to by chevrons that move along the circumference of the scope when it moves. I am using trig techniques to determine the coordinates but the chevron is jumping around and doesn't seem to be pointing to the target. I have the code broken into two: the code to determine the coordinates in the step event of the enemies class (the objects that will be pointed to) and a draw event in the same class. Additionally, when I try to rotate the chevron so it also points to the enemy, it doesn't draw at all.
Here's the coordinate algorithm and the code to draw the chevrons, respectively
//determine the angle the target makes with the player
delta_x = abs(ObjectPlayer.x - x); //x axis displacement
delta_y = abs(ObjectPlayer.y - y); //y axis displacement
angle = arctan2(delta_y,delta_x); //angle in radians
angle *= 180/pi //angle in radians
//Determine the direction based on the larger dimension and
largest_distance = max(x,y);
plusOrMinus = (largest_distance == x)?
sign(ObjectPlayer.x-x) : sign(ObjectPlayer.y-y);
//define the chevron coordinates
chevron_x = ObjectPlayer.x + plusOrMinus*(cos(angle) + 20);
chevron_y = ObjectPlayer.y + plusOrMinus*(sign(angle) + 20);
The drawing code
if(object_exists(ObjectEnemy)){
draw_text(ObjectPlayer.x, ObjectPlayer.y-10,string(angle));
draw_sprite(Spr_Chevron,-1,chevron_x,chevron_y);
//sSpr_Chevron.image_angle = angle;
}
Your current code is slightly more complex that it needs to be for this, if you want to draw chevrons pointing towards all enemies, you might as well do that on spot in Draw. And use degree-based functions if you're going to need degrees for drawing anyway
var px = ObjectPlayer.x;
var py = ObjectPlayer.y;
with (ObjectEnemy) {
var angle = point_direction(px, py, x, y);
var chevron_x = px + lengthdir_x(20, angle);
var chevron_y = py + lengthdir_y(20, angle);
draw_sprite_ext(Spr_Chevron, -1, chevron_x, chevron_y, 1, 1, angle, c_white, 1);
}
(also see: an almost-decade old blog post of mine about doing this while clamping to screen edges instead)
Specific problems with your existing code are:
Using a single-axis plusOrMinus with two axes
Adding 20 to sine/cosine instead of multiplying them by it
Trying to apply an angle to sSpr_Chevron (?) instead of using draw_sprite_ext to draw a rotated sprite.
Calculating largest_distance based on executing instance's X/Y instead of delta X/Y.
I am trying to use the function mouseMove(int x, int y) to draw a circle centered at my mouse as I click and drag it across the screen. Circles will be drawn on the moving mouse like a spray paint. So far, this is what I have
void mouseMove(int x, int y) {
glBegin(GL_POLYGON);
for (int i = 0; i <= 360; i++)
{
float theta = (2 * 3.14 * i) / 360;
glVertex2f((size/2 + x) * cos(theta), (size/2 + y) * sin(theta));
}
glEnd();
glutPostRedisplay();
}
But when using this, it draws very large circles that aren't centered around my mouse. How would I alter this to make the program draw circles centered at my mouse?
To describe the project, I am creating a painting program that changes shapes, colors, sizes, and rotations of the drawing done in mouseMove. For now, the size is an int set to 32. When the user selects the shape using the 'b' key in a keyboard function, he/she can switch the shapes that are drawn around the mouse as the user clicks and drags the mouse around. Like a spray paint. All the other shapes work shaped around the mouse except for the circle shape spray.
This answer assumes that things like your viewport and projection matrices are set up correctly, and that the input to this function is taking into account the fact that "screen coordinates" (what the mouse uses) are not the same thing as "OpenGL Coordinate Space" (this usually implies reversing the direction of the y-axis for one or the other).
The math you're using for setting your vertex coordinates is wrong. The mouse's x and y coordinates should not be multiplied by the sine/cosine functions.
The correct way to write it is
glVertex2f((size/2) * cos(theta) + x, (size/2) * sin(theta) + y);
I would also add that you appear to still be using OpenGL's Immediate Mode rendering, which is Deprecated and will offer extremely poor training for a professional setting. I highly advise you learn Modern OpenGL (3.x+) and reapply those concepts to whatever projects you're already working on. This is a very good tutorial.
I'm trying to make my asteroid keep moving once I press a button.
void Ship::applyThrust()
{
_v.setX(_v.getX() - sin(2 * M_PI * (_angle / 360)) * 2.5);
_v.setY(_v.getY() + cos(2 * M_PI * (_angle / 360)) * 2.5);
}
This is the formula I have to help my ship move.
The _v.setX and _vsetY updates the X and Y position
The M_PI is just 3.14.
The _angle is how much of a rotation I set with my left and right arrow keys and
The 2.5 is how many frames per I want it to move
My ship is moving just fine, the problem is that I'm trying to simulate space inertia and have my ship continue moving.
Any ideas on how would be the logic for this?
Thanks,
Inside of your game loop, you'll need a function that updates the position of the ship based on its x and y velocity. You're close in getting the new x and y coordinates of your ship, but you don't account for the velocity of the ship. When you apply thrust, get the x and y components of your velocity, not your new position. You'll have an additional function to update position which should be called within the game loop at a timed interval - e.g. every time you update the frame. So your applyThrust function should actually update your ship's velocity. That means you'll need to add variables to your Ship class for your ship's position and your ship's velocity if you don't already have them. I'm breaking out the components of position and velocity for simplicity, but you'll probably want to store them in a vector for clarity:
class Ship
{
private float xpos, ypos; // current position of your ship
private float velocityX, velocityY; // current velocity of your ship
}
Then, when you apply thrust, you change the velocity, and remember that applyThrust is only called when the thrust button is pushed, not every frame as the position update is:
void Ship::applyThrust()
{
/*
Assume 1 total unit of thrust is applied each time you hit the thrust button
so you break down the velocity into x and y components depending on the direction
your ship is facing.
*/
// thrustUnit is how much thrust each thrust button press gets you, it's arbitrary
// and can be the 2.5 multiplier you're using in your question
float thrustUnit = 1.0;
// now get the x and y thrust components of velocity based on your ship's direction
// assuming 0 degrees is pointing to the right
float newVX, newVY = 0.0;
newVX = thrustUnit * cos(_angle);
newVY = thrustUnit * sin(_angle); // radian conversion left out to save space
// Update your ship's velocity
updateVelocity(newVX, newVY);
}
updateVelocity will look something like: (note that the velocity is additive so it continues to drift unless a thrust is applied in the opposite direction - the same as if it were in a frictionless medium such as space)
void Ship::updateVelocity(newVX, newVY)
{
// update the x component
velocityX += newVX;
velocityY += newVY;
}
So now you'll also need an updatePosition function that takes into account your ship's velocity. This gets called with each frame update:
void Ship::updatePosition()
{
// add the x component of velocity to the ship's x position
// assuming xpos and ypos are variables in the Ship class for tracking position
xpos += velocityX;
ypos += velocityY;
}
Now the position of the ship changes incrementally according to each velocity component at each frame update. You can also make thrustUnit a member variable to allow for power-ups that can either boost or decrease your thrust component for your ship's speed and being able to control it better with a smaller thrustUnit or giving it hyperspeed with a large thrustUnit.
Good luck with your game!
What exactly is the formula to needed to do this?
So if I have two separate sprites named player (a spaceship) and exhaust (an exhaust animation for the bottom of the spaceship)
What math is needed so that when player rotates, the exhaust moves and positions itself at the bottom of the ship which could be to the left at this point, if player has rotated 90 degrees right.
I know how to spin / rotate the sprites to face the right direction. What I need to know is how to move the exhaust so that it's positioned correctly at the bottom of the ship when the ship turns.
And please don't link any "Move a sprite around a point" threads and such since that's not what I'm looking for.
Assuming the ship is similar to the one in the game "Asteroids", the position of the tail of the ship (relative to the ship's center) will be described by a circle whose radius is the distance between the center of the ship and the ship's tail.
Given that, you can calculate the position of the tail of the ship like this:
#include <math.h>
[...]
double radius = ship_height/2.0;
double ship_heading = /* current angle of rotation of the ship, in radians, e.g. 0 if the ship is facing right, pi if it is facing left, etc */
double tail_heading = ship_heading + 3.14159; /* ship's tail faces the opposite direction from the nose! */
double x_offset = cos(tail_heading)*radius;
double y_offset = sin(tail_heading)*radius;
double exhaust_center_x = ship_center_x + x_offset;
double exhaust_center_y = ship_center_y + y_offset;
(and if you want the exhaust-graphic to appear a little farther away from the tail of the ship, just increase the radius value slightly)
This question has one major question, and one minor question. I believe I am right in either question from my research, but not both.
For my physics loop, the first thing I do is apply a gravitational force to my TotalForce for a rigid body object. I then check for collisions using my TotalForce and my Velocity. My TotalForce is reset to (0, 0, 0) after every physics loop, although I will keep my velocity.
I am familiar with doing a collision check between a moving sphere and a static plane when using only velocity. However, what if I have other forces besides velocity, such as gravity? I put the other forces into TotalForces (right now I only have gravity). To compensate for that, when I determine that the sphere is not currently overlapping the plane, I do
Vector3 forces = (sphereTotalForces + sphereVelocity);
Vector3 forcesDT = forces * fElapsedTime;
float denom = Vec3Dot(&plane->GetNormal(), &forces);
However, this can be problematic for how I thought was suppose to be resting contact. I thought resting contact was computed by
denom * dist == 0.0f
Where dist is
float dist = Vec3Dot(&plane->GetNormal(), &spherePosition) - plane->d;
(For reference, the obvious denom * dist > 0.0f meaning the sphere is moving away from the plane)
However, this can never be true. Even when there appears to be "resting contact". This is due to my forces calculation above always having at least a .y of -9.8 (my gravity). When when moving towards a plane with a normal of (0, 1, 0) will produce a y of denom of -9.8.
My question is
1) Am I calculating resting contact correctly with how I mentioned with my first two code snippets?
If so,
2) How should my "other forces" such as gravity be used? Is my use of TotalForces incorrect?
For reference, my timestep is
mAcceleration = mTotalForces / mMass;
mVelocity += mAcceleration * fElapsedTime;
Vector3 translation = (mVelocity * fElapsedTime);
EDIT
Since it appears that some suggested changes will change my collision code, here is how i detect my collision states
if(fabs(dist) <= sphereRadius)
{ // There already is a collision }
else
{
Vector3 forces = (sphereTotalForces + sphereVelocity);
float denom = Vec3Dot(&plane->GetNormal(), &forces);
// Resting contact
if(dist == 0) { }
// Sphere is moving away from plane
else if(denom * dist > 0.0f) { }
// There will eventually be a collision
else
{
float fIntersectionTime = (sphereRadius - dist) / denom;
float r;
if(dist > 0.0f)
r = sphereRadius;
else
r = -sphereRadius;
Vector3 collisionPosition = spherePosition + fIntersectionTime * sphereVelocity - r * planeNormal;
}
}
You should use if(fabs(dist) < 0.0001f) { /* collided */ } This is to acocunt for floating point accuracies. You most certainly would not get an exact 0.0f at most angles or contact.
the value of dist if negative, is in fact the actual amount you need to shift the body back onto the surface of the plane in case it goes through the plane surface. sphere.position = sphere.position - plane.Normal * fabs(dist);
Once you have moved it back to the surface, you can optionally make it bounce in the opposite direction about the plane normal; or just stay on the plane.
parallel_vec = Vec3.dot(plane.normal, -sphere.velocity);
perpendicular_vec = sphere.velocity - parallel_vec;
bounce_velocity = parallel - perpendicular_vec;
you cannot blindly do totalforce = external_force + velocity unless everything has unit mass.
EDIT:
To fully define a plane in 3D space, you plane structure should store a plane normal vector and a point on the plane. http://en.wikipedia.org/wiki/Plane_(geometry) .
Vector3 planeToSphere = sphere.point - plane.point;
float dist = Vector3.dot(plane.normal, planeToSphere) - plane.radius;
if(dist < 0)
{
// collided.
}
I suggest you study more Maths first if this is the part you do not know.
NB: Sorry, the formatting is messed up... I cannot mark it as code block.
EDIT 2:
Based on my understanding on your code, either you are naming your variables badly or as I mentioned earlier, you need to revise your maths and physics theory. This line does not do anything useful.
float denom = Vec3Dot(&plane->GetNormal(), &forces);
A at any instance of time, a force on the sphere can be in any direction at all unrelated to the direction of travel. so denom essentially calculates the amount of force in the direction of the plane surface, but tells you nothing about whether the ball will hit the plane. e.g. gravity is downwards, but a ball can have upward velocity and hit a plane above. With that, you need to Vec3Dot(plane.normal, velocity) instead.
Alternatively, Mark Phariss and Gerhard Powell had already give you the physics equation for linear kinematics, you can use those to directly calculate future positions, velocity and time of impact.
e.g. s = 0.5 * (u + v) * t; gives the displacement after future time t. compare that displacement with distance from plane and you get whether the sphere will hit the plane. So again, I suggest you read up on http://en.wikipedia.org/wiki/Linear_motion and the easy stuff first then http://en.wikipedia.org/wiki/Kinematics .
Yet another method, if you expect or assume no other forces to act on the sphere, then you do a ray / plane collision test to find the time t at which it will hit the plane, in that case, read http://en.wikipedia.org/wiki/Line-plane_intersection .
There will always be -9.8y of gravity acting on the sphere. In the case of a suspended sphere this will result in downwards acceleration (net force is non-zero). In the case of the sphere resting on the plane this will result in the plane exerting a normal force on the sphere. If the plane was perfectly horizontal with the sphere at rest this normal force would be exactly +9.8y which would perfectly cancel the force of gravity. For a sphere at rest on a non-horizontal plane the normal force is 9.8y * cos(angle) (angle is between -90 and +90 degrees).
Things get more complicated when a moving sphere hits a plane as the normal force will depend on the velocity and the plane/sphere material properties. Depending what your application requirements are you could either ignore this or try some things with the normal forces and see how it works.
For your specific questions:
I believe contact is more specifically just when dist == 0.0f, that is the sphere and plane are making contact. I assume your collision takes into account that the sphere may move past the plane in any physics time step.
Right now you don't appear to have any normal forces being put on the sphere from the plane when they are making contact. I would do this by checking for contact (dist == 0.0f) and if true adding the normal force to the sphere. In the simple case of a falling sphere onto a near horizontal plane (angle between -90 and +90 degrees) it would just be sphereTotalForces += Vector3D(0, 9.8 * cos(angle), 0).
Edit:
From here your equation for dist to compute the distance from the edge of sphere to the plane may not be correct depending on the details of your problem and code (which isn't given). Assuming your plane goes through the origin the correct equation is:
dist = Vec3Dot(&spherePosition, &plane->GetNormal()) - sphereRadius;
This is the same as your equation if plane->d == sphereRadius. Note that if the plane is not at the origin then use:
D3DXVECTOR3 vecTemp(spherePosition - pointOnPlane);
dist = Vec3Dot(&vecTemp, &plane->GetNormal()) - sphereRadius;
The exact solution to this problem involves some pretty serious math. If you want an approximate solution I strongly recommend developing it in stages.
1) Make sure your sim works without gravity. The ball must travel through space and have inelastic (or partially elastic) collisions with angled frictionless surfaces.
2) Introduce gravity. This will change ballistic trajectories from straight lines to parabolae, and introduce sliding, but it won't have much effect on collisions.
3) Introduce static and kinetic friction (independently). These will change the dynamics of sliding. Don't worry about friction in collisions for now.
4) Give the ball angular velocity and a moment of inertia. This is a big step. Make sure you can apply torques to it and get realistic angular accelerations. Note that realistic behavior of a spinning mass can be counter-intuitive.
5) Try sliding the ball along a level surface, under gravity. If you've done everything right, its angular velocity will gradually increase and its linear velocity gradually decrease, until it breaks into a roll. Experiment with giving the ball some initial spin ("draw", "follow" or "english").
6) Try the same, but on a sloped surface. This is a relatively small step.
If you get this far you'll have a pretty realistic sim. Don't try to skip any of the steps, you'll only give yourself headaches.
Answers to your physics problems:
f = mg + other_f; // m = mass, g = gravity (9.8)
a = f / m; // a = acceleration
v = u + at; // v = new speed, u = old speed, t = delta time
s = 0.5 * (u + v) *t;
When you have a collision, you change the both speeds to 0 (or v and u = -(u * 0.7) if you want it to bounce).
Because speed = 0, the ball is standing still.
If it is 2D or 3D, then you just change the speed in the direction of the normal of the surface to 0, and keep the parallel speed the same. That will result in the ball rolling on the surface.
You must move the ball to the surface if it cuts the surface. You can make collision distance to a small amount (for example 0.001) to make sure it stay still.
http://www.physicsforidiots.com/dynamics.html#vuat
Edit:
NeHe is an amazing source of game engine design:
Here is a page on collision detection with very good descriptions:
http://nehe.gamedev.net/tutorial/collision_detection/17005/
Edit 2: (From NeHe)
double DotProduct=direction.dot(plane._Normal); // Dot Product Between Plane Normal And Ray Direction
Dsc=(plane._Normal.dot(plane._Position-position))/DotProduct; // Find Distance To Collision Point
Tc= Dsc*T / Dst
Collision point= Start + Velocity*Tc
I suggest after that to take a look at erin cato articles (the author of Box2D) and Glenn fiedler articles as well.
Gravity is a strong acceleration and results in strong forces. It is easy to have faulty simulations because of floating imprecisions, variable timesteps and euler integration, very quickly.
The repositionning of the sphere at the plane surface in case it starts to burry itself passed the plane is mandatory, I noticed myself that it is better to do it only if velocity of the sphere is in opposition to the plane normal (this can be compared to face culling in 3D rendering: do not take into account backfaced planes).
also, most physics engine stops simulation on idle bodies, and most games never take gravity into account while moving, only when falling. They use "navigation meshes", and custom systems as long as they are sure the simulated objet is sticking to its "ground".
I don't know of a flawless physics simulator out there, there will always be an integration explosion, a missed collision (look for "sweeped collision").... it takes a lot of empirical fine-tweaking.
Also I suggest you look for "impulses" which is a method to avoid to tweak manually the velocity when encountering a collision.
Also take a look to "what every computer scientist should know about floating points"
good luck, you entered a mine field, randomly un-understandable, finger biting area of numerical computer science :)
For higher fidelity (wouldn't solve your main problem), I'd change your timestep to
mAcceleration = mTotalForces / mMass;
Vector3 translation = (mVelocity * fElapsedTime) + 0.5 * mAcceleration * pow(fElapsedTime, 2);
mVelocity += mAcceleration * fElapsedTime;
You mentioned that the sphere was a rigid body; are you also modeling the plane as rigid? If so, you'd have an infinite point force at the moment of contact & perfectly elastic collision without some explicit dissipation of momentum.
Force & velocity cannot be summed (incompatible units); if you're just trying to model the kinematics, you can disregard mass and work with acceleration & velocity only.
Assuming the sphere is simply dropped onto a horizontal plane with a perfectly inelastic collision (no bounce), you could do [N.B., I don't really know C syntax, so this'll be Pythonic]
mAcceleration = if isContacting then (0, 0, 0) else (0, -9.8, 0)
If you add some elasticity (say half momentum conserved) to the collision, it'd be more like
mAcceleration = (0, -9.8, 0) + if isContacting then (0, 4.9, 0)