DEV

Application and Web development

Web development and design

  • As first complete project, I develop from scratch a whole website for online courses for a business school based in Valencia.

  • As second project, I had to built front page for this 3D printing company for their Barcelona, Sevilla, Valencia, Malaga and Madrid offices.

  • This was one of my favourit project because of the structure particularity and nice visuals.

  • Using only CSS, I had to rebuilt all visual and few features of this company after client will. we were exchanging on the project tasts by email.

  • Application development with FIGMA and MARVEL APP

    3D/Game development

    *Run -BAYONA.exe- file*

                              
    
                            #%:.     
                     #%=   ###%=:    
                    ##%=   |##%::    
                    ##%=   ###%=:    
                     #%%=  |##%=:    
                     ##%== ###%=:    ===
                      ##%%=!##%=:    ====
                       ###%%##%=   :==== 
                        ######%=: .:==== 
                          ####%%======= 
                           ###%%%===: 
                SCRIPT     |%#%%=:=:  
           VIDEOGAME       ####%=:   
                           |##%%:    
                    -------####%:---=
                       AI BEHAVIOURS
    
                    |   SCROLL DOWN   |  
                    v                 v
    
    THIS CODE IS THE BASE STATE OF EACH ENEMY
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public abstract class EnemyBaseState: MonoBehaviour
    {
        public abstract void EnterState(EnemyStateAgent enemy);
        public abstract void UpdateState(EnemyStateAgent enemy);
    
    }
    
    
    THIS CODE IS THE ENEMY ROTATION
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.AI;
    
    public class EnemyIdleStateRotate : EnemyBaseState
    {
        public float rotationAngle; //in degrees
        public float rotationSpeed; // in seconds
        public float waitTime;
        float stepAngle;
        float timer;
        float degreesTurned;
        float lastAnimation;
        float animation;
        Vector3 originalPos;
    
        private void Start()
        {
            stepAngle = rotationAngle / rotationSpeed;
            originalPos = transform.position;
            
        }
        public override void EnterState(EnemyStateAgent enemy)
        {
            enemy.animator.SetBool("EnemyMove", false);
            timer = -waitTime;
            degreesTurned = 0;
            lastAnimation = 0;
        }
    
        float Bezier(float t)
        {
            return -6f * (t-1f) * t;
        }
    
        public static float EaseIn(float t)
        {
            return Mathf.Pow(t, 3);
        }
        public static float Flip(float x)
        {
            return 1 - x;
        }
    
        public static float Spike(float t)
        {
            if (t <= .5f)
                return EaseIn(t / .5f);
    
            return EaseIn(Flip(t) / .5f);
        }
        public override void UpdateState(EnemyStateAgent enemy)
        {
            timer += Time.deltaTime;
            if (timer >= 0 && timer <= rotationSpeed)
            {
                animation = Mathf.Lerp(0, rotationAngle, EaseIn(timer / rotationSpeed));
                transform.Rotate(0, animation - lastAnimation, 0);
                lastAnimation = animation;
            }
            else if (timer > rotationSpeed && timer <= (rotationSpeed * 3))
            {
                animation = Mathf.Lerp(rotationAngle, -rotationAngle, EaseIn((timer - rotationSpeed) / (rotationSpeed * 2)));
                transform.Rotate(0, animation - lastAnimation, 0);
                lastAnimation = animation;
            }
            //else if (timer >= (rotationSpeed * 3) && timer <= (rotationSpeed * 4))
            //{
            //    animation = Mathf.Lerp(-rotationAngle, 0, EaseIn((timer - 3 * rotationSpeed) / (rotationSpeed)));
            //    transform.Rotate(0, animation - lastAnimation, 0);
            //    lastAnimation = animation;
            //}
            else if (timer >= 0)
            {
    
                enemy.SwitchState(enemy.patrolState);
    
            }
        }
    }
    
    
    THIS CODE IS THE WAIT STATE OF THE ENEMY
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.AI;
    
    public class EnemyIdleStateWait : EnemyBaseState
    {
        public float waitTime;
        float timer;
    
        private void Start()
        {
    
        }
        public override void EnterState(EnemyStateAgent enemy)
        {
            enemy.animator.SetBool("EnemyMove", false);
            timer = 0;
        }
    
    
        public override void UpdateState(EnemyStateAgent enemy)
        {
            timer += Time.deltaTime;
            if (timer > waitTime)
            {
                enemy.SwitchState(enemy.patrolState);
            }
        }
    }
    
    
    THIS CODE IS THE PATROL STATE OF THE ENEMY
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.AI;
    
    public class EnemyPatrolState : EnemyBaseState
    {
        public List positions;
        List destinations;
        int index;
        NavMeshAgent agent;
        private void Awake()
        {
            destinations = new List
            {
                transform.position
            };
            foreach (Transform item in positions)
            {
                destinations.Add(item.position);
            }
            index = 0;
            agent = GetComponent();
        }
        public override void EnterState(EnemyStateAgent enemy)
        {
            enemy.animator.SetBool("EnemyMove", true);
            agent.destination = destinations[index];
        }
    
        
    
        public override void UpdateState(EnemyStateAgent enemy)
        {
            Vector3 ownPosNoY = new Vector3(transform.position.x, 0, transform.position.z);
            Vector3 targetPosNoY = new Vector3(destinations[index].x, 0, destinations[index].z);
            if (Vector3.Distance(ownPosNoY, targetPosNoY) < 1)
            {
                index++;
                index %= (positions.Count + 1);
                agent.SetDestination(transform.position);
                enemy.SwitchState(enemy.idleState);
            }
            
        }
    }
    
    
    THIS CODE IS THE PATROL STATE ROTATE OF THE ENEMY
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.AI;
    
    namespace System.Runtime.CompilerServices
    {
        internal static class IsExternalInit { }
    }
    public class EnemyPatrolStateRotate : EnemyBaseState
    {
        public float rotationAngle; //in degrees
        public float rotationSpeed; // in seconds
        public float waitTime;
        Vector3 originalPosition;
        float originalRotation;
        bool animationReverse;
        float stepAngle;
        float timer;
        float degreesTurned;
        float lastAnimation;
        float anim;
        NavMeshAgent agent;
    
        record AnimationFrame(float Amount, float Seconds);
    
        private void Awake()
        {
            stepAngle = rotationAngle / rotationSpeed;
            animationReverse = false;
            originalPosition = transform.position;
            agent = GetComponent();
            originalRotation = transform.rotation.eulerAngles.y;
            
        }
        public override void EnterState(EnemyStateAgent enemy)
        {
            enemy.animator.SetBool("EnemyMove", false);
            timer = -waitTime;
            degreesTurned = 0;
            lastAnimation = 0;
        }
    
        float Bezier(float t)
        {
            return -6f * (t-1f) * t;
        }
    
        public static float EaseIn(float t)
        {
            return Mathf.Pow(t, 3);
        }
        public static float Flip(float x)
        {
            return 1 - x;
        }
    
        public static float Spike(float t)
        {
            if (t <= .5f)
                return EaseIn(t / .5f);
    
            return EaseIn(Flip(t) / .5f);
        }
        public override void UpdateState(EnemyStateAgent enemy)
        {
            //if (Vector3.Distance(transform.position, originalPosition) > 2)
            //{
            //    agent.SetDestination(originalPosition);
            //    return;
            //}
            timer += Time.deltaTime;
            if (timer >= 0 && timer <= rotationSpeed)
            {
                anim = Mathf.Lerp(0, rotationAngle, EaseIn(timer / rotationSpeed));
                transform.Rotate(0, anim - lastAnimation, 0);
                lastAnimation = anim;
            }
            else if (timer >= 0)
            {
                rotationAngle *= -1;
                enemy.SwitchState(enemy.idleState);
    
            }
        }
    }
    
    
    THIS CODE IS THE RETURN STATE OF EACH ENEMY
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.AI;
    
    public class EnemyReturnState : EnemyBaseState
    {
        Transform originalTransform;
        Quaternion originalRotation;
        NavMeshAgent agent;
    
        private void Awake()
        {
            originalTransform = Instantiate(new GameObject(),transform.position, transform.rotation).transform;
            agent = GetComponent();
    
        }
        public override void EnterState(EnemyStateAgent enemy)
        {
            agent.destination = originalTransform.position;
        }
    
    
        public override void UpdateState(EnemyStateAgent enemy)
        {
            if (agent.remainingDistance == 0)
            {
                agent.destination = transform.position;
                if (Vector3.Angle(originalTransform.forward, transform.forward) > 3)
                {
                    transform.rotation = Quaternion.Slerp(transform.rotation, originalTransform.rotation, 2 * Time.deltaTime);
                } else
                {
                    transform.rotation = originalTransform.rotation;
                    enemy.SwitchState(enemy.patrolState);
                }
            }
        }
    }
    
    
    THIS CODE IS THE REACTION OF THE ENEMY TO A TRIGGER STONE
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class EnemyStateAgent : MonoBehaviour
    {
        // Start is called before the first frame update
        [HideInInspector]
        public EnemyBaseState currentState;
        public EnemyBaseState patrolState;
        public EnemyBaseState watchState;
        public EnemyBaseState stoneState;
        public EnemyBaseState idleState;
        public EnemyReturnState returnState;
        public FieldOfView fov;
    
       
        public Animator animator;
    
        public Vector3 stonePosition;
    
    
    
        void Start()
        {
            animator = GetComponentInChildren();
            currentState = patrolState;
            currentState.EnterState(this);
        }
    
        // Update is called once per frame
        void Update()
        {
    
            if (fov.visibleTargets.Count > 0)
            {
                SwitchState(watchState);
            }
            currentState.UpdateState(this);
    
        }
    
        
        public void SwitchState(EnemyBaseState state)
        {
            if (state == stoneState)
            {
                animator.SetBool("EnemyMove", true);
            }
            currentState = state;
            state.EnterState(this);
        }
    }
    
    
    THIS CODE IS THE WATCH STATE OF THE ENEMY 
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.AI;
    
    public class EnemyWatchState : EnemyBaseState
    {
        NavMeshAgent agent;
        float radius;
        List targets;
        Transform target;
    
        int oldLength;
        private void Start()
        {
            agent = GetComponent();
        }
        public override void EnterState(EnemyStateAgent enemy)
        {
            enemy.animator.SetBool("EnemyMove", true);
            radius = enemy.fov.viewRadius;
            targets = enemy.fov.visibleTargets;
            isPlayerInReach();
            oldLength = targets.Count;
            Debug.Log("Hello from Watch");
        }
    
        void isPlayerInReach()
        {
            target = targets[0];
            foreach (var t in targets)
            {
                if (t.CompareTag("Player"))
                {
                    target = t;
                }
            } 
        }
        public override void UpdateState(EnemyStateAgent enemy)
        {
            targets = enemy.fov.visibleTargets;
            if (targets.Count != 0)
            {
                
                if (targets.Count != oldLength)
                {
                    isPlayerInReach();
                    oldLength = targets.Count;
                }
                if (Vector3.Distance(transform.position, target.position) < enemy.fov.viewRadius / 2)
                {
                    transform.LookAt(new Vector3(target.position.x, transform.position.y, target.position.z));
                } else
                {
                    Vector3 point = target.position + (transform.position - target.position).normalized * enemy.fov.viewRadius / 2;
                    agent.destination = point;
                    Debug.DrawLine(transform.position, point, Color.white);
                }
    
            } else
            {
                enemy.SwitchState(enemy.patrolState);
            }
        }
    }
    
    
    THIS CODE IS MADE TO DISPLAY THE FIELD VIEW OF THE ENEMY
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class FieldOfView : MonoBehaviour
    {
    
        public float viewRadius;
        public float aroundRadius;
        [Range(0, 360)]
        public float viewAngle;
    
        public LayerMask targetMask;
        public LayerMask obstacleMask;
    
        public List visibleTargets = new();
    
    
        public MeshFilter viewMeshFilter;
        public int edgeResolveIterations;
        public float edgeDistanceThreshold;
        Mesh viewMesh;
    
    
        public float meshResolution;
        void Start()
        {
            viewMesh = new Mesh();
            viewMesh.name = "View Mesh";
            viewMeshFilter.mesh = viewMesh;
            //float height = GetComponent().bounds.size.y;
            //viewMeshFilter.transform.position -= new Vector3(0, height / 2 - 0.1f);
            StartCoroutine("FindTargetsWithDelay", .1f);
        }
    
    
        IEnumerator FindTargetsWithDelay(float delay)
        {
            while (true)
            {
                yield return new WaitForSeconds(delay);
                FindVisibleTargets();
            }
        }
        void LateUpdate()
        {
            DrawFieldOfView();
        }
        void FindVisibleTargets()
        {
            void ApplySee(Transform target)
            {
                if (target.CompareTag("Player"))
                {
                    visibleTargets.Add(target);
                    GameManager.Instance.addAttention(50, TypesOfDeath.Seen);
                }
                if (target.CompareTag("Key"))
                {
                    MagicStone scriptCheck = target.GetComponent();
                    if (scriptCheck.isPickedUp)
                    {
                        visibleTargets.Add(target);
                        GameManager.Instance.addAttention(25, TypesOfDeath.Key);
                    }
                }
                if (target.name == "DoorTriggerObject")
                {
                    Door scriptCheckDoor = target.parent.parent.Find("Door").GetComponent();
                    if (scriptCheckDoor != null)
                    {
                        if (scriptCheckDoor.originalOpenState != scriptCheckDoor.isOpen)
                        {
                            visibleTargets.Add(target);
                            GameManager.Instance.addAttention(25, TypesOfDeath.Door);
                        }
                    }
                }
            }
            Collider[] targetsInViewRadius = Physics.OverlapSphere(transform.position, viewRadius, targetMask);
            visibleTargets.Clear();
            for (int i = 0; i < targetsInViewRadius.Length; i++)
            {
                Transform target = targetsInViewRadius[i].transform;
    
    
    
                Vector3 dirToTarget = new Vector3(
                    (target.position.x - transform.position.x), 
                    0,
                    target.position.z - transform.position.z
                    );
                float viewAngleTemp = Vector3.Angle(transform.forward, dirToTarget);
    
                if (Vector3.Distance(transform.position, target.position) < aroundRadius)
                {
                    ApplySee(target);
                } else if (viewAngleTemp  < viewAngle / 2)
                {
                    float dstToTarget = Vector3.Distance(transform.position, target.position);
                    if (!Physics.Raycast(transform.position, dirToTarget, dstToTarget, obstacleMask)) {
    
                        ApplySee(target);
                        
                    }
    
                }
    
            }
            //foreach (var item in visibleTargets)
            //{
            //    Debug.Log(item.name);
            //}
            //Debug.Log("----------------------------------");
    
    
        }
    
    
    
        void DrawFieldOfView()
        {
            float coverAngle = 360;
            int stepCount = Mathf.RoundToInt(coverAngle * meshResolution);
            float stepAngleSize = coverAngle / stepCount;
            List viewPoints = new List();
    
            ViewCastInfo oldViewCast = new ViewCastInfo();
    
            for (int i = 0; i <= stepCount; i++)
            {
                float addedAngle =  stepAngleSize * i;
                float angle = transform.eulerAngles.y - viewAngle / 2 + addedAngle;
                float magnitude;
                ViewCastInfo newViewCast;
                if (addedAngle < viewAngle)
                {
                    magnitude = viewRadius;
                } else
                {
                    magnitude = aroundRadius;
                }
                newViewCast = ViewCast(angle, magnitude);
    
    
    
    
                if ((i > 0) && (stepAngleSize * (i - 1) > viewAngle))
                {
                    bool edgeDstThresholdExceeded = Mathf.Abs(oldViewCast.dst - newViewCast.dst) > edgeDistanceThreshold;
                    if (oldViewCast.hit != newViewCast.hit || (oldViewCast.hit && newViewCast.hit  && edgeDstThresholdExceeded))
                    {
                        EdgeInfo edge = FindEdge(oldViewCast, newViewCast, magnitude);
                        if (edge.pointA != Vector3.zero)
                        {
                            viewPoints.Add(edge.pointA);
                        }
                        if (edge.pointB != Vector3.zero)
                        {
                            viewPoints.Add(edge.pointB);
                        }
                    }
                }
                viewPoints.Add(newViewCast.point);
                oldViewCast = newViewCast;
            }
    
            int vertexCount = viewPoints.Count + 1;
            Vector3[] vertices = new Vector3[vertexCount];
            int[] triangles = new int[(vertexCount - 2) * 3];
    
            vertices[0] = Vector3.zero;
            for (int i = 0; i < vertexCount - 1; i++)
            {
                vertices[i + 1] = transform.InverseTransformPoint(viewPoints[i]);
                if (i < vertexCount - 2)
                {
                    triangles[i * 3] = 0;
                    triangles[i * 3 + 1] = i + 1;
                    triangles[i * 3 + 2] = i + 2;
                }
            }
    
            viewMesh.Clear();
            viewMesh.vertices = vertices;
            viewMesh.triangles = triangles;
         
            viewMesh.RecalculateNormals();
        }
    
        EdgeInfo FindEdge(ViewCastInfo minViewCast, ViewCastInfo maxViewCast, float magnitude)
        {
            float minAngle = minViewCast.angle;
            float maxAngle = maxViewCast.angle;
            Vector3 minPoint = Vector3.zero;
            Vector3 maxPoint = Vector3.zero;
    
            for (int i = 0; i < edgeResolveIterations; i++)
            {
                float angle = (minAngle + maxAngle) / 2;
                ViewCastInfo newViewCast = ViewCast(angle, magnitude);
    
                bool edgeDstThresholdExceeded = Mathf.Abs(minViewCast.dst - newViewCast.dst) > edgeDistanceThreshold;
                if (newViewCast.hit == minViewCast.hit && !edgeDstThresholdExceeded)
                {
                    minAngle = angle;
                    minPoint = newViewCast.point;
                }
                else
                {
                    maxAngle = angle;
                    maxPoint = newViewCast.point;
                }
            }
    
            return new EdgeInfo(minPoint, maxPoint);
        }
    
        ViewCastInfo ViewCast(float globalAngle, float magnitude)
        {
            Vector3 direction = DirectionFromAngle(globalAngle, true);
            RaycastHit hit;
            if (Physics.Raycast(transform.position, direction, out hit, magnitude, obstacleMask))
            {
                return new ViewCastInfo(true, hit.point, hit.distance, globalAngle);
            } else
            {
                return new ViewCastInfo(false, transform.position + direction * magnitude, magnitude, globalAngle);
            }
        }
    
    
        public Vector3 DirectionFromAngle(float angleInDegrees, bool angleIsGlobal)
        {
            if (!angleIsGlobal)
            {
                angleInDegrees += transform.eulerAngles.y;
            }
            return new Vector3(Mathf.Sin(angleInDegrees * Mathf.Deg2Rad), 0, Mathf.Cos(angleInDegrees * Mathf.Deg2Rad));
        }
    
        public struct ViewCastInfo
        {
            public bool hit;
            public Vector3 point;
            public float dst;
            public float angle;
    
            public ViewCastInfo(bool _hit, Vector3 _point, float _dst, float _angle)
            {
                hit = _hit;
                point = _point;
                dst = _dst;
                angle = _angle;
            }
        }
    
        public struct EdgeInfo
        {
            public Vector3 pointA;
            public Vector3 pointB;
    
            public EdgeInfo(Vector3 _pointA, Vector3 _pointB)
            {
                pointA = _pointA;
                pointB = _pointB;
            }
        }
    
    }
                              
                          
                              
    
    
    
    
              / ======= \
             / __________\
            | ___________ |
            | | -GAME   | |
            | |    DEV  | |
            | |_________| |________________________
            \=____________/      THROW THE OBJECT  )
            / """"""""""" \                       /
           / ::::::::::::: \                  =D-'
          (_________________)
    
    
    
    
                    |   SCROLL DOWN   |  
                    v                 v
    
    
    
    
    
    THIS CODE IS MADE TO THROW THE OBJECT AT 
    A CERTAIN DISTANCE USING KEYBOARD
    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using TMPro;
    
    public class ThrowStone : MonoBehaviour
    {
        [Header("References")]
        public Transform cam;
        public Transform attackPoint;
        public GameObject stoneObject;
        public GameObject keyObject;
    
        [Header("Settings")]
        public float throwCooldown;
    
        [Header("Throwing")]
        public KeyCode stoneKey = KeyCode.Q;
        public KeyCode keyThrowKey = KeyCode.R;
        public float throwForce;
    
        bool readyToThrow;
    
        private void Start()
        {
            readyToThrow = true;
        }
    
        private void Update()
        {
            if (Input.GetKeyDown(stoneKey) && readyToThrow && GameManager.Instance.stones != 0 && GameManager.Instance.playerAbilities.Contains(PlayerAbilities.Stone))
            {
                Throw(stoneObject);
                GameManager.Instance.stones -= 1;   
            }
            if (Input.GetKeyDown(keyThrowKey) && readyToThrow && GameManager.Instance.inventory.Count > 0)
            {
                Throw(keyObject);
                GameManager.Instance.inventory.Clear();
            }
        }
    
        private void Throw(GameObject objectToThrow)
        {
            readyToThrow = false;
    
            // instantiate object to throw
            GameObject projectile = Instantiate(objectToThrow, cam.position + cam.transform.forward.normalized * 5, cam.rotation);
    
            // get rigidbody component
            Rigidbody projectileRb = projectile.GetComponent();
    
            // calculate direction
            Vector3 forceDirection = cam.transform.forward;
    
            RaycastHit hit;
    
            //if (Physics.Raycast(cam.position, cam.forward, out hit, 500f))
            //{
            //    forceDirection = (hit.point - attackPoint.position).normalized;
            //}
    
            // add force
            //Vector3 forceToAdd = forceDirection * throwForce + transform.up * throwUpwardForce;
            Vector3 forceToAdd = forceDirection * throwForce;
    
            projectileRb.AddForce(forceToAdd, ForceMode.Impulse);
    
    
            // implement throwCooldown
            Invoke(nameof(ResetThrow), throwCooldown);
        }
    
        private void ResetThrow()
        {
            readyToThrow = true;
        }
    }
    
    
    THIS CODE IS MADE TO SEND AN OBJECT AND RECOGNISE 
    HIS POSITION WHEN HITTING THE FLOOR
    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class StoneGroundHit : MonoBehaviour
    {
        bool hit;
        public float radius;
        public LayerMask enemyMask;
        public GameObject burstRadius;
        private void Start()
        {
            hit = false;
        }
        private void OnCollisionEnter(Collision collision)
        {
            Debug.Log("oz");
            if (!hit && !collision.transform.CompareTag("Stone"))
            {
                Collider[] enemies = Physics.OverlapSphere(transform.position, radius, enemyMask);
                foreach (var enemy in enemies)
                {
                    EnemyStateAgent stateAgent = enemy.GetComponent();
                    stateAgent.stonePosition = transform.position;
                    stateAgent.SwitchState(stateAgent.stoneState);
                }
    
                GameObject burst = Instantiate(burstRadius, transform.position, Quaternion.identity);
                burst.GetComponent().radius = radius*2;
                burst.GetComponent().startAnimation();
                GameManager.Instance.addAttention(20, TypesOfDeath.Stone);
                hit = true;
                
            }
        }
    }
    
    
    THIS CODE IS MADE TO CREATE A SPHERE 
    AROUND THE HITING POINT OF THE SENT OBJECT                            
    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class BurstRadius : MonoBehaviour
    {
        // Start is called before the first frame update
        [HideInInspector] public float radius;
        public float burstVelocity;
        float burstTime;
        bool start = false;
        float time;
        Color originalColor;
        void Start()
        {
        }
    
        public void startAnimation()
        {
            time = 0;
            burstTime = radius / burstVelocity;
            originalColor = transform.Find("Sphere").GetComponent().material.color;
            start = true;
        }
    
        public static float EaseIn(float t)
        {
            return Mathf.Pow(t, 3);
        }
        public static float EaseOut(float t)
        {
            return Flip(Square(Flip(t)));
        }
        static float Flip(float x)
        {
            return 1 - x;
        }
        static float Square(float x)
        {
            return Mathf.Pow(x, 2);
        }
    
        // Update is called once per frame
        void Update()
        {
            if (start)
            {
                time += Time.deltaTime;
                float value = Mathf.Lerp(0, radius, EaseOut( time / burstTime));
                transform.localScale = new Vector3(1, 1, 1) * value;
                float alpha = Mathf.Lerp(originalColor.a, 0, EaseOut(time / burstTime));
                alpha = Mathf.Round(alpha * 1000f) / 1000f;
                Color targetColor = new Color(originalColor.r, originalColor.g, originalColor.b, 0f);
                Color newColor = Color.Lerp(originalColor, targetColor, EaseOut(time / burstTime));
                transform.Find("Sphere").GetComponent().material.color = newColor ;
                if (time > burstTime)
                {
                    Destroy(this.gameObject);
                }
            }
        }
    }
    
    
    
    
    
    
                              
                          
                              
    
    
    
    
    
                        ############
                    ####################       
                  ########################
                ####  ####  ####  ####  ####
              ################################      
                  ######    ####    ######
                    ##   SYSTEM CODE  ##
    
    
    
    
    
    
                     |   SCROLL DOWN   |  
                     v                 v
    
    
    
    
    THIS CODE SET THE CHECKPOINTS
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlayerCheckPoint : MonoBehaviour
    {
       public int addStones;
        private void OnTriggerEnter(Collider other)
        {
            if (other.CompareTag("Player"))
            {
                if (GameManager.Instance.respawnPoint != transform.position)
                {
                    GameManager.Instance.stones += addStones;
                }
                    GameManager.Instance.SetRespawnPosition(transform.position);
            }
        }
    }        
    
    
    THIS CODE ACTIVATE THE STONE
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class ActivateStone : MonoBehaviour
    {
        // Start is called before the first frame update
        private void OnTriggerEnter(Collider other)
        {
            if (other.CompareTag("Player"))
            {
                GameManager.Instance.ActivateStones();
            }
        }
    }
    
    
    THIS CODE SET THE POP UP SYSTEM OF OUR BIRDS HELP
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using TMPro;
    using System;
    
    public class PopUpSystem : MonoBehaviour
    {
        [SerializeField] GameObject dialogBox;
        public Animator animator;
        [SerializeField] TMP_Text dialogText;
    
        [SerializeField] int lettersPerSecond;
    
        public event Action OnShowDialog;
        public event Action OnCloseDialog;
    
    
        public static PopUpSystem Instance { get; private set; }
    
        private void Awake()
        {
            Instance = this;
        }
    
        Dialog dialog;
        int currentLine = 0;
        bool isTyping;
    
        public IEnumerator ShowDialog(Dialog dialog)
        {
            yield return new WaitForEndOfFrame();
            OnShowDialog?.Invoke();
            foreach (var line in dialog.Lines)
            {
                Debug.Log(line);
            }
    
            this.dialog = dialog;
            dialogBox.SetActive(true);
            StartCoroutine(TypeDialog(dialog.Lines[0]));
            
            animator.SetTrigger("pop");
        }
    
        public void Update()
        {
            if (Input.GetKeyDown("z") && !isTyping && GameManager.Instance.dialogState == DialogState.During)
            {
                ++currentLine;
                if (currentLine < dialog.Lines.Count)
                {
                    StartCoroutine(TypeDialog(dialog.Lines[currentLine]));
                }
                else
                {
                    Debug.Log("Ended");
                    currentLine = 0;
                    dialogBox.SetActive(false);
                   
                    OnCloseDialog?.Invoke();
                }
            }
        }
    
        public IEnumerator TypeDialog(string line)
        {
            isTyping = true;
            dialogText.text = "";
            foreach (var letter in line.ToCharArray())
            {
                dialogText.text += letter;
                yield return new WaitForSeconds(1f / lettersPerSecond);
            }
            isTyping = false;
        }
        public void Start()
        {
            PopUpSystem.Instance.OnShowDialog += () =>
            {
                GameObject player = GameObject.FindGameObjectWithTag("Player");
                player.GetComponent().enabled = false;
                GameManager.Instance.dialogState = DialogState.During;
            };
            PopUpSystem.Instance.OnCloseDialog += () =>
            {
                GameObject player = GameObject.FindGameObjectWithTag("Player");
                player.GetComponent().enabled = true;
                GameManager.Instance.dialogState = DialogState.None;
                Debug.Log("End of Dialog");
            };
        }
    }
    
    
    THIS CODE TRIGGER THE BIRD TO SPEAK 
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class TriggerBird : MonoBehaviour
    {
        [SerializeField] Dialog dialog;
    
        
        public void Interact()
        {
            StartCoroutine(PopUpSystem.Instance.ShowDialog(dialog));
        }
    
        private void OnTriggerStay(Collider other)
        {
            if (other.CompareTag("Player"))
            {
                if (GameManager.Instance.dialogState == DialogState.None)
                {
                    GameManager.Instance.dialogState = DialogState.AbleTo;
                    
                }
                if (GameManager.Instance.dialogState == DialogState.AbleTo)
                {
                    if (Input.GetKeyDown(KeyCode.Z))
                    {
                        GameManager.Instance.dialogState = DialogState.Triggered;
                        Interact();     
                    }
                }
            }
        }
    
        private void OnTriggerExit(Collider other)
        {
            if (other.CompareTag("Player"))
            {
                GameManager.Instance.dialogState = DialogState.None;
            }
        }
    
        private void Start()
        {
            
        }
    }
    
    
    THIS CODE RESPAWN WHEN DEAD
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class RespawnInit : MonoBehaviour
    {
        // Start is called before the first frame update
        void Start()
        {
            if (GameManager.Instance.respawnPositions.Count == 0)
            {
                int i = 0;
                foreach (Transform item in transform.GetComponentsInChildren())
                {
                    if (i != 0)
                    {
                        GameManager.Instance.respawnPositions.Add(item);
                    }
                    i++;
                }
                GameManager.Instance.InitPlaying();
            }
        }
    
        // Update is called once per frame
        void Update()
        {
            
        }
    }
    
    
    THIS CODE END THE LEVEL
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class LevelEnd : MonoBehaviour
    {
        private void OnTriggerEnter(Collider other)
        {
            GameManager.Instance.LevelFinished();
        }
    }
    
    
    THIS CODE HIDE AND REVEAL EACH ACTUAL LEVELS
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Hide3D : MonoBehaviour
    {
        // Start is called before the first frame update
        public List levels;
        void Start()
        {
            
        }
    
        // Update is called once per frame
        void Update()
        {
            if (GameManager.Instance.gameState == GameState.Playing)
            {
                for (int i = 0; i < levels.Count; i++)
                {
                    if (i == (GameManager.Instance.currentLevel - 1))
                    {
                        levels[i].SetActive(true);
                    } else
                    {
                        levels[i].SetActive(false);
                    }
                }
            }
        }
    }
    
    
    THIS CODE SET THE CAMERA CONTROLLER
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CameraController : MonoBehaviour
    {
        public float movementSpeed;
        public float movementTime;
        public float rotationAmount;
    
        public Vector3 newPosition;
    
        //HandleChangingCameras
        public Camera FirstPersonCam, ThirdPersonCam;
        public bool fpc;
    
        //HandleRotation
        public Quaternion newRotation;
    
        public Vector3 rotateStartPosition;
        public Vector3 rotateCurrentPosition;
    
        // Start is called before the first frame update
        void Start()
        {
            newPosition = transform.position;
            newRotation = transform.rotation;
            FirstPersonCam.gameObject.SetActive(fpc);
            ThirdPersonCam.gameObject.SetActive(!fpc);
    
            /*cameraFirstPerson.enabled = true;
            cameraThirdPerson.enabled = false;*/
        }
    
        // Update is called once per frame
        void Update()
        {
            if (Input.GetKeyDown(KeyCode.C))
            {
                fpc = !fpc;
                FirstPersonCam.gameObject.SetActive(fpc);
                ThirdPersonCam.gameObject.SetActive(!fpc);
                GameObject player = GameObject.FindGameObjectWithTag("Player");
                transform.position = player.transform.position;
                transform.LookAt(transform.position + player.transform.forward);
                foreach (MonoBehaviour script in player.GetComponentsInChildren())
                {
                    script.enabled = fpc;
                }
                player.GetComponent().velocity = Vector3.zero;
            }
    
            //HandleMovementInput();
            //HandleMouseInput();
        }
    
        void HandleMouseInput()
        {
            if (Input.GetMouseButtonDown(2))
            {
                rotateStartPosition = Input.mousePosition;
            }
            if (Input.GetMouseButton(2))
            {
                rotateCurrentPosition = Input.mousePosition;
    
                Vector3 difference = rotateStartPosition - rotateCurrentPosition;
    
                rotateStartPosition = rotateCurrentPosition;
    
                newRotation *= Quaternion.Euler(Vector3.up * (-difference.x / 5f));
            }
        }
    
        void HandleMovementInput()
        {
            if (Input.GetKey(KeyCode.LeftArrow))
            {
                newRotation *= Quaternion.Euler(Vector3.up * rotationAmount);
            }
            if (Input.GetKey(KeyCode.RightArrow))
            {
                newRotation *= Quaternion.Euler(Vector3.up * -rotationAmount);
            }
    
            transform.rotation = Quaternion.Lerp(transform.rotation, newRotation, Time.deltaTime * movementTime);
        }
    }