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?
Related
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
I'm working on an editor tool and I have a problem.
If in the SceneView I hover with a mouse over a mesh vertex, how can I get its position in world space?
In your Editor Script, you need to raycast from current mouse position, transform this via HandleUtility and handle if the hitInfo.collider contains the requested object.
Note: I didn't test if this works if you don't have a collider attached
This also works only after SceneView has the focus (you clicked on it)
void OnSceneGUI()
{
Transform objectPreview; //the transform to move
Transform objectTouched = null; //the reference of the last object hit
Ray worldRay = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
RaycastHit hitInfo;
if (Physics.Raycast(worldRay, out hitInfo, Mathf.Infinity))
{
if (hitInfo.collider.gameObject != null)
{
objectTouched = hitInfo.collider.gameObject;
var yourPosition = objectTouched.transform.position;
}
}
}
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]);
}
}
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);
}
}
}
I have a problem that iv been trying to figure out for a couple of says and i cant seem to pin point the cause of it! I have created a first person shooter that consists of a few enemies on a small map. I have two scenes (the main menu and the game level). When my player dies it is takes to the main menu from which you can choose to play the game again. This then reloads the level again. The first time the game is run it runs without any problems. However when i doe and press the play game button again it returns to me a message which states the following "MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it." from the code below I can only see two types which are of GameObject. I have tried to remove the muzzleFlash to see if that is the issue however it makes no difference. I have unticked all the static boxes as i read that this may be the cause of the problem but this did not resolve the problem. this script below is attached to an enemy, I have a PlayerShot script attached to the FPS. Please could someone help?
// speed of the AI player
public var speed:int = 5;
// speed the ai player rotates by
public var rotationSpeed:int = 3;
// the waypoints
public var waypoints:Transform[];
// current waypoint id
private var waypointId:int = 0;
// the player
public var player:GameObject;
// firing toggle
private var firing:boolean = false;
// the Mesh Renderer of the Muzzle Flash GameObject
private var muzzleFlashAgent:GameObject;
/**
Start
*/
function Start()
{
// retrieve the player
player = GameObject.Find("First Person Controller");
// retrieve the muzzle flash
muzzleFlashAgent = GameObject.Find("muzzleFlashAgent");
// disable the muzzle flash renderer
muzzleFlashAgent.active = false;
}
/**
Patrol around the waypoints
*/
function Patrol()
{
// if no waypoints have been assigned
if (waypoints.Length == 0)
{
print("You need to assign some waypoints within the Inspector");
return;
}
// if distance to waypoint is less than 2 metres then start heading toward next waypoint
if (Vector3.Distance(waypoints[waypointId].position, transform.position) < 2)
{
// increase waypoint id
waypointId++;
// make sure new waypointId isn't greater than number of waypoints
// if it is then set waypointId to 0 to head towards first waypoint again
if (waypointId >= waypoints.Length) waypointId = 0;
}
// move towards the current waypointId's position
MoveTowards(waypoints[waypointId].position);
}
/**
Move towards the targetPosition
*/
function MoveTowards(targetPosition:Vector3)
{
// calculate the direction
var direction:Vector3 = targetPosition - transform.position;
// rotate over time to face the target rotation - Quaternion.LookRotation(direction)
transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation(direction), rotationSpeed * Time.deltaTime);
// set the x and z axis of rotation to 0 so the soldier stands upright (otherwise equals REALLY bad leaning)
transform.eulerAngles = Vector3(0, transform.eulerAngles.y, 0);
// use the CharacterController Component's SimpleMove(...) function
// multiply the soldiers forward vector by the speed to move the AI
GetComponent (CharacterController).SimpleMove(transform.forward * speed);
// play the walking animation
animation.Play("walk");
}
/**
Update
*/
function Update()
{
// calculate the distance to the player
var distanceToPlayer:int = Vector3.Distance(transform.position, player.transform.position);
// calculate vector direction to the player
var directionToPlayer:Vector3 = transform.position - player.transform.position;
// calculate the angle between AI forward vector and direction toward player
// we use Mathf.Abs to store the absolute value (i.e. always positive)
var angle:int = Mathf.Abs(Vector3.Angle(transform.forward, directionToPlayer));
// if player is within 30m and angle is greater than 130 (IN FRONT) then begin chasing the player
if (distanceToPlayer < 30 && angle > 130)
{
// move towards the players position
MoveTowards(player.transform.position);
// if not firing then start firing!
if (!firing) Fire();
}
// if player is within 5m and BEHIND then begin chasing
else if (distanceToPlayer < 5 && angle < 130)
{
// move towards the players position
MoveTowards(player.transform.position);
// if not firing then start firing!
if (!firing) Fire();
}
else
{
// patrol
Patrol();
// stop firing
firing = false;
}
}
/**
Fire at the player
*/
function Fire()
{
// toggle firing on
firing = true;
// check if still firing
while (firing)
{
// hit variable for RayCasting
var hit:RaycastHit;
// range of weapon
var range:int = 30;
// fire the ray from our position of our muzzle flash, forwards "range" metres and store whatever is detected in the variable "hit"
if (Physics.Raycast(muzzleFlashAgent.transform.position, transform.forward, hit, range))
{
// draw a line in the scene so we can see what's going on
Debug.DrawLine (muzzleFlashAgent.transform.position, hit.point);
// if we hit the player
if (hit.transform.name == "First Person Controller")
{
// inform the player that they have been shot
player.GetComponent(PlayerShot).Shot();
// play gunshot sound
audio.PlayOneShot(audio.clip);
// show muzzle flash for X seconds
muzzleFlashAgent.active = true;
yield WaitForSeconds(0.05);
muzzleFlashAgent.active = false;
// wait a second or two before firing again
yield WaitForSeconds(Random.Range(1.0, 2.0));
}
}
// wait till next frame to test again
yield;
}
}
this is the PlayerShot which destroys the gameobject.
// the sound to play when the player is shot
public var shotSound:AudioClip;
// the number of lives
public var lives:int = 3;
/**
Player has been shot
*/
function Shot ()
{
// play the shot audio clip
audio.PlayOneShot(shotSound);
// reduce lives
lives--;
// reload the level if no lives left
if (lives == 0)
{
// destroy the crosshair
Destroy(GetComponent(CrossHair));
// add the camera fade (black by default)
iTween.CameraFadeAdd();
// fade the transparency to 1 over 1 second and reload scene once complete
iTween.CameraFadeTo(iTween.Hash("amount", 1, "time", 1, "oncomplete", "ReloadScene", "oncompletetarget", gameObject));
}
}
/**
Reload the scene
*/
function ReloadScene()
{
// reload scene
Application.LoadLevel("MainMenu");
}
The script attached to the enemy along with the BasicAI is the SoldierShot Script which destroys the gameobject. below is the script.
public var ragdoll:GameObject;
/**
Function called to kill the soldier
*/
function Shot()
{
// instantiate the ragdoll at this transform's position and rotation
Instantiate(ragdoll, transform.position, transform.rotation);
Destroy(GetComponent(BasicAI));
// destroy the animated soldier gameobject
Destroy(gameObject);
}
It seems that you keep a reference to the destroyed GameObject (or am I wrong?).
As opposed to what happens in common C# (and probably javascript) programs that when you have a reference to an object, it will never be garbage collected, in Unity if you destroy the object all your references to it will go to null.
Easy fix, you could delete the gameObject in the "Game Level" and instantiate your soldier on the fly with:
var instance : GameObject = Instantiate(Resources.Load("Soldier"));
You just need to make a Resources folder in your project folder and put the prefab for your solder into it.