Suppose I have a ball moving in a table and I want to evaluate the reflections it will do in the walls.
Suppose it will have a given movement vector:
I know the end position is the red dot, following this logic. For simplicity, let's suppose the table is 4x2 and centered in (0,0). My code to evaluate the position is:
ball.position += ball.movement;
if(ball.position.x > 2) ball.position.x = 2 - (ball.position.x - 2);
else if(ball.position.x < -2) ball.position.x = -2 + (-2 - ball.position.x);
if(ball.position.y > 1) ball.position.y = 1 - (ball.position.y - 1);
else if(ball.position.y < -1) ball.position.y = -1 + (-1 - ball.position.y);
I'd like to know if there is a shorter / faster / smarter way to approach this evaluation, instead of 4 if-else statements.
The for if-else statements can be reduced to two by using Mathf.Abs so a check e.g.
if(x < -2)
else if(x > 2)
simply becomes
if(Mathf.Abs(x) > 2)
then in both cases you can use Vector2.Reflect. And in roder to instantly also clamp the position use Mathf.Clamp
ball.position += ball.movement;
if(Mathf.Abs(ball.position.x) > 2) ball.movement = Vector3.Reflect(ball.movement, Vector2.up);
if(Mathf.Abs(ball.position.y) > 1) ball.movement = Vector3.Reflect(ball.movement, Vector2.right);
// and for instantly also clamp the position
ball.position = new Vector2(Mathf.Clamp(ball.position.x, -2f, 2f), Mathf.Clamp(ball.position.y, -1f, 1f);
Note that you have to change movement component after reflection. So
ball.movement.vx = abs(ball.position.x) > 2 ? -ball.movement.vx: ball.movement.vx;
similar for y
Also note that more reliable model includes preliminary calculation of reflection moment instead of post-factum changing
Related
I'm using C# in Unity to make a game with a hexagonal grid. I'm trying to make the map in the shape of a hexagon rather than a rectangle. However, in order to do this with a grid, I need to apply 6 conditions (one for each side) to the rectangular grid to filter out the tiles which don't fall within the space we've determined to be within the hexagon. For instance, using pseudo-code:
foreach(tile in rectangle) { if((y <= 2/3*x + 0.5) && (condition2) && ...) { Debug.Log("It's a tile in the hexagon!") } }
I'm not great at trig, so I've been stumped on how to approach this for a while. In fact, I can't get a single condition down that would define a side of a hexagon (other than the top and the bottom stretches, which are done by default). Any help would be appreciated. Thanks!
(Additional information: the left and right sides are X and are 0.866 apart; top and bottom points are Z and are 1 apart but are fitted such that every other tile in a row is 0.25 closer to its adjacent row.)
I figured it out! I made a perfect hexagon in desmos: https://www.desmos.com/calculator/8tqplz09tt
Then, I converted this to code:
float standardUnitZ = ((mapSize.z * chunkSize.z) - 1f);
float normalUnitZ = standardUnitZ * (13f/15f);
float thisX = ((cz * mapSize.z) + hz);
float sqrt3 = (float)Math.Sqrt(3f);
if (
(theseFinalCoords.z >= (((1 - (13f / 15f)) * normalUnitZ) / 2))//1 BOTTOM
&& (theseFinalCoords.z <= normalUnitZ - (((1 - (13f/15f)) * normalUnitZ) / 2))//2 TOP
&& (theseFinalCoords.z <= sqrt3*thisX + (normalUnitZ / 2f))//3 TOP LEFT
&& (theseFinalCoords.z >= -sqrt3*thisX + (normalUnitZ / 2f))//4 BOTTOM LEFT
&& (theseFinalCoords.z <= -sqrt3*thisX + 2.2320508075f*normalUnitZ)//5 TOP RIGHT
&& (theseFinalCoords.z >= sqrt3*thisX - 1.2320508075*normalUnitZ)//6 BOTTOM RIGHT
)
A little more work to do possibly, but here's the final result:
I have a simple chart whereby the user can determine a start & end direction in radians. The control then draws the chart using an override of OnRender. I am drawing the arcs with StreamGeometryContext.ArcTo. This method has an IsLargeArc property which determines how the arc is drawn (true for > 180 degrees (PI), false for < 180 degrees). I am determining this value from a condition which works fine:
//Rule does not exceed 180 degrees in direction (radian), IsLargeArc= False else true
if (Start < Math.PI && (End - Start) < Math.PI || //currently unknow condition in here to deal with < PI when start angle is > then end angle?)
{
//IsLargeArc = false;
}
else
{
//IsLargeArc= true;
}
The issue comes when the start < end. e.g. From 270 degrees to 120 degrees. I need a condition to satisfy an angle over 180 degrees (PI) in this situation. Maths is not my strong point. I think I need to add PI*2 to the end and then somehow compare the two values but not sure on how to achieve this?
Well, you could add a full circle to the end, (or start; according to the direction) angle e.g.:
if (start < end)
start += 2 * Math.PI; //full circle in radians.
This way you'll add a full circle to the end angle, which doesn't change the position for your drawing and results in a valid and correct angle if you subtract them (start - end).
Although I must say, I would expect a start > endcondition.
If start > end or visa versa, than this tells you something about the direction.
You can use Math.Abs method to get the absolute value of the difference.
Your code might look like the following:
if ((Start < Math.PI && Math.Abs(End - Start) < Math.PI) ||
(Start > Math.PI && End - Start < 0 ))
{
//IsLargeArc = false;
}
else
{
//IsLargeArc= true;
}
private bool IsLargeArc(Styled2DRange range)
{
double angleDiff = GetPositiveAngleDifference(End - Start);
if (angleDiff > Math.PI)
{
return true;
}
return false;
}
private double GetPositiveAngleDifference(double angleDiff)
{
return (angleDiff + (2*Math.PI))%(2*Math.PI);
}
I'm working on a small game, I have objects which I want to elevate up and down. Object moves to max value of Y -> Object moves to min value of Y -> Repeat. I have a rough idea of how to do this, I would put this in a timer/my update method.
if(Y >= max)
{
direction = "down";
}
if(y =< min)
{
direction = "up";
}
if(direction == "up") Y -= speed;
if(direction == "down") Y += speed;
(Could also use a bool ofcourse but, for the sake of implicity)
But it feels like I'm just re-inventing the wheel, is there by any chance a built in method/math function to do this automatically? eg. SomeFunction(min, max, increment).
I'm using the XNA framework, so functions built into that are ofcourse welcome as well.
Forget having a separate direction flag.
Just use a negative speed for "up" to simplify the code:
if ((Y >= max) || (Y <= min)) // Hit an edge?
speed = -speed; // Reverse direction.
Y += speed;
I'm trying to separate two circles that are colliding. Thanks to the help of others, I'm right there!
This is my code:
var totalRadius : Number = _circle1.radius + _circle2.radius;
var x : Number = _circle1.position.x - _circle2.position.x;
var y : Number = _circle1.position.y - _circle2.position.y;
var distanceSquared : Number = (x * x) + (y * y);
if (distanceSquared < totalRadius * totalRadius)
{
var distance : Number = Math.sqrt(distanceSquared);
var separation : Number = totalRadius - distance;
var unitVectorX : Number = (_circle1.position.x - _circle2.position.x) / distance;
var unitVectorY : Number = (_circle1.position.y - _circle2.position.y) / distance;
_circle1.position.x += unitVectorX * (separation / 2);
_circle1.position.y += unitVectorY * (separation / 2);
_circle2.position.x -= unitVectorX * (separation / 2);
_circle2.position.y -= unitVectorY * (separation / 2);
}
It works great if the circles have the same velocity. The problem occurs when they have different velocities and the problem is because I'm splitting the separation evenly (separation / 2) I think!
Everything works perfectly if circle1 has a velocity of 1,0 and circle2 has a velocity of -1,0. The two circles hit each other and stop.
If circle1 has a velocity of 2,0 and circle2 has a velocity of -1,0, the circles gradually move to the right. I imagine this is what's happening:
frame1:
circle1 (99, 100)
circle2 (101, 100)
frame2:
circle1 (101, 100)
circle2 (100, 100)
collision detected, corrected position of -0.5 and +0.5 respectively.
circle1 (100.5, 100)
circle2 (100.5, 100)
frame3:
circle1 (102.5, 100)
circle2 (99.5, 100)
collision detected, corrected position of -1.5 and +1.5 respectively.
circle1 (101, 100)
circle2 (101, 100)
frame4:
circle1 (103, 100)
circle2 (100, 100)
collision detected, corrected position of -1.5 and +1.5 respectively.
circle1 (101.5, 100)
circle2 (101.5, 100)
As you can see, both circles are gaining +0.5 to the right because of the difference of velocity.
So finally, my question: How can I factor in their velocity into the equation so that it doesn't play a factor in their separation?
Thanks!
You need to calculate the point of impact, rather than just (arbitrarily) moving them both backwards equally.
A quick search found these links:
http://www.t3hprogrammer.com/research/circle-circle-collision-tutorial#TOC-Static-Circle-Circle-Collision-Dete (the section "Dynamic Circle-Circle Collision")
http://nonlinear.openspark.com/tutorials/vectors/index.htm (section "Impact, not intersection")
to make the answer short: you will have to get momentum in there ;)
As I guess you want to have the masses the same it comes down to "v1_before^2 + v2_before^2 = v1_after^2 + v2_after^2". As the wiki-article suggests I just "switch" the velocities.
What I do not understand is why you think the circles will both move to the right? Isn't this suppost to be a elastic collision? Then they should go in different directions if you want them to have same mass.
Heads up: Even though this problem arose while I was working with Unity, it has nothing specific to Unity, and is more about programming logic, so please don't shy away.
I'm using Unity and rotating an object by script. The thing is, if I rotate it to, say, 180 degrees, the object does not rotate exactly to that much and tends to stop at between 179 and 181 degrees. So, to check if rotation is complete I check if the rotation angle is targetAngle +/- 1, which works.
I check using
if (transform.eulerAngles.z > lowerLimit && transform.eulerAngles.z < upperLimit)
where
lowerLimit = targetAngle-1;
upperLimit = targetAngle + 1;
Now, the problem arises when the targetAngle is 0. In this case, my script checks if rotation angle is between -1 and 1. But, -1 should really be 359, so it needs to check if the angle lies between 359 and 1.
How can I implement this?
In other words, I guess I'm asking how to implement a wrap-around number system.
EDIT
Found one work-around. If targetAngle is 0, I treat is specially. It works, but isn't the most elegant.
if (targetAngle == 0.0)
{
if ((transform.eulerAngles.z > 359.0 && transform.eulerAngles.z <= 360.0) || (transform.eulerAngles.z >= 0.0 && transform.eulerAngles.z <= 1))
{
rotate = false;
}
}
else
{
if (transform.eulerAngles.z > targetAngle - 1 && transform.eulerAngles.z < targetAngle + 1)
{
rotate = false;
}
}
You could do ...
lowerLimit = (targetAngle % 360) + 359; //360 - 1;
upperLimit = (targetAngle % 360) + 361; //360 + 1;
if (((transform.eulerAngles.z + 360) % 360) > lowerLimit
&& ((transform.eulerAngles.z + 360) % 360) < upperLimit)
This moves the check away from the zero and you wouldn't have to deal with positive/negative checking.
EDIT
The % operator on the targetAngle restricts the rotating to +/-359 degrees, so a target angle of 721 would come down to 1, and a target angle of -359 would come down to 1. This should do nicely for all cases I think.
EDIT 2
To fix the last case you mentioned in your comment, I guess you'd need to apply the same wrapping logic to your transform.eulerAngles.z values. Probably best to put this wrapping in an extra function now, so try this:
int wrapNumber(int input) // replace int with whatever your type is
{
// this will always return an angle between 0 and 360:
// the inner % 360 restricts everything to +/- 360
// +360 moves negative values to the positive range, and positive ones to > 360
// the final % 360 caps everything to 0...360
return ((input % 360) + 360) % 360;
}
lowerLimit = wrapNumber(targetAngle) + 359; //360 - 1;
upperLimit = wrapNumber(targetAngle) + 361; //360 + 1;
if (wrapNumber(transform.eulerAngles.z) + 360 > lowerLimit
&& wrapNumber(transform.eulerAngles.z) + 360 < upperLimit)
Depending on how often you need to use this, checking for some cases might remove unneeded overhead. For example, the final % 360 within wrapNumber is only needed if the input was positive. If you're calling this ten times per minute it probably won't matter. If you're calling it a hundred times per second, you may want to check how it performs in this situation.
This may be an old thread but after looking at many different snippets all trying to deal with Wrapping I found that Unity has a nice builtin function that simply takes care of business, At least in my case that the end result i required was a lerp so i only had to change it to LerpAngle and it returned a solid result.
Mathf.LerpAngle is your friend.. solved all my issues with popping etc..
http://docs.unity3d.com/ScriptReference/Mathf.LerpAngle.html