Author Topic: Alpha-tested Hit Check  (Read 8640 times)

BeShifty

  • Jr. Member
  • **
  • Thank You
  • -Given: 5
  • -Receive: 7
  • Posts: 52
    • View Profile
Alpha-tested Hit Check
« on: January 27, 2014, 07:26:29 PM »
I whipped up an alpha-tested hit check script to use with the recent API addition. Attach it to the widget with the collider. Please note that Read/Write must be enabled on your atlas texture. I've tested it with UISprites and UITextures, but not extensively. Please let me know what you think. Here it is:

  1. using UnityEngine;
  2. using System.Collections;
  3.  
  4. [RequireComponent(typeof(UIWidget))]
  5. public class UIHitAlphaTest : MonoBehaviour {
  6.         [Range(0.01f,1f)]
  7.         public float threshold = 0.5f;
  8.  
  9.         void Start () {
  10.                 GetComponent<UIWidget>().hitCheck = HitCheck;
  11.         }
  12.  
  13.         bool HitCheck(Vector3 worldPos) {
  14.                 UIWidget w = GetComponent<UIWidget>();
  15.                 Vector3 localPos = w.cachedTransform.InverseTransformPoint(worldPos);
  16.                 Texture2D tex = w.mainTexture as Texture2D;
  17.  
  18.                 for( int i = 0; i < w.geometry.verts.size; i+=4 ) {
  19.                         if( CheckTriangle( localPos,
  20.                                         w.geometry.verts.buffer[i], w.geometry.verts.buffer[i+2], w.geometry.verts.buffer[i+1],
  21.                                     w.geometry.uvs.buffer[i], w.geometry.uvs.buffer[i+2], w.geometry.uvs.buffer[i+1], tex )     ||
  22.                                 CheckTriangle( localPos,
  23.                                     w.geometry.verts.buffer[i], w.geometry.verts.buffer[i+3], w.geometry.verts.buffer[i+2],
  24.                                     w.geometry.uvs.buffer[i], w.geometry.uvs.buffer[i+3], w.geometry.uvs.buffer[i+2], tex ) )
  25.                                 return true;
  26.                 }
  27.                 return false;
  28.         }
  29.  
  30.         bool CheckTriangle(Vector3 pos, Vector3 v1, Vector3 v2, Vector3 v3, Vector2 uv1, Vector2 uv2, Vector2 uv3, Texture2D tex) {
  31.                 Vector3 normal = Vector3.Cross( v3-v1, v2-v1 );
  32.                 if( normal.sqrMagnitude < 0.01f )
  33.                         return false;
  34.  
  35.                 pos = pos - Vector3.Dot( pos-v1, normal ) * normal;             //Project point onto plane
  36.  
  37.                 float area = normal.magnitude;  //Technically area*2
  38.  
  39.                 float a1 = Vector3.Cross( v2-pos, v3-pos ).magnitude / area;
  40.                 if( a1 < 0 || a1 > 1 )
  41.                         return false;
  42.  
  43.                 float a2 = Vector3.Cross( v3-pos, v1-pos ).magnitude / area;
  44.                 if( a2 < 0 || a2 > 1 )
  45.                         return false;
  46.  
  47.                 float a3 = 1f-a1-a2;    //Vector3.Cross( v1-pos, v2-pos ).magnitude / (2*area);
  48.                 if( a3 < 0 || a3 > 1 )
  49.                         return false;
  50.  
  51.                 Vector2 finalUV = uv1 * a1 + uv2 * a2 + uv3 * a3;
  52.                 Color pixel = tex.GetPixel( (int)(finalUV.x * tex.width), (int)(finalUV.y * tex.height) );
  53.  
  54.                 return pixel.a >= threshold;
  55.         }
  56. }
  57.  
« Last Edit: January 28, 2014, 01:16:35 PM by BeShifty »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Alpha-tested Hit Check
« Reply #1 on: January 28, 2014, 09:17:35 AM »
I would suggest you convert the hit position into relative-to-UIWidget.geometry coordinates. This way you don't need to go through the whole WriteToBuffers part, and all you have to do is go through UIWidget.geometry.verts and UVs instead.

BeShifty

  • Jr. Member
  • **
  • Thank You
  • -Given: 5
  • -Receive: 7
  • Posts: 52
    • View Profile
Re: Alpha-tested Hit Check
« Reply #2 on: January 28, 2014, 01:17:58 PM »
Good call. I've updated the code with that change, as well as writing a smarter inside-triangle test. It should be pretty fast now. I've also tested that this script works with Bitmap labels.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Alpha-tested Hit Check
« Reply #3 on: January 29, 2014, 09:55:39 AM »
Very nice. I'll add this to the Useful Stuff sticky. :)

ComicSans

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 6
    • View Profile
Re: Alpha-tested Hit Check
« Reply #4 on: March 07, 2014, 09:54:03 AM »
I think there's a bug in the winding order of one of the CheckTriangle calls.  I switched my local copy to the following, and it seemed to fix it.  Regardless, thanks for this.  I absolutely needed it and you saved me a ton of time.

Also, if you use this, make sure you have the latest NGUI.  There was a bug in HitCheck as of January, per this thread.

Cheers, ComicSans

  1.         if (CheckTriangle(
  2.             localPos, w.geometry.verts.buffer[i], w.geometry.verts.buffer[i + 1],
  3.             w.geometry.verts.buffer[i + 2], w.geometry.uvs.buffer[i], w.geometry.uvs.buffer[i + 1],
  4.             w.geometry.uvs.buffer[i + 2], tex)
  5.             || CheckTriangle(
  6.                 localPos, w.geometry.verts.buffer[i + 2], w.geometry.verts.buffer[i + 3],
  7.                 w.geometry.verts.buffer[i], w.geometry.uvs.buffer[i + 2],
  8.                 w.geometry.uvs.buffer[i + 3], w.geometry.uvs.buffer[i], tex)) {
  9.  

gk

  • Newbie
  • *
  • Thank You
  • -Given: 1
  • -Receive: 1
  • Posts: 7
    • View Profile
Re: Alpha-tested Hit Check
« Reply #5 on: May 28, 2015, 12:40:42 AM »
Was working fine with Unity 4, after I switched to Unity 5 the script seem to not working.

HISPID

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 2
    • View Profile
Re: Alpha-tested Hit Check
« Reply #6 on: May 19, 2016, 07:10:14 AM »
Was working fine with Unity 4, after I switched to Unity 5 the script seem to not working.

Yep, does not work on Unity 5. I think there are were changed behaviour of widget.cachedTransform.InverseTransformPoint method.
Here is updated version, which works on Unity 5. Added iteration over all UIWidget childs, to check child's alpha too.

  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4.  
  5. [RequireComponent(typeof(UIWidget))]
  6. public class UIHitAlphaTest : MonoBehaviour
  7. {
  8.         [Range(0.01f,1f)]
  9.         public float threshold = 0.5f;
  10.  
  11.         List<UIWidget> childs = new List<UIWidget>();
  12.  
  13.         void Start ()
  14.         {
  15.                 UIWidget w = GetComponent<UIWidget>();
  16.                 w.hitCheck = HitCheck;
  17.  
  18.                 childs.Add(w);
  19.                 childs.AddRange(w.GetComponentsInChildren<UIWidget>());
  20.         }
  21.  
  22.         Vector3 t1p1 = Vector3.zero;
  23.         Vector3 t1p2 = Vector3.zero;
  24.         Vector3 t1p3 = Vector3.zero;
  25.  
  26.         Vector3 t2p1 = Vector3.zero;
  27.         Vector3 t2p2 = Vector3.zero;
  28.         Vector3 t2p3 = Vector3.zero;
  29.  
  30.         Vector2 t1uv1 = Vector2.zero;
  31.         Vector2 t1uv2 = Vector2.zero;
  32.         Vector2 t1uv3 = Vector2.zero;
  33.  
  34.         Vector2 t2uv1 = Vector2.zero;
  35.         Vector2 t2uv2 = Vector2.zero;
  36.         Vector2 t2uv3 = Vector2.zero;
  37.  
  38.         bool HitCheck(Vector3 worldPos)
  39.         {
  40.                 for (int widgetIndex = 0; widgetIndex < childs.Count; widgetIndex++)
  41.                 {
  42.                         UIWidget widget = childs[widgetIndex];
  43.                         Vector3 localPos = widget.cachedTransform.InverseTransformPoint(worldPos);
  44.                         Texture2D tex = widget.mainTexture as Texture2D;
  45.  
  46.                         for( int i = 0; i < widget.geometry.verts.size; i += 4 )
  47.                         {
  48.                                 t1p1 =  widget.geometry.verts.buffer[i];
  49.                                 t1p2 =  widget.geometry.verts.buffer[i + 1];
  50.                                 t1p3 =  widget.geometry.verts.buffer[i + 2];
  51.  
  52.                                 t2p1 =  widget.geometry.verts.buffer[i + 2];
  53.                                 t2p2 =  widget.geometry.verts.buffer[i + 3];
  54.                                 t2p3 =  widget.geometry.verts.buffer[i];
  55.  
  56.  
  57.                                 t1uv1 = widget.geometry.uvs.buffer[i];
  58.                                 t1uv2 = widget.geometry.uvs.buffer[i + 1];
  59.                                 t1uv3 = widget.geometry.uvs.buffer[i + 2];
  60.  
  61.                                 t2uv1 = widget.geometry.uvs.buffer[i + 2];
  62.                                 t2uv2 = widget.geometry.uvs.buffer[i + 3];
  63.                                 t2uv3 = widget.geometry.uvs.buffer[i];
  64.  
  65.                                 bool t1 = CheckTriangle
  66.                                         (localPos,
  67.                                                 t1p1, t1p2, t1p3,
  68.                                                 t1uv1, t1uv2, t1uv3,
  69.                                                 tex);
  70.  
  71.                                 bool t2 = CheckTriangle
  72.                                         (localPos,
  73.                                                 t2p1, t2p2, t2p3,
  74.                                                 t2uv1, t2uv2, t2uv3,
  75.                                                 tex);
  76.  
  77.                                 if (t1 || t2)
  78.                                 {
  79.                                         return true;
  80.                                 }
  81.                         }
  82.                 }
  83.  
  84.                 return false;
  85.         }
  86.  
  87.         bool CheckTriangle(Vector3 pos, Vector3 v1, Vector3 v2, Vector3 v3, Vector2 uv1, Vector2 uv2, Vector2 uv3, Texture2D tex)
  88.         {
  89.                 // Normal of triangle
  90.                 Vector3 normal = Vector3.Cross( v3-v1, v2-v1 );
  91.  
  92.                 if( normal.sqrMagnitude < 0.01f )
  93.                         return false;
  94.  
  95.                 pos = pos - Vector3.Dot( pos-v1, normal ) * normal; // Project point onto plane
  96.  
  97.                 float xScale = (pos.x - v2.x) / (v3.x - v2.x);
  98.                 float yScale = (pos.y - v1.y) / (v2.y - v1.y);
  99.  
  100.                 Vector2 finalUV = new Vector2(uv2.x + (uv3.x - uv2.x) * xScale, uv1.y + (uv2.y - uv1.y) * yScale);
  101.                 Color pixel = tex.GetPixel( (int)(finalUV.x * tex.width), (int)(finalUV.y * tex.height) );
  102.  
  103.                 return pixel.a >= threshold;
  104.         }
  105. }
« Last Edit: October 06, 2017, 03:07:38 AM by HISPID »