I have Shadow Caster 2D working on regular objects (doors in my game) but it doesn’t seem to work on my TileMap walls.
Is this a known issue or am I doing something wrong?
I have Shadow Caster 2D working on regular objects (doors in my game) but it doesn’t seem to work on my TileMap walls.
Is this a known issue or am I doing something wrong?
The current ShadowCaster2D component appears to only take the bounds of a collider and/or object. You can modify the Awake() function in ShadowCaster2D.cs to check if the type of collider is a polygon, then use the path of the polygon as the shape for the generated shadow mesh. The file was located at “Library\PackageCache\com.unity.render-pipelines.universal@7.1.7\Runtime\2D” for me.
private void Awake() {
if(m_ApplyToSortingLayers == null)
m_ApplyToSortingLayers = SetDefaultSortingLayers();
Bounds bounds = new Bounds(transform.position, Vector3.one);
Renderer renderer = GetComponent<Renderer>();
if (renderer != null)
{
bounds = renderer.bounds;
}
else
{
Collider2D collider = GetComponent<Collider2D>();
if (collider != null)
if (collider.GetType() == typeof(PolygonCollider2D)) {
m_ShapePath = Array.ConvertAll<Vector2, Vector3>(((PolygonCollider2D)collider).GetPath(0), vec2To3);
m_UseRendererSilhouette = false;
} else {
bounds = collider.bounds;
}
}
Vector3 relOffset = bounds.center - transform.position;
if (m_ShapePath == null || m_ShapePath.Length == 0)
{
m_ShapePath = new Vector3[]
{
relOffset + new Vector3(-bounds.extents.x, -bounds.extents.y),
relOffset + new Vector3(bounds.extents.x, -bounds.extents.y),
relOffset + new Vector3(bounds.extents.x, bounds.extents.y),
relOffset + new Vector3(-bounds.extents.x, bounds.extents.y)
};
}
}
I then use a different script that requires a CompositeCollider2D to be attached to my tilemap and builds a ShadowCaster2D for each distinct path in the composite collider, which looks like the following:
public void Start() {
tilemapCollider = GetComponent<CompositeCollider2D>();
GameObject shadowCasterContainer = GameObject.Find("shadow_casters");
for (int i = 0; i < tilemapCollider.pathCount; i++) {
Vector2[] pathVertices = new Vector2[tilemapCollider.GetPathPointCount(i)];
tilemapCollider.GetPath(i, pathVertices);
GameObject shadowCaster = new GameObject("shadow_caster_" + i);
PolygonCollider2D shadowPolygon = (PolygonCollider2D)shadowCaster.AddComponent(typeof(PolygonCollider2D));
shadowCaster.transform.parent = shadowCasterContainer.transform;
shadowPolygon.points = pathVertices;
shadowPolygon.enabled = false;
ShadowCaster2D shadowCasterComponent = shadowCaster.AddComponent<ShadowCaster2D>();
shadowCasterComponent.selfShadows = true;
}
}
Editing ShadowCaster2D should be a somewhat temporary fix until Unity alter the implementation, as in my case it can be refetched and your changes will be lost (unless you fork the ScriptableRenderPipeline repository and point the package manager to use your changes) - so I’d recommend saving the modified ShadowCaster2D script somewhere as a backup to copy and paste if it does the same for you.
Thanks, I’ve tried your fix but I receive the following error after modifying ShadowCaster2D.cs
Library\PackageCache\com.unity.render-pipelines.universal@7.1.5\Runtime\2D\ShadowCaster2D.cs(102,116): error CS0103: The name ‘vec2To3’ does not exist in the current context
Ah I forgot to add this method in my original snippet as-well, add this method in and it should hopefully work:
private Vector3 vec2To3(Vector2 inputVector) {
return new Vector3(inputVector.x, inputVector.y, 0);
}
That fixed the compiler error but sadly the light is still going through my walls.
At least I know it’s a limitation of the component. Sounds like it will be better to wait for them to implement Tilemap shadows.
Thanks for your help anyway.
@thomasedw Your solution works great! @ayockel90 just make sure that you have a TilemapCollider2D on the tilemap, with the “Used By Composite” checkbox checked, in addition to having a CompositeCollider2D component on there.
I made some changes thanks to your ideas and got it working on dynamic tilemaps (adding and removing tiles):
@thomasedw you’re amazing! Your fix works perfectly!
@thomasedw Well I just discovered that when you close down and reopen Unity, that the ShadowCaster2D script reverts back to it’s original form, so anything I added isn’t there anymore. Do you know of a way to prevent this?
You need to fork this GitHub repository https://github.com/Unity-Technologies/ScriptableRenderPipeline, make changes in that and then you link to your forked version in the package manager by clicking the “+” icon and choosing “Add package from git URL…”
@thomasedw Thank you for your help! I tried adding it in the package manager using this forked URL:
But I get an error saying "Cannot perform upm operation: Unable to add package…Package name is invalid. Am I using the incorrect URL?
Perhaps change the name of the package… like CustomScriptableRenderPipeline
It’s been a while since I first added it - but @dotaxis posted on this here: https://discussions.unity.com/t/772801 , they altered their manifest.json file directly (incase using the package manager doesn’t work).
@thomasedw Ahhhh beautiful, this worked perfectly…thanks again, you’re awesome!
hey, i wrote an alternative script that needs no tinkering with the pipeline package. it uses a simple prefab with nothing but a 1 by 1 shadowcaster, instantiates it a bunch and sets position and scaling (to reduce the number of casters). i posted it on reddit, theres a screenshot aswell, i dont wanna sum it up again^^ Reddit - Dive into anything
I replied to this here:
https://forum.unity.com/threads/the-new-2d-lighting-on-tilemaps-advice-and-suggestions.810927/#post-5436126
Is there any plan by Unity dev team to implement a built-in solution for this?
This should really be officially supported by Unity…
I do not know if it’s the same problem, but I was having issues with shadows when scaling the game object (Unity Shadow Caster 2D asset causing issues when object's scale is altered. - Questions & Answers - Unity Discussions). The issue seems to be fiXed in the lastest release 2020.1.0f1
If you would like to avoid having to mess around with packages and forks, you could use reflection instead. I’ve done:
private static FieldInfo sShapeField = typeof(ShadowCaster2D).GetField("m_ShapePath", BindingFlags.Instance | BindingFlags.NonPublic);
private static void SetShapePath(ShadowCaster2D caster, Vector2[] points)
{
Vector3[] shapePath = Array.ConvertAll(points, vec2To3);
sShapeField.SetValue(caster, shapePath);
}
private static Vector3 vec2To3(Vector2 inputVector)
{
return new Vector3(inputVector.x, inputVector.y, 0);
}