Project info

Dit was mijn eerste groepsproject op het glu. Het samenwerken en produceren van de game ging heel goed en alles is afgekomen zoals gepland. Tijdens dit project moesten we een shmup maken met eigen thema en dit is wat er uit is gekomen. Een game waarbij je als een mechanische wesp andere mechanische insecten moet neer schieten.

Tijdens dit project heb ik gewerkt aan.

  • PlayerMovement
  • Map Loop
  • MR-Zandhoopje
  • PowerUps
  • Schieten / Health
  • PlayerMovement

    De player movement is een simpele vector3 movement en hier heb ik nog een dash aan toe gevoegt. De dash werkt door de player speed tijdelijk wat hoger te zetten. Om de player minder statisch door het beeld te zien gleiden heb ik rotaties toegevoegd, gebaseerd op de richting waarin de player beweegt en dit heb ik gedaan met een lerp.

    code snippet * PlayerMovement.cs
    
    public class PlayerMovement : MonoBehaviour
    {
        void Update()
        {
            X = Input.GetAxisRaw("Horizontal");
            Z = Input.GetAxisRaw("Vertical");
    
            if (CanMove)
            {
                rb.velocity = new Vector3(X * Speed, 0, Z * Speed);
            }
            if (!CanMove)
            {
                rb.velocity = new Vector3(0, 0, 0);
            }
    
            TurnTimer += Time.deltaTime;
            PerCentage = TurnTimer / TurnTime;
    
            EndRotation = Quaternion.Euler(Z * RotationAmountZ, 0, X * -RotationAmountX);
    
            if (X == -1 || X == 1 || X == 0 || Z == -1 || Z == 1 || Z == 0)
            {
                TurnTimer = 0f;
                RotationStart = transform.rotation;
            }
    
            transform.rotation = Quaternion.Slerp(RotationStart, EndRotation, PerCentage);
    
            // dash
            if (DashTimer < DashTimerReset)
            {
                DashTimer += Time.deltaTime;
            }
    
            if (Candash && DashTimer >= DashTimerReset && !PlayerUps.SpeedBoostPowerUp)
            {
                if (Input.GetKeyDown(KeyCode.E) || Input.GetMouseButtonDown(1))
                {
                    Dash();
                    StartCoroutine(DashReset());
                }
            }
        }
    
        private IEnumerator DashReset()
        {
            yield return new WaitForSeconds(DashResetSeconds);
            Speed = SpeedReset;
            Dashing = false;
            StopCoroutine(DashReset());
        }
    
        private void Dash()
        {
            SoundManager.Dash.Play();
            Dashing = true;
            DashTimer = 0f;
            Speed = DashSpeed;
        }
        

    MapLoop

    De maploop is gemaakt met waypoints en alle map delen kunnen in een array worden gezet. Het laatste deel van de map is een ghost deel dat later word verwijderd. Als de game start worden er waypoints geplaats op de eerste en de laatse map deel van de array en het laatste deel van de map word verwijderd zodat de delen niet in elkaar clippen. alle delen bewegen dan naar de 2e checkpoit en als die is berrijkt dan word dat map deel naar de eerste checkpoint gezet.

    code snippet * MapScript.cs
    
    public class MapScript : MonoBehaviour
    {
        [SerializeField] private Transform[] Waypoints;
        [SerializeField] private List MapDelen;
        [SerializeField] private float Speed;
        // 1 is voor de endwaypoint en 0 is de startwaypoint
    
        private void Awake()
        {
            Waypoints[0].transform.position = MapDelen[0].transform.position;
            Waypoints[1].transform.position = MapDelen[MapDelen.Count - 1].transform.position;
        }
    
        void Update()
        {
            if (MapDelen[MapDelen.Count - 1].transform.position == Waypoints[1].transform.position)
            {
                MapDelen[MapDelen.Count - 1].SetActive(false);
            }
            transform.position = Vector3.MoveTowards(transform.position, Waypoints[1].transform.position, Speed * Time.deltaTime);
    
            if (transform.position == Waypoints[1].transform.position)
            {
                transform.position = Waypoints[0].transform.position;
            }
        }
    }
        

    MR-Zandhoopje

    MR-Zandhoopje heb ik gemaakt nadat er tijd over was. Het leek me leuk om een wezen te hebben dat de player vanuit de grond met een spike aanvalt en dan tijdelijk vasthoudt. Later is Zandhoopje veranderd naar een wolkje.

    MR-Zandhoopje wordt elke wave gespawnd en spawnt zelf een waypoint waar hij naartoe gaat. De waypoint wordt een paar keer van positie veranderd zodat MR-Zandhoopje met een Movetowards er heen kan. Na een paar keer langs de waypoint te zijn gegaan gaat MR-Zandhoopje achter de player aan. Als MR-Zandhoopje onder de player zit valt hij aan. De player kan deze aanval ontwijken door op tijd te dashen. Lukt dat niet dan zit de player vast aan de spike van MR-Zandhoopje voor een aantal seconden. MR-Zandhoopje laat dan weer los gaat nog één keer langs een waypoint en verdwijnt.

    code snippet * MrZandhoopje.cs
    
    //(dit zijn de beste stukjes uit de code niet het hele script)
    public class MrZandhoopje : MonoBehaviour
    {
        [Header("Movemnet")]
        [SerializeField] private float Speed;
        [SerializeField] private float ChargeSpeed;
    
        [Header("Overig")]
        [SerializeField] private LayerMask PlayerLayer;
        [SerializeField] private float SpawnYofset;
        [SerializeField] private ParticleSystem ZandHoopjeDeath;
        private float PlayerHealth;
        private bool charging = false;
        private bool reachedplayer = false;
        private bool lastwaypointset = false;
    
        [Header("Arrow")]
        [SerializeField] private GameObject Arrowprefab;
        private GameObject Arrow;
        [SerializeField] private float ArrowUpSpeed;
        [SerializeField] private float ArrowDownSpeed;
        [SerializeField] private float SpikeScaleBig;
    
        [Header("ArrowBools")]
        private bool ArrowPullBackAnimb = false;
        private bool Attacking = false;
        private bool Fleeing = false;
        private bool CanAttac = true;
    
        [Header("dodge")]
        [SerializeField] private bool InDashRange;
        [SerializeField] private GameObject DashRangeCkeck;
        [SerializeField] private float DashRange;
        private bool dodge = false;
    
        [Header("sound effects")]
        [SerializeField] private AudioSource SpikeUp;
        [SerializeField] private AudioSource SpikeDown;
        private soundManager SoundManager;
    
        void Update()
        {
            InDashRange = Physics.CheckSphere(DashRangeCkeck.transform.position, DashRange, PlayerLayer);
    
            if (Player != null)
            {
                Movement();
            }
    
            ArrowMovement();
    
            if (ArrowPullBackAnimb)
            {
                ArrowPullBackAnim();
            }
            if (Attacking)
            {
                Attac();
            }
            if (Fleeing)
            {
                Flee();
            }
            if (charging && InDashRange && Player.Dashing)
            {
                dodge = true;
                charging = false;
                Attacking = true;
                Waypoint.transform.position = transform.position;
            }
        }
    
        private void Movement()
        {
            //movement richting waypoint
            if (!charging)
            {
                transform.position = Vector3.MoveTowards(transform.position, Waypoint.transform.position, Speed * Time.deltaTime);
            }
            Vector3 PlayerPoint = new Vector3 (Player.transform.position.x, transform.position.y ,Player.transform.position.z);
    
            //is waypoint berreikt/ replace waypoint als nodig is en ga anders achter de player aan
            if (transform.position == Waypoint.transform.position)
            {
                if (WaypointsTogo > 0)
                {
                    WaypointsTogo--;
                    ReplaceWayPoint();
                }
                else if (!reachedplayer)
                {
                    charging = true;
                }
            }
    
            // val de player aan als zandhoopje recht onder de player zit
            if (transform.position == PlayerPoint)
            {
                charging = false;
                Waypoint.transform.position = PlayerPoint;
                if (CanAttac)
                {
                    Attac();
                    CanAttac = false;
                }
            }
    
            //movement richting player
            if (charging)
            {
                transform.position = Vector3.MoveTowards(transform.position, PlayerPoint, ChargeSpeed * Time.deltaTime);
            }
        }
        /// 
        /// zet de waypoint op een nieuwe positie binnen het aangegeven speelveld
        /// 
        private void ReplaceWayPoint()
        {
            float X = Random.Range((int)spawner.FieldPoints[0].transform.position.z, (int)spawner.FieldPoints[1].transform.position.z);
            float Z = Random.Range((int)spawner.FieldPoints[0].transform.position.z, (int)spawner.FieldPoints[1].transform.position.z);
    
            Waypoint.transform.position = new Vector3(X,transform.position.y,Z);
            if (reachedplayer)
            {
                lastwaypointset = true;
            }
        }
    
        bool SoundEffectUp = false;
        /// 
        /// laat de spike groter worden/ zet de player vast
        /// 
        private void Attac()
        {
            Vector3 newSize = new Vector3(25, 25, SpikeScaleBig);
            reachedplayer = true;
            Attacking = true;
            if (WaitForAttac > 0)
            {
                WaitForAttac -= Time.deltaTime;
            }
            if (WaitForAttac <= 0)
            {
                if (!SoundEffectUp)
                {
                    SpikeUp.Play();
                    SoundEffectUp = true;
                }
                Arrow.transform.localScale = Vector3.Slerp(Arrow.transform.localScale, newSize, ArrowUpSpeed * Time.deltaTime);
                if (!dodge)
                {
                    // als de player niet heeft gedasht check dan of zandhoopje onder de player zit om de player vast te zetten en damage te doen
                    if (Physics.Raycast(transform.position, Vector3.up, out RaycastHit HitInfo, Mathf.Infinity, PlayerLayer))
                    {
                        Debug.DrawRay(transform.position, Vector3.up * HitInfo.distance, Color.blue);
                        PlayerMove.CanMove = false; //zet player vast door playermove.canmove false te zetten
                        DealDamage();
                    }
                }
            }
            if (Arrow.transform.localScale == newSize)
            {
                Attacking = false;
                ArrowPullBackAnimb = true;
            }
        }
    
        bool soundEffectDown = false;
        /// 
        /// trek de spike terug zandhoopje in en laat de player los
        /// 
        private void ArrowPullBackAnim()
        {
            Vector3 NewSize = new Vector3(25, 25, 5);
            if (WaitTillArrowPullBack > 0)
            {
                WaitTillArrowPullBack -= Time.deltaTime;
            }
            if (WaitTillArrowPullBack <= 0)
            {
                if (Arrow.transform.localScale != NewSize)
                {
                    if (soundEffectDown == false)
                    {
                        SpikeDown.Play();
                        soundEffectDown = true;
                    }
                    Arrow.transform.localScale = Vector3.Slerp(Arrow.transform.localScale, NewSize, ArrowDownSpeed * Time.deltaTime);
                    PlayerMove.CanMove = true;
                }
            }
            if (Arrow.transform.localScale == NewSize)
            {
                Fleeing = true;
                ArrowPullBackAnimb = false;
                Arrow.SetActive(false);
            }
        }
        private void DealDamage()
        {
            if (PlayerStats.PlayerHealth == PlayerHealth)
            {
                PlayerStats.PlayerHealth--;
            }
        }
        /// 
        /// ga naar een laatste checkpoint na dat eenm attac is uitgevoert
        /// 
        private void Flee()
        {
            if (WaitForFlee > 0)
            {
                WaitForFlee -= Time.deltaTime;
            }
            if (WaitForFlee <= 0)
            {
                if (!lastwaypointset)
                {
                    ReplaceWayPoint();
                }
                Destroy(gameObject, TimeTillDestroy);
            }   
        }
        

    PowerUps

    Er zijn meerdere power-ups zoals [shield, speed, tripleBullets, HealthUp]. Deze power-ups worden random gespawnd binnen een aangegeven veld. Zodra de power-up wordt gespawnd schiet hij een raycast naar beneden die op de grond vervolgens een landing spot spawnt om duidelijk te maken waar de power-up landt. De power-up valt vervolgens langzaam naar beneden.

    code snippet * PlayerPowerUps.cs
    
    public class PlayerPowerUps : MonoBehaviour
    {
        void Update()
        {
            //run de power up timers als deze power ups in gebruik zijn.
            if (PlayerStats.TrippbleBullets || SpeedBoostPowerUp)
            {
                PowerUpTime -= Time.deltaTime;
            }
            //reset power upa als de tijd op is
            if (PowerUpTime <= 0)
            {
                PowerUpTime = PowerUpTimeReset;
    
                PlayerStats.TrippbleBullets = false;
    
                SpeedBoostPowerUp = false;
    
                PlayerMove.Speed = PlayerMove.SpeedReset;
            }   
            //zet het schild uit als de HP op is
            if (ShieldHealth <= 0)
            {
                Shield.SetActive(false);
                ShieldHealth = ShieldHealthReset;
            }
        }
    
        private void OnCollisionEnter(Collision collision)
        {
            if (collision.gameObject.CompareTag("TrippleGunPowerUp"))
            {
                PlayerStats.TrippbleBullets = true;
                PowerUpSoundEffect.Play();
            }else if (collision.gameObject.CompareTag("LiveUp"))
            {
                PlayerStats.PlayerHealth = PlayerStats.PlayerMaxHealth;
                PowerUpSoundEffect.Play();
            }else if (collision.gameObject.CompareTag("Shield"))
            {//is het schild actief/ nee dan zet het actief
                if (!Shield.activeInHierarchy)
                {
                    Shield.SetActive(true);
                    PowerUpSoundEffect.Play();
                }
                else
                {//anders geef HP aan het schild 
                    if (ShieldHealth < ShieldHealthReset)
                    {
                        ShieldHealth += ShieldRegen;
                        PowerUpSoundEffect.Play();
                    }
                }           
            }else if (collision.gameObject.CompareTag("SpeedBoost"))
            {
                PowerUpSoundEffect.Play();
                PlayerMove.Speed += SpeedBoost;
                SpeedBoostPowerUp = true;
            }
        }
    }
        
    code snippet * powerUpScript.cs
    
    public class powerUpScript : MonoBehaviour
    {
        [SerializeField] private int TimeTillDestroyed;
        [SerializeField] private float Speed;
    
        private Rigidbody rb;
    
        [SerializeField] private LayerMask Ground;
        [SerializeField] private GameObject LandingSpot;
        private GameObject LandObject;
    
        private void Awake()
        {
            rb = GetComponent();
        }
        void Start()
        {
            Destroy(gameObject, TimeTillDestroyed);
            HitOnGround();
        }
    
        void Update()
        {
            rb.velocity = new Vector3(0f, -Speed);
        }
    
        private void OnCollisionEnter(Collision collision)
        {
            if (collision.gameObject.CompareTag("Player") || collision.gameObject.CompareTag("ground") || collision.gameObject.CompareTag("landSpot"))
            {
                Destroy(LandObject);
                Destroy(gameObject);
            }
        }
        /// 
        /// kijk waar de power up gaat landen en plaats een spot op de grond voor visuele feedback
        /// 
        private void HitOnGround()
        {
            if (Physics.Raycast(transform.position, Vector3.down, out RaycastHit Hitinfo, Mathf.Infinity, Ground))
            {
                Debug.DrawRay(transform.position, Vector3.down * Hitinfo.distance, Color.green);
                LandObject = Instantiate(LandingSpot,Hitinfo.point,Quaternion.identity);
            }
        }
    }
        
    code snippet * PowerUpSpawner.cs
    
    public class PowerUpSpawner : MonoBehaviour
    {
    	private void Awake()
        {
            Player = FindObjectOfType();
    
            FieldPoint1X = ((int)SpawnField[0].transform.position.x);
            FieldPoint1Z = ((int)SpawnField[0].transform.position.z);
    
            FieldPoint2X = ((int)SpawnField[1].transform.position.x);
            FieldPoint2Z = ((int)SpawnField[1].transform.position.z);
    
            SpawnPointX = Random.Range(FieldPoint1X, FieldPoint2X);
            SpawnPointZ = Random.Range(FieldPoint1Z, FieldPoint2Z);
    
            PowerUpTypeSpawning = Random.Range(0, PowerUpTypes.Count() - 1);
        }
        void Update()
        {
            if (TimeTillPowerUpCanSpawn > -5)
            {
                TimeTillPowerUpCanSpawn -= Time.deltaTime;
            }
    
            SpawnTimer += Time.deltaTime;
    
            Position = new Vector3(SpawnPointX, FieldY, SpawnPointZ);
    
            if (SpawnTimer > SpawnInterval && TimeTillPowerUpCanSpawn < 0)
            {
                Spawn();
            }
        }
    
        private void RandomizeLocation()
        {
            SpawnPointX = Random.Range(FieldPoint1X, FieldPoint2X);
            SpawnPointZ = Random.Range(FieldPoint1Z, FieldPoint2Z);
        }
    
        private void RandomizeType()
        {
            PowerUpTypeSpawning = Random.Range(0, PowerUpTypes.Count());
        }
    
        private void RandomizeTime()
        {
            SpawnTimer = 0;
            SpawnInterval = Random.Range(FastedSpawnTime, LatestSpawnTime);
        }
    
        private void Spawn()
        {
            Instantiate(PowerUpTypes[PowerUpTypeSpawning], Position, Quaternion.identity);
            RandomizeLocation();
            RandomizeTime();
            RandomizeType();
        }
    }
        
    code snippet * ShieldScript.cs
    								
    public class ShieldScript : MonoBehaviour
    {
        [SerializeField] private PlayerPowerUps Player;
    
        private void Awake()
        {
            Player = FindObjectOfType();
        }
    
        private void OnCollisionEnter(Collision collision)
        {
            Player.ShieldHealth -= Player.ShieldDamage;
            Player.ShieldhitSoundEffect.Play();
        }
    }
        

    Schieten/Health

    Het schieten van de trippel bullets heb ik gemaakt door 3 bullets te spawnen op 3 verschillende empty gameobject met vershillende rotaties.

    code snippet * PlayerStats.cs
    
    public class PlayerStats : MonoBehaviour
    {
        [SerializeField] private GameObject bulletPrefab;
        [SerializeField] private GameObject[] TrippelBulletSpawners;
        [SerializeField] private GameObject BulletSpawner;
    
        private ScoreManager scoremanager;
    
        public float PlayerHealth;
        [HideInInspector] public float PlayerMaxHealth;
    
        private float ShootTimer;
        [SerializeField] private float ShootTimerReset;
    
        public bool TrippbleBullets;
        [SerializeField] private bool CanChangeBulletType;
    
        public bool GameOver = false;
    
        [SerializeField] private float Damage;
    
        [SerializeField] private AudioSource ShootSoundEffect;
        [SerializeField] private AudioSource PlayerHitSoundEffect;
        private void Awake()
        {
            scoremanager = FindObjectOfType();
            PlayerMaxHealth = PlayerHealth;
        }
    
        void Update()
        {
            ShootTimer -= Time.deltaTime;
    
            if (Input.GetKeyDown(KeyCode.Space) && ShootTimer < 0 || Input.GetMouseButtonDown(0) && ShootTimer < 0)
            {
                Shoot();
                ShootTimer = ShootTimerReset;
            }
    
            if (PlayerHealth <= 0)
            {
                Time.timeScale = 0;
                GameOver = true;
                gameObject.SetActive(false);
            }
        }
    
        private void Shoot()
        {
            ShootSoundEffect.Play();
            if (TrippbleBullets)
            {
                Instantiate(bulletPrefab, TrippelBulletSpawners[0].transform.position, TrippelBulletSpawners[0].transform.rotation);
                Instantiate(bulletPrefab, TrippelBulletSpawners[1].transform.position, TrippelBulletSpawners[1].transform.rotation);
                Instantiate(bulletPrefab, TrippelBulletSpawners[2].transform.position, TrippelBulletSpawners[2].transform.rotation);
            }
            else 
            Instantiate(bulletPrefab, BulletSpawner.transform.position, Quaternion.Euler(0, 90, 90));
        }
    
        private void OnCollisionEnter(Collision collision)
        {
            if (collision.gameObject.CompareTag("Bullet") || collision.gameObject.CompareTag("BossBullet"))
            {
                PlayerHealth -= collision.gameObject.GetComponent().damage;
                //scoremanager.score -= scoremanager.ScoreLossOnHit;
                if (scoremanager.score >= scoremanager.ScoreLossOnHit)
                {
                    scoremanager.score -= scoremanager.ScoreLossOnHit;
                }
                else scoremanager.score = 0;
            }
        }
        private void OnTriggerEnter(Collider other)
        {
            if (other.gameObject.CompareTag("Bomb"))
            {
                PlayerHealth -= 2;
                //scoremanager.score -= scoremanager.ScoreLossOnHit;
                if (scoremanager.score >= scoremanager.ScoreLossOnHit)
                {
                    scoremanager.score -= scoremanager.ScoreLossOnHit;
                }
                else scoremanager.score = 0;
            }
        }
    }