Transparency of Canopy based on Player position

A lot of people have been super helpful during my time in these courses, so thought I’d give something back. I wanted to only fade the area around the player when I was walking underneath it, rather than the entire canopy so created a script.

Simply add it to your Canopy game object and it should work.

This is the effect:

using UnityEngine;
using UnityEngine.Tilemaps;
using System.Collections.Generic;
using System.Linq;

public class TileFader : MonoBehaviour
{
    private Tilemap _tilemap;
    private Grid _tileGrid;
    private readonly Dictionary<Vector3Int, float> _fadedTiles = new();
    private readonly List<PlayerController> _collidingPlayers = new();

    [SerializeField] private float fadeRange = 2f;
    [SerializeField] private float maxOpacity = 0.9f;

    private void Awake()
    {
        _tilemap = GetComponent<Tilemap>();
        _tileGrid = _tilemap.layoutGrid;
    }

    private void OnTriggerEnter2D(Collider2D col)
    {
        if (col.gameObject.TryGetComponent<PlayerController>(out var playerController))
        {
            _collidingPlayers.Add(playerController);
        }
    }

    private void Update()
    {
        if (_collidingPlayers.Any())
        {
            FadeTilesAroundPlayer();
        }
    }

    private void FadeTilesAroundPlayer()
    {
        foreach (var player in _collidingPlayers)
        {
            var playerPosition = player.transform.position;
            var bounds = _tilemap.cellBounds;

            // Remove tiles that are out of bounds
            foreach (var tileToRemove in _fadedTiles
                                                 .Where(x => !bounds.Contains(x.Key))
                                                 .Select(x => x.Key)
                                                 .ToList())
            {
                _fadedTiles.Remove(tileToRemove);
            }

            var nearbyTiles = GetNearbyTiles(playerPosition, fadeRange).ToList();
            if (!nearbyTiles.Any())
            {
                // Remove from colliding players here rather than OnTriggerExit2D as that 
                // way leaves some alpha faded tiles where you exited
                _collidingPlayers.Remove(player);
                return;
            }

            foreach (var cellPos in nearbyTiles)
            {
                var distance = Vector3.Distance(playerPosition, _tileGrid.GetCellCenterWorld(cellPos));

                if (distance == 0f)
                {
                    var alpha = maxOpacity;
                    var fadedColor = new Color(1f, 1f, 1f, alpha);
                    _tilemap.SetColor(cellPos, fadedColor);
                    _fadedTiles[cellPos] = alpha;
                }
                else
                {
                    var alpha = Mathf.Lerp(maxOpacity, 1f, distance / fadeRange);

                    SetCellAlpha(cellPos, alpha);
                }
            }
        }
    }

    private void SetCellAlpha(Vector3Int cellPos, float alpha)
    {
        var cellColor = _tilemap.GetColor(cellPos);
        if (Math.Abs(cellColor.a - alpha) < 0.001) return;

        var fadedColor = new Color(cellColor.r, cellColor.g, cellColor.b, alpha);

        _tilemap.SetColor(cellPos, fadedColor);

        _fadedTiles[cellPos] = alpha;
    }

    private IEnumerable<Vector3Int> GetNearbyTiles(Vector3 playerPosition, float radius)
    {
        var playerCellPos = _tileGrid.WorldToCell(playerPosition);

        for (var x = playerCellPos.x - Mathf.FloorToInt(radius); x <= playerCellPos.x + Mathf.FloorToInt(radius); x++)
        {
            for (var y = playerCellPos.y - Mathf.FloorToInt(radius);
                 y <= playerCellPos.y + Mathf.FloorToInt(radius);
                 y++)
            {
                var cellPos = new Vector3Int(x, y, playerCellPos.z);
                yield return cellPos;
            }
        }
    }
}
3 Likes

Privacy & Terms