Programming

Sam was tasked as the Technical Designer for the project and produced the game’s code including the player’s movement, jumping, and abilities.

The following are some examples of that code:

PlayerAbility.cs

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Serialization;
using UnityEngine.UI;
public class PlayerAbility : Singleton<PlayerAbility>
{
    [SerializeField] private float distance = 5;
    [SerializeField] private Transform cameraFollow;
    [SerializeField] private LayerMask objectSelectionMask;
    private bool _lookForObjects;
    [FormerlySerializedAs("_currentAbility")] [Range(0, 1)]public int currentAbility; // 0 = Gravity, 1 = Possession
    [SerializeField] private AbilitySelectionHUD abilitySelectionHUD;
    [SerializeField] private SelectionAreaHUD selectionAreaHUD;
    [SerializeField] private Slider abilitySelectionSlider;
    private int _abilitySelectionIndex;
    private float _originalTimeScale;
    private int _abilityStageIndex;
    public SelectionArea selectionArea;
    [FormerlySerializedAs("_forceAmount")] public float forceAmount = 60;
    [SerializeField, Range(1, 10)] private float forceMovementTime = 5f;
    [SerializeField, Range(1, 10)] private float forceMovementEnd = 10f;
    private List<GameObject> _objectRigidbodies;
    [SerializeField] private GameObject cinemachineCamera;
    [SerializeField] private float cooldownTime = 5f;
    private bool _abilityIsCoolingDown;
    private int abilityCharge = 4;
    public bool useAbilityCharge = false;
    PlayerMovement _playerMovement;
    [SerializeField] private GameObject cooldownAbilityHUD;
    private void Start()
    {
        _playerMovement = GetComponent<PlayerMovement>();
        _originalTimeScale = Time.timeScale;
        _abilityStageIndex = 0;
    }
    public void OnAbilityInteract(InputAction.CallbackContext context)
    {
        if (GameManager.Instance.isOnSettings) return;
        if (abilityCharge == 0 && useAbilityCharge) return;
        if (_abilityIsCoolingDown) return;
        if (GameManager.Instance.gameIsOver) return;
        Debug.Log($"Ability Stage Index: {_abilityStageIndex}");
        if (!context.performed) return;
        if (_abilityStageIndex == 0) Selection();
    }

    public void OnAbilityCast(InputAction.CallbackContext context)
    {
        if (!context.performed || _abilityStageIndex != 1) return;
        Debug.Log(currentAbility.ToString());
        Casting();
    }
    private void Selection()
    {
        abilitySelectionHUD.gameObject.SetActive(true);
        selectionArea.gameObject.SetActive(true);
        _abilityStageIndex++;
    }
    private void Casting()
    {
        // enable cinemachine
        cinemachineCamera.gameObject.SetActive(true);
        abilitySelectionHUD.gameObject.SetActive(false);
        selectionArea.gameObject.SetActive(true);
        PerformAbility(currentAbility, selectionArea.GetObjects(), selectionArea.GetRadius());
        abilityCharge--;
        Debug.Log($"Ability Charge: {abilityCharge}");
        selectionArea.gameObject.SetActive(false);
        _abilityStageIndex = 0;
        StartCoroutine(CoolDownAbility());
    }

    public void AddAbilityCharge(int charge)
    {
        abilityCharge += charge;
    }

    private void PerformAbility(int ability, List<GameObject> objects, float radius)
    {
        _objectRigidbodies = objects;
        switch (ability)
        {
            case 0:
                foreach (var objectRigidBody in objects.Select(selectedObject => selectedObject.GetComponent<Rigidbody>()))
                {
                    StartCoroutine(MoveObjectUsingGravity(objectRigidBody, radius));
                }
                break;
            case 1:
                foreach (var interactable in objects.Select(selectedObject =>
                             selectedObject.GetComponent<Interactables>()))
                {
                    StartCoroutine(MoveObjects(interactable));
                }
                break;
        }
        // 0 = Gravity
        // 1 = Possession
    }
    IEnumerator CoolDownAbility()
    {
        _abilityIsCoolingDown = true;
        cooldownAbilityHUD.SetActive(true);
        yield return new WaitForSeconds(cooldownTime);
        _abilityIsCoolingDown = false;
        cooldownAbilityHUD.SetActive(false);

    }

    IEnumerator MoveObjects(Interactables objects)
    {
        objects.canMove = true;
        yield return new WaitForSecondsRealtime(forceMovementEnd);
        objects.canMove = false;
    }
    private IEnumerator MoveObjectUsingGravity(Rigidbody objectRb, float radius)
    {
        objectRb.useGravity = false;
        Vector3 forceToAdd = Vector3.up * (radius * forceAmount);
        Debug.Log($"Force to add {forceToAdd}");
        objectRb.AddForce(forceToAdd * Time.fixedDeltaTime, ForceMode.Impulse);
        yield return new WaitForSecondsRealtime(forceMovementEnd);
        CancelAbility();
    }
    public void OnAbilityCancel(InputAction.CallbackContext context)
    {
        Time.timeScale = _originalTimeScale;
        if (abilitySelectionHUD != null) abilitySelectionHUD.gameObject.SetActive(false);
        if (selectionArea != null) selectionArea.gameObject.SetActive(false);
        _abilityStageIndex = 0;
        CancelAbility();
    }
    void CancelAbility()
    {
        if (_objectRigidbodies == null) return;
        foreach (var objectRb in _objectRigidbodies)
        {
            objectRb.transform.parent = null;
            Rigidbody rb = objectRb.GetComponent<Rigidbody>();
            rb.useGravity = true;
        }
        _objectRigidbodies.Clear();
    }
}

Interactables.cs

using UnityEngine;
using UnityEngine.Serialization;

public class Interactables : MonoBehaviour
{
    Vector3 _originalPosition;
    Quaternion _originalRotation;
    public bool canMove;
    private float _movementSpeed = 7.5f;
    private Vector2 _movementDirection;
    PlayerMovement _playerMovement;
    Rigidbody _rb;
    Camera _camera;
    // Start is called before the first frame update
    private void Start()
    {
        _playerMovement = FindObjectOfType<PlayerMovement>();
        _originalPosition = transform.position;
        _originalRotation = transform.rotation;
        _rb = GetComponent<Rigidbody>();
        _camera = Camera.main;
    }

    private void Update()
    {
        GetMovement();
    }

    private void FixedUpdate()
    {
        MoveObject();
    }

    void MoveObject()
    {
        if (_rb == null) return;
        if (!canMove)
        {
            _rb.isKinematic = false;
            return;
        }
        _rb.isKinematic = true;
        Vector3 movement = _camera.transform.right * _movementDirection.x + _camera.transform.forward * _movementDirection.y;
        movement.y = 0f;
        _rb.MovePosition(_rb.position + movement * (_movementSpeed * Time.fixedDeltaTime));
    }
    
    void GetMovement()
    {
        _movementDirection = _playerMovement.direction;
    }

    public void ResetPosition()
    {
        transform.position = _originalPosition;
        transform.rotation = _originalRotation;
    }
}

AbilitySelectionHUD

using System;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

public class AbilitySelectionHUD : MonoBehaviour
{
    [SerializeField] private Image abilityImageSlot;
    [SerializeField] private Sprite possessionAbilityIcon;
    [SerializeField] private Sprite gravityAbilityIcon;
    private PlayerAbility _ability;
    [SerializeField] private TextMeshProUGUI abilityMultiplierText;
    private float _currentAbilitySelection;
    [SerializeField] TextMeshProUGUI useAbilityText;
    [SerializeField] private String useAbilityTextController;
    [SerializeField] private String useAbilityTextKbm;
    GameManager _gameManager;
    private void OnEnable()
    {
        _gameManager = FindObjectOfType<GameManager>();
        _ability = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerAbility>();
        ChangeSelectedAbility(0);
    }

    private void Update()
    {
        useAbilityText.text = _gameManager.isUsingController ? useAbilityTextController : useAbilityTextKbm;
    }

    public void OnDetectInputDevice(InputAction.CallbackContext context)
    {
        switch (context.control.device)
        {
            case Gamepad:
                useAbilityText.text = useAbilityTextController;
                break;
            case Keyboard:
                useAbilityText.text = useAbilityTextKbm;
                break;
        }
    }

    public void OnChangeAbility(InputAction.CallbackContext context)
    {
        if (!context.performed) return;
        _currentAbilitySelection = _currentAbilitySelection switch
        {
            0 => 1,
            1 => 0,
            _ => _currentAbilitySelection
        };
        ChangeSelectedAbility(_currentAbilitySelection);
    }
    void ChangeSelectedAbility(Single ability)
    {
        abilityImageSlot.sprite = ability switch
        {
            0 => gravityAbilityIcon,
            1 => possessionAbilityIcon,
            _ => abilityImageSlot.sprite
        };
        _ability.currentAbility = (int)ability;
    }
}

You can find more examples of code on Sam’s blog: METAL MADNESS by Sam Cox – Metal Madness