Search Unity

Generate 2D collider around shadows

Discussion in 'Scripting' started by jister, Apr 4, 2014.

  1. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    so the idea is that a kind of lemmings can walk over shadows created by the player by putting 3D objects in front of a light.

    I have some vague ideas of how i could do it but I hit a wall in both of them...

    1) raycast for the light through all the placed objects, detect if the object is within the radius of the spotlight. if so I'm thinking cast new rays from the light to top, bottom, left and right of the object onto the plane that catches it's shadow and i should get some coordinates i could create a collider from.
    problem here is, the objects consist of cubes, spheres and piramides and how to find the 4 points that are most top, bottom, left and right of the object for the light. also the objects can turn on all axis.

    2)reading grayscale values of pixels around a lemming, if it's dark enough it can walk over the pixels.
    problem here is do i read the shadow from the screen? and what if it doesn't line up for the cam a 100%, since the light can cast a bridgable gap of shadow there could maybe temporarily be something in from of that in the camera view...
    so do i read the shadow on the plane? what do i read the since the shadow is not in my texture? and it doesn't have a lightmap (or does it) that i can acces?

    2b) if i could read all "blackness" on my plane from it's shader could i generate a 2D collider from that shape?

    the last is probably the best way only my head starts spinning when it comes to shaders...
     
  2. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    aha found a better way. raycast from my light through the highest points/verts on the object onto the plane and create a EdgeCollider2D with these points ;-)
    so simple, i was was fetching it way to far.
     
  3. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    so it's kind of working...
    i still get results like the pic below
    i can't think of the right compare method to get the verts needed the track the shadow right.

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System;
    5. using System.Linq;
    6.  
    7. public class LightShadow : MonoBehaviour {
    8.  
    9.     public GameObject[] casters;
    10.     public List<GameObject> puppets;
    11.     private Light light;
    12.     private Vector3[] vertices;
    13.     private Vector3[] sortedVectors;
    14.     private List<EdgeCollider2D> ecList=new List<EdgeCollider2D>();
    15.     public GameObject edgeCollider;
    16.     public List<Vector2> pointsList=new List<Vector2>();
    17.     EdgeCollider2D ec;
    18.     int index;
    19.  
    20.     float a;
    21.     float b;
    22.     float c;
    23.  
    24.     void Start ()
    25.     {
    26.         casters=GameObject.FindGameObjectsWithTag("Object");
    27.         light=gameObject.GetComponent<Light>();
    28.     }
    29.    
    30.     void Update ()
    31.     {
    32.         CheckObjectsInLight();
    33.         CreateShadowCollision();
    34.     }
    35.     void CheckObjectsInLight()
    36.     {
    37.         for(int i=0; i< casters.Length; i++)
    38.         {      
    39.             a = casters[i].transform.position.x - transform.position.x;
    40.             b = casters[i].transform.position.z - transform.position.z;
    41.             c = Mathf.Sqrt(Mathf.Pow(a, 2)+Mathf.Pow(b, 2));
    42.  
    43.             float degree = 90 - (Mathf.Acos(a/c)*Mathf.Rad2Deg);
    44.             if(Mathf.Abs(degree) <= light.spotAngle/2)
    45.             {
    46.                 if(puppets.Count>0)
    47.                 {
    48.                     if(!puppets.Contains(casters[i]))
    49.                     {
    50.                         puppets.Add(casters[i]);
    51.                     }
    52.                 }
    53.                 else if(puppets.Count == 0)
    54.                     puppets.Add(casters[i]);
    55.             }
    56.             else if(Mathf.Abs(degree) > light.spotAngle/2)
    57.                 puppets.Remove(casters[i]);
    58.         }
    59.     }
    60.     void CreateShadowCollision()
    61.     {
    62.         if(ecList.Count < puppets.Count)
    63.         {
    64.             ec = edgeCollider.AddComponent<EdgeCollider2D>();
    65.             ecList.Add (ec);
    66.         }
    67.         else if(ecList.Count > puppets.Count)
    68.         {
    69.             ecList.Remove (ec);
    70.             Destroy(ec);
    71.         }
    72.         for(int i=0; i<puppets.Count; i++)
    73.         {
    74.             //pointsList=new List<Vector2>();
    75.             if(pointsList.Count < 3)
    76.             {
    77.                 Vector2 point = new Vector2(0,0);
    78.                 pointsList.Add(point);
    79.             }
    80.             Mesh mesh = puppets[i].GetComponent<MeshFilter>().sharedMesh;
    81.             vertices = mesh.vertices;
    82.             for(int j=0; j<vertices.Length; j++)
    83.                 vertices[j] = puppets[i].transform.TransformPoint(vertices[j]);
    84.             sortedVectors = vertices.OrderBy(v => v.y).ToArray<Vector3>();
    85.             Array.Reverse(sortedVectors);
    86.             for(int jj=0; jj<7; jj+=3)
    87.             {
    88.                 Ray ray = new Ray(transform.position, (sortedVectors[jj]-transform.position));
    89.                 RaycastHit hit;
    90.  
    91.                 int layerMask = 1 << LayerMask.NameToLayer("BackGround");
    92.                 if(Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask))
    93.                 {
    94.                     index=jj/3;
    95.                     pointsList[index] = new Vector2(hit.point.x, hit.point.y);
    96.                     Debug.DrawRay(transform.position, (sortedVectors[jj]-transform.position).normalized*100);
    97.                 }
    98.             }
    99.             Vector2[] sortedPoints = pointsList.OrderBy(v => v.x).ToArray<Vector2>();
    100.             //Vector2[] sortedPoints = pointsList.OrderBy(v => v.y).ToArray<Vector2>();
    101.             Array.Reverse(sortedPoints);
    102.             ecList[i].points = sortedPoints;
    103.         }
    104.     }
    105. //  public Vector3 SortByValue()
    106. //  {
    107. //     
    108. //  }
    109. }
    110.  
    any help would be great.
     

    Attached Files:

  4. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    no ideas as how to get the right verts that make up the shadow bounds?

    maybe i should just shoot trough all verts... but then i still need the right algorithm to sort them to pass to the edgecollider.

    or filter out the verts "invisible" to the light, but how do i detect them if i ignore the cube in my raycast... hmm

    can't help but feel there's some perfect sorting algorithm that will solve all my problems at once...

    EDIT: ok filtered out the invisible verts by making the collider of the box 0.9 in scale, this way it can easily pass through the verts but not through the body... there's probably a beter to do this but anyway it closely gives the result i wanted

    Code (csharp):
    1. for(int jj=0; jj<7; jj+=3)
    2.             {
    3.                 Ray ray = new Ray(transform.position, (sortedVectors[jj]-transform.position));
    4.                 RaycastHit hit;
    5.  
    6.                 //int layerMask = 1 << LayerMask.NameToLayer("BackGround");
    7.                 if(Physics.Raycast(ray, out hit))
    8.                 {
    9.                     if(hit.collider.tag != "Object")
    10.                     {
    11.                         index=jj/3;
    12.                         pointsList[index] = new Vector2(hit.point.x, hit.point.y);
    13.                         Debug.DrawRay(transform.position, (sortedVectors[jj]-transform.position).normalized*100);
    14.                     }
    15.                 }
    16.             }
    EDIT2: arg that doesn't cut it, of course not since now the cubes can't be in front of each other! Back to the drawing table!
     
    Last edited: Apr 6, 2014
  5. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    edit:
    i'll just keep this post updated for anyone interested.

    made a puppets class as i should have all along.
    so the edge colliders stick to one object now, but still need to make a better sorting for the list.
    anyway almost their.

    code on light source:
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System;
    5. using System.Linq;
    6.  
    7. public class ShadowCollision : MonoBehaviour {
    8.    
    9.     public GameObject[] casters;
    10.     public List<GameObject> objInLight = new List<GameObject>();
    11.     public List<Puppet> puppets = new List<Puppet>();
    12.     public List<EdgeCollider2D> ecList = new List<EdgeCollider2D>();
    13.     public GameObject edgeCollider;
    14.  
    15.     private Light light;
    16.     private float a;
    17.     private float b;
    18.     private float c;
    19.    
    20.     void Start ()
    21.     {
    22.         casters=GameObject.FindGameObjectsWithTag("Object");
    23.         light=gameObject.GetComponent<Light>();
    24.     }
    25.    
    26.     void Update ()
    27.     {
    28.         CheckObjectsInLight();
    29.     }
    30.     void CheckObjectsInLight()
    31.     {
    32.         for(int i=0; i< casters.Length; i++)
    33.         {      
    34.             a = casters[i].transform.position.x - transform.position.x;
    35.             b = casters[i].transform.position.z - transform.position.z;
    36.             c = Mathf.Sqrt(Mathf.Pow(a, 2)+Mathf.Pow(b, 2));
    37.            
    38.             float degree = 90 - (Mathf.Acos(a/c)*Mathf.Rad2Deg);
    39.             if(Mathf.Abs(degree) <= light.spotAngle/2)
    40.             {
    41.                 if(objInLight.Count == 0)
    42.                 {
    43.                     objInLight.Add(casters[i]);
    44.                     Puppet p = ScriptableObject.CreateInstance<Puppet>();
    45.                     p.Create(casters[i], transform.position);
    46.                     puppets.Add(p);
    47.                     EdgeCollider2D ec = edgeCollider.AddComponent<EdgeCollider2D>();
    48.                     ecList.Add (ec);
    49.                 }
    50.                 else if(!objInLight.Contains(casters[i]))
    51.                 {
    52.                     objInLight.Add(casters[i]);
    53.                     Puppet p = ScriptableObject.CreateInstance<Puppet>();
    54.                     p.Create(casters[i], transform.position);
    55.                     puppets.Add(p);
    56.                     EdgeCollider2D ec = edgeCollider.AddComponent<EdgeCollider2D>();
    57.                     ecList.Add (ec);
    58.                 }
    59.             }
    60.             else if(Mathf.Abs(degree) > light.spotAngle/2)
    61.             {
    62.                 if(objInLight.Contains(casters[i]))
    63.                 {
    64.                     int n = objInLight.IndexOf(casters[i]);
    65.                     print (n);
    66.                     objInLight.RemoveAt(n);
    67.                     puppets.RemoveAt(n);
    68.                     Destroy(ecList[n]);
    69.                     ecList.RemoveAt(n);
    70.                 }
    71.             }
    72.         }
    73.         for(int ii=0; ii<puppets.Count; ii++)
    74.         {
    75.             puppets[ii].origin = transform.position;
    76.             puppets[ii].CreateShadowCollision();
    77.             ecList[ii].points = puppets[ii].sortedPoints;
    78.         }
    79.     }
    80. }
    81.  
    Puppet Class:
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using System;
    6.  
    7. [System.Serializable]
    8. public class Puppet : ScriptableObject
    9. {
    10.  
    11.     public GameObject obj;
    12.     public Vector3 origin;
    13.     public Vector3[] vertices;
    14.     public Vector3[] sortedVectors;
    15.     public List<Vector2> pointsList=new List<Vector2>();
    16.     public Vector2[] sortedPoints;
    17.     public List<Vector3> shadowVerts = new List<Vector3>();
    18.     public int index;
    19.    
    20.     public void Create (GameObject go, Vector3 v)
    21.     {
    22.         obj = go;
    23.         origin = v;
    24.         for(int i=0; i<3; i++)
    25.         {
    26.             Vector2 point = new Vector2(0,0);
    27.             pointsList.Add(point);
    28.         }
    29.     }
    30.  
    31.     public void CreateShadowCollision()
    32.     {
    33.         Mesh mesh = obj.GetComponent<MeshFilter>().mesh;
    34.         vertices = mesh.vertices;
    35.         for(int j=0; j<vertices.Length; j++)
    36.             vertices[j] = obj.transform.TransformPoint(vertices[j]);
    37.         sortedVectors = vertices.OrderBy(v => v.y).ToArray<Vector3>();
    38.         Array.Reverse(sortedVectors);
    39.         shadowVerts = sortedVectors.OfType<Vector3>().ToList();
    40.         for(int i=0; i<sortedVectors.Length; i++)
    41.         {
    42.             Ray ray = new Ray(origin, (sortedVectors[i]-origin));
    43.             RaycastHit hit;
    44.             if(Physics.Raycast(ray, out hit))
    45.             {
    46.                 if(hit.collider == obj.collider)
    47.                 {
    48.                     shadowVerts.Remove(sortedVectors[i]);
    49.                 }
    50.             }
    51.         }
    52.         for(int jj=0; jj<7; jj+=3)
    53.         {
    54.             Ray ray = new Ray(origin, (shadowVerts[jj]-origin));
    55.             RaycastHit hit;
    56.             LayerMask layermask = 1<<LayerMask.NameToLayer("BackGround");
    57.             if(Physics.Raycast(ray, out hit, Mathf.Infinity, layermask))
    58.             {
    59.                 index=jj/3;
    60.                 pointsList[index] = new Vector2(hit.point.x, hit.point.y);
    61.                 Debug.DrawRay(origin, (shadowVerts[jj]-origin).normalized*50);
    62.             }
    63.         }
    64.         sortedPoints = pointsList.OrderBy(v => v.x).ToArray<Vector2>();
    65.         //Vector2[] sortedPoints = pointsList.OrderBy(v => v.y).ToArray<Vector2>();
    66.         Array.Reverse(sortedPoints);
    67.     }
    68. }
    69.  
     
    Last edited: Apr 8, 2014
  6. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    edited pervious to final version. it works very well but not perfect.
    if anyone is interested in using, you need to put your background in a layer called "BackGround" and just put the ShadowCollision script on your lightsource.
    i only needed the top part of the shadow to collide, so if your want full contour of the shadow to collide you'll have to adjust.
    anyways it's fun messing around letting characters walk on shadow.
     
  7. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    after someone asking m e about this, i rewrote and adapted it to simply check if a point of one shadow is inside an other shadow silhouette... there's a package attached with a simple test scene.
     

    Attached Files:

  8. siddhantxshirguppe

    siddhantxshirguppe

    Joined:
    Jun 21, 2018
    Posts:
    1
    hey can up upload a scene with the entire working thing?