I am trying to get a number that x% between a minimum and a maximum number.
So for example if I have the following:
min = 5;
max = 10;
// `c` is a Collider type
float distance = Vector3.Distance(c.transform.position, transform.position);
float percent = distance / radius;
So now what I am looking for, is when object B is closer to the edge of object A's radius for my final number to be closer to 5. When object B is closer to the center of object A, I would like the final number to be closer to 10.
I have the above so far, but I am not sure what the final formula is to get an Object Damage Amount. Basically think of this a proximity mine, the farther you are away the less damage you will take and the closer you are the more damage you will take.
var result = (1.0f - percent) * (max - min) + min;
just make sure that distance is always <= radius (i.e. percent >= 0.0 and percent <= 1.0)
Related
I have two positions and want to calculate the position where the red star is. (I want to move B to the "red star location" but I don't know the coordinates.)
I have the position of A and B and a minimum distance from position A. So my question is how do I calculate the nearest position to B within the specified distance.
Hope someone understand what i'm trying to accomplish.
Assuming that:
You want B to be in the same direction from A as before
You want to move B to a specific distance from A
If B is farther away, move it closer to A to get to the distance
If B is closer than the specific distance, move it away from A to get to the distance
Then this is the way to do it:
Calculate the current distance from A to B
Calculate the difference in position between A and B (in 2 or 3 dimensions)
Divide this difference by a ratio that is "current distance / wanted distance"
For instance, if the current distance is 2x as far away from A as you want, you would divide the difference in position by 2
Set the new position of B to be A + the new differences
Here's some sample code:
var wantedDistance = 40.0;
var distance = Math.Sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y));
var diffX = B.x - A.x;
var diffY = B.y - A.y;
var ratio = distance / wantedDistance;
var newDiffX = diffX / ratio;
var newDiffY = diffY / ratio;
var newB = new PointF(A.x + newDiffX, A.y + newDiffY);
In a comment you say that if B is inside the radius, then it is in range so it should not be moved. You would simply handle this by comparing distance with wantedDistance and if lower, then you're done. Simply add this line of code after the var distance = ... line:
if (distance < wantedDistance)
return B; // or whatever you want to do when "done"
If you need to do this in 3D space, simply augment every calculation and diff to handle the Z dimension as well.
The Problem
How would one interpolate between two given angles, given a certain time delta, so that the simulated motion from rotation A or rotation B would take a similar amount of time when the algorithm is ran at different frequencies (without a fixed time step dependency).
Potential Solution
I have been using the following C# code to do this kind of interpolation between two points. It solves the differential for the situation:
Vector3 SmoothLerpVector3(Vector3 x0, Vector3 y0, Vector3 yt, double t, float k)
{
// x0 = current position
// y0 = last target position
// yt = current target position
// t = time delta between last and current target positions
// k = damping
Vector3 value = x0;
if (t > 0)
{
Vector3 f = x0 - y0 + (yt - y0) / (k * (float)t);
value = yt - (yt - y0) / (k * (float)t) + f * (float)Math.Exp(-k * t);
}
return value;
}
This code is usable for 2D coordinates by having the Z coordinate of the Vector3 set as 0.
The "last" and "current" positions are because the target can move during the interpolation. Not taking this in to account causes motion jitter at moderately high speeds.
I did not write this code and it appears to work. I had trouble altering this for angles because, for example, an interpolation between the angles 350° and 10° would take the 'long' way round instead of going in the direction of the 20° difference in angle.
I've looked into quaternion slerp but haven't been able to find an implementation that takes a time delta into account. Something that I have thought of, but not been able to implement either, is to interpolate between both angles twice, but the second time with a phase difference of 180° on each angle and to output the smaller of the two multiplied by -1.
Would appreciate any help or direction!
The way I've done this before, is to test if the difference between the two angles is greater than 180°, and if so, add 360° to the smaller value, and then do your stuff with those two angle values. So in your example, instead of interpolating between 350° and 10°, you interpolate between 350° and 370°. You can always modulo 360 the result if you need to display it.
Use Slerp() and make sure you wrap the angles between -π and π with one of these helper functions
/// <summary>
/// Wraps angle between -π and π
/// </summary>
/// <param name="angle">The angle</param>
/// <returns>A bounded angle value</returns>
public static double WrapBetweenPI(this double angle)
{
return angle+(2*Math.PI)*Math.Floor((Math.PI-angle)/(2*Math.PI));
}
/// <summary>
/// Wraps angle between -180 and 180
/// </summary>
/// <param name="angle">The angle</param>
/// <returns>A bounded angle value</returns>
public static double WrapBetween180(this double angle)
{
return angle+360*Math.Floor((180-angle)/360);
}
Caution: Related Post for Inconsistency with Math.Round()
The solution
I have some working code using quaternions. In order to take time steps into account (to remove reliance on a fixed step update) the amount slerp/lerp is calculated using amount = 1 - Math.Exp(-k * t). The constant k effects damping (1 - very sluggish, 20 - almost instant snap to target).
I decided to not try and get this to work for 3D as I'm developing a 2D game.
public static float SlerpAngle(
float currentAngle, float targetAngle, double t, float k)
{
// No time has passed, keep angle at current
if (t == 0)
return currentAngle;
// Avoid unexpected large angles
currentAngle = MathHelper.WrapAngle(currentAngle);
targetAngle = MathHelper.WrapAngle(targetAngle);
// Make sure the shortest path between
// current -> target doesn't overflow from
// -pi -> pi range otherwise the 'long
// way round' will be calculated
float difference = Math.Abs(currentAngle - targetAngle);
if (difference > MathHelper.Pi)
{
if (currentAngle > targetAngle)
{
targetAngle += MathHelper.TwoPi;
}
else
{
currentAngle += MathHelper.TwoPi;
}
}
// Quaternion.Slerp was outputing a close-to-0 value
// when target was in the range (-pi, 0). Ensuring
// positivity, halfing difference between current
// and target then doubling result before output
// solves this.
currentAngle += MathHelper.TwoPi;
targetAngle += MathHelper.TwoPi;
currentAngle /= 2;
targetAngle /= 2;
// Calculate spherical interpolation
Quaternion qCurrent = Quaternion.CreateFromAxisAngle(
Vector3.UnitZ, currentAngle);
Quaternion qTarget = Quaternion.CreateFromAxisAngle(
Vector3.UnitZ, targetAngle);
Quaternion qResult = Quaternion.Slerp(
qCurrent, qTarget, (float)(1 - Math.Exp(-k * t)));
// Double value as above
float value = 2 * 2 * (float)Math.Acos(qResult.W);
return value;
}
Rotation speed is consistent over the 5Hz -> 1000Hz range. I thought these were suitable extremes. There's no real reason the run this higher than 60Hz.
I'm currently developing a 2-player Ping-Pong game (in 2D - real simple) from scratch, and it's going good. However Theres a problem I just can't seem to solve - I'm not sure if this should be located here or on MathExchange - anyway here goes.
Initially the ball should be located in the center of the canvas. When pressing a button the ball should be fired off in a completely random direction - but always with the same velocity.
The Ball object has (simplified) 4 fields - The position in X and Y, and the velocity in X and Y. This makes it simple to bounce the ball off the walls when it hits, simple by inverting the velocities.
public void Move()
{
if (X - Radius < 0 || X + Radius > GameWidth)
{
XVelocity = -XVelocity;
}
if (Y - Radius < 0 || Y + Radius > GameHeight)
{
YVelocity = -YVelocity;
}
X+= XVelocity;
Y+= YVelocity;
}
I figured the velocity should be the same in each game, so I figures I would use Pythagoras - the square of the two velocities should always be the same.
SO for the question:
Is there a way to randomly select two numbers (doubles) such that the sum of their squares is always a specific number - more formally:
double x = RandomDouble();
double y = RandomDouble();
if (x^2 + y^2 = 16) {/* should always be true */ }
Any help appreciated :)
Randomly pick an angle theta and multiply that by the magnitude of the distance d you want. Something like:
double theta = rand.NextDouble() * 2.0 * Math.PI;
double x = d * Math.Cos(theta);
double y = d * Math.Sin(theta);
If the constant is C, pick a number x between 0 and sqrt(C).
Solve for the other number y using simple algebra.
why not try this:
double x = RandomDouble();
double y = square(16-x^2);
as your application allow double type.
does this solve your problem?
if not, please let me know
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I'm new in game development and I'm stuck in a problem.
I would like to know the new player position each seconds, here an example :
The player start at (2.5;2.5) and he go to (6.5;3.8).
His velocity is by example 2 units per seconds, and I would like to know the player position after 1sec. So something like this :
What I would like to know it's A every seconds but I don't know at all how I can do that...
I hope you will be able to help me, thanks in advance !
His velocity is by example 2 units per seconds.
I assume, that 'unit' means 'vector of length = 1'.
First of all, you need to calculate the AB vector (movement vector):
mov_vec = [xb-xa, yb-ya] = [6.5 - 2.5, 3.8 - 2.5] = [4, 1.3]
So, we know, that total unit did movement by [4, 1.3]. We need to normalize this vector. Normalized vector (unit vector) 'norm_mov_vec' will be codirectional with 'mov_vec', but it's length will be 1. See this link if you want to know more abut unit vectors.
Compute the length of movement vector:
mov_vec_len = sqrt( 4^2 + 1.3^2 ) ~= 4.2059
Compute normalized vector:
norm_mov_vec = [4/4.2059, 1.3/4.2059] ~= [0.9510, 0.3090]
And that's it. 'norm_mov_vec' is your 'unit-movement-vector', so if player is moving in that direction with speed of N units per second, you can very easily compute it's position after T seconds:
pos_after_T_sec_with_speed_N_units_per_sec = start_pos + ( N * T * norm_mov_vec )
EDIT:
Sample code, using Vector2 type from XNA. Can't test it, but I hope you will get the idea:
//In your case:
//start_pos = 'A' point
//end_pos = 'B' point
//time = number of seconds that elapsed
//speed = number of units per second
Vector2 calculatePosition(ref Vector2 start_pos, ref Vector2 end_pos, Uint32 time, Uint32 speed)
{
Vector2 mov_vec = Vector2.Substract(end_pos, start_pos);
Vector2 norm_mov_vec = Vector2.Normalize(mov_vec);
Vector2 delta_vec = norm_mov_vec * time * speed;
return Vector2.Add(start_pos, delta_vec);
}
First you need to work out the total distance covered, that's your vector. A vector is a movement, not two points in space.
Then you just divide each dimension, x and y in this case, by the time taken to do the move in unit of measurement (seconds) to get the distance per second.
Then you multiply each x and y by the number of seconds from 0, i.e. 1 second in your example, to get the position after 1 second.
I don't know what's available to you in your framework or libraries but a good Vector class will be so helpful, you'll want to be able to do math on the vector instance directly, such as:
Point origin = sprite.Position; // Assumes some sprite object with a position.
Point dest = new Point(200,344); // Destination.
Vector totalTranslation = new Vector(dest.X - origin.X, dest.Y - origin.Y);
Vector perSecond = totalTranslation / 60; // assuming takes a minute to move.
Vector distanceMoved = perSecond * 4; // distance moved after 4 seconds.
Point newPosition = new Point(origin.X + distanceMoved.X, origin.Y + distanceMoved.Y);
sprite.Position = newPosition; // Or using some orchestration class...
spriteManager.Move(sprite, newPosition); // ...like this.
Note being able to divide a vector directly. Else you have to divide each spatial dimension of the vector and make a new vector, or make a helper class to do it.
In real life, you might want to calculate based on milliseconds. I wouldn't use a fixed frame counter since it could look juddery, but work everything out based on a timer.
As I say, a good library or immutable Vector struct/class is the key here. Then its a case of thinking about the problem on graph paper.
Also, build up a palette of small functions you can chain together to do cooler, bigger stuff.
Another interesting problem is using an easing function to work out a coordinate after a given time to achieve the effect of a sprite slowing down as it 'lands'.
This is not programming, but vector math mostly, but anyway:
Your player is moving along the vector BA ( Point B minus Point A ) which is
Direction Vector: ( 4.0 / 1.3 )
This vector has a length of:
SquareRoot(4.0 * 4.0 + 1.3 * 1.3) = 4.2
A vector of the same direction and length of one unit would therefore be the vector with both components divided by the length of 4.2:
Direction Vector of length 1: (0.95 / 0.30)
As your player is fast and moves two units, it would be double length:
Direction Vector of length 2: (1.90 / 0.60)
Now each tick, add 1.90 and 0.60 respectively to your player coordinates, until they equal (roughly) the target coordinates.
x-displacement: 6.5-2.5 = 4
y-displacement: 3.8-2.5 = 1.3
Math.sqrt((4n)(4n)+(1.3n)(1.3n)) = 2
n=2/Math.sqrt(17.69)
x-displacement/second = 4n = 8/Math.sqrt(17.69) = 1.90207
y-displacement/second = 1.3n = 2.6/Math.sqrt(17.69) = 0.61817
so after get these values, it is really easy to calculate the position each second
You can use (as a general solution) these simple trigonometry formulae:
x = A.x + v * cos(fi) * t;
y = B.y + v * sin(fi) * t;
fi = atan2(B.y - A.y, B.x - A.x);
sample solution
// Since there's no common 2d Point double based type,
// let (x, y) point be represented as Tuple<Double, Double>
// where Item1 is x, and Item2 is y
public static Tuple<Double, Double> Move(Tuple<Double, Double> fromPoint,
Tuple<Double, Double> toPoint,
Double velocity,
Double time) {
Double fi = Math.Atan2(toPoint.Item2 - fromPoint.Item2, toPoint.Item1 - fromPoint.Item1);
return new Tuple<Double, Double>(
fromPoint.Item1 + velocity * Math.Cos(fi) * time,
fromPoint.Item2 + velocity * Math.Sin(fi) * time);
}
...
for (int t = 0; t < 10; ++t) {
Tuple<Double, Double> position =
Move(new Tuple<Double, Double>(2.5, 2.5),
new Tuple<Double, Double>(6.5, 3.8),
2.0,
t);
Console.Write("t = ");
Console.Write(t);
Console.Write(" x = ");
Console.Write(position.Item1);
Console.Write(" y = ");
Console.Write(position.Item2);
Console.WriteLine();
}
I'm working on a simple game and I'm trying to simplify part of the 2D collision reaction in the game. When certain objects hit walls, I'm calculating a collision normal (collisionPoint - objectCenter) and reflecting based on that normal. I'm interested in rounding that normal vector to its nearest 15° but I'm not sure of a good way to go about that.
My current thought is doing something like this
float angle = atan2(normal.Y, normal.X) * Rad2Deg;
float newAngle = ((int)(angle + 7.5f) / 15) * 15.0f * Deg2Rad;
vector2 newNormal = vector2(cos(newAngle), sin(newAngle));
Is this a reasonable way to do it? Is there a better way?
Try this:
float roundAngle = 15 * Deg2Rad;
float angle = (float)Math.Atan2(normal.Y, normal.X);
Vector2 newNormal;
if (angle % roundAngle != 0)
{
float newAngle = (float)Math.Round(angle / roundAngle) * roundAngle;
newNormal = new Vector2((float)Math.Cos(newAngle), (float)Math.Sin(newAngle));
}
else
{
newNormal = Vector2.Normalize(normal);
}
You don't need to add 7.5, take this example:
// 4 degrees should round to 0
(4 + 7.5) / 15 == 11.5 / 15 == 0.77
// When this gets rounded up to 1 and multiplied by 15 again, it becomes 15 degrees.
// Don't add 7.5, and you get this:
4 / 15 == 0.27
// When rounded, it becomes 0 and, as such the correct answer
// Now how about a negative number; -12
-12 / 15 == -0.8
// Again, when rounded we get the correct number
actually this is more correct if you want the nearest 15 degree angle :
do this:
newangle% = INT(((angle%+7.5)/15)*15)
INT ALWAYS rounds DOWN by default this should properly give you the nearest angle in any case that is positive or negative have fun!!
and add the part where you use degree to rad and rad to degree if needed INSIDE the parens (like right next to angle% if that angle is not given in degrees then use some sort of rad2deg multiplier inside there
this is more like how you would do this in basic, with some modification It will work in c code or such, well good luck!!