Unity Input.Touches if hold touch do nothing? - c#

Newbie...
Learning Unity to make mobile games and I have a small hex grid map that when touched the tiles change to a random color. I have a script for the camera to move it around with a finger press and zoom in an out with two fingers.
I want the tiles to only change color on a single touch, and not change when I pan or zoom the camera. I have been trying for two days and googled myself out and can't seem to figure it out. It seems pretty simple.
Here is the code that is called from Update() when a raycast hits an object. This just changes the hex's color but I want it to only do it when not panning or zooming the camera.
void Touch_Hex(GameObject ourHitObject)
{
Touch[] touches = Input.touches;
if (touches[0].phase == TouchPhase.Ended)
{
MeshRenderer mr = ourHitObject.GetComponentInChildren<MeshRenderer>();
mr.material.color = color[Random.Range(0, color.Length)];
}
}
I've tried all different combos of phases etc but just cannot get it to work as I want it. I'm thinking deltaTime or deltaPostion is the answer but I can't work it out.
Cheers

You need to use a combination of a boolean variable and touches[0].phase == TouchPhase.Moved. When touches[0].phase == TouchPhase.Moved is true then your input is now considered to be invalid. You can then set to boolean variable to true or false then use it later on to decide whether to change the color or not.
bool onlyTouched;
void Update()
{
if (Input.touchCount > 0)
{
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
RaycastHit hit;
if (Input.GetTouch(0).phase == TouchPhase.Began)
{
//Touched
onlyTouched = true;
}
if (Input.GetTouch(0).phase == TouchPhase.Moved)
{
//Moved Finger (Now Invalid!)
onlyTouched = false;
}
if (Input.GetTouch(0).phase == TouchPhase.Ended)
{
//Check if only touched then change color
if (onlyTouched)
{
Debug.Log("Only touched Finger");
//MeshRenderer mr = ourHitObject.GetComponentInChildren<MeshRenderer>();
//mr.material.color = color[Random.Range(0, color.Length)];
if (Physics.Raycast(ray, out hit))
{
if (hit.transform != null)
{
MeshRenderer mr = hit.transform.gameObject.GetComponentInChildren<MeshRenderer>();
mr.material.color = color[Random.Range(0, color.Length)];
//hit.transform.GetComponent<MeshRenderer>().material.color = Color.red;
}
}
}
else
{
Debug.Log("Finger was moved while touching");
}
//Reset onlyTouched for next read
onlyTouched = false;
}
}
}

Related

Rotating an object at which I am looking (ARFoundation)

2 items are placed in AR. i would like to rotate 1 of those items while its in the middle of the screen (using touch controls and Raycast).
I would like all the rotation part of the code to be executed when the camera is looking at the object.
this is the script i use on an object placed in the scene, the Move function works since that is only activated when you tap on the screen where the object is and drag it away (Raycast to a Collider). however my rotation works on all objects in the scene when i swipe anywhere because if i swipe on the collider i move the object, so for rotation i have to stay outside that collider.
public class MoveObject : MonoBehaviour
{
public bool holding;
private Component rotator;
int doubleTap = 0;
void Start()
{
holding = false;
}
void Update()
{
if (holding)
{
Move();
}
// One finger
if (Input.touchCount == 1)
{
// Tap on Object
if (Input.GetTouch(0).phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
RaycastHit hitTouch;
if (Physics.Raycast(ray, out hitTouch, 100f))
{
if (hitTouch.transform == transform)
{
holding = true;
}
}
}
// Release
if (Input.GetTouch(0).phase == TouchPhase.Ended)
{
holding = false;
}
}
if (Input.touchCount == 1 && !holding) //THIS is the rotation part
{
// GET TOUCH 0
Touch touch0 = Input.GetTouch(0);
// APPLY ROTATION
if (touch0.phase == TouchPhase.Moved)
{
transform.Rotate(0f, touch0.deltaPosition.x * 0.5f, 0f);
}
}
}
void Move()
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
// The GameObject this script attached should be on layer "Surface"
if (Physics.Raycast(ray, out hit, 30.0f,
LayerMask.GetMask("Surface")))
{
transform.position = new Vector3(hit.point.x,
transform.position.y,
hit.point.z);
}
}
}
You'll probably want another flag similar to holding to make sure that you don't rotate an object the center of the screen passes over during a swipe, such as when the camera is moving and the object passes through the center of the screen.
You can use Camera.ViewportPointToRay with input of new Vector3(0.5f, 0.5f, 0) to create a ray coming from the center of the screen. From there, the process is similar to your holding code.
Also, with a little rearranging, you can re-use the raycast that is already used to check if this object was touched because it also checks if any object was touched. Only checking to start rotating when the first raycast touches nothing avoids rotating (and raycasting unnecessarily) in the situation where this object is in the center of the screen but another object was touched.
As a sidenote, it's recommended to cache the result of Camera.main because it does a FindGameObjectsWithTag internally every time you reference it and that can add up to extra computation time.
Altogether, it could look like this:
private bool rotating;
private Camera cam;
void Start()
{
holding = false;
rotating = false;
cam = Camera.main;
}
void Update()
{
// One finger
if (Input.touchCount == 1)
{
Touch touch0 = Input.GetTouch(0);
if (touch0.phase == TouchPhase.Began)
{
Ray ray;
RaycastHit hitTouch;
// test hold start
ray = cam.ScreenPointToRay(touch0.position);
if (Physics.Raycast(ray, out hitTouch, 100f))
{
if (hitTouch.transform == transform)
{
holding = true;
}
}
else // avoid rotating/raycasting again in situation
// where this object may be in center of screen
// but this or other object was touched.
{
// test rotate start
ray = cam.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0));
if (Physics.Raycast(ray, out hitTouch, 100f)))
{
if (hitTouch.transform == transform)
{
rotating = true;
}
}
}
}
else if (touch0.phase == TouchPhase.Moved)
{
if (holding)
{
Move();
}
else if (rotating)
{
transform.Rotate(0f, touch0.deltaPosition.x * 0.5f, 0f);
}
}
// Release
else if (touch0.phase == TouchPhase.Ended)
{
holding = false;
rotating = false;
}
}
}

How to convert Raycast 3D to 2D in Unity [duplicate]

I have this code below which could control a touched player among 3 players. I was able to implement it by like sort of "cheating" by way of adding a 3D cube behind a 2D sprite since my game should be in 2D and I am having hard time implementing it in 2D. I'm really confused on how to do it in 2D because I'm really confuse on the parameters.
Although I already implemented it by the way mentioned above, I still want to implement it in pure 2D. And because I have this problem when the selected player moves, the sprite moves faster.
public GameObject target = null;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update ()
{
if(Input.touchCount > 0 || Input.GetTouch(0).phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
Debug.DrawRay(ray.origin,ray.direction * 20,Color.red);
RaycastHit hit;
if(Physics.Raycast(ray, out hit,Mathf.Infinity))
{
Debug.Log(hit.transform.gameObject.name);
if(this.target != null){
SelectMove sm = this.target.GetComponent<SelectMove>();
if(sm != null){ sm.enabled = false; }
}
target = hit.transform.gameObject;
//Destroy(hit.transform.gameObject);
selectedPlayer();
}
}
}
void selectedPlayer(){
SelectMove sm = this.target.GetComponent<SelectMove>();
if(sm == null){
target.AddComponent<SelectMove>();
}
sm.enabled = true;
}
use RaycastHit2D to do this instead of RaycastHit then change Physics.Raycast to Physics2D.Raycast. The parameters are different the code below should do it.You may also have to change && to || depending on your game logic.
Select the Sprite then add Box Colider 2D from the Editor. You can also use Circle Collider 2D for the Sprite if your Sprite is round. If you don't add any 2D collider to the Sprite, ray won't detect the object when clicked on.
FOR MOBILE DEVICES
if ((Input.touchCount > 0) && (Input.GetTouch(0).phase == TouchPhase.Began))
{
Vector2 cubeRay = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position);
RaycastHit2D cubeHit = Physics2D.Raycast(cubeRay, Vector2.zero);
if (cubeHit)
{
//We hit something
Debug.Log(cubeHit.transform.gameObject.name);
if (this.target != null)
{
SelectMove sm = this.target.GetComponent<SelectMove>();
if (sm != null) { sm.enabled = false; }
}
target = cubeHit.transform.gameObject;
//Destroy(cubeHit.transform.gameObject);
selectedPlayer();
}
}
FOR DESKTOPS / EDITOR
if (Input.GetMouseButtonDown(0))
{
Vector2 cubeRay = Camera.main.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D cubeHit = Physics2D.Raycast(cubeRay, Vector2.zero);
if (cubeHit)
{
//We hit something
Debug.Log(cubeHit.transform.gameObject.name);
if (this.target != null)
{
SelectMove sm = this.target.GetComponent<SelectMove>();
if (sm != null) { sm.enabled = false; }
}
target = cubeHit.transform.gameObject;
//Destroy(cubeHit.transform.gameObject);
selectedPlayer();
}
}

RaycastHit2D - touch for 2D game

I'm trying to build a 2D game for touch devices. Now I'm trying to cast a ray against colliders in the scene to do do something. For example, if hit the ray hit the left button the player move to the left.
I've searched everywhere in the google and youtube but haven't figured how to do this. I'm new to Unity and programming in general, but from what I searched it seems if I want to detect a touch on the screen for a 2D game is more complicated than a 3D game !!!
void Update ()
{
if (Input.touchCount > 0 )
{
for ( int i = 0; i < Input.touchCount; i++)
{
if ( Input.GetTouch(i).phase == TouchPhase.Began)
{
Ray2D ray = Camera.main.ScreenToWorldPoint (Input.GetTouch(i).position);
RaycastHit2D hit;
if ( Physics2D.Raycast ( ray, out hit) ) )
{
if (hit.transform.gameobject.name == "left")
{
// Do Something
}
}
}
}
}
Dunno if this help but this is how i do it:
//Get current worldspace position of mouse cursor
RaycastHit2D[] hits = Physics2D.LinecastAll(clickedPos,clickedPos,theLayer);
// Copy Linecast to HashSet (unique records), clean out the "Background" GameObject
foreach (RaycastHit2D arf2D in hits) {
//linecast_GameObject = GameObject.FindWithTag(arf2D.collider.tag);
linecast_GameObject = GameObject.Find(arf2D.collider.name);
//if (linecast_GameObject.name != "Background") {
if (linecast_GameObject.tag != "Background") {
linecast_GameObject_HashSet.Add(linecast_GameObject);
clickedOnTheBackground = false;
}
else if (hits.Length == 1) {
clickedOnTheBackground = true;
fingerSelection_GO = GameObject.FindWithTag("Dummy_GameObject");
//fingerSelection_GO = GameObject.Find("Dummy_GameObject");
}
}

Convert 3D Raycast to Raycast2D

I have this code below which could control a touched player among 3 players. I was able to implement it by like sort of "cheating" by way of adding a 3D cube behind a 2D sprite since my game should be in 2D and I am having hard time implementing it in 2D. I'm really confused on how to do it in 2D because I'm really confuse on the parameters.
Although I already implemented it by the way mentioned above, I still want to implement it in pure 2D. And because I have this problem when the selected player moves, the sprite moves faster.
public GameObject target = null;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update ()
{
if(Input.touchCount > 0 || Input.GetTouch(0).phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
Debug.DrawRay(ray.origin,ray.direction * 20,Color.red);
RaycastHit hit;
if(Physics.Raycast(ray, out hit,Mathf.Infinity))
{
Debug.Log(hit.transform.gameObject.name);
if(this.target != null){
SelectMove sm = this.target.GetComponent<SelectMove>();
if(sm != null){ sm.enabled = false; }
}
target = hit.transform.gameObject;
//Destroy(hit.transform.gameObject);
selectedPlayer();
}
}
}
void selectedPlayer(){
SelectMove sm = this.target.GetComponent<SelectMove>();
if(sm == null){
target.AddComponent<SelectMove>();
}
sm.enabled = true;
}
use RaycastHit2D to do this instead of RaycastHit then change Physics.Raycast to Physics2D.Raycast. The parameters are different the code below should do it.You may also have to change && to || depending on your game logic.
Select the Sprite then add Box Colider 2D from the Editor. You can also use Circle Collider 2D for the Sprite if your Sprite is round. If you don't add any 2D collider to the Sprite, ray won't detect the object when clicked on.
FOR MOBILE DEVICES
if ((Input.touchCount > 0) && (Input.GetTouch(0).phase == TouchPhase.Began))
{
Vector2 cubeRay = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position);
RaycastHit2D cubeHit = Physics2D.Raycast(cubeRay, Vector2.zero);
if (cubeHit)
{
//We hit something
Debug.Log(cubeHit.transform.gameObject.name);
if (this.target != null)
{
SelectMove sm = this.target.GetComponent<SelectMove>();
if (sm != null) { sm.enabled = false; }
}
target = cubeHit.transform.gameObject;
//Destroy(cubeHit.transform.gameObject);
selectedPlayer();
}
}
FOR DESKTOPS / EDITOR
if (Input.GetMouseButtonDown(0))
{
Vector2 cubeRay = Camera.main.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D cubeHit = Physics2D.Raycast(cubeRay, Vector2.zero);
if (cubeHit)
{
//We hit something
Debug.Log(cubeHit.transform.gameObject.name);
if (this.target != null)
{
SelectMove sm = this.target.GetComponent<SelectMove>();
if (sm != null) { sm.enabled = false; }
}
target = cubeHit.transform.gameObject;
//Destroy(cubeHit.transform.gameObject);
selectedPlayer();
}
}

Difficulties selecting a moving gameobject

I am developing a mobile game with touch controls. I can select a non-moving gameobject very easily and it responds, however it's very difficult to select it when it's moving since it's kind of small (falling for example, it's all physics based). Is there a way to increase the touch radius of the gameobject so that it can be pressed more easily, or is there another solution?
private void Update() {
//User input (touches) will select cubes
if(Input.touchCount > 0) {
Touch touch = Input.GetTouch(0);
}
Touch[] touches = Input.touches;
foreach(var touchInput in touches) {
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint((Input.GetTouch(0).position)), Vector2.zero);
if (hit.collider != null) {
selectedCube = hit.collider.gameObject;
selectedCube.GetComponent<SpriteRenderer>().color = Color32.Lerp(defaultColor, darkerColor, 1);
}
}
}
Rather than increasing the object's collider size (which you discussed in the comments), how about approaching this the opposite way? Check in an area around the touch for a collision, instead of just the single point, using Physics2D.CircleCast:
private void Update() {
//User input (touches) will select cubes
if(Input.touchCount > 0) {
Touch touch = Input.GetTouch(0);
}
Touch[] touches = Input.touches;
foreach(var touchInput in touches) {
float radius = 1.0f; // Change as needed based on testing
RaycastHit2D hit = Physics2D.CircleCast(Camera.main.ScreenToWorldPoint((Input.GetTouch(0).position)), radius, Vector2.zero);
if (hit.collider != null) {
selectedCube = hit.collider.gameObject;
selectedCube.GetComponent<SpriteRenderer>().color = Color32.Lerp(defaultColor, darkerColor, 1);
}
}
}
Note that this won't be great if you've got tons of selectable objects flush against each other...but then again, increasing collider size in that case wouldn't help either. (I'd say just increase object size. No way to otherwise improve user accuracy. Or allow multi-select, and use Physics2D.CircleCastAll).
Hope this helps! Let me know if you have any questions.
EDIT: For better accuracy, since the "first" result returned by Physics2D.CircleCast may be arbitrarily selected, you can instead use Physics2D.CircleCastAll to get all objects within the touch radius, and only select the one which is closest to the original touch point:
private void Update() {
//User input (touches) will select cubes
if(Input.touchCount > 0) {
Touch touch = Input.GetTouch(0);
}
Touch[] touches = Input.touches;
foreach(var touchInput in touches) {
float radius = 1.0f; // Change as needed based on testing
Vector2 worldTouchPoint = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position);
RaycastHit2D[] allHits = Physics2D.CircleCastAll(worldTouchPoint, radius, Vector2.zero);
// Find closest collider that was hit
float closestDist = Mathf.Infinity;
GameObject closestObject = null;
foreach (RaycastHit2D hit in allHits){
// Record the object if it's the first one we check,
// or is closer to the touch point than the previous
if (closestObject == null ||
Vector2.Distance(closestObject.transform.position, worldTouchPoint) < closestDist){
closestObject = hit.collider.gameObject;
closestDist = Vector2.Distance(closestObject.transform.position, worldTouchPoint);
}
}
// Finally, select the object we chose based on the criteria
if (closestObject != null) {
selectedCube = closestObject;
selectedCube.GetComponent<SpriteRenderer>().color = Color32.Lerp(defaultColor, darkerColor, 1);
}
}
}

Categories