I have two gameobjects roller and marker. When I use physics raycast it works, but with raycastall it doesn't. I need to use raycastall, because i need to iterate through colliders. When marker is on roller normal raycast doesn't work and I need to use raycastall.
This code works:
if (Input.touchCount > 0)
{
// get mouse position in screen space
// (if touch, gets average of all touches)
Vector3 screenPos = Input.mousePosition;
// set a distance from the camera
screenPos.z = 10.0f;
// convert mouse position to world space
Ray cameraRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
Physics.Raycast(cameraRay, out hit);
if (hit.collider.tag == "Floor")
{
transform.position = hit.point;
}
/*
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
// get current position of this GameObject
Vector3 newPos = transform.position;
// set x position to mouse world-space x position
newPos.x = worldPos.x;
newPos.z = worldPos.z-3f;
// apply new position
transform.position = newPos;
*/
if (Input.GetTouch(0).phase == TouchPhase.Ended)
{
Destroy(gameObject);
touchhandler = GameObject.Find("Roller").GetComponent<TouchHandler>();
touchhandler.markerexists = false;
}
}
And this doesn't:
if (Input.touchCount > 0)
{
// get mouse position in screen space
// (if touch, gets average of all touches)
Vector3 screenPos = Input.mousePosition;
// set a distance from the camera
screenPos.z = 10.0f;
// convert mouse position to world space
Ray cameraRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit[] hits;
hits = Physics.RaycastAll(Camera.main.ScreenPointToRay(Input.mousePosition), 100.0F);
while (i < hits.Length)
{
RaycastHit hit = hits[i];
if (hit.collider.tag == "Floor")
{
transform.position = hit.point;
}
i++;
}
if (Input.GetTouch(0).phase == TouchPhase.Ended)
{
Destroy(gameObject);
touchhandler = GameObject.Find("Roller").GetComponent<TouchHandler>();
touchhandler.markerexists = false;
}
}
Related
I have a question, please help and thanks!
I want to know how to show the Angle information(degree) between AR camera and target object on a plane. (Using Smartphone)
And I want to know how to "code" with Unity and AR Foundation.
I tried using some code(below), but it seems only work on Distance, not work on Angle...
Thank again!
void Start ()
{
//get the components
private ARRaycastManager RayManager;
private GameObject visual;
public Camera CameraStart;
public Text textField2;
public Text textField;
float distance;
float angle;
RayManager = FindObjectOfType<ARRaycastManager>();
visual = transform.GetChild(0).gameObject;
}
void Update ()
{
// shoot a raycast from the center of the screen
List<ARRaycastHit> hits = new List<ARRaycastHit>();
RayManager.Raycast(new Vector2(Screen.width / 2, Screen.height / 2), hits, TrackableType.Planes);
RaycastHit hit;
//Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
// check if we hit an AR plane, update the position and rotation
if(hits.Count > 0)
{
transform.position = hits[0].pose.position;
transform.rotation = hits[0].pose.rotation;
distance = Vector3.Distance(CameraStart.transform.position, transform.position);
textField.text = distance.ToString("N2") + "meter";
Physics.Raycast(transform.position, out hit);
angle = Vector3.Angle(hit.normal, transform.forward);
textField2.text = angle.ToString("N2") + "degree";
if(!visual.activeInHierarchy)
visual.SetActive(true);
}
}
}
I would check that the ray hits:
if (Physics.Raycast(transform.position, out hit)) {
angle = Vector3.Angle(hit.normal, transform.forward);
textField2.text = angle.ToString("N2") + "degree";
} else {
Debug.LogError("Did not hit")
}
Also I would make sure that the target has a collider and that the layer mask is correct.
I want to know if there is a way to rotate the object only when the user click and drag on the gameobject. If the user click and drag anywhere else, the gameobject should not rotate.
Object with this script on it is rotating no matter where the user clicks.
void Update()
{
if (Input.touchCount > 0)
{
touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Moved)
{
Vector2 dir = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position) - Camera.main.ScreenToWorldPoint(transform.position);
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
Quaternion rotation = Quaternion.AngleAxis(angle - 90, Vector3.forward);
transform.rotation = rotation;
transform.rotation = rotationZ * transform.rotation;
}
}
Your current code indeed only checks for touches .. it doesn't matter whether these are "hitting" anything or not.
You could e.g. use a Physics.Raycast in order to check if your touch is above an object using
// store the direction from object to touch when the touch started
private Vector2 startDirection;
// store the objects orientation when the touch started
private Quaternion startRotation;
// only process touch moved if the touch actually started above this object
private bool isValidTouch;
// The collider of this object
[SerializeField] Collider _collider;
// The main camera
[SerializeField] Camera _mainCamera;
private void Awake()
{
if(!_collider) _collider = GetComponent<Collider>();
if(!_mainCamera) _mainCamera = Camera.main;
}
void Update()
{
if (Input.touchCount > 0)
{
touch = Input.GetTouch(0);
var touchPosition = touch.position;
switch(touch.phase)
{
case TouchPhase.Began:
// Check if this touch actually is over this object
if(_collider.RayCast(_mainCamera.ScreenPointToRay(touchPosition), out var hit))
{
// Now store initial data
// You want to do ONLY ONE:
// - Either transform the touch into worldspace (more complicated)
// - OR transform the position into screenspace (easier)
// You definitely do not want to do both!
startDirection = (touchPosition - (Vector2)_mainCamera.WorldToScreenPoint(transform.position));
startRotation = transform.rotation;
isValidTouch = true;
}
break;
case TouchPhase.Moved
if(isValidTouch)
{
// Get the current touch direction
var direction = (touchPosition - (Vector2)_mainCamera.WorldToScreenPoint(transform.position));
// Get the angle between them
var differenceAngle = Vector3.SignedAngle(startDirection, direction, Vector3.forward);
// rotate the original rotation according to the current angle
Quaternion rotation = startRotation * Quaternion.AngleAxis(differenceAngle, Vector3.forward);
transform.rotation = rotation;
}
break;
case TouchPhase.Ended
isValidTouch = false;
break;
}
}
}
The script works perfectly, exactly as it should, except when zooming too much and the SphereCast collides with the mesh. When it hits a mesh, I want the camera to just stop, and not try to go through the mesh.
if(Input.touchCount == 2)
{
Touch first = Input.GetTouch(0);
Touch second = Input.GetTouch(1);
origin = mainCamera.transform.position;
direction = mainCamera.transform.forward;
RaycastHit hit;
if(Physics.SphereCast(origin, sphereRadius, direction, out hit, maxDistance))
{
inCollision = true;
}
else
{
inCollision = false;
}
if(first.phase == TouchPhase.Began || second.phase == TouchPhase.Began)
{
initialDistance = Vector3.Distance(first.position, second.position);
}
if(first.phase == TouchPhase.Moved || second.phase == TouchPhase.Moved)
{
movedDistance = Vector3.Distance(first.position, second.position) - initialDistance;
if(inCollision)
{
movedDistance = -Mathf.Abs(movedDistance);
}
mainCamera.transform.localPosition = new Vector3(0, 0, mainCamera.transform.localPosition.z + movedDistance * zoomSensitivity * Time.deltaTime);
initialDistance = Vector3.Distance(first.position, second.position);
}
}
I tried to:
Set movedDistance to 0 when SphereCast hits the mesh;
Clamp mainCamera.transform.localPosition.z to mainCamera.transform.localPosition.z, but only when SphereCast hits the mesh, so the z doesn't zoom more than it's current value.
As commented before you all the time move forward, collide, move backwards, don't collide anymore, move forward etc.
You could probably rather not move backwards but check if you would collide if you would move and do not move at all in this case:
if(first.phase == TouchPhase.Began || second.phase == TouchPhase.Began)
{
initialDistance = Vector3.Distance(first.position, second.position);
}
else if(first.phase == TouchPhase.Moved || second.phase == TouchPhase.Moved)
{
movedDistance = Vector3.Distance(first.position, second.position) - initialDistance;
// Only check until here if you WOULD collide
origin = mainCamera.transform.position;
var targetPosition = origin + mainCamera.transform.forward * movedDistance * Time.deltaTime * zoomSensitivity;
direction = mainCamera.transform.forward;
// only move if there WILL BE no collision
if(!Physics.SphereCast(targetPosition, sphereRadius, direction, out var hit, maxDistance))
{
mainCamera.position = targetPosition;
}
initialDistance = Vector3.Distance(first.position, second.position);
}
From the example for SphereCast you can see that you could even take the hit.distance into account in order to at least move the camera the closest possible.
Goal
Hello Guys! I am new with unity 3-D . I want to make controls exactly like Mini Golf king . Like when ball stops pointers becomes visible on the screen . On clicking that pointer and dragged its scale changes and points always towards ball.
This is what I want to achieve
MY Approach
Initially according to my knowledge I Have implemented something using ray-cast. In the pointer I placed 3-d pointer as child object of ball . when i touch on the screen I send a ray cast to world from screen that detects Ball if i am touching Ball the i show Pointer and on every point on the screen i am continuously sending ray-casts on the world and changing pointer direction accordingly but i think it is not the good way on performance point
RaycastHit GenerateRayCast(Vector2 position, Camera camera,LayerMask mask) { // GENERATE RAY CAST AT GIVEN DIRECTION IN 3D WORLD
RaycastHit hito;
Ray ray = camera.ScreenPointToRay(position);
Physics.Raycast(ray, out hito, rayLength, mask);
return hito;
}
#endregion
#region Updatemethod
void FixedUpdate () {
if (IsBallInStopPos) { // IF BALL IN STATIC POSITOIN THEN LISTEN FOR TOUCH INPUTS
//if (Input.GetMouseButton(0) && !EventSystem.current.IsPointerOverGameObject())
if (Input.touchCount > 0 && !EventSystem.current.IsPointerOverGameObject()) // CHECK FOR SREEN TOUCH
{
touchInput = Input.touches[0];
// touchInput = Input.mousePosition; // get touch information
hitObject = GenerateRayCast(touchInput.position, Camera.main, mask);
if (touchInput.phase == TouchPhase.Began)
{
if (hitObject.collider.name == "Pointer") { // IF TOUCHED ON POINTER :
if (touchPointSet == false) {
touchPointSet = true;
touchStartPoint = hitObject.point; // SETTING START TOUCH POINT
}
}
}
else if (touchInput.phase == TouchPhase.Moved) {
newScale= Mathf.Clamp (Vector3.Distance(pointerChild.transform.position , hitObject.point),1f,2f);
pointerPivot.transform.localScale = Vector3.one * newScale;// SETTING SCALE ACCORDING TO DRAG VALUE
if (touchPointSet == true) { // WHEN TOUCH DRAGGED AND PREVIOULY POINTER IS TOUCHED ROTATE THE POINTER AT GIVEN DIRECTION
newDirection = new Vector3(hitObject.point.x, transform.position.y, hitObject.point.z);
transform.LookAt(2 * transform.position - newDirection);
canShoot = true;
}
}
}
else if (touchInput.phase == TouchPhase.Ended)
{
if (newScale <= 1f)
return;
if (IsBallInStopPos && canShoot )
{
//shooted = true;
newScale = newScale -1;// AS SCALE VALUE IS BETWEEN 1 AND 2 SO SUBSTACT FROM TO MAKE IT BETWEEN
pointerPivot.gameObject.SetActive(false);
rigidBody.constraints = RigidbodyConstraints.None;
GetComponent<Rigidbody>().AddRelativeForce(new Vector3(0f, 0f, 10000f * newScale));
// Time.timeScale = 2f;
canShoot = false;
IsBallInStopPos = false; // AFTER SHOOT : after the ball is shooted now the ball is not in staticstatic condition
}
}
}
I want to make a jostick that created (Instatiated or whatever) in touch position(Input.getTouch(i).position) and destroyed when user doesn't touch the screen anymore. This is the source code below. Please give me an idea how to do it. Thank you.
using UnityEngine;
using System.Collections;
public class joy : MonoBehaviour
{
public GameObject joystick; //Self explanitory
public GameObject bg; //Background object for the joystick boundaries
public const float deadzone = 0f; //Deadzone for the joystick
public const float radius = 2.0f; //The radius that the joystick thumb is allowed to move from its origin
public Camera cam;
protected Collider joyCol;
protected int lastfingerid = -1; //Used for latching onto the finger
public Vector2 position = Vector2.zero;
void Awake ()
{
joyCol = bg.GetComponent<Collider>();
}
public void Disable ()
{
gameObject.active = false; //Turns off the joystick when called
}
public void ResetJoystick () //Called when the joystick should be moved back to it's original position (relative to parent object)
{
lastfingerid = -1;
joystick.transform.localPosition = Vector3.zero;
}
void Update ()
{
if (Input.touchCount == 0)
{
ResetJoystick (); //I would have figured this would be obvious? :P
}
else
{
for (int i=0; i < Input.touchCount; i++)
{
Touch touch = Input.GetTouch(i);
bool latchFinger = false;
if (touch.phase == TouchPhase.Began) //When you begin the touch, the code here gets called once
{
//Vector3 touchpos = Input.GetTouch(i).position;
//touchpos.z = 0.0f;
//Vector3 createpos = cam.ScreenToWorldPoint(touchpos);
Ray ray = cam.ScreenPointToRay (touch.position); //Creates a ray at the touch spot
RaycastHit hit; //Used for determining if something was hit
//joystick = (GameObject)Instantiate(Resources.Load("Type3/joystick"),createpos, Quaternion.identity);
//bg = (GameObject)Instantiate(Resources.Load("Type3/Bg"),createpos, Quaternion.identity);
if (joyCol.Raycast(ray,out hit,Mathf.Infinity)) //The ray hit something...
{
if (hit.collider == joyCol) //Apparently, that ray hit your joystick
{
latchFinger = true; //This turns on and sets off a chain reaction
}
}
}
if (latchFinger ) //Latch finger being true turns this code on
{
if ((lastfingerid == -1 || lastfingerid != touch.fingerId))
{
lastfingerid = touch.fingerId;
}
}
if (lastfingerid == touch.fingerId) //The real meat of the code
{
Vector3 sTW = cam.ScreenToWorldPoint (new Vector3 (touch.position.x, touch.position.y, 1)); //Transforms the screen touch coordinates into world coordinates to move our joystick
joystick.transform.position = sTW; //Hurr :O
joystick.transform.localPosition = new Vector3(joystick.transform.localPosition.x,joystick.transform.localPosition.y,0);
//float xClamp = Mathf.Clamp (joystick.transform.localPosition.x, xClampMin, xClampMax); //Limit movement to the boundaries
//float yClamp = Mathf.Clamp (joystick.transform.localPosition.y, yClampMin, yClampMax);
/*float distFromCenter = Mathf.Sqrt(joystick.transform.localPosition.x*joystick.transform.localPosition.x + joystick.transform.localPosition.y*joystick.transform.transform.localPosition.y);
float actualPercent = Mathf.Clamp01(distFromCenter/radius);
float percent = Mathf.Clamp01((distFromCenter - deadzone)/(radius - deadzone));
*/
joystick.transform.localPosition = Vector3.ClampMagnitude(joystick.transform.localPosition,radius);
//joystick.transform.localPosition = new Vector3 (xClamp, yClamp, joystick.transform.localPosition.z); //Finally, the joystick moves like it should with everything in place
if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
{
ResetJoystick ();
}
}
Ray touchray = cam.ScreenPointToRay (touch.position); //Creates a ray at the touch spot
//RaycastHit hit; //Used for determining if something was hit
Debug.DrawRay (touchray.origin, touchray.direction * 100, Color.yellow); //Delete this line. It's not important.
}
}
//set our position variables x and y values to the joysticks values but clamped to a percent value instead of world coords
position.x = joystick.transform.localPosition.x/radius;
position.y = joystick.transform.localPosition.y/radius;
if(position.magnitude < deadzone)
{
position.x = 0;
position.y = 0;
}
}
}