I am trying to make a gun in Unity that shoots a Raycast with a Line. (in 2d) I am using the player position as the origin and the mouse position as the direction. This works perfectly when there is a collision detection, but when there is not a collision, I have to find the direction and calculate the end point manually, because I still want the line to render if the player misses their shot. Here is my code to find this end point:
//finding the position of Mouse in worldspace
Vector3 mousePos = CameraController.mouseToWorld(Input.mousePosition);
mousePos.z = 0;
//Calculating whether or not the raycast is a hit
RaycastHit2D Ray = Physics2D.Raycast(Player.transform.position,mousePos,distance);
if(Ray.collider != null)
{
PassTheHit(Ray, Ray.collider.gameObject.name);
point = Ray.point;
}
else
{
//Calculating the farthest point on the line. this does not work.
point = (transform.position - mousePos).normalized;
point *= -1 * (distance / 2);
point.z = 0f;
}
Particularly note the else{} statement. The calculation to find the point does not work. It is waaaay too long and does not shoot in the right direction.
Any help is greatly appreciated.
Either way you are using your Physics2D.Raycast wrong. It expects a
Start position
and a direction
you are passing in a second position.
What you rather want to do is using the direction
Vector2 mousePos = CameraController.mouseToWorld(Input.mousePosition);
Vector2 direction = ((Vector2)(mousePos - transform.position)).normalized;
which then you can use for both things
RaycastHit2D Ray = Physics2D.Raycast(Player.transform.position, /*here*/ direction, distance);
if(Ray.collider != null)
{
PassTheHit(Ray, Ray.collider.gameObject.name);
point = Ray.point;
}
else
{
point = transform.position + /*and here*/ direction * distance;
}
Sidenote: Avoid to give a RaycastHit2D the name Ray as there is a type that is called Ray so it is misleading ;)
Related
OBJECTIVE
I am using this script I got from Unity's forums to move my camera around. It works perfectly and this is exactly the style of camera I want. Here it is in action:
https://streamable.com/j7ozps
I wanted to create a zoom effect when the user holds the right mouse button.
PROBLEM
After zooming in, the aim is off by a lot. Here's what happens:
https://streamable.com/8aiil0
MY ATTEMPT
void Update () {
if (Input.GetMouseButton(1))
{
int layerMask = 1 << CombatManager.GROUND_LAYER; // -> public const int GROUND_LAYER = 8;
Ray mouseClick = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit[] hitInfos;
float maxDistance = 100.0f;
hitInfos = Physics.RaycastAll(mouseClick, maxDistance,
layerMask, QueryTriggerInteraction.Ignore);
mainCamera.fieldOfView = 10;
mainCamera.transform.LookAt(hitInfos[0].point);
}
else
mainCamera.fieldOfView = startingFieldOfView;
I also tried swapping the order of the LookAt and field of view assignment.
What am I doing wrong?
Seems that what you want is to zoom towards the cursor position, not towards where the ray hits which are likely not the same point. To check that out you can draw a debug cube where the ray hits, to check if the point you are looking at is the one you need, like so:
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.transform.position = hitInfos[0].point;
Check Camera.ScreenToWorldPoint that gives you the worldspace point created by converting the screen space point at the provided distance z from the camera plane.
What I would try:
Vector3 point = Camera.main.ScreenToWorldPoint(Input.mousePosition);
//check if this is the point you want to look at
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.transform.position = point;
//look at it
mainCamera.transform.LookAt(point);
It should not mind if you are using the cam.nearClipPlane or the cam.farClipPlane in the ScreenToWorldPoint function, but I would play with that in case something is not working as intended.
Vector3 screenPoint = Input.mousePosition;
screenPoint.z = Camera.main.farClipPlane;
Vector3 worldPoint = Camera.main.ScreenToWorldPoint(screenPoint);
What I want to do is to find a way to know if there is something on the left/right of the player but for some reason, my code does not work.
Vector3 rayDirection = new Vector3();
switch (currentCameraSide)
{
case CameraSide.Left:
rayDirection = -transform.right;
break;
case CameraSide.Right:
rayDirection = transform.right;
break;
}
// Start the ray pos from the player's head
Vector3 playerStartPoint = transform.position + (transform.up * 1.5f);
// The direction of the ray (left/right)
Vector3 playerEndPoint = playerStartPoint + rayDirection;
if (Physics.Raycast(playerStartPoint, playerEndPoint, out RaycastHit raycastHitA, sideCameraRayLenght, aimColliderMask))
{
Debug.Log($"Hitting: {raycastHitA.transform.name}");
}
Basically what is happening is no matter what direction the player is facing, the direction of the hit is always left (possible world coords?)
Raycast in the form you use it expects
a start position
a direction
you are giving it two positions ...
-> simply use the rayDirection itself as second parameter
I am trying to learn how to Raycast in Unity2D.
Here is my code:
Ray ray;
void Update()
{
ray = new Ray(transform.position, Input.mousePosition);
Debug.DrawRay(transform.position,Input.mousePosition);
}
This results in strange behavior.
transform.position is the Vector3 of the player object.
I want to shoot a ray from the Player Object through the Mouse Position.
However, this does not seem to work.
The line is drawn, but behaves weirdly. It does not point toward it at all. It's hard to accurately describe its behavior as anything except erratic.
What am I doing wrong?
I think there may be something wrong with putting in Input.mousePosition and directions in general. I would be very thankful for any help you can give.
Note: I am using Unity 2D.
When you use Input.mousePosition you get a Vector2 which represents the position of the mouse relative to the camera. This means the X and Y you get are actually the X and Y of the position of the mouse on the screen.
These coordinates are not world coordinates.
For example if you were to draw a ray from transform.position of a player standing at 0,0,0 to the mouse position of 500, 250 you would get a gigantic and seemingly randomly pointing ray.
To convert from where the mouse is on the screen(Screen Coordinates) to what it's pointing to in-game(World Coordinates) you can use Camera.ScreenToWorldPoint.
For example
Ray ray;
void Update()
{
// get the mouse screen pos
Vector3 mousePosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y);
// convert it
Vector3 worldMousePosition = Camera.Main.ScreenToWorldPoint(mousePosition);
ray = new Ray(transform.position, );
Debug.DrawRay(transform.position, worldMousePosition);
}
I'm trying to add dashing to the player in a Diablo-esque game I'm working on, and it "works" where you can dash towards your cursor.
if (isDashing)
{
Ray ray = cam.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100, movementMask))
{
motor.MoveToPoint(hit.point);
motor.speed = 150;
}
}
I get the mouse position, then move to that position with enormous velocity. MoveToPoint() function uses NavMeshAgent's SetDestination(point) function and this again "works" but the problem is naturally there isn't a limit to how far you can dash. You can have the mouse way over at the end of the screen and the character will still dash there.
I figured I should be using a Vector3 to define the limits of the dash, but I can't quite nail it down. I get the direction below and then apply the magnitude, but it does not work how I imagined it would.
if (Physics.Raycast(ray, out hit, 100, movementMask))
{
float mag = (hit.point - transform.position).magnitude;
if (mag < dashLength)
{
Vector3 dir = (hit.point - transform.position).normalized;
Vector3 newVector = dir * (dashLength - mag);
motor.MoveToPoint(newVector);
}
else
motor.MoveToPoint(hit.point);
motor.agent.speed = 150;
}
Any help would be appreciated
The main issue here is that you are treating a movement vector like a position. You want to start a the current transform.position and ADD the movement vector you calculated.
Further instead of extracting and checking the magnitude you can simply use Vector3.ClampMagnitude to keep the direction but make sure that the magnitude is not greater than your dashLength like e.g.
if (Physics.Raycast(ray, out hit, 100, movementMask))
{
// Calculate the difference only once
var movement = hit.point - transform.position;
// Make sure that delta has a maximum magnitude of dashLength
movement = Vector3.ClampMagnitude(delta, dashLength);
// MoveToPoint expects a position not only the movement vector
// so you start at your current position and add the movement
var newPosition = transform.position + movement;
motor.MoveToPoint(newPosition);
motor.agent.speed = 150;
}
im trying to move between a path made from different objects. I apply a constant speed with Translate() and Rotating with the perpendicular vector from the object on the right using Raycasting
Although it turns , is it does not rotate fast enough to fully turn and moves out of the path.
Any ideas how to fix this? Or some other way to implement it?
any help will be appreciated
image to help visualize:
Raycast and Rotation image
void Update()
{
RaycastHit hit;
if (!Physics.Raycast(transform.position, Vector3.right, out hit))
return;
MeshCollider meshCollider = hit.collider as MeshCollider;
if (meshCollider == null || meshCollider.sharedMesh == null)
return;
Mesh mesh = meshCollider.sharedMesh;
Vector3[] normals = mesh.normals;
int[] triangles = mesh.triangles;
Vector3 n0 = normals[triangles[hit.triangleIndex * 3 + 0]];
Vector3 n1 = normals[triangles[hit.triangleIndex * 3 + 1]];
Vector3 n2 = normals[triangles[hit.triangleIndex * 3 + 2]];
Vector3 baryCenter = hit.barycentricCoordinate;
Vector3 interpolatedNormal = n0 * baryCenter.x + n1 * baryCenter.y + n2 * baryCenter.z;
interpolatedNormal = interpolatedNormal.normalized;
Transform hitTransform = hit.collider.transform;
interpolatedNormal = hitTransform.TransformDirection(interpolatedNormal);
Vector3 targetDir = Vector3.Cross(interpolatedNormal, Vector3.up); // Get the perpendicular vector
Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, 20f, 0f);
transform.rotation = Quaternion.LookRotation(newDir); // Rotate Object
transform.Translate(0,0,0.2f); // Constant Speed
Debug.DrawRay(transform.position, perp,Color.red);
}
I don't think this is a good method, but it works for me. Maybe this can help you.
public float fixedDist = 2.0f;
void WallDetect() {
RaycastHit hit;
if (!Physics.Raycast(transform.position, transform.TransformPoint(Vector3.right) - transform.position, out hit))
return;
Vector3 perp = Vector3.Cross(hit.normal, Vector3.up);
Vector3 targetDir = Vector3.Project(transform.forward, perp).normalized;
Vector3 currentDir = transform.TransformPoint (Vector3.forward) - transform.position;
RaycastHit hit2;
if (Physics.Raycast (transform.position, -hit.normal, out hit2)) {
Vector3 fixedPos = hit2.point + hit.normal * fixedDist;
Vector3 predictPos = fixedPos + targetDir;
transform.position = Vector3.MoveTowards (transform.position, predictPos, 0.01f);
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation (predictPos - transform.position), 0.05f);
}
}
First thing: you seem not to be using data from HitInfo struct - and believe it or not it already contains a .normal Vector3 member, calculated during the raycast (or lazily calculated when requested, I am not sure but it makes no difference), it's best to use it rather than roll your own, simpler and less error prone (your manual finding of the normal looks correct neverless, I haven't tried it though)
Second thing: your last line has a Quaternion.Lerp with t=0.05 which means for each new rotation, you are still taking 95% of the original rotation, which is pretty darn slow rotation indeed. Try something in the range of Time.deltaTime (which is rougly an equivalent of getting close within a second)
Third thing: for rotation its better to use Slerp rather than Lerp, unless you are realt concerned about performance, which doesn't seem to be an issue considering the rest of the code.
Fourt thing: instead of hardcoding linear and rotation speed, try to use multiplies of Time.deltaTime, this way they won't be framerate dependend (as they currently are)
Fifth thing: I have a feeling you shouldn't be setting your target rotation based on normal at current position. The way you are doing it now your rotation lags behind - you should be raycasting from a position one step in the future from your current position, so you know what rotation to take so it is correct by the time you make that step. Currently you set target a future rotation to a rotation correct now, which will lag a frame. Alternatively you could just move the translate step to the top of the loop, the transform will update and the rest should flow as it does.
Finally, your image link doesn't work.
I hope that helps at all