Hey,
I want to achieve the following: I have a Tile-Based RTS (2d plane from above). Each tile is currently its own Entity which has a small plane as its rendermesh.
When a City is created on a tile I want to overlay the PNG in the attachments, to show the user visually where they can create new buildings. How should I do that?
First I tried MaterialOverrides, but I could not understand how to use them to override the BaseMap
property of the LitShader. Then I thought I might have to have two different materials and switch them around during runtime, but I also did not find a good guide. Or is there an easier way altogether?
Any pointers are welcome! Thanks!
Hi!
BaseMap property is not in the list of supported properties of the urp Lit shader. As you can not override if, you have two choices, shader graph or material swap. I’ll definitely go with material swap, as it is simpler and probably more efficient than material override (I believe that if texture inputs cannot be overridden in standard shaders, it is because unity does not recommend or support it). To swap material at runtime, it is fairly easy if you have your overlay material in your RenderMeshArray. Just query for MaterialMeshInfo component on your entity and swap the material index to the overlay one.
The complicated part comes in baking. You need to make sure your RenderMeshArray has the overlay material at a place you can find in the array. One thing you can do is place a dummy object in the top position of your subscenes (with just a mesh filter with a dummy quad mesh, and a mesh renderer with the overlay material). It would then place the overlay material on top of your RenderMeshArray material list, with its index being -1.
If you then cache the original material index in a IComponentData (either with a baking system or in oncreate of your swapping material system), you can swap it back to the original material afterward. I made a small script to show you how it works, just add the CacheMaterialIndexAuthoring to your tiles, and don’t forget to add the dummy object with overlay material on top of your subscene hierarchy :
using System.Collections;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Rendering;
using UnityEngine;
[BurstCompile]
public partial struct MaterialSwapScript : ISystem
{
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<MaterialIndex>();
}
public void OnUpdate(ref SystemState state)
{
if (Input.GetKeyDown(KeyCode.A))
{
foreach ( var (materialInfoRo, materialIndex) in SystemAPI.Query<RefRW<MaterialMeshInfo>, MaterialIndex>())
{
materialInfoRo.ValueRW.Material = materialInfoRo.ValueRO.Material == -1 ? materialIndex.Index : -1;
}
}
}
}
[BurstCompile]
public partial struct MaterialCacheScript : ISystem
{
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<CacheMaterialIndexFlag>();
}
public void OnUpdate(ref SystemState state)
{
var ecb = new EntityCommandBuffer(Allocator.TempJob);
foreach ( var (materialInfo,entity) in SystemAPI.Query<MaterialMeshInfo>().WithEntityAccess().WithAll<CacheMaterialIndexFlag>())
{
ecb.AddComponent(entity, new MaterialIndex()
{
Index = materialInfo.Material
});
ecb.RemoveComponent<CacheMaterialIndexFlag>(entity);
}
ecb.Playback(state.EntityManager);
ecb.Dispose();
}
}
using Unity.Entities;
public struct MaterialIndex : IComponentData
{
public int Index;
}
public struct CacheMaterialIndexFlag : IComponentData
{
}
using Unity.Entities;
using UnityEngine;
public class CacheMaterialIndexAuthoring: MonoBehaviour
{
public class Oven : Baker<CacheMaterialIndexAuthoring>
{
public override void Bake(CacheMaterialIndexAuthoring authoring)
{
AddComponent<CacheMaterialIndexFlag>(GetEntity(TransformUsageFlags.Dynamic));
}
}
}
1 Like
Thank you very much for the elaborate answer! Another way I came up with is splitting the actual game tile entity (simulation data) and its visual stuff (meshes) into two different Entities.
That way, if I create a settlement, I can just delete the visual entity for that tile and instantiate a new one from a Prefab, which will have the right material immediately.
1 Like
Good to know you found a way, and thank you for letting me know it, i did not thought about this kind of solution, so good to know it can be done that way.
On the solution i provided, one could also cache the overlay index in the material index component. It would save from the trick of the dummy hierarchy top position renderer.
Anyway, i’m glad you found a solution to your problem
1 Like