Unity3D - Raycast not hitting correct position - c#

I'm trying to make a gun by shooting a ray from the centre of the camera which the gun is a child of, I also have a particle effect where the impact happens only, the impact happens in the wrong position.
I did Debug.DrawLine and saw that the origin of the ray was happening 0.8 units above the center of the camera, and it sometimes hits directly on the camera
Here is the gun script
using UnityEngine;
public class GunShoot : MonoBehaviour
{
public float gunDamage = 1f;
public float fireRate = .1f; // wait x seconds to fire again
public float weaponRange = 100f;
public float hitForce = 100f;
public Camera playerCam;
//public ParticleSystem muzzleFlash;
public GameObject impactEffect;
private float nextFire; // Holds the time for which the gun is able to fire again
void Update()
{
Shoot();
}
public void Shoot()
{
if (Input.GetButtonDown("Fire1") && Time.time > nextFire)
{
nextFire = Time.time + fireRate;
RaycastHit hit;
if (Physics.Raycast(playerCam.transform.position, playerCam.transform.forward, out hit, weaponRange)) // Center of the camera, directly forward, hat it hits and that infomation, the range of the weapon
{
Target target = hit.transform.GetComponent<Target>();
if (target != null)
{
target.TakeDamage(gunDamage);
}
GameObject impactGameObject = Instantiate(impactEffect, hit.point, Quaternion.LookRotation(hit.normal));
Destroy(impactGameObject, .15f);
}
}
}
}
I have been using a tutorial by Brackleys https://www.youtube.com/watch?v=THnivyG0Mvo but i followed it perfcetly (execpt chaning some variable names) and i cannot see where it is going wrong.
I cant just reduce the high of the initial ray as it sometimes maybe 1/10 times shoots from the right position
As you can see it clearly isn't shooting from the middle where the crosshair is
Any help would be great thanks!

Consider using Camera.ScreenPointToRay.
If you're always shooting from the center of the screen you could use Camera.ViewportPointToRay
Ray ray = Camera.main.ViewportPointToRay(new Vector2(0.5f, 0.5f));
if (Physics.Raycast(ray, out RaycastHit hit, weaponRange)) // handle hit

Related

How can I lock the rotation of a player in Unity?

I'm currently making a 2D game as a beginner and I made a spinning platform. But when it's rotating the player's rotation (z-axis) also changes because it's a child of the platform. I need this when I use moving platforms. Now I want to lock the z-axis of the rotation of the player. I already tried it in 3 different ways, but none of them seems to be working. Does anybody know how to do this?
These are the three ways I tried:
// 1
PlayerTrans.transform.Rotate(
PlayerTrans.transform.rotation.x,
PlayerTrans.transform.rotation.y,
0);
// 2
PlayerTrans.transform.Rotate(
PlayerTrans.transform.rotation.x,
PlayerTrans.transform.rotation.y,
0,
Space.Self);
// 3
PlayerTrans.transform.localRotation = Quaternion.Euler(new Vector3(
PlayerTrans.transform.localEulerAngles.x,
PlayerTrans.transform.localEulerAngles.y,
0f));
and this is, what my code looks like for staying on the moving platforms. I used raycasting for this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Raycasting : MonoBehaviour
{
// Start is called before the first frame update
Transform PlayerTrans;
public float RayCastRange = 3;
void Start()
{
PlayerTrans = transform.parent;
}
// Update is called once per frame
void Update()
{
RaycastHit2D PlattformCheck = Physics2D.Raycast(transform.position, -Vector2.up, RayCastRange);
if (PlattformCheck.collider != null)
{
if (PlattformCheck.collider.gameObject.tag == "Platform")
{
PlayerTrans.transform.SetParent(PlattformCheck.collider.gameObject.transform);
}
else
{
PlayerTrans.transform.SetParent(null);
}
}
else
{
PlayerTrans.transform.SetParent(null);
}
}
}
There are 2 ways that might help you:
Just freeze the rotation from the inspector:
you can use some LookAt function (there is one for 3D but you can look and find ones for 2D) and just look at the camera.
(if you cant find it let me know and I will add it)
You should raycast directly down and then apply velocities to both objects (un-child the player from the platforms). You could do something like this for the player:
public LayerMask mask; //set ‘mask’ to the mask of the
//platform in the Unity Editor.
public Vector3 velocity;
void Update()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, -Vector3.up, out hit, 0.1f, mask))
//0.1f is the distance to the platform to be able to be moved by the platform.
{
velocity = hit.collider.gameObject.GetComponent<Platform>().velocity;
}
float h = Input.GetAxis(“Horizontal”);
//this one is for CharacterController:
cc.Move(velocity);
//this one is for rigidbody:
rb.velocity = velocity;
velocity = 0;
}
It takes the velocity from the ‘Platform’ script and moves the player based on it. Now we should add the platform script. Call it ‘Platform’.
public Vector3 velocity;
public Vector3 a; //a and b are the two points you want the platform to go between.
public Vector3 b;
public float period = 2f; //the amount of seconds per cycle.
float cycles;
void Update()
{
cycles = Time.time / period;
float amplitude = Mathf.Sin(Mathf.PI * 2f * cycles);
Vector3 location = Vector3.Lerp(a, b, amplitude);
velocity = transform.position - location;
transform.position = velocity;
}
This script interpolates between the point a and b, then it finds the velocity and applies it. The player takes this velocity and it moves the player. Comment if you found an error.

Unity camera follows player script

I'm pretty new to Unity. I tried to create a script that the camera would follow the actor (with a little difference). Is there a way to improve the code? It works just fine. But I wonder if I did it the best way. I want to do it about as I wrote, so if you have any tips. Thank you
Maybe change Update to FixedUpdate ?
public GameObject player;
// Start is called before the first frame update
void Start()
{
player = GameObject.Find("Cube"); // The player
}
// Update is called once per frame
void Update()
{
transform.position = new Vector3(player.transform.position.x, player.transform.position.y + 5, player.transform.position.z - 10);
}
Making the camera following the player is quite straight forward.
Add this script to your main camera.
Drag the reference of the player object to the script and then you are done.
You can change the values in the Vector 3 depending on how far you want the camera to be from the player.
using UnityEngine;
public class Follow_player : MonoBehaviour {
public Transform player;
// Update is called once per frame
void Update () {
transform.position = player.transform.position + new Vector3(0, 1, -5);
}
}
Follows player in the back constantly when the player rotates, no parenting needed, with smoothing.
Piece of Knowledges:
Apparently, Quaternion * Vector3 is going to rotate the point of the Vector3 around the origin of the Vector3 by the angle of the Quaternion
The Lerp method in Vector3 and Quaternion stand for linear interpolation, where the first parameter gets closer to the second parameter by the amount of third parameter each frame.
using UnityEngine;
public class CameraFollow : MonoBehaviour
{
public Transform target;
public float smoothSpeed = 0.125f;
public Vector3 locationOffset;
public Vector3 rotationOffset;
void FixedUpdate()
{
Vector3 desiredPosition = target.position + target.rotation * locationOffset;
Vector3 smoothedPosition = Vector3.Lerp(transform.position, desiredPosition, smoothSpeed);
transform.position = smoothedPosition;
Quaternion desiredrotation = target.rotation * Quaternion.Euler(rotationOffset);
Quaternion smoothedrotation = Quaternion.Lerp(transform.rotation, desiredrotation, smoothSpeed);
transform.rotation = smoothedrotation;
}
}
This will always follow the player from the same direction, and if the player rotates it will still stay the same. This may be good for top-down or side-scrolling view, but the camera setup seems to be more fitting for 3rd person, in which case you'd want to rotate the camera when the player turns.
The easiest way to do this is actually not with code alone, simply make the camera a child of the player object, that way its position relative to the player will always stay the same!
If you do want to do it through code, you can change the code to be like this:
void Update()
{
Vector3 back = -player.transform.forward;
back.y = 0.5f; // this determines how high. Increase for higher view angle.
transform.position = player.transform.position - back * distance;
transform.forward = player.transform.position - transform.position;
}
You get the direction of the back of the player (opposite of transform's forward). Then you increase the height a little so the angle will be a bit from above like in your example. Last you set the camera's position to be the player's position and add the back direction multiplied by the distance. That will place the camera behind the player.
You also need to rotate the camera so it points at the player, and that's the last line - setting the camera's forward direction to point at the player.
Here is just another option. I always find it easier to have the variables which are populated in the inspector so you can adjust it and fine tune it as needed.
public GameObject player;
[SerializeField]
private float xAxis, yAxis, zAxis;
private void Update()
{
transform.position = new Vector3(player.transform.position.x + xAxis, player.transform.position.y + yAxis, player.transform.position.z + zAxis);
}

Unity3D - Using Raycasting to detect Game Objects. Top-down view

I'm making a top down space resource-gathering game, with 3D models.
I'm trying to cast a ray towards my mouse position, to detect collisions with the objects in my game,
for my little spaceship to know which direction to head to find resources.
The resources are in the form of asteroids the player has to break to gather.
In the code, I use the "center" vector to find the middle of the screen/player position, as the camera follows the player around. All movement happens along the X,Y axis. center.Z is set to 15 simply to counteract the -15Z position of the main camera in worldspace.
So far, the code "works" to the extent that it can detect a hit, but the ray doesn't travel in the direction of the mouse. I have to hold it way off for it to hit, and it's difficult to replicate, to the point where it almost seems random. Might the ray not be able to make sense of the mouse position?
In case I've worded myself poorly, this search ability is not meant to break the asteroid, simply locate it. The breaking ability is a separate script.
Code:
public class AbilitySearch : MonoBehaviour
{
Vector3 center;
Vector3 mousePos;
Vector3 direction;
private float range = 100f;
void Update()
{
center = Camera.main.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 15.0f));
mousePos = Input.mousePosition;
direction = mousePos - transform.position;
if (Input.GetMouseButton(1))
{
Search();
}
}
void Search()
{
RaycastHit hit;
if (Physics.Raycast(center, direction, out hit, range))
{
Debug.Log("You hit " + hit.transform.name);
}
}
}
Thanks in advance
When using Input.mousePosition the position is a pixel coordinate on your screen, so if your mouse was in the bottom left corner of your screen it would be 0,0. This causes the direction to be inaccurate. When what you need is to use the game space coordinates, which is done by using Camera.ScreenToWorldPoint(Input.mousePosition), as an example.
This solution allows you to click on a gameObject (provided it has a collider attached), move the current gameObject towards the clicked object, as well as gather an array of possible gameObjects (RaycastHit[s]) in the direction of the clicked one.
Vector3 destination;
RaycastHit[] possibleTargets;
bool isTravelling;
float searchDistance = 5f;
private void Update()
{
//If right mouse button has been pressed down
if (Input.GetMouseButtonDown(1))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit)) //If ray collides with something
{
Search(hit);
}
}
//If going to destination
if (isTravelling)
{
float step = 2f * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, destination, step);
//If approx arrived
if (Vector3.Distance(transform.position, destination) < 0.1f)
{
isTravelling = false; //Stop moving to destination
Debug.Log("You've arrived");
}
}
}
private void Search(RaycastHit _hit)
{
//Normalise directional vectors, so (if needed) they can be multipled as expected
Vector3 direction = (_hit.point - transform.position).Normalized();
RaycastHit secondHit;
//Grab objects in direction of clicked object (including the one clicked)
possibleTargets = Physics.RaycastAll(transform.position, direction, searchDistance);
Debug.Log($"There are {possibleTargets.Length} possible targets ahead");
Debug.Log($"You hit {_hit.transform.name}");
//Set destination, and set to move
destination = _hit.point;
isTravelling = true;
}
You used the ViewportToWorldPoint method, which expects normalized viewport coordinates in range 0 to 1, but you supplied what seems to me as world coordinates of your camera as its parameter.
You only need to cast a ray from camera to mouse pointer world position (see first line of code in method FindAsteroid) to check for collision with asteroid. The returned RaycastHit provides you with information about the collision - hit position, gameobject, collider - which you can use for other game logic, e.g. shooting a projectile from spaceship to asteroid hit point.
I edited your class and included a screenshot from my simple scene below, which shows the two different "rays":
The yellow ray goes from camera to asteroid hit point
The magenta ray goes from spaceship position to asteroid hit point.
I would also recommend filtering the raycast to affect only specific colliders -
see LayerMask (section Casting Rays Selectively)
public class AbilitySearch : MonoBehaviour
{
private float range = 100f;
private Camera mainCam;
private void Awake()
{
// TIP: Save reference to main camera - avoid internal FindGameObjectWithTag call
mainCam = Camera.main;
}
private void Update()
{
if (Input.GetMouseButton(1))
{
if (FindAsteroid(out var asteroidHit))
{
// Draw a line from spaceship to asteroid hit position
Debug.DrawLine(transform.position, asteroidHit.point, Color.magenta, Time.deltaTime);
Debug.Log($"You hit {asteroidHit.transform.name}");
}
}
}
private bool FindAsteroid(out RaycastHit hit)
{
// Create a ray going from camera position to mouse position in world space
var ray = mainCam.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, range))
{
// Draw a line from camera to world mouse position
Debug.DrawLine(ray.origin, hit.point, Color.yellow, Time.deltaTime);
// An asteroid was found
return true;
}
// An asteroid was NOT found
return false;
}
}
Sample spaceship scene

How do I drag objects on the screen using `OnMouseDrag`?

Tried doing 2D and 3D. I want the player to click and drag objects to stack them. I tried copying and pasting code from a video and it still didn't work.
UPDATE! Now it kinda works but it teleports the object far off the screen. I want the player to be able to smoothly click and drag the object.
The code as of now:
using UnityEngine;
using System.Collections;
public class DragObject : MonoBehaviour
{
private float deltax, deltay;
private void OnMouseDown()
{
deltax = 1f;//Camera.main.ScreenToWorldPoint(Input.mousePosition).x - transform.position.x;
deltay = 1f; //Camera.main.ScreenToWorldPoint(Input.mousePosition).y - transform.position.y;
}
private void OnMouseDrag()
{
Vector2 currentTouchPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
transform.position = new Vector2((Input.mousePosition).x - deltax, (Input.mousePosition.y) - deltay);
}
}
It's not clear at all what exact problem is for you, you obviously need to give more info and describe your issue better. But I'll try to help as I can.
To get mouse position you need to use:
Input.mousePosition
If you need mouse position in 2d coordinates you can use this:
Vector2 currentTouchPos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
This method works regardless of your camera type or how the camera moves between frames
First, when the object is first clicked, get the plane at the object's position perpendicular to the direction of the camera:
Plane dragPlane = new Plane(Camera.main.transform.forward, transform.position);
Then, get the ray coming out of the camera from where the mouse is:
Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);
Find out where that ray intersects using Plane.Raycast:
float enter = 0.0f;
if (dragPlane.Raycast(camRay, out enter))
{
Vector3 fingerPosition = camRay.GetPoint(enter);
This gives you a position in world space, which is where you can consider the finger to currently be at. Just put the object there:
transform.position = fingerPosition;
Altogether it might look like this:
void OnMouseDrag()
{
Plane dragPlane = new Plane(Camera.main.transform.forward, transform.position);
Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);
float enter = 0.0f;
if (dragPlane.Raycast(camRay, out enter))
{
Vector3 fingerPosition = camRay.GetPoint(enter);
transform.position = fingerPosition;
}
}
No OnMouseDown necessary.
An alternative is to keep track of how fingerPosition changes over time, and Translate the transform based on that. Keeping as much math as possible in world units makes it easy:
Vector3 prevFingerPosition = Vector3.zero;
// Same code as before but we use it in different places so it goes in its own method.
// If the Raycast fails, return a decent guess.
private Vector3 GetMouseOnPositionInWorldSpace()
{
Plane dragPlane = new Plane(Camera.main.transform.forward, transform.position);
Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);
float enter = 0.0f;
if (dragPlane.Raycast(camRay, out enter))
{
return camRay.GetPoint(enter);
}
return prevFingerPosition;
}
void OnMouseDown()
{
prevFingerPosition = GetMouseOnPositionInWorldSpace();
}
void OnMouseDrag()
{
Vector3 fingerPosition = GetMouseOnPositionInWorldSpace();
transform.Translate(fingerPosition-prevFingerPosition);
prevFingerPosition = fingerPosition;
}
There is an issue behind using OnMouseDrag to handle dragging objects around is that if the cursor moves too quickly, it can move off the object in the span of a single frame, and it will "drop" the object.
If that's not desirable, you might want to set a bool to be true in OnMouseDown that remembers that the object was clicked and a move the logic out of OnMouseDrag and into Update where it can do its thing if that bool is true. And when the mouse is released, set that bool back to false.

Unity Problem with Raycasting Masklayers or distance

Im trying to make 2 types of enemies, melees and ranged, so im trying that both have a raycast to the player to know if they can see the player and are enought near, but I have two problems. The first one is that when I check the distance between the enemy and the player is always 0, and the second is that Raycast doesnt print when it should.
Let me explain, Im in front of the enemies and it prints that sees me, but other times not, in the same conditions, so i dont know what im missing.
Here is my code :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMelee : EnemyBase {
public int rangoAttack;
RaycastHit hit;
int layerMask = 1 << 2;
void Update () {
perseguir();
attackMele();
}
void attackMele() {
if (Physics.Raycast(transform.position, player.transform.position, layerMask)){
print(hit.distance);
}
Debug.DrawLine(transform.position, player.transform.position, Color.red);
}
}
Enemies are on Layer 2
Pd: EnemyBase script just have movement to the player with nav.SetDestination (player.position);
Image of the enemy (cant upload images yet): https://ibb.co/eq7pMV
First problem occurs because you don't use hit in the code. So hit.distance = 0.
Second problem occurs because in Physics.Raycast(transform.position, player.transform.position, layerMask) you should set direction in 2nd argument (Not endpoint). You can calculate direction by this: Vector3 direction = player.transform.position - transform.position.
Try this code:
RaycastHit hit;
int layerMask = 1 << 2;
public float maxDistance;
void Update ()
{
attackMele();
}
void attackMele()
{
Vector3 direction = player.transform.position - transform.position;
if (Physics.Raycast(transform.position, direction, out hit, maxDistance, layerMask))
print(hit.distance);
Debug.DrawLine(transform.position, player.transform.position, Color.red);
}
I hope it helps you.

Categories