Discuss Scratch

2Dproductions
Scratcher
500+ posts

Is there anyway I can speed this up? (C# w/ Unity)

So I've been working on a project for a while now and was wondering how to speed up lighting.

It uses Unity's tilemaps and is very slow (getting about ~3 fps)

Here's my code,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
using System.Linq;
namespace Dragons.Lighting {
    public class FloodFill
    {
        private Dictionary<Vector2Int, float> blocksLight;
        private Dictionary<Vector2Int, float> backgroundsLight;
        private List<Vector2Int> lit;
        private Tilemap blocks;
        private Tilemap backgrounds;
        public struct LightMap {
            public Dictionary<Vector2Int, float> blocks;
            public Dictionary<Vector2Int, float> backgrounds;
        }
        public LightMap generateLightMap(Dictionary<Vector2Int, float> lightSources, float ambientIntensity, Tilemap blocks, Tilemap backgrounds, int lowestXBounds, int lowerYBounds, Vector2Int dimensions, Tile[] clearTiles) {
            this.blocks = blocks;
            this.backgrounds = backgrounds;
            LightMap finalMap = new LightMap();
            lit = new List<Vector2Int>();
            blocksLight = new Dictionary<Vector2Int, float>();
            backgroundsLight = new Dictionary<Vector2Int, float>();
            //Go through every block in light chunk and check if clear, if so, propagate light.
            for (int x = lowestXBounds; x < dimensions.x + lowestXBounds; x++) {
                for (int y = lowerYBounds; y > lowerYBounds - dimensions.y; y--) {
                    Vector2Int pos = new Vector2Int(x, y);
                    Vector2Int posX = new Vector2Int(pos.x + 1, pos.y);
                    Vector2Int negX = new Vector2Int(pos.x - 1, pos.y);
                    Vector2Int posY = new Vector2Int(pos.x, pos.y + 1);
                    Vector2Int negY = new Vector2Int(pos.x, pos.y - 1);
                    //Check if clear.
                    if (blocks.GetTile((Vector3Int) pos) == null && backgrounds.GetTile((Vector3Int) pos) == null) {
                        //Check if one or more blocks surrounding light are solid, else don't propagate.
                        if (blocks.GetTile((Vector3Int) posX) != null || backgrounds.GetTile((Vector3Int) posX) != null)
                            floodFill(pos, ambientIntensity);
                        else if (blocks.GetTile((Vector3Int) negX) != null || backgrounds.GetTile((Vector3Int) negX) != null)
                            floodFill(pos, ambientIntensity);
                        else if (blocks.GetTile((Vector3Int) posY) != null || backgrounds.GetTile((Vector3Int) posY) != null)
                            floodFill(pos, ambientIntensity);
                        else if (blocks.GetTile((Vector3Int) negY) != null || backgrounds.GetTile((Vector3Int) negY) != null)
                            floodFill(pos, ambientIntensity);
                    }
                }
            }
            //Go through every light source on map and check if in light chunk.
            for (int i = 0; i < lightSources.Count; i++) {
                var pair = lightSources.ElementAt(i);
                floodFill(pair.Key, pair.Value);
            }
            finalMap.backgrounds = backgroundsLight;
            finalMap.blocks = blocksLight;
            return finalMap;
        }
        //Internal
        private void floodFill(Vector2Int pos, float intensity) {
            //Check if intensity > 0.05 to prevent infinite loops
            //Else return.
            if (intensity > 0.05f) {
                float div = 1.5f;
                if (blocks.GetTile((Vector3Int) pos) != null) {
                    div = 2f;
                }
                //Get all propagation points.
                Vector2Int posX = new Vector2Int(pos.x + 1, pos.y);
                Vector2Int negX = new Vector2Int(pos.x - 1, pos.y);
                Vector2Int posY = new Vector2Int(pos.x, pos.y + 1);
                Vector2Int negY = new Vector2Int(pos.x, pos.y - 1);
                lit.Add(pos);
                //Light blocks.
                if (blocks.GetTile((Vector3Int) pos) != null || backgrounds.GetTile((Vector3Int) pos) != null) {
                    //Check if intensity is greater than previous intensity on block,
                    //or check if unlit.
                    if (!blocksLight.ContainsKey(pos)) 
                        blocksLight.Add(pos, intensity);
                    else
                        blocksLight[pos] = intensity;
                    if (!backgroundsLight.ContainsKey(pos)) 
                        backgroundsLight.Add(pos, intensity);
                    else
                        backgroundsLight[pos] = intensity;
                }
                float newIntensity = intensity / div;
                //Propagate light.
                if (!checkIfLit(posX, newIntensity))
                    floodFill(posX, newIntensity);
                if (!checkIfLit(negX, newIntensity))
                    floodFill(negX, newIntensity);
                if (!checkIfLit(posY, newIntensity))
                    floodFill(posY, newIntensity);
                if (!checkIfLit(negY, newIntensity))
                    floodFill(negY, newIntensity);
                return;
            } else {
                return;
            }
        }
        bool checkIfLit(Vector2Int pos, float intensity) {
            if (blocksLight.ContainsKey(pos) || backgroundsLight.ContainsKey(pos)) {
                return lit.Contains(pos) && (!(intensity > blocksLight[pos]) || !(intensity > backgroundsLight[pos]));
            }
            return lit.Contains(pos);
        }
    }
}

Any help would be greatly appreciated.

Last edited by 2Dproductions (April 19, 2019 05:54:30)

2Dproductions
Scratcher
500+ posts

Is there anyway I can speed this up? (C# w/ Unity)

bump
_RageMage_
Scratcher
24 posts

Is there anyway I can speed this up? (C# w/ Unity)

(In the floodfill function) You could bump up the value to prevent infinite loops to, say, 0.1, if a low-quality mode is added.
Furthermore, instead of continuously calling the function to propagate light, you could just set a circle to be lit around a block and manually light that, running the function only once.
If that doesn't work, you can try deleting the part in which you check if the block's intensity is greater than the previous intensity, and just add intensity to all four points of propagation disregarding how bright they already are.

Last edited by _RageMage_ (April 19, 2019 18:01:18)

2Dproductions
Scratcher
500+ posts

Is there anyway I can speed this up? (C# w/ Unity)

_RageMage_ wrote:

(In the floodfill function) You could bump up the value to prevent infinite loops to, say, 0.1, if a low-quality mode is added.
Furthermore, instead of continuously calling the function to propagate light, you could just set a circle to be lit around a block and manually light that, running the function only once.
If that doesn't work, you can try deleting the part in which you check if the block's intensity is greater than the previous intensity, and just add intensity to all four points of propagation disregarding how bright they already are.

hmm thanks! however I'm not sure what you mean by lighting blocks around a circle, it seems somewhat counter-intuitive.

bumping up the cutoff has gained me an extra ~3 fps, running as a standalone it runs at 60 fps with no problem.
2Dproductions
Scratcher
500+ posts

Is there anyway I can speed this up? (C# w/ Unity)

bump

Powered by DjangoBB