I'm trying to develop a simple racing 2d game (view top-down) in C#, sdl.net.
Now, I'm trying to manage speed, acceleration and brakes of my car.
My problem is the algorithm.
I have the loop (Events_Tick) executed 50 times per seconds, where the position of my car is processed like the following:
private void Events_Tick(object sender, TickEventArgs e)
{
car.ProcessPosition();
}
and ProcessPosition is something like:
if (throttle)
{
speed += 1;
x += 1;
}
and finally, I draw my sprite on the new X.
The problem is that it is too fast!
So I'm asking you how to maintain 50 FPS (frames per second) and move my sprite (the car) only N pixels per second (based on its speed).
Thank you in advance! Regards!
First of all, because you have 2d not 1d game, you'll need your speed to be
class Vector
{
double x;
double y;
}
with this class, you should maintain position and speed.
Since you are 2d top-down, you'll have to implement some .RotateLeft() and .RotateRight() on the speed vector.
Rotation will be implemented like:
x' = cos(a) * x - sin(a) * y
y' = sin(a) * x + cos(a) * y
And you'll have to implement your .Move() method as follows:
void Move(Vector v)
{
x+=v.x;
y+=v.y;
}
EDIT: please ask away if clarification is needed, or some more in-depth discussion.
Also, you can use timer here, but try to calculate time spent from last timer event, and then multiply speed with that value when adding to the current position, you will get more accurate position that way.
a in sin() and cos() will be an angle in radians, and you will probably want degrees here.
Here is something to get you going regarding degrees and radians.
Use a double value for speed and x, then you can use Math.Round(x) for drawing.
if (throttle == true) {
speed+=0.05;
}
x+=speed; //move car even without acceleration
Follow Henk's advice to make your program more flexible and to be able to show the user something like 50mph instead of 0.05px per 0.02s:
double speedInMph = 50;
double screenSizeInMiles = .1;
double screenSizeInPx = 500;
if(throttle)
{
speedInMph += 1;
}
double speedInMpms = 50/(60*60*1000);//Miles per millisecond
double xInMiles = speedInMpms * 50;
double pxPerMile = screenSizeInPx/screenSizeInMiles;
x+= xInMiles * pxPerMile;
Acceleration will be constant, depending on user input (on a simple level the car is either accelerating, deccelerating or maintaining speed).
Speed will change each frame depending on that acceleration. Each frame, update the car's speed based on both the acceleration and the proportion of a second the frame covers (i.e. 1/50th of a second in your case).
So, if your car is accelerating at 10 units distance per second, in your example the speed will increase by 1/5th of a unit distance per frame.
Just thought it was worth elaborating a little on the other answers, instead of just posting more code :-)
You should add velocity according to current acceleration and handle just acceleration with your controls, limiting speed with a cap and increasing it according to time elapsed between two game loop iterations.
float accel = 10.0f; // units per squared second
float maxSpeed = 50.0f // units per second
float curSpeed = 0.0f;
//game init
while (1) {
long timeBefore = time();
// game loop
long timeElapsed = time() - timeBefore;
if (throttle) {
curSpeed += accel*(timeElapsed / 1000.0);
if (curSpeed > maxSpeed)
curSpeed = maxSpeed;
}
}
In this way you take into account the fact that a longer frame will increase your car speed more than a quicker ore, keeping it consistent with time.
This model implies a constant acceleration while you could hypotetically want a dynamic one, in that case you just move the update to the acceleration (taking into account the frame duration as for speed) and cap it to not go over a fixed threshold.
Henk is right. You will want to create a
Thread
and call its
Sleep(Int32)
method each time in your loop to slow the animation down, where the int parameter is the pause time in milliseconds.
In your case, you will want to move the car N/50 pixels each time looping, and sleep the thread for 20 milliseconds each time.
Related
I'm working on a game for game jam and part of it is to make platforms that move smoothly. They slow down at the ends of their movement before turning around. The platforms simply move side to side or up and down between two waypoints(which are just empty transforms). I have code that uses cosine to determine the speed which works well except it doesn't align with the waypoints, the platforms tend to slow and change direction before ever reaching the waypoints. I need a way to use the distance between the waypoints as a variable in determining how the cosine equation changes speed so that the platforms slow and reverse direction exactly at the waypoints.
Here is what I have so far:
void Side_to_side()
{
if (waypointIndex < horWaypoints.Length)
{
platformSpeed = (1f * (float)Mathf.Cos(2f * (float)Mathf.PI * 1f * totalTime));
Vector3 targetPosition = horWaypoints[waypointIndex].position;
float delta = platformSpeed * Time.deltaTime;
transform.position = Vector2.MoveTowards(transform.position, targetPosition, delta);
if (transform.position.x == targetPosition.x && transform.position.y == targetPosition.y)
{
if (waypointIndex + 1 == horWaypoints.Length)
waypointIndex = 0;
else
waypointIndex++;
}
}
else
{
waypointIndex = 0;
}
//Translate platform back and forth between two waypoints
}
As I have said this code moves the platforms in the motions i want but they don't use the waypoints as turn around points. I understand I could do away with the waypoints and just calculate how far I would like each platform to go before turning around individually but that would take time to do it for each platform whereas I'd like to quickly put down waypoint pairs for them to use and the script calculates what the perfect values would be to match the waypoint locations.
If I understand you correctly you want to move an object forth and back between exactly two positions and apply some smoothing to the movement.
I would rather use a combination of Vector2.Lerp with a Mathf.PingPong as factor and you can then apply ease in and out using additionally Mathf.SmoothStep.
This could look like e.g.
public Transform startPoint;
public Transform endPoint;
// Desired duration in seconds to go exactly one loop startPoint -> endPoint -> startPoint
public float duration = 1f;
private void Update ()
{
// This will move forth and back between 0 and 1 within "duration" seconds
var factor = Mathf.PingPong(Time.time / (2f * duration), 1f);
// This adds additional ease-in and -out near to 0 and 1
factor = Mathf.SmoothStep(0, 1, factor);
// This interpolates between the given positions according to the given factor
transform.position = Vector2.Lerp(startPoint, endPoint, factor);
}
you could of course still use cosine if necessary, basically any function that returns a value between 0 and 1. You just have to use the correct multiplier in order to achieve the desired duration in seconds.
Note: Typed on the phone and not 100% sure on the math but I hope the idea gets clear
I have an object that spins perfectly around a circle non-dynamically with the following code.
Radius = 4.7f;
angle += .25f;
float x = Radius * Mathf.Cos(angle);
float y = Radius * Mathf.Sin(angle);
float z = 0;
ball.transform.position = new Vector3(x,y,z);
I wish to get a radius of the circle dynamically and have the circle as a UI image inside of a canvas. The width of the UI Image is 125 and can be seen by the blue arrows. The logic I though that would work would be to have radius = width/2.
125/2=62.5, which is not close 4.7f. What else am I not taking into account? Scaling or pixel image sizing?
Any help is appreciated!
First, you need to account for the scale of the image (as #derHugo suggested in comment)
But you also need to account for the fact that your canvas will also apply a scale on the units, depending on how you set it.
A good solution is to use RectTransform.GetWorldCorners. This will allow you to know the world coordinate of your circle without having to do the scale computation yourself.
Vector3[] v = new Vector3[4];
rt.GetWorldCorners(v);
var radius = v[0].x - v[2].x; // Not sure about the order of the corners, the indices might be wrong
Another comment: your code is framerate dependent: you add 0.25 degrees every frame. Meaning that the FPS will impact the speed of the rotation (with 60 fps, you will rotate 0.25 * 60 = 15 degrees per seconds. But with 30 FPS you will rotate 0.25 * 30 = 7.5 degrees per seconds)
To be framerate independent, you can do a simple computation using Time.deltaTime (this is the time the last frame took to completely render)
Instead of angle += .25f;
Do that:
var anglePerSeconds = 15; // Choose the angular speed you want
angle += anglePerSeconds * Time.deltaTime;
I have two positions in a unity scene and I want a sphere game object to move between them. This is easy enough to do with the move towards command, but what I want to happen is the ball to accelerate when leaving one, then decelerate when approaching the next in a controlled curve. I need it to osculate between the two positions in a manor similar to that of a ball being rolled.
I was told that I could do this with a sin wave, however I'm unsure how. Is there a simple way to do this ?
Preamble
There are a number of ways to solve your problem, not only by using sine function.
Sine function example
Here's an example:
public float frequency = 1.0f; // in Hz
public Vector3 positionA;
public Vector3 positionB;
private float elapsedTime = 0.0f;
public void Update()
{
elapsedTime += Time.deltaTime;
float cosineValue = Mathf.Cos(2.0f * Mathf.PI * frequency * elapsedTime);
transform.position = positionA + (positionB - positionA) * 0.5f * (1 - cosineValue);
}
Cosine is used here instead of sine just for convenience.
I faced with almost the same task. My objects were located in random positions, the line between two of them was not parallel to axis. So, I created common solution which can helps anybody.
Here is demo video, source code on Github.
The script is very simple by itself. All we need to do is calculate two vectors - vector of main direction and orthogonal vector. After this we need to subordinate orthogonal vector by sine function, and then perform addition of these two vectors, so the sum will move object following exact sine curve.
void FixedUpdate () {
float t = Time.time - startTime;
rb.velocity = direction * speed + orthogonal * amplitude * Mathf.Sin (frequency * t);
}
You can find more details here - how to setup scene, components, etc.
I am trying to create a jump method in XNA, but I am facing a lot of problems, it doesn't work for me, I've been trying doing it for like 2 hours long and still no "luck". Can anybody give me a code sample, or at least a direction?
Note: I want the jump to be realistic, using gravity and such.
Thank you!
I erased all my work but here's the latest I tried, I know it shouldn't work for sure, but still..
public void Jump(GameTime gameTime)
{
float currentTime = (float)0.1;
if (position.Y == 200)
{
position.Y += velocity.Y*currentTime -gravity * (float)(Math.Pow(currentTime,2)) / 2;
}
if (position.Y == 200 + jumpHeight)
{
position.Y -= velocity.Y * currentTime - gravity * (float)(Math.Pow(currentTime, 2)) / 2;
}
}
I see that you've learned the equations of motion from your code (which I don't think was in place when I made my comment). However you're a little off, your issue is with your time variable. You're passing in your game time, but then using .1f for your time variable. What you really want for your time variable is the time since you started the jump. Further, position.Y is unlikely to ever be exactly equal to 200 or 200 + jumpHeight. It's a float (I assume), so you can never trust that it'll be a nice round number. If you want to specify an exact maximum jump height, you'll have to perform some equations before and set your velocity.Y accordingly (solve for when velocity equals 0 i.e. the top of your jump).
So, to fix your original code I think something like this will work, albeit totally untested:
public void Jump(GameTime gameTime)
{
if(jumping && position.Y > groundLevelAtPlayer) {
//Get the total time since the jump started
float currentTime = gameTime.totalTime - character.timeofjumpStart;
//gravity should be a negative number, so add it
position.Y += (velocity.Y * currentTime)
+ (gravity * ((float)(Math.Pow(currentTime, 2)) / 2));
}
else
{
jumping = false;
}
}
How exactly do you want to make your player jump? Since you mentioned realism I am assuming that you want your player to jump and move across both the X and Y axis, so that it will move along an arc(parabola if you will).
If that is what you are after, you will need to replicate projectile motion. This previous SO thread contains various methods in which you could implement such motion.
Edit:
You should be able to use the same equations presented. For vertical jumps, the angle will be 90 degrees, or pi/2 if you are working with radians. If you are pressing your direction keys, you will have to use the same equations. The angle at which you want your player to start the jump will have to be selected by yourself. Usually maximum range is obtained at an angle of 45 degrees (pi/4) assuming that the same force is used, so your option is really between 0 and 45 degrees.
I am trying to accomplish a gravity, where airtime is included, and also acceleration.I have tried using usual gravity, which looks something like this:
velocity += gravity * dt;
position += velocity * dt;
This would probably work good enough for a normal platformer game, but I am trying to make a game inspired by "The helicopter game", where you have to navigate through a tunnel, without touching the walls.what I want to do different, is that I want to be able to save up speed on the way down, which will be used on the way up again, so I will have some acceleration at the beginning.I also want some kind of airtime, so when you hit the top it would not force you down as fast as It would, if I had used the gravity from the code sample.This image illustrates the curve I would like to have:Please note that the whole controlling is done by one key, so for example you would fly up if you held down space, and dive if you released it.The character also never moves left or right, since it will have a static X position on the screen, so vectors can't be used.I have spent hours trying to make it work, but with no success. I have also tried searching on the internet, but without any luck.The game "Whale Trails" got the gravity I kind of want.Here is a link for a video of the game: http://www.youtube.com/watch?v=5OQ0OWcuDJsI'm not that big of a physics guy, so it would be cool if you could give an example of the actual code I hope anyone can help me figure this out.
Gravity is the force that pulls objects down. Your player is the force that pulls objects up. Accordingly your code must be:
if(keyPressed) {
velocity += POWER_OF_PLAYER;
}
velocity += G;
position += velocity;
This is enough to create a curve like you illustrated. Of course POWER_OF_PLAYER must be of a different sign and the absolute value must be greater to make this work.
G = -9.81
POWER_OF_PLAYER = 20
Saving power is then a simple check.
if(keyPressed) {
if(powerSaved > 0) {
velocity += POWER_OF_PLAYER;
powerSaved -= SOMETHING;
}
} else if (velocity >= SOME_MINIMUM_SPEED_BEFORE_GETTING_POWER) {
powerSaved += SOMETHING;
}
SOME_MINIMUM_SPEED_BEFORE_GETTING_POWER should be something less or equal 0.
P.S. I assumed your Y axis starts at ground and shoots into the sky. Signs put accordingly.
It looks like the velocity of the fish is constant.
Try the following:
velocity is fixed and should not change (unless the fish eats a speed power-up).
angle = 0 is equivalent to level flight.
angle -= gravity * dt;
if (angle < - Math.PI / 2) {
angle = Math.PI / 2;
}
if (keyPressed) {
angle += power * dt;
}
if (angle < - Math.PI / 2) {
// Stop the player from doing a looping
angle = - Math.PI / 2;
}
if (angle > Math.PI / 2) {
// Stop the player from doing an inverted looping
angle = Math.PI / 2;
}
// Move the fish (vertical component of velocity)
position += velocity * dt * Math.sin(angle);
// Move the background (horizontal component of velocity)
background_position -= velocity * dt * Math.sin(angle);
It sounds like incorporating 'lift' that is based on horizontal speed and have the button press trigger a 'noseup' movement would work sufficiently well.
So lift would be some constant k multiplied by the horizontal speed Vx and vertical speed Vy would be the difference of gravity and lift times the change in time dt
lift = k * Vx
Vy += ( lift - gravity ) * dt
def noseup
k = 0.01 #some small chunk
dx = k * Vx
dy = k * Vy
Vx -= dy
Vy += dx
When the plane (or whatever) noses up it basically lowers the velocity on one axis while increasing it on the other.
Probably wouldn't be a bad idea to throw drag in there somewhere as well now that I think about it, would have to be dependent on the absolute velocity V = ( Vx**2 + Vy**2 )**0.5...and weight is a better word than gravity in this case (less confusing, units wise) but it works I guess.
Not exactly "physics" but a close approximation that should work fairly well. Play around with the values of k and see if you can make it do what you want.
BTW sorry for the uber crappy psuedocode :-P