Programming Floaty Motion - c#

Anyone have a good idea for a method of achieving random floaty motion? Like, just general smooth drift in a constrained area.
This is what I tried:
var rangeX = 100;
var rangeY = 100;
var gravity = .001;
$('.floating').each(function() {
$(this).data('startX', $(this).position().left);
$(this).data('startY', $(this).position().top);
$(this).data('velocityX', 0);
$(this).data('velocityY', 0);
chooseDestination();
});
setInterval(function() {
$('.floating').each(function() {
var changeX = ($(this).data('destinationX') - $(this).position().left) * gravity;
var changeY = ($(this).data('destinationY') - $(this).position().top) * gravity;
var velocityX = $(this).data('velocityX') + changeX;
var velocityY = $(this).data('velocityY') + changeY;
$(this).data('velocityX', velocityX);
$(this).data('velocityY', velocityY);
$(this).css('left', $(this).position().left + velocityX);
$(this).css('top', $(this).position().top + velocityY);
});
}, 10);
setInterval(chooseDestination, 2000);
function chooseDestination() {
$('.floating').each(function() {
$(this).data('destinationX', $(this).data('startX') - rangeX/2 + Math.random() * rangeX);
$(this).data('destinationY', $(this).data('startY') - rangeY/2 + Math.random() * rangeY);
});
}
It really doesn't look very floaty though.

"Floaty" generally requires slow changes in velocity. What I would do, from a high-level perspective, is set up an acceleration/velocity/position model for movement, where your random generator affects acceleration only. You already have the other two layers. Establish limits for velocity and acceleration at any given point in time (I'd limit absolute distance, not just distance in X and Y), and don't update the rate of acceleration every frame. Lastly, deal in fractional values for velocity and acceleration, which will be turned into an integer position change only at the last minute when you're drawing the object's new position. What you'll end up with is an object that will slowly head off in one direction, then start drifting in a different direction, maybe stop and hover for a second, then start going again.

Related

Constant acceleration movement with minus acceleration

I making movement process on Unity.
I would like to make a process that moves the object to a specified position, but as the title suggests, I want the object not only to move, but also to reach a predetermined distance with its velocity decaying.
If the acceleration is negative, I can not to process it well.
Specifically, I want to reach a position without turning back when the initial velocity is 10 as shown in the gif.
I used "s=v0t+1/2at^2" from the constant acceleration movement formula to find the acceleration "a", but that does not seem to be enough.
I would appreciate it if you could help me.
public class Test : MonoBehaviour
{
public float t;
public float initSpd;
public Transform t1, t2;
IEnumerator Start()
{
var p = t1.position;
while (true) {
t1.position = p;
var v0t = initSpd * t;
var distance = Vector2.Distance(t1.position, t2.position);
var direction = (t2.position - t1.position).normalized;
var a = (2 * (distance - v0t)) / (t * t);
var v = initSpd;
// update
yield return Utils.Coroutine.WhileForSeconds(t, () =>
{
t1.Translate(direction * v * Time.deltaTime);
v += a * Time.deltaTime;
});
}
}
}
span=3, initial velocity=0
span=3, initial velocity=3
span=3, initial velocity=10
Your math to compute a is correct, but you have posed the problem in a way that cannot be solved to your satisfaction.
As you said, with constant acceleration the position is a quadratic function of time, so it is described by
s = b t^2 + c t + d
for some constants b, c, d. The value of d is already fixed by the initial position. The value of c is already fixed by the initial velocity. There is only one free parameter left, and when you solve for s(finalTime) = goalPosition, you can't control whether or not the resulting parabola overshoots the goal or not.
You can increase the polynomial degree, but there will always be some initial velocity that is too large and causes overshoot.
Essentially, you have an optimal control / trajectory optimization problem, something like
minimize: integral(acceleration(t)^2) from t = 0 to T
subject to: x(0) given
v(0) given
x(T) given
x(t) <= x(T) for all t in [0, T] (assuming x(0) < x(T))
As you stated the problem, there was no optimization objective, but you need either a cost or a constraint on the acceleration, otherwise you could get solutions with infinite acceleration (e.g. accelerate really hard in the first time step, then coast to the goal at constant velocity).
The inequality makes it complicated. If you want a continuous-time analytic solution, then Pontryagin's principle would be a hammer to solve it, but there might be easier tricks. If you discretize time and let the acceleration be piecewise-constant, then it's an easy convex optimization problem.
If you are willing to relax the "never overshoot" and "get there at exactly this time" constraints, then very simple solution, that will scale better to more complex scenarios like external forces, is to use a feedback control law like a PD controller:
a = kp * vectorToGoal - kd * velocityVector,
where kp and kd are hand-tuned positive constants, and this expression is re-evaluated in every frame. You can tune kp and kd to minimize overshoot in your typical scenarios.
Or, you could do what most games do - allow velocity to change instantaneously :)
I found the ideal behavior achieved by changing the speed in two steps.
IEnumerator Start()
{
var p = t1.position;
while (true)
{
t1.position = p;
var direction = (t2.position - t1.position).normalized;
var distance = Vector2.Distance(t1.position, t2.position);
var v0 = initSpd;
var M = distance;
var T = duration;
var tm = M / v0;
var vm = v0 / T * tm;
var accel1 = (vm - v0) / (tm - 0);
var accel2 = (0 - vm) / (T - tm);
Debug.Log($"vo={v0}, M={M}, T={T}, tm={tm}, vm={vm}, accel1={accel1}, accel2={accel2}");
var v = initSpd;
var stime = Time.time;
var hist = 0f;
// update
yield return Utils.Coroutine.WhileForSeconds(T, () =>
{
t1.Translate(direction * v * Time.deltaTime);
hist += v * Time.deltaTime;
if (Time.time - stime <= tm)
v += accel1 * Time.deltaTime;
else
v += accel2 * Time.deltaTime;
});
Debug.Log($"elapsed={Time.time - stime}, moved distance={hist}, v={v}");
}
}

How does Spaceflight simulator predict flight-paths?

I am trying to make a game with Unity that uses rockets that are in orbit and can transition to different planets, but I need to predict the rocket path in real-time, depending on the current velocity/direction of the rocket allot like spaceflight simulator does.
I have managed to get realistic physics and orbits when adding gravitational forces to a rigidBody:
var sateliteCords = Satelite.transform.position;
var planetCords = gameObject.transform.position;
var distance = sateliteCords - planetCords;
distanceFromSatelite = Vector3.Distance(Satelite.transform.position, gameObject.transform.position);
F = (Gravity * Mass) / Mathf.Pow(distanceFromSatelite,2);
forces = (distance / Mathf.Sqrt(Mathf.Pow(distance.x, 2)+ Mathf.Pow(distance.y, 2)+ Mathf.Pow(distance.z, 2))) * -F;
Satelite.GetComponent<Rigidbody>().AddForce(forces * Time.deltaTime, ForceMode.Force);
but I don't know how to predict future points in time to give me the orbit shape.
I have tried "speeding up the simulation" with Time.timeScale = 50; but it isn't fast enough.
I read somewhere that I need a copy of the solar system running independently form the unity updates, and calculate the rocket trajectory from there but I don't know how to approach that.
I'm a bit of a noob when it comes to physics simulations so can one of you geniuses please help?
Here's what your code is actually doing:
var sateliteCords = Satelite.transform.position;
var planetCords = gameObject.transform.position;
var distance = sateliteCords - planetCords;
distanceFromSatelite = distance.magnitude;
F = (Gravity * Mass) / Mathf.Pow(distanceFromSatelite,2);
forces = distance / distanceFromSatellite * -F;
Satelite.GetComponent<Rigidbody>().AddForce(forces * Time.deltaTime, ForceMode.Force);
You can further reduce it by doing:
var distance = sateliteCords - planetCords;
distanceSqrMag = distance.sqrMagnitude;
F = (Gravity * Mass) / distanceSqrMag;
forces = distance.normalized * -F;
Satelite.GetComponent<Rigidbody>().AddForce(forces * Time.deltaTime, ForceMode.Force);
Now that the code is much simpler, I can find a crucial mistake. ForceMode.Force already multiplies the Force by Time.deltaTime. What you want here is to switch to ForceMode.Impulse, or remove the deltaTime.
F = (Gravity * Mass) / distanceSqrMag;
forces = distance.normalized * -F;
Satelite.GetComponent<Rigidbody>().AddForce(forces, ForceMode.Force);
Or better yet, assuming Mass is the mass of the satellite, and gravity is not a general constant, but just the local gravity of your planet. ForceMode.Force divides the force by the mass of the rigidbody it is applied on.
F = Gravity / distanceSqrMag;
forces = distance.normalized * -F;
Satelite.GetComponent<Rigidbody>().AddForce(forces, ForceMode.Acceleration);
Now that's all out the way, you can do a very simple approximation by doing something like
var currentPos = _rigidBody.position;
var prevPos = currentPos;
var currentVelocity = _rigidBody.velocity;
var planetCords = gameObject.transform.position;
for (int i = 0; i < stepCount; i++)
{
var distance = planetCords - currentPos;
var forceMag = Gravity / distance.sqrMagnitude;
forces = distance.normalized * forceMag;
currentVelocity += forces * Time.fixedDeltaTime;
currentPos += currentVelocity * Time.fixedDeltaTime;
//Replace by a TrailRenderer you reset each frame
Debug.DrawLine(prevPos, currentPos, Color.Red, Time.deltaTime);
prevPos = currentPos;
}
Note that this code is untested, I probably made a mistake here or there, but that's the gist of it.
Don't worry about getting any difference in trajectory, this is exactly what unity does to predict positions. No need for fancy calculations when you got all the computing power you need.
If you want more than one-body gravity simulation, it's simple. Either apply the gravity of all planets, or simply the gravity of the closest planet taking its mass into account. I recommend the former if you have less than a dozen planets.
You don't need conics, or advanced math, or any of that. You're not kerbal space program. Sometime brute force is the best solution.

Moving mouse in direction and rate

I'm trying to create a simple mouse emulator controlled by a joystick's right thumbstick. I was trying to have the mouse move in the direction the stick pointed with a smooth gradient of pressure values dictating speed, but I've hit a number of snags when trying to do so.
The first is how to accurately translate the angle into accurate X and Y values. I can't find a way to implement the angle correctly. The way I have it, the diagonals are likely to move considerably faster than the cardinals.
I was thinking I need something like Math.Cos(angle) for the X values, and Math.Sin(angle) for the Y values to increment the mouse, but I can't think of a way to set it up.
The second, is smooth movement of the mouse, and this is probably the more important of the two. Since the SetPosition() function only works with integers, the rate at which pixels move over time seems very limited. The code I have is very basic, and only registers whole number values of 1-10. That not only creates small 'jumps' in acceleration, but limits diagonal movement as well.
The goal would to have something like 10 pixels-per-second, with the program running at 100hz, and each cycle outputting 0.1 pixel movement.
I'd imagine I might be able to keep track of the pixel 'decimals' for the X and Y values and add them to the axes when they build to whole numbers, but I'd imagine there's a more efficient way to do so and still not anger the SetPosition() function.
I feel like Vector2 objects should get this done, but I don't know how the angle would fit in.
Sample code:
//Poll Gamepad and Mouse. Update all variables.
public void updateData(){
padOne = GamePad.GetState(PlayerIndex.One, GamePadDeadZone.None);
mouse = Mouse.GetState();
currentStickRX = padOne.ThumbSticks.Right.X;
currentStickRY = padOne.ThumbSticks.Right.Y;
currentMouseX = mouse.X;
currentMouseY = mouse.Y;
angle = Math.Atan2(currentStickRY, currentStickRX);
vectorX = (int)( currentStickRX*10 );
vectorY = (int)( -currentStickRY*10 );
mouseMoveVector.X = vectorX;
mouseMoveVector.Y = vectorY;
magnitude = Math.Sqrt( Math.Pow( (currentStickRX - 0), 2 ) + Math.Pow( (currentStickRY - 0), 2 ) );
if (magnitude > 1){
magnitude = 1;
}
//Get values not in deadzone range and re-scale them from 0-1
if(magnitude >= deadZone){
activeRange = (magnitude - deadZone)/(1 - deadZone);
}
Console.WriteLine(); //Test Code
}
//Move mouse in in direction at specific rate.
public void moveMouse(){
if (magnitude > deadZone){
Mouse.SetPosition( (currentMouseX + vectorX), (currentMouseY + vectorY));
}
previousStickRX = currentStickRX;
previousStickRY = currentStickRY;
previousActiveRange = activeRange;
}
Note: I'm using all the xna frameworks.
Anyway, apologies if I'm explaining these things incorrectly. I haven't been able to find a good resource for this, and the vector examples I searched only move in integer increments and from point A to B.
Any help with any part of this is greatly appreciated.
I haven't tried it myself but from my point of view, you should normalize the pad axis after reading them, that way diagonals would move the same speed as cardinals. And for the second part, I would keep track of the mouse in floating variables, such as a Vector2 and do the cast (maybe rounding better) when setting the mouse position.
public void Start()
{
mousePosV2 = Mouse.GetState().Position.ToVector2();
}
public void Update(float dt)
{
Vector2 stickMovement = padOne.ThumbSticks.Right;
stickMovement.Normalize();
mousePosV2 += stickMovement*dt*desiredMouseSpeed;
/// clamp here values of mousePosV2 according to Screen Size
/// ...
Point roundedPos = new Point(Math.Round(mousePosV2.X), Math.Round(mousePosV2.Y));
Mouse.SetPosition(roundedPos.X, roundedPos.Y);
}

Why doesn't this simple physics freefall code work

I have some code I created to simulate a freefall of an object in a vacuum. When timestep is set to 1000ms it works perfectly with no problems. when I set timestep lower than 1000ms it deviates wildly from actual values falling far faster than normal. It takes around 14 seconds to fall 1000 meters at 1000ms which is the correct value. At 100ms timestep it only takes 5 seconds. at 10ms it only takes 2.2 seconds.
Can someone please tell me what I am doing wrong? I thought I set the timestep calculation to be able to handle smaller steps.
Thank you
Body testbody = new Body();
testbody.pos = new Vector(0, 1000);
testbody.velocity = new Vector(0, 0);
Bodytrack(testbody);
static void Bodytrack(Body body)
{
watch.Start();
int timestep = 1000;
while (body.pos.Y > 0)
{
body.pos = body.pos + (body.velocity * (timestep / 1000.0));
if (body.pos.Y <= 0) { break; }
Thread.Sleep(timestep);
CalculateAcceleration(body);
Console.Clear();
Console.WriteLine(body.velocity.Y);
Console.WriteLine(body.pos.Y);
Console.WriteLine(watch.Elapsed.TotalSeconds);
}
watch.Stop();
}
public static void CalculateAcceleration(Body body)
{
body.acceleration = new Vector(0, -9.80665);
body.velocity = Vector.Add(body.acceleration, body.velocity);
}
This line is your problem:
body.acceleration = new Vector(0, -9.80665);
Every time through the loop, you set the acceleration to -9.8 (which is the acceleration for 1000ms) instead of the acceleration for the amount of time that has passed.
You need to take into account the amount of time that as passed. If 500ms has passed, then acceleration should only be -9.80665/2.
You need to change the method to this:
public static void CalculateAcceleration(Body body, int timestep)
{
body.acceleration = new Vector(0, -9.80665 * (timestep/1000));
body.velocity = Vector.Add(body.acceleration, body.velocity);
}
If I remember my math correctly (and manage to write it down using markdown)...
The idea is that the horizontal position is not affected by anything, gravity only affects the vertical position. So we can easily calculate where the object is horizontally (assuming no collisions).
X(t) = Vx * t.
This means that when you throw something horizontally at 10 metres per second, it will have traveled 100 metres after 10 seconds.
The formula för the horizontal positioning is a bit more advanced. It contains two parts, the first part is the movement without gravity, and the second part acts as a countering force by the gravity.
Y(t) = Vy * t - (g * t^2)/2 where g is gravity (usually 9.82).
Vy * t is the constant motion upwards if gravity wouldn't have pulled it down.
(g * t^2)/2 is the increasing pull of gravity.
A object that's dropped will have no initial force in any direction, thus { Vx = 0, Vy = 0 } and you can easily get the current position of it, at any time, using pos(t) = -(g * t^2)/2. (We also know that it will fall straight down so you do not have to calculate any horizontal positioning.) You already know the speed of it by speed(t) = g * t.
You can not trust time-slices as your code does. For one, Thread.Sleep isn't exact. It's better if you calculate time elapsed since last update, and use that in your calculations.

Galaxian-like Enemy movement

I'm making a galaxian-like shooter, and my enemy objects have a destination Vector which they travel towards, using this bit of code:
position.X -= (Motion.X / Magnitude) * Speed;
position.Y -= (Motion.Y / Magnitude) * Speed;
Motion is worked out by:
this.Motion = InitialPosition - Destination;
This makes them travel in a straight line towards the destination.
However, I want to make them a bit more interesting, and travel on a sin or cos wave, a bit like Galaxian did.
How can I do this?
You might be better off defining a bezier curve for the movement function than simple functions like a sine wave. Galaxian certainly had more complex movements than that.
Here is a link to a primer on the maths of Bezier curves. It's quite a long document, but does a good job of covering the maths involved, with plenty of examples.
Hope that helps inspire you.
One way to do this would be to create an acceleration factor for the horizontal motion and add that factor to the horizontal speed every tick. So if your horizontal speed for a given enemy was 2 to begin, and your acceleration was -.01, then after 200 ticks the enemy would be going straight down, and after another 200 ticks it would be moving at a horizontal speed of -2. This will give a nice curve.
By determining the speed and acceleration randomly for each enemy (within certain limits determined by experimentation) you can create a nice looking variety of attack profiles without too much effort. This would give a very Galaxian-like motion.
You can do the same thing with the vertical as well, though, of course, the acceleration limits would be very different...for the horizontal acceleration you would probably want to determine a range that was equal in magnitude on either side of 0 (say -.02 to +.02), while for the vertical acceleration, you probably always want the ship to end up going down off the bottom of the screen, so you probably want that acceleration to always end up positive (or negative depending on how you're doing screen coordinates.)
You would do this by utilizing waypoint navigation, in line with your current motion code. You would calculate the waypoints by graphing the sine wave. You would do this by using something to the effect of Destination.Y = Math.Sin(Destination.X) - it's a little difficult to say for sure without seeing your code at large.
Creating an oscillator and moving the enemy (even without momentum) perpendicularly to its direction by an offset equals to the sine or cosine of the oscillator would be enough.
The following example, while working, is clearly just a guideline. I hope it can help you.
var dest = new PointF(200, 100);
var pos = new PointF(30, 140);
var oscAngle = 0d;
var dirAngle = Math.Atan2(dest.Y - pos.Y, dest.X - pos.X);
//Constants for your simulation
const int movSpeed = 2;
const int amp = 2;
const double frequency = Math.PI / 5;
//Inappropriate loop condition, change it to proper
while (true)
{
oscAngle += frequency;
//Scalar offset, you can use Cos as well
var oscDelta = Math.Sin(oscAngle);
//Linear movement
var stepVector = new SizeF((float)(Math.Cos(dirAngle) * movSpeed), (float)(Math.Sin(dirAngle) * movSpeed));
//Oscillating movement, making it transversal by adding 90° to the direction angle
var oscNormalAngle = dirAngle + Math.PI / 2;
//Vector for the oscillation
var oscVector = new SizeF((float)(Math.Cos(oscNormalAngle) * oscDelta) * amp, (float)(Math.Sin(oscNormalAngle) * oscDelta) * amp);
pos += stepVector + oscVector;
//Operate below
}

Categories

Resources