Projectile trajectory based on angle - c#

In my 3D game (top down shooter), I try to do this:
When you click the mouse button (raycast on point) on a specific area on the map -> a shot was fired from the gun -> the projectile flies in the direction of the barrel with a certain path and at the same distance as the mouse pointer (but not the direction).
I already have the source code for calculating the projectile trajectory.
Help make the projectile fly in the direction of the barrel, BUT with the distance shown by the mouseposition raycast, NOT the mouseposition direction.
```private void FireCannonAtPoint(Vector3 point)
{
var velocity = BallisticVelocity(point, _angle);
GameObject projectile = Instantiate(_projectile, _shootPoint.transform.position, Quaternion.identity) as GameObject;
projectile.GetComponent<Rigidbody>().velocity = velocity;
}
private Vector3 BallisticVelocity(Vector3 destination, float angle)
{
Vector3 dir = destination - _shootPoint.transform.position;
float height = dir.y;
dir.y = 0;
float dist = dir.magnitude;
float a = angle * Mathf.Deg2Rad;
dir.y = dist * Mathf.Tan(a);
dist += height / Mathf.Tan(a);
float velocity = Mathf.Sqrt(dist * Physics.gravity.magnitude / Mathf.Sin(2 * a));
return velocity * dir.normalized;
}```

Barrel: segment [turret_end_1, turret_end2] in 3D, where turret_end_1 the point on the turret where the bullet leaves it.
Screen: the focal point F and the plane of the screen, named screen_plane.
# From 3D to 2D: projection from 3D space on the screen_plane:
Mouse = [mouse_x, mouse_y] # given on the screen_plane
B1 = project_on_screen_plane(turret_end_1)
B2 = project_on_screen_plane(turret_end_2)
# Calculations in 2D, i.e. on the screen_plane
U = B1-B2
U = ( dot_product(Mouse-B1, U) / dot_product(U, U) ) * U
U = B1 + U # this is the point you are looking for on the screen
# From 2D back to 3D: connect the focal point with the point U on the screen plane
# to form a line and find its intersection point in 3D with the line B1 B2
# aligned with the turret.
# line through F with directing vector Vector_from_F_to(U)
# line through B1 with directing vector B1-B2
V = intersect( F, Vector_from_F_to(U), B1, B1-B2 ) # the point you are looking for in 3D

Related

A point on a perpendicular line on a specific side

I have a point P1 and P2 (this gives me a Vector that has a direction and a turn)
How to determine P1L and P1R?
L is always on the left side and R is always on the right side (no matter how the line is marked)
In the code below, I add and subtract values, but that doesn't tell me when it's going to be right and left.
I would like to point specifically to the point to the left and right as I stand in P1 and look towards P2
Vector2 vNormalized = (endP - startP).normalized;
Vector2 vPerpendicular = new Vector2(vNormalized.y, -vNormalized.x).normalized;
var P1 = startP + vPerpendicular * Thickness / 2;
var P2 = startP - vPerpendicular * Thickness / 2;
var P3 = endP - vPerpendicular * Thickness / 2;
var P4 = endP + vPerpendicular * Thickness / 2;
You can think in 3d and it will be easier:
You have your P1-P2 vector in 3d:
Vector3 v = new Vector3( vNormalized.x, vNormalized.y, 0.0f );
and the normal vector:
Vector3 normal = new Vector3( 0.0f, 0.0f, 1.0f );
Then by using Cross product you can calculate the left and right vectors:
Vector3 perpendicularLeft = Vector3.Cross( v, normal ).normalized;
Vector3 perpendicularRight = Vector3.Cross( normal, v ).normalized;
And then you can calculate your points as:
Vector3 PLeft = startP + Thickness * perpendicularLeft;
Vector3 PRight = startP + Thickness * perpendicularRight;
Where:
Vector2 left = new Vector2( PLeft.x, PLeft.y );
Vector2 right = new Vector2( PRight.x, PRight.y );
You are almost there. P1 and P2 will always be clockwise/counterclockwise compared to the direction of the line.
If you want P1L and P1R to be on the Left/right side relative to the viewer you can simply compare the X-coordinate and switch the order of them. or you can switch the sign of the line direction:
if(vPerpendicular.X < 0){
vPerpendicular = -vPerpendicular;
}
That should ensure that P1 and P2 has a consistent left/right order. But you might need to change the check to vPerpendicular.X > 0 depending on the desired order. It might depend on the coordinate system you are using.
Also, there should be no need to normalize twice. Once should be sufficient.
Find the rotation angle from P1 to P2:
Vector2 diff = P2 - P1;
float angle = Mathf.Atan2(diff.y, diff.x);
Add 90 degrees to the angle to get the angle to P1L. Note that Mathf.Atan2 will return angles in radians:
float P1L_angle = angle + 0.5*Mathf.PI;
Now pick some length p1l_length and use sine and cosine to get the x/y values:
float P1L_length = 0.5f;
Vector2 P1L = P1L_length*(new Vector2(Mathf.Cos(P1L_angle), Mathf.Sin(P1L_angle)));
Without offsetting by P1, the P1R is just the opposite of P1L
Vector2 P1R = -P1L;
And then you add P1 to both to get your final answer:
P1L += P1;
P1R += P1;

Given world position and normalized world space compute 2D canvas location

I am trying to create a F1 2021 Radar like shown in the repository here: https://github.com/ryry6/f1-radar-releases
Or in the image here
I have computed the delta X and Z coordinates to each of the cars, however I am struggling to get the cars to appear as intended on a 2D Canvas (C#, WPF).
The following is the data provided by the game:
public float m_worldPositionX; // World space X position
public float m_worldPositionY; // World space Y position
public float m_worldPositionZ; // World space Z position
public float m_worldVelocityX; // Velocity in world space X
public float m_worldVelocityY; // Velocity in world space Y
public float m_worldVelocityZ; // Velocity in world space Z
public short m_worldForwardDirX; // World space forward X direction (normalised)
public short m_worldForwardDirY; // World space forward Y direction (normalised)
public short m_worldForwardDirZ; // World space forward Z direction (normalised)
public short m_worldRightDirX; // World space right X direction (normalised)
public short m_worldRightDirY; // World space right Y direction (normalised)
public short m_worldRightDirZ; // World space right Z direction (normalised)
public float m_gForceLateral; // Lateral G-Force component
public float m_gForceLongitudinal; // Longitudinal G-Force component
public float m_gForceVertical; // Vertical G-Force component
public float m_yaw; // Yaw angle in radians
public float m_pitch; // Pitch angle in radians
public float m_roll; // Roll angle in radians
The following is the code I am unsure about...
var deltaX = PlayerCar.m_worldPositionX - OtherCar.m_worldPositionX;
var deltaZ = PlayerCar.m_worldPositionZ - OtherCar.m_worldPositionZ;
var deltaYawRad = PlayerCar.m_yaw - OtherCar.m_yaw;
// Convert from RAD to DEG
double deltaYaw = (180 / Math.PI) * deltaYawRad;
// This is the part I need help with
var moveUp = deltaZ * worldRightDirZ + deltaX * worldForwardDirX;
var moveLeft = deltaX * worldRightDirX + deltaZ * worldForwardDirZ;
// Set the rectangle to a position on the canvas and scale it accordingly
Canvas.SetTop(rec, RadarModel.PlayerCarTop + moveUp * RadarModel.Scale);
Canvas.SetLeft(rec, RadarModel.PlayerCarLeft + moveLeft * RadarModel.Scale);

How to randomly find one of the vectors from point P tangent to a sphere

I would like to randomly select a vector originating at point P, such that the line formed alongside this vector is tangent to the surface of some sphere. To do this, I need a collection of all points S forming a circle on the sphere, such that the line SP is tangent to the sphere at point S. Then, given this information, I can select one point S', and create a direction vector from point P to point S'.
I would like to do this in Unity. I don't have much preference over how the vector is created itself, as long as it can be randomised and points to a point on the abovementioned circle. I believe an ideal solution would consider an angle θ ∈ [0, 2π] which can be randomised to give a vector from the origin of the circle (not the sphere) to the associated S' point.
I would appreciate a solution in C#, but I am happy with other languages too. Please do note that while the mathematical solutions are appreciated, I am specifically looking for implementation details, as I am not very fluent with Unity engine, their coordinate system and vector operations yet.
Visualisation below:
The solution in Unity is as follows:
Compute the circle's center point
Compute the circle's radius
Create a projection using a randomly selected point in 3D coordinates and point P
Use the center point and the radius to position the point, which results in finding S', such that PS' is tangent to the sphere
using UnityEngine;
public class Script : MonoBehaviour
{
private readonly float _accuracy = 0.1f;
private readonly int _vectorsCount = 1000;
private readonly Vector3 _point = new Vector3(600, 600, 600);
private readonly float _sphereRadius = 500f;
private void Start()
{
// This value will be used to calculate both the circle coordinates and its radius.
var quadraticSum = Mathf.Pow(_point.x, 2) + Mathf.Pow(_point.y, 2) + Mathf.Pow(_point.z, 2);
// Find out coordinates of the circle created by the intersection of the plane with the sphere
Vector3 circleCenter;
circleCenter.x = _point.x * Mathf.Pow(_sphereRadius, 2) / quadraticSum;
circleCenter.y = _point.y * Mathf.Pow(_sphereRadius, 2) / quadraticSum;
circleCenter.z = _point.z * Mathf.Pow(_sphereRadius, 2) / quadraticSum;
// Find out radius of the above circle
var circleRadius = _sphereRadius * Mathf.Sqrt(quadraticSum - Mathf.Pow(_sphereRadius, 2)) /
Mathf.Sqrt(quadraticSum);
/*
* At this point, we can start drawing - let's draw:
*
* - the point using red colour
* - the sphere using blue colour
* - the circle using green colour
*
* Below assumes center of the sphere is at (0, 0, 0)
*/
Debug.DrawLine(Vector3.zero, _point, Color.red, 1000);
DrawSphere();
DrawCircle(circleCenter, circleRadius);
}
private void DrawSphere()
{
for (var theta = -Mathf.PI; theta < Mathf.PI; theta += _accuracy)
{
for (var phi = -Mathf.PI; phi < Mathf.PI; phi += _accuracy)
{
var ray = new Vector3(
_sphereRadius * Mathf.Sin(theta) * Mathf.Cos(phi),
_sphereRadius * Mathf.Sin(theta) * Mathf.Sin(phi),
_sphereRadius * Mathf.Cos(theta)
);
Debug.DrawLine(Vector3.zero, ray, Color.blue, 1000);
}
}
}
private void DrawCircle(Vector3 center, float radius)
{
for (int i = 0; i < _vectorsCount; i++)
{
// Since I wanted random vectors, I am repeatedly drawing a random vector on the circle
var tangentPoint = Vector3.ProjectOnPlane(Random.insideUnitSphere, _point).normalized * radius + center;
Debug.DrawLine(Vector3.zero, tangentPoint, Color.green, 1000);
//Debug.DrawLine(_point, tangentPoint, Color.cyan, 1000);
}
}
}
See the screenshots for visualisation:
Here is an angle with the red line drawn after the sphere and the circle are drawn, to see the center of the sphere:

How to find a position, knowing a direction, angle and distance?

Using the diagram provided, I know:
Character at position A, facing B (posA)
Object at position B (posB), facing a random direction.
desired angle (20) for C (desiredAngle), based in the direction A to B.
distance from B to C
How would I go about finding the position of C?
In the code below, I managed to get the vector BC, now how can define a point along that vector using the distance from A to B?
//The angle of the hand in relation to the object
public float angleFromPlayer = -60f;
//Get the direction vector from the selectedObject to the actor
Vector3 objectDirectionToActor = actorParent.position - selectedObjectCenter;
//Make it horizontal (Flatten the y)
objectDirectionToActor.y = 0;
//Get the rotated vector using the desiredAngle
Vector3 rotatedVector = Quaternion.Euler(0, angleFromPlayer, 0) * objectDirectionToActor;
Figured it out, here is the solution for posterity:
//How high the hand will be from the dropSurface/selectedObjectPivot
public float handOffsetFromDropSurface = 0.15f;
//The angle of the hand in relation to the object
public float targetAngleFromPlayer = -80f;
//Extra distance between selectedObjectCenter and wristBone (since the handDistance is a bit too short)
public float extraHandDistance = 0f;
Vector3 GetHandBonePositionForDropSurface(Vector3 selectedObjectCenter) {
//Get the direction vector from the selectedObject to the actor
Vector3 objectDirectionToActor = actorParent.position - selectedObjectCenter;
//Make it horizontal (Flatten the y)
objectDirectionToActor.y = 0;
//Change vector size to 1 for multiplication
objectDirectionToActor.Normalize();
//Get the rotated vector using the desiredAngle
Vector3 rotatedVector = Quaternion.Euler(0, targetAngleFromPlayer, 0) * objectDirectionToActor;
//Get the distance we want the point to be from the center of the selectedObject
float handDistance =
Vector3.Distance(playerProperties.boneRightIndexProximal.position,
playerProperties.boneRightHand.position) + extraHandDistance;
//Resize vector to match the hand distance
rotatedVector *= handDistance;
//Get finalPosition, adding the handOffsetFromDropSurface
Vector3 wristTargetPosition = selectedObjectCenter + rotatedVector + (Vector3.up * handOffsetFromDropSurface);
return wristTargetPosition;
}

Rotate item: 90 degree rotations

I found this script that lets you click on an item then drag to rotate it, but it rotates based on where your finger is, what can I do to rotate the object 90 degrees like you would in Tetris?
public class RotateDrag : MonoBehaviour {
void OnMouseDrag(){
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
pos = Input.mousePosition - pos;
float ang = Mathf.Atan2(pos.y, pos.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(ang, Vector3.forward);
}
}
Example
In photoshop when you want to rotate an item in a layer you can hold Shift and it will snap to 45° angles as you rotate.
I however would like 90° angle snapping.
What you need to do is just take the direction from the point selected, as the sign on the angle and apply that to a 90 degree rotation thus:
float ang = 90.0 * Math.Sign(Mathf.Atan2(pos.y, pos.x) * Mathf.Rad2Deg);
transform.rotation = Quaternion.AngleAxis(ang, Vector3.forward);

Categories