TetrisBlock.cs
```
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class TetrisBlock : MonoBehaviour
{
    public static bool isLineClearAnimationInProgress = false;
    public static int LinesCleared;
    public static int height = 21;
    public static int width = 10;
    public static float fallTimeDecrease = 0.1f;
    public static float minFallTime = 0.1f;
    public static float fallTime;
    public static float CurrentFallTime { get; private set; } = 1f;
    public int linesPerLevel = 10;
    public bool gameOver = false;
    
    private static int linesClearingCount = 0;
    private static Transform[,] grid = new Transform[width, height];
    private float initialFallTime = 1f;
    private float previousTime;
    private int level = 0;
    
    [SerializeField] private string lineClearAnimationTrigger = "LineClear";
    [SerializeField] private Animator myAnimationController;
    [SerializeField] private AudioClip moveSound;
    [SerializeField] private AnimationClip lineClearClip;
    
    private GameManager gameManager;
    private GameObject startMenu;
    private SpawnTetromino spawnTetromino;
    private TextMeshProUGUI linesClearedText;
    private AudioSource audioSource;
    private Animator blockAnimator;
    public TMP_Text Lines_Cleared;
    public Vector3 rotationPoint;

    void Awake()
    {
        gameManager = FindObjectOfType<GameManager>();
        spawnTetromino = FindObjectOfType<SpawnTetromino>();
        linesClearedText = GameObject.Find("Lines_Cleared").GetComponent<TextMeshProUGUI>();
        blockAnimator = GetComponentInChildren<Animator>();
        fallTime = CurrentFallTime;

        if (gameManager == null) Debug.LogError("GameManager not found in the scene!");
        if (linesClearedText == null) Debug.LogError("Lines_Cleared TextMeshProUGUI component not found!");
        if (blockAnimator == null) Debug.LogError("Animator component not found on the Tetris block or its children!");
        if (lineClearClip == null) Debug.LogError("Line Clear AnimationClip is not assigned!");
        if (gameManager != null) startMenu = gameManager.StartMenu;

        ResetGame();
        SetupAudioSource();
    }
    
    void Update()
    {
        if (startMenu != null && !startMenu.activeSelf)
        {
            InputHandler();
        }
    }

    
    void ResetGame()
    {
        if (SceneController.hasRestartedScene)
        {
            startMenu.SetActive(false);
            fallTime = initialFallTime;
            level = 0;
            LinesCleared = 0;
            linesClearingCount = 0;
            UpdateUi();
            SceneController.hasRestartedScene = false;
            Debug.Log($"Scene Has restarted, hasRestartedScene: {SceneController.hasRestartedScene}, fallTime: {fallTime}");
        }
    }


    private void InputHandler()
    {
        if (Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.A))
        {
            MoveHorizontal(-1);
        }
        else if (Input.GetKeyDown(KeyCode.RightArrow) || Input.GetKeyDown(KeyCode.D))
        {
            MoveHorizontal(1);
        }
        else if (Input.GetKeyDown(KeyCode.X))
        {
            Rotate();
        }

        if (Time.time - previousTime > (Input.GetKey(KeyCode.DownArrow) || Input.GetKey(KeyCode.S) ? fallTime / 10 : fallTime))
        {
            MoveDown();
        }
    }

    private void MoveHorizontal(int direction)
    {
        PlayMoveSound();
        transform.position += new Vector3(direction, 0, 0);
        if (!ValidMove())
            transform.position -= new Vector3(direction, 0, 0);
    }

    private void Rotate()
    {
        transform.RotateAround(transform.TransformPoint(rotationPoint), Vector3.forward, 90);
        if (!ValidMove())
            transform.RotateAround(transform.TransformPoint(rotationPoint), Vector3.forward, -90);
    }
    
    private void MoveDown()
    {
        transform.position += Vector3.down;
        if (!ValidMove())
        {
            transform.position -= Vector3.down;
            AddToGrid();
            CheckForLines();
            this.enabled = false;
            
            if (linesClearingCount == 0 && !gameOver)
            {
                spawnTetromino.NewTetromino();
            }
        }
        previousTime = Time.time;
    }

    IEnumerator ClearMultipleLines(List<int> linesToClear)
    {
        isLineClearAnimationInProgress = true;
        List<Animator> allAnimators = new List<Animator>();
    
        foreach (int line in linesToClear)
        {
            for (int j = 0; j < width; j++)
            {
                if (grid[j, line] != null)
                {
                    Animator animator = grid[j, line].GetComponent<Animator>();
                    if (animator != null && !allAnimators.Contains(animator))
                    {
                        allAnimators.Add(animator);
                        animator.SetTrigger(lineClearAnimationTrigger);
                    }
                }
            }
        }
        
        // !TODO finish this a bit more so it removes all at the same time

        float animationLength = (lineClearClip != null ? lineClearClip.length : 1f) * 6;
        Debug.Log(animationLength);
        yield return new WaitForSeconds(animationLength);
    
        foreach (int line in linesToClear)
        {
            for (int j = 0; j < width; j++)
            {
                if (grid[j, line] != null)
                {
                    Destroy(grid[j, line].gameObject);
                    grid[j, line] = null;
                }
            }
        }
    
        foreach (int line in linesToClear)
        {
            RowDown(line);
        }

        UpdateUi();

        linesClearingCount -= linesToClear.Count;
    
        isLineClearAnimationInProgress = false;
        if (!gameOver)
        {
            System.Threading.Thread.Sleep(500);
            spawnTetromino.NewTetromino();
        }
    }

    void CheckForLines()
    {
        List<int> fullLines = new List<int>();

        for (int i = height - 1; i >= 0; i--)
        {
            if (HasLine(i))
            {
                LinesCleared++;
                fullLines.Add(i);
                if (LinesCleared % linesPerLevel == 0)
                {
                    LevelUp();
                }
            }
        }

        if (fullLines.Count > 0)
        {
            linesClearingCount += fullLines.Count;
            StartCoroutine(ClearMultipleLines(fullLines));
            isLineClearAnimationInProgress = true;
        }
    }

    bool HasLine(int i)
    {
        for(int j = 0; j < width; j++)
        {
            if (grid[j, i] == null)
                return false;
        }

        return true;
    }

    void RowDown(int i)
    {
        for (int y = i; y < height - 1; y++)
        {
            for (int j = 0; j < width; j++)
            {
                if (grid[j, y + 1] != null)
                {
                    grid[j, y] = grid[j, y + 1];
                    grid[j, y + 1] = null;
                    grid[j, y].transform.position -= new Vector3(0, 1, 0);
                }
            }
        }
        HandleFloatingBlocks();
    }

    void AddToGrid()
    {
        bool gameOverFlag = false;
        foreach (Transform children in transform)
        {
            int roundedX = Mathf.RoundToInt(children.transform.position.x);
            int roundedY = Mathf.RoundToInt(children.transform.position.y);

            if (roundedX < 0 || roundedX >= width || roundedY < 0 || roundedY >= height)
            {
                gameOverFlag = true;
                break;
            }

            grid[roundedX, roundedY] = children;
            
            if (children.transform.position.y >= 19.5f)
            {
                gameOverFlag = true;
            }
        }

        if (gameOverFlag)
        {
            gameOver = true;
            FindObjectOfType<SpawnTetromino>().GameOver();
        }
    }

    bool ValidMove()
    {
        foreach (Transform children in transform)
        {
            int roundedX = Mathf.RoundToInt(children.transform.position.x);
            int roundedY = Mathf.RoundToInt(children.transform.position.y);

            if(roundedX < 0 || roundedX >= width || roundedY < 0 ||roundedY >= height)
            {
                return false;
            }

            if (grid[roundedX, roundedY] != null)
                return false;
        }

        return true;
    }

    public void UpdateUi()
    {
        if (linesClearedText == null)
        {
            if (linesClearedText == null)
            {
                Debug.LogError("Lines_Cleared TextMeshProUGUI component not found!");
                return;
            }
        }

        linesClearedText.text = "Lines Cleared: " + LinesCleared.ToString();
        Debug.Log(linesClearedText.text);
    }

    void LevelUp()
    {
        int newLevel = LinesCleared / linesPerLevel;

        if (newLevel > level)
        {
            int levelDifference = newLevel - level;
            level = newLevel;

            for (int i = 0; i < levelDifference; i++)
            {
                CurrentFallTime = Mathf.Max(CurrentFallTime - fallTimeDecrease, minFallTime);
                Debug.Log($"Updated Fall Time: {CurrentFallTime}");
            }
        }
    }

    public static void ResetStaticVariables()
    {
        grid = new Transform[width, height];
        LinesCleared = 0;
    }
    
    void HandleFloatingBlocks()
    {
        bool blocksMoved;
        do
        {
            blocksMoved = false;
            for (int y = 0; y < height - 1; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    if (grid[x, y] != null && IsFloating(x, y))
                    {
                        grid[x, y - 1] = grid[x, y];
                        grid[x, y] = null;
                        grid[x, y - 1].transform.position -= new Vector3(0, 1, 0);
                        blocksMoved = true;
                    }
                }
            }
        } while (blocksMoved);
    }
    
    bool IsFloating(int x, int y)
    {
        if (y > 0 && grid[x, y - 1] == null)
        {
            bool noLeftBlock = (x == 0) || (grid[x - 1, y] == null);
            bool noRightBlock = (x == width - 1) || (grid[x + 1, y] == null);
            bool noTopBlock = (y == height - 1) || (grid[x, y + 1] == null);

            return noLeftBlock && noRightBlock && noTopBlock;
        }
        return false;
    }
    
    void PlayMoveSound()
    {
        if (audioSource != null && moveSound != null)
        {
            audioSource.PlayOneShot(moveSound);
        }
    }
    
    
    void SetupAudioSource()
    {
        if (audioSource == null)
        {
            audioSource = gameObject.AddComponent<AudioSource>();
        }
        
        audioSource.clip = moveSound;
        audioSource.playOnAwake = false;
    }
    
}
```
SpawnTetromnio.cs:
```
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using Random = UnityEngine.Random;
using UnityEngine;

public class SpawnTetromino : MonoBehaviour
{
    public GameObject[] Tetrominoes;
    public Transform previewTransform;
    private GameObject previewPiece;
    private int currentIndex;
    private int nextIndex;
    public GameObject GameOverScreen;
    public bool GameIsOver = false;
    
    void Start()
    {
        currentIndex = Random.Range(0, Tetrominoes.Length);
        nextIndex = Random.Range(0, Tetrominoes.Length);
        
        CreatePreview();
        SpawnCurrentPiece();
        
        GameOverScreen.SetActive(false);
    }
    

    private void SpawnCurrentPiece()
    {
        if (!GameIsOver)
        {
            GameObject spawnedTetromino = Instantiate(Tetrominoes[currentIndex], transform.position, Quaternion.identity);
            TetrisBlock tetrisBlock = spawnedTetromino.GetComponent<TetrisBlock>();
        }
    }

    public void NewTetromino()
    {
        if (!GameIsOver && !TetrisBlock.isLineClearAnimationInProgress)
        {
            currentIndex = nextIndex;
            nextIndex = Random.Range(0, Tetrominoes.Length);
            
            SpawnCurrentPiece();
            
            if (previewPiece != null)
            {
                Destroy(previewPiece);
            }
            CreatePreview();
        }
    }
    
    private void CreatePreview()
    {
        switch (nextIndex)
        {
            case 0:
                previewPiece = Instantiate(Tetrominoes[nextIndex], new Vector3(12.5f, 16.73f, 1), Quaternion.identity);
                break;
            case 3:
                previewPiece = Instantiate(Tetrominoes[nextIndex], new Vector3(12.5f, 15.73f, 1), Quaternion.identity);
                break;
            case 2:
            case 6:
                previewPiece = Instantiate(Tetrominoes[nextIndex], new Vector3(12, 16.73f, 1), Quaternion.identity);
                break;
            case 5:
                previewPiece = Instantiate(Tetrominoes[nextIndex], new Vector3(13, 15.73f, 1), Quaternion.identity);
                break;
            default:
                previewPiece = Instantiate(Tetrominoes[nextIndex], previewTransform.position, Quaternion.identity);
                break;
        }
        
        TetrisBlock previewBlock = previewPiece.GetComponent<TetrisBlock>();
        if (previewBlock != null)
        {
            previewBlock.enabled = false;
        }
        
    }
    
    public void GameOver()
    {
        GameIsOver = true;
        GameOverScreen.SetActive(true);
        Debug.Log("You lost");
        // TODO!! finish the main menu (add some animation of tetras in the background), work on the shop/powerups, load/save functions and design.
    }
}
```


Optimize this code for the best responsivnes, fps and frame time