Neolit

Neolit is a 5-man project made over the course of several months. I was one of three programmers that worked on the project. This project was my first full Unity project, where I took part in the actual design of the game. The goal of the game is simple: Survive as long as possible while gaining points! The player can do this by dodging / shooting obstacles, collecting powerups and obtaining point orbs.


My responsibilities

  • Gameplay: I worked on the player movement, and the shooting mechanic.
  • Procedural Generation: I worked on the procedural generation of items, and improved obstacle spawning.
  • Game Design: Being a small team, most of us got some experience with game design. We designed the idea from scratch, and all it’s gameplay mechanics.
  • Project Management: I took charge with the planning of the project and made sure the project stayed on track and hit milestole goals.
  • Code Quality: I was responsible for making sure all the submitted code was efficient, following conventions, and easily scalable.

Lessons learned

This project learned me a lot about the values of clean code, maintaining code and making mechanics scalable. Because it was an infinite runner project, we had to make sure everything was procedural. As an example, we had items. I ensured it was easy for a non-programmer to be able to add items, or obstacles. Up until the project, I had never used inheritance too much, but this project really made me realize how useful it is.

On top of that, optimization was really key in this project. Learned a lot of techniques to make code run far more efficient.


Project Specifications


Code Snippets

    //-----------------------------------------------
    //METHOD DESCRIPTION:
    //calculates which obstacles need to be spawned based on percentage
    //does not call the Spawners in the warmup and cooldown
    //-----------------------------------------------
    void ObstacleSpawner(Vector3 position, Quaternion rotation)
    {
        if (Random.value < _actualObstacleSpawnChance * _player.SpeedModifier)
        {
            //Calculate totalWeight
            int totalWeight = 0;
            foreach (Obstacle o in _obstacleSpawnList)
            {
                if (_timeManager.ElapsedSeconds >= o.MinimumTime)
                    totalWeight += o.Weight;

            }

            int currentWeight = 0;
            int weightChance = Random.Range(0, totalWeight);
            foreach (Obstacle o in _obstacleSpawnList)
            {
                if (_timeManager.ElapsedSeconds >= o.MinimumTime)
                {
                    if (weightChance >= currentWeight && weightChance < currentWeight + o.Weight)
                    {
                        GameObject t = Instantiate(o.Prefab, position, rotation);
                        if (!t.GetComponent<Sc_Obstacle>())
                            t.AddComponent<Sc_Obstacle>();
                        if (!o.Rotate)
                            t.GetComponent<Sc_Obstacle>().ClockWise = 0;

                        t.transform.rotation = Quaternion.AngleAxis(Random.Range(0, 360), Vector3.forward);
                        _obstacleList.Add(t);
                    }
                    currentWeight += o.Weight;
                }
            }
        }
    }
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//-----------------------------------------------
//CLASS DESCRIPTION:
//Base class for the pickup objects
//-----------------------------------------------
public class Sc_Pickup : MonoBehaviour
{
    [Header("General")]
    [Tooltip("The sound that plays when the item gets picked up")]
    [SerializeField] protected AudioClip _soundFile; //The sound that plays when the item gets picked up
    [Tooltip("The distance the pickup has to be away from the player before it gets deleted")]
    [SerializeField] protected float _maxDistance = 1f; //The distance the pickup has to be away from the player before it gets deleted
    [Tooltip("The speed at which the pickup accelerates towards the player")]
    [SerializeField] protected float _acceleration = 150f; //The speed at which the pickup accelerates towards the player

    //Other general variables
    private float _range = -1f; //The range at which the pickup accelerates towards the player
    public float Range
    {
        get { return _range; }
        set { _range = value; }
    }

    private GameObject _player;
    private bool _followPlayer;
    private float _speed = 0f;

    private void Start()
    {
        _player = GameObject.FindGameObjectWithTag("Player");
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player")
        {
            _followPlayer = true;
        }
    }

    private void Update()
    {
        if (Range > 0)
        {
            if (isInRange(_player.transform.position, Range, transform.position))
            {
                _followPlayer = true;
            }
        }

        if (_followPlayer)
        {
            _speed += _acceleration * Time.deltaTime;
            float step = _speed * Time.deltaTime;
            this.transform.position = Vector3.MoveTowards(this.transform.position, _player.transform.position, step);
            Vector3 vector = this.transform.position - _player.transform.position;
            float distance = vector.magnitude;
            if (distance <= _maxDistance)
            {
                ApplyItem();
                Sc_SoundHandler.PlaySound(_soundFile, false);
                Destroy(this.gameObject);
            }
        }
    }

    //-----------------------------------------------
    //METHOD DESCRIPTION:
    //Apply the item effect (override in the inherited class)
    //-----------------------------------------------
    protected virtual void ApplyItem(){}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


//-----------------------------------------------
//CLASS DESCRIPTION:
//Inherits from the pickup class, adds ammo when the player collects ammo
//-----------------------------------------------
public class Sc_Ammo : Sc_Pickup
{

    [Header("Ammo")]
    [Tooltip("The amount of ammo the player gets when he picks up ammo")]
    [Range(0,1)]
    [SerializeField] private float _ammoIncrease = 0.25f; //The amount of points the player gets when he picks up ammo (percentual)

    //-----------------------------------------------
    //METHOD DESCRIPTION:
    //Increase the score
    //-----------------------------------------------
    protected override void ApplyItem()
    {
        FindObjectOfType<Sc_PickUpEffectManager>().Ammo += _ammoIncrease;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//-----------------------------------------------
//CLASS DESCRIPTION:
//Base obstacle class
//-----------------------------------------------
public class Sc_Obstacle : MonoBehaviour {

    public int ClockWise = -1;

    // Use this for initialization
    void Start ()
    {
		SpawnLogic();

        float random = Random.value;
        if (random > 0.5)
            ClockWise = 1;
    }

    protected virtual void SpawnLogic()
    {
        //Do nothing
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//-----------------------------------------------
//CLASS DESCRIPTION:
//Inherits from the obstacle class, spawns the forcefield
//-----------------------------------------------
public class Sc_Forcefield : Sc_Obstacle
{
    [SerializeField] private GameObject _leftEdgeObject;
    [SerializeField] private GameObject _middleObject;
    [SerializeField] private GameObject _rightEdgeObject;
    [SerializeField]
    private float _minAngle = 90f;
    [SerializeField]
    private float _maxAngle = 270f;
    [SerializeField]
    private float _partAngle = 22.5f;

    protected override void SpawnLogic()
    {

        int min = (int)(_minAngle / _partAngle);
        int max = (int)(_maxAngle / _partAngle);
        int amountOfPanels = Random.Range(min, max);

        GameObject part;

        for (int i = 0; i < amountOfPanels; ++i)
        {
            if (i == 0)
                part = Instantiate(_rightEdgeObject, transform);
            else if (i == amountOfPanels - 1)
                part = Instantiate(_leftEdgeObject, transform);
            else
                part = Instantiate(_middleObject, transform);

            part.transform.SetParent(transform);
            part.transform.Rotate(0, 0, _partAngle * i);
        }
    }
}

Project Showcase