Dragging an object with raycast is laggy - unity3d

When dragging an object slowly with a raycast the object moves with a bit of lag.
When dragging the object fast the mouse pointer gets out of the field of layer raycasting so the object is no more moving.
The main project is dragging a cube on a plane.
But in order to make the project more simpler, I opened a new 2D project and made a circle and assigned to it the following script, and attached to it a sphere collider (the main target is in 3D space).
// If some one wrote:
private Vector2 deltaPos;
void Update () {
Vector2 touchPos;
if (Input.GetMouseButtonDown (0)) { // Clicking the Target
RaycastHit hit;
var ray = Camera.main.ScreenPointToRay (Input.mousePosition);
if (Physics.Raycast (ray, out hit, Mathf.Infinity)) {
touchPos = new Vector3 (hit.point.x, hit.point.y);
deltaPos.x = touchPos.x - transform.position.x;
deltaPos.y = touchPos.y - transform.position.y;
Debug.Log ("You Clicked Me");
}
}
if (Input.GetMouseButton (0)) {
RaycastHit hit;
var ray = Camera.main.ScreenPointToRay (Input.mousePosition);
if (Physics.Raycast (ray, out hit, Mathf.Infinity)) {
transform.position = new Vector2 (hit.point.x - deltaPos.x, hit.point.y - deltaPos.y);
}
}
}
I expected to drag the sphere regularly, but what happens is that the pointer goes outside the sphere collider when moving fast, therefore the circle stops moving.
I found this article, then I changed the void from Update() to FixedUpdate() but same result.

Try putting your physics code in FixedUpdate() function which is built for physics calculation. then if it's laggy, you can changeFixed Timestep in physics settings.
Fixed Timestep: A framerate-independent interval that dictates when physics calculations and FixedUpdate() events are performed.
https://docs.unity3d.com/Manual/class-TimeManager.html
EDIT
this code:
if (Physics.Raycast (ray, out hit, Mathf.Infinity))
checks if the raycast has hit something, therefore when the pointer goes out of the sphere there is nothing else to hit, so the if condition returns false and you cannot move it, to solve the problem add a big quad or plane as child to your camera and make sure it fills the camera view perfectly, and it's behind all your other scene elements.
you also can make a script that sets this plane for you.
EDIT 2
As another approach to solve the issue, you can use flags.
Add this to your camera, or edit it to your needs.
public class DragObjects : MonoBehaviour
{
Camera thisCamera;
private Transform DraggingObject;
bool DraggingFlag = false;
RaycastHit raycast;
Ray ray;
Vector3 deltaPosition;
void Start()
{
thisCamera = GetComponent<Camera>();
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
ray = thisCamera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out raycast, Mathf.Infinity))
{
DraggingObject = raycast.collider.transform;
DraggingFlag = true;
deltaPosition = thisCamera.ScreenToWorldPoint (Input.mousePosition) - DraggingObject.position;
}
}
else if (Input.GetMouseButton(0))
{
if (DraggingFlag)
{
DraggingObject.position = new Vector3 (thisCamera.ScreenToWorldPoint (Input.mousePosition).x - deltaPosition.x, thisCamera.ScreenToWorldPoint (Input.mousePosition).y - deltaPosition.y, DraggingObject.position.z);
}
}
else if (Input.GetMouseButtonUp(0))
{
DraggingFlag = false;
}
}
}
Remember to cash your Camera.main in a variable at the start, because it is not performant and does this in the background:
GameObject.FindGameObjectsWithTag("MainCamera").GetComponent<Camera>();

Related

Got an error about RayCast when getting the mouse position

NullReferenceException: Object reference not set to an instance of an object
DragBlock.Update () (at Assets/Script/DragBlock.cs:13)
using UnityEngine;
public class DragBlock : MonoBehaviour
{
private bool isBeingHeld = false;
private Vector3 startPos;
private Transform heldObject;
private void Update()
{
if (Input.GetMouseButton(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
if (hit.transform.CompareTag("Blocks"))
{
isBeingHeld = true;
startPos = hit.transform.position;
heldObject = hit.transform;
}
}
}
if (isBeingHeld)
{
Vector3 currentMousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
currentMousePos.z = heldObject.position.z;
heldObject.position = currentMousePos;
}
if (Input.GetMouseButtonUp(0))
{
isBeingHeld = false;
}
}
}
I tried to change the group of the object tried to check the position and so on in this code it should check the position of the mouse and drag the object with the Blocks tag
If line 13 is this one...
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
... then the only part of the code that could realistically be null is the Camera.main property. Camera.main is returned by Unity when Unity looks for, and finds, a Camera in your scene that's tagged "MainCamera".
I suggest that you might have removed or changed that tag of the Camera, or a parent of the camera, and now Unity can't find an appropriate camera.
This is echoed in the online documentation https://docs.unity3d.com/ScriptReference/Camera-main.html

Raycast only returns false, even when true expected

I have the following FollowingNPC game object, which is supposed to follow the Player object if there is direct vision:
Here the Player object:
The BlockingLayer and Player Layers have the physics collision enabled between each other:
The FollowingNPC hast the following script attached, that always returns a "Not Hit" result: Raycast always false. This is not the expected output, as both objects seem to have a clear view between each other and the debug ray can be drawn without problems.
public class FollowingNPC : MonoBehaviour
{
private Vector3 heading, direction;
private float distance;
void Update()
{
heading = GameObject.FindGameObjectWithTag("Player").transform.position - transform.position;
distance = heading.magnitude;
direction = heading / distance;
RaycastHit hit;
if (Physics.Raycast(transform.position, direction, out hit))
{
if (hit.transform== GameObject.FindGameObjectWithTag("Player").transform)
{
Debug.DrawRay(transform.position, direction * distance, Color.red);
Debug.Log("Hit Player");
}
else
{
Debug.DrawRay(transform.position, direction * distance, Color.yellow);
Debug.Log("Hit Wall");
}
}
else
{
Debug.DrawRay(transform.position, direction * distance, Color.white);
Debug.Log("Not Hit");
}
}
}
Solved:
As derHugo suggested, Physics2D.Raycast should be used here. This function does not return a bool, so this is the implementation that worked for me:
void Update()
{
heading = GameObject.FindGameObjectWithTag("Player").transform.position - transform.position;
RaycastHit2D hit = Physics2D.Raycast(transform.position, heading.normalized, LayerMask.GetMask("Player"));
if (hit.collider != null)
if (hit.transform == GameObject.FindGameObjectWithTag("Player").transform)
Debug.Log("Hit Player");
else
Debug.Log("Hit Wall");
else
Debug.Log("Not Hit");
}
Also, it's important to note that, even with the mask, the Raycast returned the hit with the collider of FolloginNPC, so I had to disable it. I'll have to do some investigation or search a workaround.
Note that in Unity the Physics and Physics2D are completely independent and separated Physics engines!
Built-in 3D physics (Nvidia PhysX engine integration)
Built-in 2D physics (Box2D engine integration)
You have Rigidbody2D and Collider2D so you would rather want to use Physics2D.Raycast!
You should also strongly avoid using FindGameObjectWithTag in Update and even worse multiple times as it is quite expensive! Rather do it once and store the result.
// If possible and best would be to already reference it via the Inspector
[SerilaizeField] private Transform player;
// As fallback get it ONCE on runtime
private void Awake()
{
if(!player) player = GameObject.FindGameObjectWithTag("Player").transform;
}
void Update()
{
// actually those wouldn't really need to be fields
heading = player.position - transform.position;
distance = heading.magnitude;
// This line isn't really necessary
// If something I would use
//direction = heading.normalized
direction = heading / distance;
var hit = Physics2D.Raycast(transform.position, direction);
if (hit.collider)
{
if (hit.transform == player)
{
Debug.DrawRay(transform.position, direction * distance, Color.red);
Debug.Log("Hit Player");
}
else
{
Debug.DrawRay(transform.position, direction * distance, Color.yellow);
Debug.Log("Hit Wall");
}
}
else
{
Debug.DrawRay(transform.position, direction * distance, Color.white);
Debug.Log("Not Hit");
}
}
Later you should also remove all these Debug.Log since they are also quite expensive when done in Update!

Moving GameObject with RayCast hit position causing object to move towards Raycast start position

I have RayCast represented by a LineRenderer in Unity, so it looks like a laser. I want this laser to move objects it collides with so that the object follows the hit.point of the Raycast hit.
My code doesn't work, because I move these GameObjects to the hit.point, which causes the object to comes towards the start point of the Raycast, because a new hit.point gets calculated since the object is moving to hit.point. I understand why this is happening, but I'm not sure how to get the object to move with the Raycast, but not effect a new hit.point.
Here's my update function in my script attached to my Laser GameObject. Does anyone know how I can fix my code so that the object moves with the hit.point?
void Update()
{
Vector3 target = calculateDeltaVector();
lr.SetPosition(0, palm.transform.position);
RaycastHit hit;
if (Physics.Raycast(palm.transform.position, target , out hit))
{
if (hit.collider)
{
lr.SetPosition(1, hit.point);
if (hit.transform.gameObject.tag == "Chair")
{
GameObject chair = hit.transform.gameObject;
// !!! move object to hit point, problem HERE
chair.transform.position = hit.point;
hitLock = false;
}
}
}
else lr.SetPosition(1, target * 50);
}
In Unity Inspector, you can select the object and change the layer to "2: Ignore Raycast" This will make the raycast ignore the object and go through it.
Don't know, but your code should move the chair to the chair, which likely will make the chair go towards you.
You have to implement Begin and End the raycast move, e.g using mouse click.
Below is the example
public class Mover : MonoBehaviour
{
public Collider selectedChair;
void Update ()
{
Vector3 target = calculateDeltaVector();
lr.SetPosition(0, palm.transform.position);
RaycastHit hit;
if (Physics.Raycast(palm.transform.position, target , out hit))
{
if (hit.collider)
{
lr.SetPosition(1, hit.point);
if (hit.transform.gameObject.tag == "Chair" && Input.GetMouseButton(0)) //if you want to move it you have to click mouse button first
{
selectedChair = hit.collider;
hit.collider.enabled = false; //disable the collider of currently selected chair so it won't go towards you
}
if (selectedChair)
{
// !!! move object to hit point, problem HERE
selectedChair.transform.position = hit.point;
hitLock = false;
}
if (Input.GetMouseButton(0))
{
selectedChair.enabled true;
selectedChair = null; //release it
}
}
}
else lr.SetPosition(1, target * 50);
}
}

Moving object from one postion to another in unity

I have this code here ,i am trying to move an object from one position to another ,on mouse click ,but everytime i run it it justs instantiate the projectile object in a specific position while it had to instantiate it in a the ffor object position
using UnityEngine;
using System.Collections;
public class Shoot : MonoBehaviour
{
public GameObject projectile;
public GameObject foot;
public GameObject mouse;
void Start()
{
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector2 Target = Input.mousePosition;
Vector2 pos1 = Camera.main.ScreenToWorldPoint(Target);
GameObject newObj = (GameObject)GameObject.Instantiate(projectile);
Vector2 pos2 = foot.transform.position;
transform.position=Vector2.Lerp(pos2,pos1,Time.deltaTime);
}
}
There are several issues here, I'll address them one at a time. First, the code for moving the projectile is wired up wrong:
transform.position=Vector2.Lerp(pos2,pos1,Time.deltaTime);
You are moving the object that "Shoot" is attached to, not the new game object (newObj, as you have named it.)
Second, it's important to understand the Update pattern and how to properly use it. Update is run every frame. Time.deltaTime is how much time has passed between the last frame render and this one. This number is usually very small. Lastly, Input.GetMouseButtonDown is true only on the first frame that the mouse is pressed.
The current code you have only attempts to (but fails, due to other code problems) move the projectile the one frame the mouse is clicked. What we want is the mouse click to spawn a projectile, and the projectile to move forward EVERY update.
This will be best accomplished with two classes. I will call them Gun and SeekerBullet. The Gun class will be responsible for creating a bullet every time the mouse button is pressed. The SeekerBullet class will be responsible for moving the bullet to it's target.
Gun
public SeekerBullet ProjectilePrefab;
void Update()
{
if (Input.GetMouseButton(0))
{
Vector2 target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
FireBullet(target);
}
}
void FireBullet(Vector2 target)
{
GameObject projectile = (GameObject)GameObject.Instantiate(ProjectilePrefab, transform.position, Quaternion.Identity);
projectile.GetComponent<SeekerBullet>().Target = target;
}
SeekerBullet
public float MoveSpeed = 5;
public Vector2 Target { get; set; }
public void Update()
{
transform.position = Vector3.MoveTowards(transform.position, Target, MoveSpeed * Time.deltaTime);
if (transform.position == Target)
OnReachTarget();
}
void OnReachTarget()
{
// Do whatever you want here
Destroy(gameObject); // delete this seekerbullet
}
The main Idea I'm trying to stress is isolating your code to perform it's one function well. The gun shouldn't be responsible for moving the projectile, just creating it and telling it where to go.
Also, note that the projectile is starting wherever the Gun is. See this line
GameObject projectile = (GameObject)GameObject.Instantiate(ProjectilePrefab, transform.position, Quaternion.Identity);
The transform.position is the position of the gun object. If you want it to start at the foot, like you have in your code, you can re-implement that just like you have in your example.
Why don't you use Raycast. It's pretty simple. Here's an example (Moves the object (transform) in the direction and distance of translation.):
var obj:Transform;
var hit:RaycastHit;
var move: boolean = false;
var moveSpeed:float;
var moveTime:float;
private var startTime:float;
function Update ()
{
if (Input.GetButtonDown ("Fire1"))
{
var ray = Camera.main.ScreenPointToRay (Input.mousePosition);
if (Physics.Raycast (ray, hit, 10000)){
move = true;
startTime = Time.time;
}
}
if(move){
var curTime = Time.time;
var elapsedTime = curTime - startTime;
var amountToMove = elapsedTime / moveTime;
print(amountToMove);
obj.transform.position = Vector3.Lerp(obj.transform.position, hit.point, amountToMove);
if(obj.transform.position == hit.point){
move = false;
}
This code creates a plane, and a cube. Assign the above code to the plane, assign the cube to the obj var in the inspector. Click on the plane, and watch the fun.
This is pretty simple. You can use the Vector3.Lerp function to achieve this. Use raycasting to get the mouse click position or the touch position. Then use the initial and the final position in the lerp function. The initial position being the position that the gameobject is at now and the final position being the click / touch position.
You can find the article by The Game Contriver on the same here
Move to Touch / Click Position - The Game Contriver

Raycasting to find mouseclick on Object in unity 2d games

I am trying to delete the object on which the mouse is clicked. I am making a 2D game using the new Unity3D 4.3. Here is the code I'm using
void Update () {
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Physics.Raycast(ray,out hit))
{
isHit = false;
Destroy(GameObject.Find(hit.collider.gameObject.name));
}
}
}
The control is not entering the inner if loop. (isHit is not being set as false).
You cannot use 3D physics functions on the new 2D stuff. Use the 2D functions instead. Example:
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
if(hit.collider != null)
{
Debug.Log ("Target Position: " + hit.collider.gameObject.transform.position);
}
This question is a bit old, but I was looking for a a way to get a GameObject with a mouse click in unity 2D, and the Answer from Esa almost helped me, but I couldn't afford to make it to work, so with a bit of research I saw that Camera.main.ScreenToWorldPoint was returning the center of the screen area of the Camera and to it work right. it required to enter the difference in Z position from the camera and the nearest GameObject. My Camera was set by default in -10 and my GameObject was in 0, so all I needed to do is set my Input.mousePosition.z to 10. So if you are getting problem to work with Esa's code (like me :( )the code bellow may help you:
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10)), Vector2.zero);
if(hit.collider != null)
{
Debug.Log ("Target Position: " + hit.collider.gameObject.transform.position);
}
First attach any type of 2D collider to your GameObject, then pick one of those solutions;
1st Case - If there are more than 1 GameObject on top of each other, and you try to understand specific GameObject is clicked:
void Update ()
{
if (Input.GetMouseButtonDown (0)) {
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit2D[] hits = Physics2D.GetRayIntersectionAll (ray, Mathf.Infinity);
foreach (var hit in hits) {
if (hit.collider.name == name) {
MyFunction ();
}
}
}
}
2nd Case - If there is only 1 GameObject, and you try to understand if it is clicked:
void Update ()
{
if (Input.GetMouseButtonDown (0)) {
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit2D hit = Physics2D.GetRayIntersection (ray, Mathf.Infinity);
if (hit.collider != null && hit.collider.name == name) {
MyFunction ();
}
}
}
You've to attach a mesh collider(any collider) with your object first to enter the inner If. Then,
Destroy(hit.collider.gameObject);
will simply do the job.
There's might be an other work around here.
void Update () {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Physics.Raycast(ray,out hit))
{
if(Input.GetMouseButtonDown(0))
{
isHit = false;
Destroy(hit.collider.gameObject);
}
}
}