Approach for showing a building template before placement

Hi Everyone,

I can’t seem to find any discussion of what I am trying to do here, so perhaps someone here can help me.

I need to display building templates (think a 50% opacity 1 material version of the building) under the mouse before it is placed.

I’m not sure the best way to approach this, the options I’ve considered seem messy and I am wondering if I might be missing something obvious.

I’ve considered just instantiating the prefabs of the actual building and holding them in a certain state. This would require me to push out the materials to a temporary array, replace them all with one material, then swap them back if the building was placed, it seems a bit cumbersome.

The other solution which seemed obvious was to have an object which just rendered the relevant mesh, with one material instead of the correct ones, but many of the prefabs contain multiple meshes, so it would also be a lot of config.

Perhaps there isn’t an easier way, but I would be glad for any opinions/help.

Thanks for your time

I would use something like this:

// C#
public Transform prefab;
public Material PreviewMaterial;

public Transform CreatePreview (Transform aPrefab)
{
    Transform obj = (Transform)Instantiate(prefab);
    foreach (var renderer in obj.GetComponentsInChildren<Renderer>(true))
        renderer.sharedMaterial = PreviewMaterial;
    // If the building / object has some scripts or other components
    // which shouldn't be on the preview, remove them here:
    foreach (var script in obj.GetComponentsInChildren<MonoBehaviour>(true))
        Destroy(script);
    return obj;
}

That way you can easily create a preview version of any prefab and have always the same “preview look”. When it comes to creating the actual building, just Instantiate the prefab normally.

You can also add a script to the preview which can hold the prefab reference from which it has been created, so it’s easier to Instantiate the actual building / object

public class PreviewObject : MonoBehaviour
{
    public Transform prefah;
    public Transform Place()
    {
        Transform obj = (Transform)Instantiate(prefab, transform.position, transform.rotation);
        Destroy(gameObject);
        return obj;
    }
}

In this case your CreatePreview function should look like this:

public PreviewObject CreatePreview (Transform aPrefab)
{
    Transform obj = (Transform)Instantiate(prefab);
    foreach (var renderer in obj.GetComponentsInChildren<Renderer>(true))
        renderer.sharedMaterial = PreviewMaterial;
    foreach (var script in obj.GetComponentsInChildren<MonoBehaviour>(true))
        Destroy(script);
    PreviewObject po = obj.AddComponent<PreviewObject>();
    po.prefab = aPrefab;
    return po;
}

The returned PreviewObject can be used to move / place the preview and when done, just call Place() and the preview will be replaced be a real object

I have a game where I do this very thing. When I place the object I instantiate the template and broadcast “OnPlacement” on the clone which causes all the renderes to change material and sets the building up with the environment. Maybe if you don’t want to change materials you could have 2 prefabs, the template and the actual, and clone the actual even though you show the template.

I implemented a similar system for my game. While the player was placing their units on a map, each unit would appear transparent, and upon placing them, they’d become opaque. I did this by creating a new component, which was attached to each prefab instance of my unit type,

public class TransparencyController : MonoBehaviour
{
  const float TRANSPARENT_AMOUNT = 0.5f;

  public void ToggleTransparency (bool transparent)
  {
    float desiredTransparency = transparent ? TRANSPARENT_AMOUNT : 1.0f;
    // Find all the renderers on the object, including the children
    var renderers = GetComponentsInChildren<Renderer>(true);
    foreach (var renderer in renderers)
    {
      var color = renderer.material.color;
      renderer.material.color = new Color(color.r, color.g, color.b, desiredTransparency.a);
    }
  }
}

Before placing my units, I’d call ToggleTransparency(true) on each unit, making them appear transparent. Then upon placing them on the map, I’d call ToggleTransparency(false), which would render them opaque. This a very clean, very simple way of achieving the effect that you described.