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;
}
}
}
Related
I have a prefab that is instantiated when the user buys the item from my In-game store, how ever many is instantiated, the all of prefab has a start position of a certain position. The prefab can be dragged around the scene using this TouchScript package I found online! My issue: I want to play the prefab's animation every time the user is dragging the prefab around the screen, I attempted this by creating a RaycastHit2D function that would allow me to detect if the user has clicked on the prefab's collider, script below:
if (Input.GetMouseButtonDown (0)) {
Vector2 worldPoint = Camera.main.ScreenToWorldPoint (Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast (worldPoint, Vector2.zero);
if (hit.collider != null) {
if (this.gameObject.name == "Item5 (1)(Clone)" +item5Increase.i) {
monkeyAnim.SetBool ("draging", true);
Debug.Log (hit.collider.name);
}
} else {
monkeyAnim.SetBool ("draging", false);
}
}
However if I were to buy more than one prefab, all instantiated prefabs will play it's animation when I start to drag only one of the instantiated prefabs, hope I'm making sense. Can some one help me? Thank you!
I faced a similar issue with platforms in my 2D game. The solution I would suggest is to create a GameObject that acts as the current item you wish to animate, and a LayerMask that acts as a filter for which objects your raycast can hit. You can use this LayerMask in conjunction with the Physics2D.Raycast API, which has an overload method that takes a LayerMask as a parameter.
Start by creating a new layer, which can be done by going to the top right of an object in your scene and accessing the "Layer" box. Once you've created a new layer (I called mine "item"), make sure your prefab's layer is assigned correctly.
Then, create an empty object in your scene, and attach this script to it. On that object you will see a dropdown menu that asks which layers your raycast should hit. Assign it the "item" layer; this ensures that your raycast can only hit objects in that layer, so clicking on anything else in your game will produce no effect.
using UnityEngine;
public class ItemAnimation : MonoBehaviour
{
private GameObject itemToAnimate;
private Animator itemAnim;
[SerializeField]
private LayerMask itemMask;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
CheckItemAnimations();
}
else if (Input.GetMouseButtonUp(0) && itemToAnimate != null) //reset the GameObject once the user is no longer holding down the mouse
{
itemAnim.SetBool("draging", false);
itemToAnimate = null;
}
}
private void CheckItemAnimations()
{
Vector2 worldPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(worldPoint, Vector2.zero, 1, itemMask);
if (hit) //if the raycast hit an object in the "item" layer
{
itemToAnimate = hit.collider.gameObject;
itemAnim = itemToAnimate.GetComponent<Animator>();
itemAnim.SetBool("draging", true);
Debug.Log(itemToAnimate.name);
}
else //the raycast didn't make contact with an item
{
return;
}
}
}
I'm making a simple character that follows the player's cursor. What I also want is for when the game object "enemy" appears the character then goes to that location to alert the player. Once the enemy is gone the character continues to follow the cursor like normal. Is there a reason why my script won't work. How else can I paraphrase it?
public class FollowCursor : MonoBehaviour
{
void Update ()
{
//transform.position = Camera.main.ScreenToWorldPoint( new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
if (gameObject.FindWithTag == "Enemy")
{
GameObject.FindWithTag("Enemy").transform.position
}
if (gameObject.FindWithTag != "Enemy")
{
transform.position = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
}
}
}
You are not using FindWithTag correctly, as it is a method that takes a string as parameter you need to use it like this:
GameObject.FindwithTag("Something") as stated in the Unity scripting API
Now to apply this to your code you would need to do the following to set your players position based on wether or not an enemy is found (assuming this script is on your actual player object):
if(GameObject.FindWithTag("Enemy"))
{
//If an enemy is found with the tag "Enemy", set the position of the object this script is attatched to to be the same as that of the found GameObject.
transform.position = GameObject.FindWithTag("Enemy").transform.position;
}
else
{
//when no enemy with the tag "Enemy" is found, set this GameObject its position to the the same as that of the cursor
transform.position = Camera.main.ScreenToWorldPoint( new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
}
However this code will just snap your player instantly to the position of the found Enemy. If this is not the desired behaviour you could use a function like Vector3.MoveTowards instead to make the player move to it gradually.
This code also has room for optimisation as searching for a GameObject every update frame is not the ideal solution. But for now it should work.
I'm going to code coding all the function for you, I'm not pretty sure about the beavihour of your code, I understand a gameobject will be attached to the mouse position, so not really following....
Vector3 targetPosition;
public float step = 0.01f;
void Update()
{
//if there is any enemy "near"/close
//targetPosition = enemy.position;
//else
//targetPosition = MouseInput;
transform.position = Vector3.MoveTowards(transform.position, targetPosition , step);
}
For the f you can use a SphereCast and from the enemies returned get the closest one.
I have 2 Spheres in my scene. I want to be able to drag and drop my mouse from one sphere to the other, and have an indicator (a straight line for example) while dragging. After releasing the mouse button, I want to store in the first sphere the other sphere (as GameObject).
I need this in UnityScript, but I can accept C# ideas.
So far I have thought about onMouseDown event on the first Sphere, and then onMouseEnter to the other sphere, I'll store the data in some global variable (which I donno how to do yet) and in case of onMouseExit I'll just put the global variable as null.
Then onMouseUp on the first Sphere I'll store the global variable in the pressed object (the sphere).
Any tips and tricks on how to do it?
Assumptions/Setup
You're working in a 2D worldspace; the logic for dragging the object for this part of the solution would need changing.
Setting parent after click drag, referred to setting the object you didn't click to be the child of the parent you did click on.
The camera should be an orthographic camera; using a perspective camera will cause the dragging to not align with where you think it should be.
In order to make the dragging work, I created a quad that was used as the 'background' to the 2D scene, and put that on a new layer called 'background'. Then setup the layermask field to only use the background layer.
Create and setup a material for the line renderer, (I'm using a Particles/Additive shader for the above example), and for the parameters of the line renderer I'm using Start Width: 0.75, End Width: 0, and make sure Use World Space is ticked.
Explanation
Firstly setup the otherSphere field to be pointing to the other sphere in the scene. OnMouseDown and OnMouseUp toggle the mouseDown bool, that is used to determine if the line renderer should be updated. These are also used to turn on/off the line renderer, and set/remove the parent transform of the two spheres.
To get the position to be dragged to, I'm getting a ray based off of the mouse position on screen, using the method ScreenPointToRay. If you wanted to create add smoothing to this drag functionality, you should enable the if (...) else statement at the bottom, and set the lerpTime to a value (0.25 worked well for me).
Note; when you release the mouse, the parent is immediately set, as such both objects end up getting dragged, but the child will return to it's former position anyway.
[RequireComponent(typeof(LineRenderer))]
public class ConnectedSphere : MonoBehaviour
{
[SerializeField]
private Transform m_otherSphere;
[SerializeField]
private float m_lerpTime;
[SerializeField]
private LayerMask m_layerMask;
private LineRenderer m_lineRenderer;
private bool m_mouseDown;
private Vector3 m_position;
private RaycastHit m_hit;
public void Start()
{
m_lineRenderer = GetComponent<LineRenderer>();
m_lineRenderer.enabled = false;
m_position = transform.position;
}
public void OnMouseDown()
{
//Un-parent objects
transform.parent = null;
m_otherSphere.parent = null;
m_mouseDown = true;
m_lineRenderer.enabled = true;
}
public void OnMouseUp()
{
//Parent other object
m_otherSphere.parent = transform;
m_mouseDown = false;
m_lineRenderer.enabled = false;
}
public void Update()
{
//Update line renderer and target position whilst mouse down
if (m_mouseDown)
{
//Set line renderer
m_lineRenderer.SetPosition(0, transform.position);
m_lineRenderer.SetPosition(1, m_otherSphere.position);
//Get mouse world position
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out m_hit, m_layerMask))
{
m_position.x = m_hit.point.x;
m_position.y = m_hit.point.y;
}
}
//Set position (2D world space)
//if (m_lerpTime == 0f)
transform.position = m_position;
//else
//transform.position = Vector3.Lerp(transform.position, m_position, Time.deltaTime / m_lerpTime);
}
}
Hope this helped someone.
I have an AR scene that has one AR camera, an image target and 3d object below as.
I create an .cs file and attach to ARCamera. I want to move AR object to mouse click position. I tried many codes for this. But I couldn't success it.
I know that Input.mouseposition returns screen position. I convert to ScreenToWorldPosition and put 3d object on this position. 3d object is moved, but not mouse click position. I don't know where it is moved.
How can I move to mouse click position? My code is here :
Camera cam;
Vector3 target = new Vector3(0.0f, 10f,0.5f);
// Use this for initialization
void Start () {
if (cam == null)
cam = Camera.main;
}
void Update()
{
if (Input.GetMouseButtonDown(0)) {
Debug.Log("MouseDown");
Vector3 mousePos = Input.mousePosition;
mousePos = cam.ScreenToWorldPoint(mousePos);
GameObject.Find("Car1").gameObject.transform.position = mousePos;
}
}
EDIT 1
If I add a plane to scene, I can move to the position of mouse click only on the plane. The code is taken from here. But the plane prevents to show AR camera view. The screenshot is below:
Instead of playing with ScreenToWorldPoint, you should use a raycast against a plane, see this answer: https://stackoverflow.com/a/29754194/785171.
The plane should be attached to your AR marker.
ScreenToWorldPoint takes a Vector3, mousePosition is basically a Vector2 (with a 0 z-axis value). You need to set the mousePosition.z value to something for it to be placed in a viewable position. Example:
Vector3 mousePos = Input.mousePosition;
mousePos = cam.ScreenToWorldPoint(mousePos);
mousePos.z = 10;
GameObject.Find("Car1").gameObject.transform.position = mousePos;
This would set the position to 10 units away from the camera.
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);
}
}
}