Hide walls on collisions in 2D, finding an optimal way

Hello.
I don’t really have a problem just came in search for a better solution for this kind of situation. I wanted to control my Wall Resizement from one manager. Each of walls would have script attached and it would have stored two kinds of sprites, one for when wall is hidden one when is fully visibile.

9225135--1288200--upload_2023-8-15_21-5-2.png9225135--1288203--upload_2023-8-15_21-5-9.png
Manager is looking like this:

using System.Collections.Generic;
using UnityEngine;
using Unity.Netcode;

public class WallResize : NetworkBehaviour
{
    public GameObject player;
    [SerializeField] private float radiusTest = 0.5f;
    [SerializeField] private float testY = 0.15f;
    [SerializeField] private float testX = -0.2f;

    public override void OnNetworkSpawn()
    {
        player = GameObject.FindWithTag("Player");
    }
    private void Update()
    {
        GetWalls();
    }
    private void GetWalls()
    {
        if (player != null)
        {
            Vector2 pointA = player.transform.position + new Vector3(testX, testY);
            Vector2 pointB = pointA + new Vector2(radiusTest, -radiusTest);
            Collider2D[] colliders = Physics2D.OverlapAreaAll(pointA, pointB);
            foreach (Collider2D collider in colliders)
            {
                if (collider.transform.position.z == player.transform.position.z)
                {
                   WallResizeSpriteSetter wallsToResize = collider.GetComponentInChildren<WallResizeSpriteSetter>();
                   if (wallsToResize != null)
                   {
                       wallsToResize.GetIndividualWalls();
                   }
                }
            }
        }
    }

And it resizes a wall down just fine. Thing is when I try to reset it to default size I don’t have an idea of an optimal way to do so. Each wall has this script on it, which takes one side, and changes it’s childre’s sprites accordingly.

using UnityEngine;


public class WallResizeSpriteSetter : MonoBehaviour
{
    // Start is called before the first frame update    public Sprite[] normalWall;
    public Sprite[] normalWall;
    public Sprite[] cutOffWall;
    public bool wallResized = false;
    SpriteRenderer[] spriteRenderers;

public void GetIndividualWalls()
    {
        for (int i = 0; i < this.transform.childCount; i++)
        {
            Transform child = this.transform.GetChild(i);
            GetIndWallParts(child);
        }

    }
    private void GetIndWallParts(Transform partOfWall)
    {
        if (partOfWall.gameObject.name.Contains("Wall"))
        {
            spriteRenderers = partOfWall.GetComponentsInChildren<SpriteRenderer>();
            if (wallResized) // this is used with OnTriggerEnter/Exit2D
            {
                SetSprites(normalWall, spriteRenderers);
            }
            else
            {
                SetSprites(cutOffWall, spriteRenderers);
            }

        }
    }


    private void SetSprites(Sprite[] sprites, SpriteRenderer[] actualSprites)
    {
        for (int i = 0; i < actualSprites.Length; i++)
        {
            if (i < sprites.Length)
            {
                actualSprites[i].sprite = sprites[i];
                actualSprites[i].enabled = true;
            }
            else
            {
                actualSprites[i].enabled = false;
            }
        }
    }
}

I’ve managed to make it work with Update() on these scripts but that doesn’t sound very good in terms of potential performance and when more walls will be present.
Also OnTriggerExit2D and OnTriggerEnter2D works just fine, but again I think there is better way to do so.

May current way of finding collisions for this problem looks very promising but can’t really grasp it.
9225135--1288206--upload_2023-8-15_21-13-22.png

9225135--1288191--upload_2023-8-15_21-4-9.png
9225135--1288194--upload_2023-8-15_21-4-18.png

Update may not be the way, but i think TriggerEnter and Exit would be more performant. They are only called, as implied, once on Enter and then Exit. Use the profiler to see how much switching from Update to OnTrigger saves.

Another question, is it running slowly currently or are you just trying to optimize as you go?

Also, don’t use the “All” suffixes as they create an array which you’ll then leave to the GC creating memory pressure leading to more GC activity and poorer performance.

All 2D physics queries allow you to pass a List and reuse it; it’ll automatically have its capacity changed for you too. Always use the queries without a suffix so in your case, Physics2D.OverlapArea.

Using trigger callbacks will be quicker in this case if you’re simply detecting against static trigger colliders.

I am a little confused over the use of “Area” though in this isometric layout.

@Cornysam & @MelvMay for some reason I can’t answer under your answers.

Update was getting really slow after a while. Was just wondering how heave it might me to have OnTriggerEnter/Exit2D on every wall.
I was just trying to optimize. I was expecting that going for Update() on every wall can’t be optimal. So tried to play with it little bit.

Well in a case where I go with OnTriggerEnter/Exit2D I won’t be really in need Physics2D.OverlapArea to do what I want. Thanks for pointing ‘All’ prefix downside though.

So this what I ended up with.

using UnityEngine;
public class WallResizeSpriteSetter : MonoBehaviour
{
    public Sprite[] normalWall;
    public Sprite[] cutOffWall;
    public bool wallResized = false;
    SpriteRenderer[] spriteRenderers;

    private void OnTriggerExit2D(Collider2D collision)
    {
        if (wallResized == false) { return; }
        GetIndividualWalls();
        wallResized = false;
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.transform.position.z == this.gameObject.transform.position.z)
        {
            GetIndividualWalls();
        }
        wallResized = true;
    }
    public void GetIndividualWalls()
    {
        for (int i = 0; i < this.transform.childCount; i++)
        {
            Transform child = this.transform.GetChild(i);
            GetIndWallParts(child);
        }

    }
    private void GetIndWallParts(Transform partOfWall)
    {
        if (partOfWall.gameObject.name.Contains("Wall"))
        {
            spriteRenderers = partOfWall.GetComponentsInChildren<SpriteRenderer>();
            if (wallResized)
            {
                SetSprites(normalWall, spriteRenderers);
            }
            else
            {
                SetSprites(cutOffWall, spriteRenderers);
            }
        }
    }

    private void SetSprites(Sprite[] sprites, SpriteRenderer[] actualSprites)
    {
        for (int i = 0; i < actualSprites.Length; i++)
        {
            if (i < sprites.Length)
            {
                actualSprites[i].sprite = sprites[i];
                actualSprites[i].enabled = true;
            }
            else
            {
                actualSprites[i].enabled = false;
            }
        }
    }
}
1 Like

The only minor observation in the above is to not compare float values as you do with the Z position because of float precision issues; use Mathf.Approximately.