How to make raycasting very accurate? - unity3d

I'm using raycast to detect the object the player is looking at so it can be picked up. However, it is not very accurate. in my game all small objects are pickable. I'm facing a problem when trying to pick up an item next to many other pickable items because when I'm looking directly at one item and click the pick-up button, it picks another near item instead of the one I'm currently looking at.
raycastPos = mainCamera.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2, 0));
RaycastHit hit;
if (Physics.SphereCast(raycastPos, sphereCastRadius, mainCamera.transform.forward, out hit, maxDistance, 1 << interactableLayerIndex))
{
lookObject = hit.collider.transform.gameObject;
}
else
{
lookObject = null;
}
if (PickingUp)
{
if (currentlyPickedUpObject == null)
{
if (lookObject != null)
{
PickupObject();
if (lookObject.CompareTag("TargetObj") && !targetObjectsList.Contains(lookObject.gameObject))
{
if (aSource)
{
aSource.Play();
}
targetObjectsList.Add(lookObject.gameObject);
if (targetObjectsList.Count == targetObjects.Length)
{
//
}
}
CenterIcon.SetActive(false);
}
}
else
{
// pickupRB.transform.position = PickupParent.position;
BreakConnection();
}
How can I make the raycasting very accurate so only the object I'm pointing to gets picked?

Spherecaat/boxcast cover an area which can be great for small objects as the user does not have to be pixel perfect to click on them especially bid they have a mesh collider such as a key. However. If you have many objects near each other a wider cast can mean other objects are picked up first. Decreasing the radius so only 1 object is covered is one way or using a normal raycast will be more accurate at the expense of needing to be more accurately over said item

Related

How to allow raycasting when the player is at a certain distance from object?

I've implemented opening drawers/doors features on my game but the issue I'm facing is that when The player opens a drawer, it gets pushed back so the drawer has some room to be opened. sometimes it jitters when pushed back.
Physics.Raycast(mainCamera.transform.position, mainCamera.transform.forward, out hit, 3f);
if (hit.transform)
{
interactiveObjects = hit.transform.GetComponent<InteractiveObjects>();
}
else
{
lookObject = null;
interactiveObjects = null;
}
if (Open)
{
if (interactiveObjects)
{
interactiveObjects.OnOpen();
}
}
I'm using raycast to open the drawer. Is there a way to only allow the raycasting, when the player is not too close to the drawer? so it doesn't get pushed back by the drawer.
You can check the distance after doing the raycast. If the distance is within the tolerable range, execute the rest of your code.
if (Physics.Raycast(mainCamera.transform.position, mainCamera.transform.forward, out hit, 3f))
{
if (hit.distance >= minDistance)
{
// Code to execute when range is acceptable
}
else
{
Debug.Log("Player is too close to object!");
}
}
The distance used above does not take into account the height difference between the hit point and the camera. You can get a much more consistent distance by setting the y component of both vectors equal before getting the distance.
var cameraPos = mainCamera.transform.position;
cameraPos.y = hit.point.y;
var distance = Vector3.Distance(hit.point, cameraPos);

how to use Raycast NGUI in RTS Game?

I am working on the 2D Isometric RTS project.
It's like They are Billions or Starcraft.
But while working on the map-editor, RayCast faced an unbreakable problem with NGUI.
Although you must create the ground when you click the mouse, UICamera.hoveredObject will continue to return UIRoot
Scenes returning UIRoot
private void CreateTileOnMousePos()
{
Debug.Log(UICamera.hoveredObject);
if (UICamera.hoveredObject == null)
{
RaycastHit2D hit;
if (hit = Physics2D.GetRayIntersection(Camera.main.ScreenPointToRay(Input.mousePosition)))//, grid.unwalkableMask))
{
Node node = grid.NodeFromWorldPoint(hit.point);
Vector3Int pos = tilemaps[0].WorldToCell(node.worldPosition);// + tilemaps[0].tileAnchor);
node.enviormentTile = EnviormentTile.Gress;
tilemaps[0].SetTile(pos, tilePrefabs[0]);
}
}
}
If UIRoot's SetActive is off, of course it works.
Select another tile or save/load map information through UIButton.
I want to use these NGUI to implement the interface of real game
I don't know if the transition is stupid because it's my first time working on it.
I would appreciate it if you could tell me how to do what I intended without turning off UIRoot SetActive
===========================================================================
I don't know how to use GraphicRayCast due to lack of skills, but first I solved it by that method.
if (UICamera.Raycast(Input.mousePosition) == true)
{
Debug.Log("UICamera.Raycast ture");
}
else
{
RaycastHit2D hit;
if (hit = Physics2D.GetRayIntersection(Camera.main.ScreenPointToRay(Input.mousePosition)))//, grid.unwalkableMask))
{
Node node = grid.NodeFromWorldPoint(hit.point);
Vector3Int pos = tilemaps[0].WorldToCell(node.worldPosition);// + tilemaps[0].tileAnchor);
node.enviormentTile = EnviormentTile.Gress;
tilemaps[0].SetTile(pos, tilePrefabs[0]);
}
}

Tap to select specific plane in ARCore with Unity

I'm trying to create a modification to the ARCore sample Andy placement app by only rendering a single plane instead of all the detected planes. I'd like to achieve this by tapping on the plane to be rendered. Here's what I've tried.
protected override void OnEndManipulation(TapGesture gesture)
{
if (gesture.WasCancelled)
{
return;
}
// If gesture is targeting an existing object we are done.
if (gesture.TargetObject != null)
{
return;
}
// Raycast against the location the player touched to search for planes.
TrackableHit hit;
TrackableHitFlags raycastFilter = TrackableHitFlags.PlaneWithinInfinity | TrackableHitFlags.PlaneWithinPolygon;
if (Frame.Raycast(gesture.StartPosition.x, gesture.StartPosition.y, raycastFilter, out hit))
{
Debug.Log("Entered into hit if condition");
// Use hit pose and camera pose to check if hittest is from the
// back of the plane, if it is, no need to create the anchor.
if ((hit.Trackable is DetectedPlane) &&
Vector3.Dot(FirstPersonCamera.transform.position - hit.Pose.position,
hit.Pose.rotation * Vector3.up) < 0)
{
//var andyObject = Instantiate(AndyPrefab, hit.Pose.position, hit.Pose.rotation);
Debug.Log("Hit at back of the current DetectedPlane");
}
else
{
if (PlaneFixed is false)
{
foreach (GameObject plane in DetectedPlaneGenerator.instance.PLANES) //RK
{
// THE PROBLEM IS HERE! HOW TO GET CORRECT TYPECAST?
if (hit.Trackable is plane)
{
Debug.Log("Plane Selected");
PlaneFixed = true;
FixPositionButton.gameObject.SetActive(true);
}
else
{
plane.SetActive(false);
}
}
}
}
}
}
The problem is that I'm unable to get the correct matching between a GameObject (PLANES - created in DetectedPlaneGenerator.cs) and the DetectedPlane object (return by hit.Trackable, using the PlaneWithinInfinity | PlaneWithinPolygon raycastfilters).
I've also seen the method to try and associate a 'tag' to the DetectedPlaneVisualizer (solution to this). However, I see that this too deals with planes using a GameObject. How to match the GameObject and Hit.Trackable (DetectedPlane Object) to identify which specific plane was just tapped on?

Strange If/Else Behavior

Context
I am making a mobile game in which the player is required to touch objects in a specified order. The correct order is determined in a List called clickOrder. To determine the current object the player is supposed to click, currClickIndex is used.
Problem
When touching a correct object, the debug text will display "Correct" for a split second, and will then immediately change to "Wrong." What I am unsure about is why both the if and else blocks are executed when only touching a single object.
Code
void Update()
{
if (Input.touchCount == 1)
{
if (this.enabled)
{
Vector2 worldPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(worldPoint, Vector2.zero);
if (hit != null && hit.collider != null)
{
// check if the touched object is the correct one
if (hit.collider.gameObject == clickOrder[MyData.currClickIndex])
{
debug.text = "Correct";
MyData.currClickIndex++;
}
else
{
debug.text = "Wrong";
}
}
}
}
}
As soon as the correct object is being touched, you do this:
MyData.currClickIndex++;
which moves you forward in the ordered sequence, and from then on, the previously correct object is not correct anymore. But you're still touching it.
If you want to avoid this, you need to move forward in the sequence after you've touched the correct object.
if (there are touches and the correct object is being touched)
{
set a flag;
}
else if (a flag has been set)
{
MyData.currClickIndex++;
reset the flag;
}

Only select the first sprite in a pile of sprites (Unity3D 2D-game)

I have a number of sprites on top of each-other on the game board. When i use the mouse and select the sprite on top, all sprites under the mouse position is selected. My question is how to only select the sprite that I click on and not catch the ones below?
Here is the code i am using for my tests, attached to the sprites:
function Update () {
if(Input.GetMouseButtonDown(0)) {
var theActualMousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
// print("ALL MousePosition: " + theActualMousePosition);
if(collider2D.OverlapPoint(theActualMousePosition)) {
// print("HIT theActualMousePosition: " + theActualMousePosition);
print(collider2D.gameObject.tag);
}
}
}
The results you are getting are completely expected as your code is placed on the GameObjects, what you should do is to push your script out of those objects or use another function than OverlapPoint (because overlap point does not check for collision it simply checks if you are in the bounds of an object, which means it is valid for all object)
Some ideas :
Using OnMouseDown should provide you with an event only for the first collider encountered
Using A raycast from the camera : Camera.ScreenPointToRay should be able to be used for a raycast and then to check only the first collider encountered
Using Layers depending on layer collision Order.
EDIT :
Or you could also cast the ray the other way around :
function Update () {
if(Input.GetMouseButtonDown(0)) {
var theActualMousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 direction = (transform.Position-theActualMousePosition).normalized;
Ray ray = Ray(transform.Position,direction)
if(Physics.Raycast(ray) == null) {
//Then there is nothing between the object and the camera then the object is the first one
print(collider2D.gameObject.tag);
}
}
}