Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Touch Input and Send Message

Discussion in 'Scripting' started by Chrisenium, Sep 8, 2018.

  1. Chrisenium

    Chrisenium

    Joined:
    May 17, 2013
    Posts:
    11
    Hi,

    So I've got a simple little mobile 2d game that I'm working on. I've got a Touch Manager script that registers my touches (see below), and should then ideally trigger some code in the script attached to the object it's touching. I don't have a huge amount of object classes that need to run a function when touched; around 4-6 (I could possibly merge a few of them), but I'd like the option of extending this with a couple more if I need to.

    I was doing some research and saw a few people suggesting just doing a SendMessage to the script component of the touched object, but I know SendMessage is a performance drainer. Is that still the best solution (is it just bad in update, but no issue if it's every 2 seconds or so)? Or do I encapsulate a number of references and set it up manually (seems messy)? Or is there some other much better solution?

    Functions include grabbing the player and dragging it, multi tapping enemies, interacting with special obstacles etc. if that's relevant.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class TouchManager : MonoBehaviour {
    4.  
    5.     private Collider2D touchCollider;
    6.  
    7.     [SerializeField]
    8.     private LayerMask inputLayerMask;
    9.  
    10.     private void Start()
    11.     {
    12.         touchCollider = GetComponent<Collider2D>();
    13.     }
    14.  
    15.     private void Update()
    16.     {
    17.         if (Input.touchCount > 0)
    18.         {
    19.             foreach (Touch touch in Input.touches)
    20.             {
    21.                 Vector3 touchPoint =
    22.                 Camera.main.ScreenToWorldPoint(Input.GetTouch(touch.fingerId).position);
    23.                
    24.                 if (touchCollider == Physics2D.OverlapPoint (touchPoint, inputLayerMask) &&
    25.                 Input.GetTouch(touch.fingerId).phase == TouchPhase.Began)
    26.                 {
    27.                     Debug.Log("Touching: " + gameObject.name);
    28.                     // This is where I need stuff to happen.
    29.  
    30.                 }
    31.             }
    32.         }
    33.  
    34.     }
    35. }
     
  2. Chrisenium

    Chrisenium

    Joined:
    May 17, 2013
    Posts:
    11
    I saw someone mention using inheritance instead of SendMessage, but I didn't fully grasp everything they suggested. Could I just make base class called Touchable Object and have all my touchable objects inherit from that? How do I communicate with that from this script though? Does this script then also inherit from that? I'm a bit of a noob with inheritance scripting still...
     
  3. GeorgeCH

    GeorgeCH

    Joined:
    Oct 5, 2016
    Posts:
    222
    You're right, SendMessage is not a sustainable solution. I usually handle touch input using a combination of RaycastAllNonAlloc, CompareTag and GetComponent, specifically:
    1. Each touch triggers a RaycastAllNonAlloc, which stores results in a pre-created RaycastHit array
    2. Loop through the array using CompareTag() to look for a specific object
    3. Depending on the results of the CompareTag() function, use the appropriate GetComponent() on it and make it do things
    Also, assuming that you've multiple things that can get tapped in this manner, you may also want to consider creating something like an ITappable interface with a public Tap() method. This way, once you've determined which objects you tapped, you can just do a GetComponent call for the ITappable interface rather than spelling out which component to get based on the object you hit.

    For example (without interfaces):

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using UnityEngine;
    4. using System;
    5.  
    6. int hitCount = 0;
    7. int colliderHitCount = 0;
    8. RaycastHit[] raycastHits = new RaycastHit[25];
    9. Collider[] colliderHits = new Collider[25];
    10.  
    11. private void CheckIfStillOnPath()
    12.     {
    13.         if (GameController.gameState == GameController.GameState.Running)
    14.         {
    15.             hitCount = Physics.RaycastNonAlloc(transform.position, Vector3.down, raycastHits, Mathf.Infinity);
    16.  
    17.             colliderHitCount = Physics.OverlapSphereNonAlloc(transform.position, 1.1f, colliderHits);
    18.  
    19.             if (hitCount > 0)
    20.             {
    21.                 CheckIfSameTile();
    22.             }
    23.  
    24.         }
    25.     }
    Code (CSharp):
    1. private void CheckIfSameTile()
    2.     {
    3.  
    4.         for (int i = 0; i < hitCount; i++)
    5.         {
    6.             if (raycastHits[i].collider.CompareTag("Path Segment"))
    7.             {
    8.                 if (currentTile == null)
    9.                 {
    10.                     currentTile = raycastHits[i].collider.gameObject;
    11.                     currentSegment = currentTile.GetComponent<CurveSegment>();
    12.                 }
    13.  
    14.                 if (raycastHits[i].collider.gameObject != currentTile)
    15.                 {
    16.                     EnterNewTile(raycastHits[i].collider.gameObject);
    17.                 }
    18.            
    19.             }
    20.         }
    21.     }
     
    Last edited: Sep 11, 2018