- Home /
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
Answer by Bunny83 · May 18, 2013 at 06:18 PM
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
Thanks, this looks like a pretty complete solution. I'll go away and see if I have any issues, then come back and mark this answered.
Particularly I like the fact it contains the entire process coherently in one place.
Answer by sotirosn · May 18, 2013 at 05:09 PM
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.
Answer by hiddenspring81 · May 18, 2013 at 05:59 PM
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.
Thanks for this, I'm still getting used to Unity's workflow and this makes sense now that I read it.
This one doesn't address stopping scripts running however, so I'm inclined to go with Bunny83's in terms of most complete.
Thanks for you help though - I've learnt something form this :)
Your answer
![](https://koobas.hobune.stream/wayback/20220613102508im_/https://answers.unity.com/themes/thub/images/avi.jpg)