How to check if my NPC sees the back of a target? - c#

I think the title says it all. Can it be done using only vector math?
var toTarget = (enemy.transform.position - npc.transform.position).normalized;
var seesBack = Vector3.Dot(toTarget, npc.transform.forward) < 0;
It seems I should somehow mix target's forward vector into the equation, but I'm really lame when it comes to vector math (well, math in general ;) ). Anyone?
EDIT:
I've also tried doing this, but the angle is too low. For example if the NPC is on the right of it's target, the calculated angle is ~60 degrees:
var angle = Mathf.Abs(Vector3.Angle(enemy.transform.forward * -1, npc.transform.forward));
var seesBack = angle <= 70;

Close! Consider that for the npc to see the back of the enemy, the enemy has to be looking roughly in the same direction as the vector from the npc to the enemy, aka toTarget:
var toTarget = (enemy.transform.position - npc.transform.position).normalized;
var seesBack = Vector3.Dot(toTarget, enemy.transform.forward) > 0;
Note that the Dot is against the enemy transform, and greater than 0.

Both of these tell you the same thing:
Are the two facing in the same direction?
Neither one tell you anything about which one is in front.
The dot-product one is going to be better, but you need to also check who's behind whom.
The one off-the-top-of-my-head way that I can think of is to compare the distance between the two entities, add a small amount along the NPC's forward vector (like, 0.5 units, something half the size of its collision volume's thickness) and get the distance from that point to the center of the other entity.
If the offset distance is smaller, the NPC is behind (as by moving forward, it would get closer). You'll probably also want a distance check involved somewhere as well, so that "behind" doesn't include "three rooms over." But I assume you've done that already.

Can it be done using only vector math?
This will check if the enemy is both facing away and positioned in-front of the NPC.
var r = Vector3.Dot(transform.forward, enemy.transform.forward) > 0f &&
Vector3.Dot(transform.forward, enemy.transform.position - transform.position) > 0f;

Related

Bouncing my player further the closer I am

I'm recreating the movement system from the DS game Dragon Quest Heroes Rocket Slime in Unity. Currently I've got pretty desirable results but it's not 100%.
One of the things that are different is that my player bounces back from walls further on long distance slings than he does in the original game. (left is my recreation, right is original game). Obviously, I can't find out how the game was made and the exact maths/values they use for slinging but one of the things I got from the game is that:
The player moves at constant velocity while slinging in a direction (no slow down, they just stop at a certain point)
Which led me to believe the game doesn't use traditional velocity mechanics so I made it so:
While stretching, the players 'pVelocity' (potentialVelocity) goes up. When they let go a point create at curPlayerPosition + pVelocity is created. I then move the player at a constant velocity to that point. My idea to fix my issue of bouncing too far is by getting the distance from the position where I slung from to the end point and making the player bounce less the larger the distance and further the smaller the distance.
Problem is, I'm not really sure how to do that last part.
Here's my code:
Vector2 reflectedVelocity = Vector2.Reflect(pVelNormalized, foundHit.normal);
playerAnimator.SetFloat("VelocityX", reflectedVelocity.x);
playerAnimator.SetFloat("VelocityY", reflectedVelocity.y);
float DistanceFromOrigin = Vector3.Distance(slingOrigin, transform.position);
print(DistanceFromOrigin);
//ToDo: reflectedVelocity is lower the higher DistanceFromOrigin is.
The other solution I thought of is that maybe my idea of how the 'velocity' works in the original game is completely wrong and if so, please tell me how it actually is/looks to be actually done
reflectedVelocity is lower the higher DistanceFromOrigin is.
So reflectedVelocity should have an inverse relationship with DistanceFromOrigin
Vector2 reflectedVelocity = Vector2.Reflect(pVelNormalized, foundHit.normal);
float DistanceFromOrigin = Vector3.Distance(slingOrigin, transform.position);
if(DistanceFromOrigin == 0) DistanceFromOrigin = 0.00001f;//to avoid division by zero
reflectedVelocity *= (SomeConstant / DistanceFromOrigin);
playerAnimator.SetFloat("VelocityX", reflectedVelocity.x);
playerAnimator.SetFloat("VelocityY", reflectedVelocity.y);
You may use a linear relationship instead of non-linear:
reflectedVelocity *= (SomeConstant - DistanceFromOrigin);

How to move an object back based on its rotation and bounds in Unity?

Alright, so I have an object and it also has a bounds. I also have an anchorPoint, if you will, which is where the object is positioned and I have a boundary Vector3 position that I don't want the object to pass.
EDIT:
I will clarify my example in the following scenario:
Alright, lets take a rectangular prism since it is a perfect example. Obviously, when you are looking at it long ways it won't be as close to you if you rotate it 90 degrees on the y to where you are looking at the end of the object. Even though the object didn't change positions and is essentially in the same position, just rotated. I want to check if during rotation did the object pass a certain point called boundary. If it is past the point then I want to move the object forward in its z-direction so that the end of the object intersects the boundary position.
Assume that the object's initial position stays the same in the image below.
END EDIT
The problem comes into play when I want to rotate the object. How do I detect if part of the object has moved in front of this boundary position; that is not to say that the center of the object has passed this position but that part of the object has. I figured the best way to do this would be using bounds but, I'm not quite sure how to incorporate the rotation in with the bounds.
I haven't really had any luck with anything I have written so far. Any help on the matter would be greatly appreciated.
Assuming the simplest case, where you have a rectangular, axis aligned bounding box, then you have 8 points that make up your bounding box (the 8 corners).
The first check you want to make is to see if your anchor point is on the "wrong" side of your boundary plane. This is a simple vector dot product check.
Vector3 anchorDir = anchorPoint - boundaryPoint;
bool wrongSide = Vector3.Dot(anchorDir, boundaryDir) <= 0;
As long as your anchor point is on the right side of the boundary, you can move on to check each of the 8 bounding box points in the exact same way. However, you will need to make sure that your bounding points and your boundaryPoint are in the same coordinate system (world vs local) (this actually applies to your anchorPoint too). You could translate all the points to World space using Transform.TransformPoint (https://docs.unity3d.com/ScriptReference/Transform.TransformPoint.html) but it would be more efficient to translate the boundaryPoint to your bounding box's local coordinate system using InverseTransformPoint (https://docs.unity3d.com/ScriptReference/Transform.InverseTransformPoint.html).
Transform boundingBox = ...
Vector3 localBoundaryPoint = boundingBox.InverseTransformPoint(worldBoundaryPoint);
Vector3 localBoundaryDir = boundingBox.InverseTransformDirection(boundaryDirection);
foreach(Vector3 pt in boundingBoxPoints)
{
Vector3 pointDir = pt- localBoundaryPoint;
bool ptWrongSide = Vector3.Dot(pointDir , localBoundaryDir ) <= 0;
//How you handle that is up to you (return false, set a local flag and break, whatever)
}
EDIT:
If you want to know how far you are past the boundary so that you can shift your box back, you will need to handle that a little differently. You will have to check all the points and not just return when you find the first violator because you can't be sure which one is furthest.
float maxDistance = 0;
foreach(Vector3 pt in boundingBoxPoints)
{
Vector3 pointDir = pt- localBoundaryPoint;
//note the negative sign
float distance = -Vector3.Dot(pointDir, localBoundaryDir );
if (distance > maxDistance) maxDistance = distance;
}
if (maxDistance> 0)
{
//Shift your object by maxDistance amount along the localBoundaryDir;
}
Note that for your Dot Product to return the correct distance, localBoundaryDir needs to be a unit vector.
EDIT 2
Easier to describe the Boundary Point issue here than in the comments.
Your question doesn't explain how you define a Boundary Point, but let's assume that it's just a GameObject. You can implement a script on it but technically you don't even need that.
GameObject boundaryObject = ...
Vector3 boundaryPoint = boundaryObject.transform.position;
Vector3 boundaryDir = boundaryObject.transform.forward;
transform.forward always points along the z-axis, the blue arrow. So if, in the Unity editor you are constructing your scene, the blue arrow points along your plane's normal. Your "boundary line" extends to the right and left (x axis, red arrow) and up and down (y axis, green arrow).

Teach an AI to catch a ball?

I have a game that effectively has two paddles in it, and I've got it working set up so that either the players or an AI can control either paddle. However, I'm trying to figure out how to get my AI to actually smoothly run after the ball and catch it, instead of simply brute-forcing it with a basic "Direction of Ball" = Opposite direction AI moves.
In order to do so, I'm trying to write some code that will allow the AI to predict where it will land, when the ball is in it's court or going to be. It's not going that well. This is a diagram of what I want to achieve:
So, I've been studying Unity3D and came up with this:
Vector3 BallMinder = BallPosition.gameObject.rigidbody.velocity;
float BallX = BallMinder.x;
float BallY = BallMinder.y * Time.deltaTime;
float GroundHitPointX = (BallY + BallX);
Vector3 BallImpactPoint = new Vector3 (GroundHitPointX, 0, 0);
Debug.Log (BallImpactPoint);
Debug.DrawRay (BallImpactPoint, Vector3.up, Color.green);
However, I feel like I've oversimplified it or forgotten something. My calculations are way off, and the ray isn't showing up where it should, if it does at all. What have I gotten wrong?
All you need is the Kinematics equations.
These equations perfectly describe a projectile in motion. Therefore, you can use them to predict where and with what components the ball will land.
Here is a graph to help you understand.
This is going to be simplified by assuming that gravity is downward, and the floor is at Y=0. It could be expanded using vector math for arbitrary floorlines and gravity directions, but for your purposes you should be fine using the two aforementioned assumptions. I'm also assuming for this explanation that BallPosition is a Transform object, but the math would be the same if it weren't. I'm also assuming no air friction on the ball, which would make the math a fair bit more complicated.
Essentially, you need to calculate the time until the ball hits the ground and then extrapolate the ball's X-position at that time.
The classic formula dictating the motion of an accelerating object is d = vt + at^2 / 2, where v is the current velocity, a is the acceleration, and t is the amount of time that has passed. To figure out the time of impact, we simply solve for t.
To figure out when the ball will hit the ground you'll want to set d = BallPosition.position.y * -1, a = Physics.gravity.y, and v = BallPosition.rigidbody.velocity.y. This will give us the number of seconds until impact.
Since gravity is assumed to be entirely downward and no other forces are acting on the ball, we can know that the X-position of the ball at impact time is BallPosition.position.x + BallPosition.rigidbody.velocity.x * impactTime. This would be the X-position your computer player should move towards.
...The formula should work any time the gravity is entirely downward, even if the ball is on the upward portion of its trajectory or moving away from your computer player. You'll probably want to come up with some strategy for what the computer should do while waiting for the human to hit the ball and set the ball's new trajectory, since you probably don't want the computer to try to run towards the human's side of the net. Depending on how your players are able to hit the ball, you might be able to predict the new velocity the ball would have after the human hits it, and then feed that data to this formula.
essentialy you apply gravity to the ball at every step to make it seem natural something like this:
ball is at your position ready to be trown
ball.x = 20;
ball.y = 10;
xVelocity = 0;
yVelocity = 0;
gravity = 0.1;
you trow the ball
When you trow the ball you set xVelocity to a constant lets say 1 and yVelocity to 5. So:
xVelocity = 1;
yVelocity = 5;
ball.x+=xVelocity;
ball.y+=yVelocity; //to start the ball moving
Now in the gameloop you calculate the ball position like this:
tick
{
if(ball.y > 10) //if the ball is off the ground (i asume 10 is ground)
{
ball.x+=xVelocity;
ball.y+=yVelocity;
yVelocity-=gravity;
}
}
with this the ball should fly in a perfect arc now to calculate the landing zone you can simply do the same thing again with dummy numbers
while(dummyBall.y > 10)
{
dummyBall.x+=xVelocity;
dummyBall.y+=yVelocity;
yVelocity-=gravity;
}
landingPosX = dummyBall.x;
You will probably need to tweak the numbers but if i remember everything correctly it should work something like this. Hope it make sense

Bouncing a ball off a wall with arbitrary angle?

I'm trying to let the user draw a paddle that they can then use to hit a ball. However, I cannot seem to get the ball to bounce correctly because the x and y components of the ball's velocity are not lined up with the wall. How can I get around this?
I tried the advice given by Gareth Rees here, but apparently I don't know enough about vectors to be able to follow it. For example, I don't know what exactly you store in a vector - I know it's a value with direction, but do you store the 2 points it's between, the slope, the angle?
What I really need is given the angle of the wall and the x and y velocities as the ball hits, to find the new x and y velocities afterwards.
Gareth Rees got the formula correct, but I find the pictures and explanation here a little more clear. That is, the basic formula is:
Vnew = -2*(V dot N)*N + V
where
V = Incoming Velocity Vector
N = The Normal Vector of the wall
Since you're not familiar with vector notation, here's what you need to know for this formula: Vectors are basically just x,y pairs, so V = (v.x, v.y) and N = (n.x, n.y). Planes are best described by the normal to the plane, that is a vector of unit length that is perpendicular to the plane. Then a few formula, b*V = (b*v.x, b*v.y); V dot N = v.x*n.x+v.y*n.y, that is, it's a scalar; and A + B = (a.x+b.x, a.y+b.y). Finally, to find a unit vector based on an arbitrary vector, it's N = M/sqrt(M dot M).
If the surface is curved, use the normal at the point of contact.

WPF: Speed of Movement (Translation) varies with distance

With reference to this programming game I am currently building.
I wrote the below method to move (translate) a canvas to a specific distance and according to its current angle:
private void MoveBot(double pix, MoveDirection dir)
{
if (dir == MoveDirection.Forward)
{
Animator_Body_X.To = Math.Sin(HeadingRadians) * pix;
Animator_Body_Y.To = ((Math.Cos(HeadingRadians) * pix) * -1);
}
else
{
Animator_Body_X.To = ((Math.Sin(HeadingRadians) * pix) * -1);
Animator_Body_Y.To = Math.Cos(HeadingRadians) * pix;
}
Animator_Body_X.To += Translate_Body.X;
Animator_Body_Y.To += Translate_Body.Y;
Animator_Body_X.From = Translate_Body.X;
Translate_Body.BeginAnimation(TranslateTransform.XProperty, Animator_Body_X);
Animator_Body_Y.From = Translate_Body.Y;
Translate_Body.BeginAnimation(TranslateTransform.YProperty, Animator_Body_Y);
TriggerCallback();
}
One of the parameters it accepts is a number of pixels that should be covered when translating.
As regards the above code, Animator_Body_X and Animator_Body_Y are of type DoubleAnimation, which are then applied to the robot's TranslateTransform object: Translate_Body
The problem that I am facing is that the Robot (which is a canvas) is moving at a different speed according to the inputted distance. Thus, the longer the distance, the faster the robot moves! So to put you into perspective, if the inputted distance is 20, the robot moves fairly slow, but if the inputted distance is 800, it literally shoots off the screen.
I need to make this speed constant, irrelevant of the inputted distance.
I think I need to tweak some of the Animator_Body_X and Animator_Body_Y properties in according to the inputted distance, but I don't know what to tweak exactly (I think some Math has to be done as well).
Here is a list of the DoubleAnimation properties that maybe you will want to take a look at to figure this out.
Is there are reason you're using DoubleAnimation? DoubleAnimation is designed to take a value from A to B over a specific time period using linear interpolation acceleration/deceleration at the start/end of that period if required (which is why it's "faster" for longer distance.. it has further to go in the same time!). By the looks of things what you are trying to do is move something a fixed distance each "frame" depending on what direction it is facing? That doesn't seem to fit to me.
You could calculate the length of the animation, depending on the distance, so the length is longer for longer distances, then the item is always moving at the same "speed". To me, it makes more sense to just move the item yourself though. You can calculate a objects velocity based on your angle criteria, then each "frame" you can manually move the item as far as it needs to go based on that velocity. With this method you could also easily apply friction etc. to the velocity if required.
The math you have to do is: velocity*time=distance
So, to keep the speed constant you have to change the animation's duration:
double pixelsPerSecond = 5;
animation.Duration = TimeSpan.FromSeconds(distance/pixelsPerSecond);
BTW, I don't think animations are the best solution for moving your robots.

Categories

Resources