{"id":53,"date":"2025-02-06T11:34:48","date_gmt":"2025-02-06T11:34:48","guid":{"rendered":"https:\/\/sites.wsagames.com\/kineticpanic\/?page_id=53"},"modified":"2025-05-15T13:20:58","modified_gmt":"2025-05-15T13:20:58","slug":"programming","status":"publish","type":"page","link":"https:\/\/sites.wsagames.com\/kineticpanic\/programming\/","title":{"rendered":"Programming"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Sam was tasked as the Technical Designer for the project and produced the game&#8217;s code including the player&#8217;s movement, jumping, and abilities.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The following are some examples of that code:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PlayerAbility.cs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>using System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing UnityEngine;\nusing UnityEngine.InputSystem;\nusing UnityEngine.Serialization;\nusing UnityEngine.UI;\npublic class PlayerAbility : Singleton&lt;PlayerAbility&gt;\n{\n    &#091;SerializeField] private float distance = 5;\n    &#091;SerializeField] private Transform cameraFollow;\n    &#091;SerializeField] private LayerMask objectSelectionMask;\n    private bool _lookForObjects;\n    &#091;FormerlySerializedAs(\"_currentAbility\")] &#091;Range(0, 1)]public int currentAbility; \/\/ 0 = Gravity, 1 = Possession\n    &#091;SerializeField] private AbilitySelectionHUD abilitySelectionHUD;\n    &#091;SerializeField] private SelectionAreaHUD selectionAreaHUD;\n    &#091;SerializeField] private Slider abilitySelectionSlider;\n    private int _abilitySelectionIndex;\n    private float _originalTimeScale;\n    private int _abilityStageIndex;\n    public SelectionArea selectionArea;\n    &#091;FormerlySerializedAs(\"_forceAmount\")] public float forceAmount = 60;\n    &#091;SerializeField, Range(1, 10)] private float forceMovementTime = 5f;\n    &#091;SerializeField, Range(1, 10)] private float forceMovementEnd = 10f;\n    private List&lt;GameObject&gt; _objectRigidbodies;\n    &#091;SerializeField] private GameObject cinemachineCamera;\n    &#091;SerializeField] private float cooldownTime = 5f;\n    private bool _abilityIsCoolingDown;\n    private int abilityCharge = 4;\n    public bool useAbilityCharge = false;\n    PlayerMovement _playerMovement;\n    &#091;SerializeField] private GameObject cooldownAbilityHUD;\n    private void Start()\n    {\n        _playerMovement = GetComponent&lt;PlayerMovement&gt;();\n        _originalTimeScale = Time.timeScale;\n        _abilityStageIndex = 0;\n    }\n    public void OnAbilityInteract(InputAction.CallbackContext context)\n    {\n        if (GameManager.Instance.isOnSettings) return;\n        if (abilityCharge == 0 &amp;&amp; useAbilityCharge) return;\n        if (_abilityIsCoolingDown) return;\n        if (GameManager.Instance.gameIsOver) return;\n        Debug.Log($\"Ability Stage Index: {_abilityStageIndex}\");\n        if (!context.performed) return;\n        if (_abilityStageIndex == 0) Selection();\n    }\n\n    public void OnAbilityCast(InputAction.CallbackContext context)\n    {\n        if (!context.performed || _abilityStageIndex != 1) return;\n        Debug.Log(currentAbility.ToString());\n        Casting();\n    }\n    private void Selection()\n    {\n        abilitySelectionHUD.gameObject.SetActive(true);\n        selectionArea.gameObject.SetActive(true);\n        _abilityStageIndex++;\n    }\n    private void Casting()\n    {\n        \/\/ enable cinemachine\n        cinemachineCamera.gameObject.SetActive(true);\n        abilitySelectionHUD.gameObject.SetActive(false);\n        selectionArea.gameObject.SetActive(true);\n        PerformAbility(currentAbility, selectionArea.GetObjects(), selectionArea.GetRadius());\n        abilityCharge--;\n        Debug.Log($\"Ability Charge: {abilityCharge}\");\n        selectionArea.gameObject.SetActive(false);\n        _abilityStageIndex = 0;\n        StartCoroutine(CoolDownAbility());\n    }\n\n    public void AddAbilityCharge(int charge)\n    {\n        abilityCharge += charge;\n    }\n\n    private void PerformAbility(int ability, List&lt;GameObject&gt; objects, float radius)\n    {\n        _objectRigidbodies = objects;\n        switch (ability)\n        {\n            case 0:\n                foreach (var objectRigidBody in objects.Select(selectedObject =&gt; selectedObject.GetComponent&lt;Rigidbody&gt;()))\n                {\n                    StartCoroutine(MoveObjectUsingGravity(objectRigidBody, radius));\n                }\n                break;\n            case 1:\n                foreach (var interactable in objects.Select(selectedObject =&gt;\n                             selectedObject.GetComponent&lt;Interactables&gt;()))\n                {\n                    StartCoroutine(MoveObjects(interactable));\n                }\n                break;\n        }\n        \/\/ 0 = Gravity\n        \/\/ 1 = Possession\n    }\n    IEnumerator CoolDownAbility()\n    {\n        _abilityIsCoolingDown = true;\n        cooldownAbilityHUD.SetActive(true);\n        yield return new WaitForSeconds(cooldownTime);\n        _abilityIsCoolingDown = false;\n        cooldownAbilityHUD.SetActive(false);\n\n    }\n\n    IEnumerator MoveObjects(Interactables objects)\n    {\n        objects.canMove = true;\n        yield return new WaitForSecondsRealtime(forceMovementEnd);\n        objects.canMove = false;\n    }\n    private IEnumerator MoveObjectUsingGravity(Rigidbody objectRb, float radius)\n    {\n        objectRb.useGravity = false;\n        Vector3 forceToAdd = Vector3.up * (radius * forceAmount);\n        Debug.Log($\"Force to add {forceToAdd}\");\n        objectRb.AddForce(forceToAdd * Time.fixedDeltaTime, ForceMode.Impulse);\n        yield return new WaitForSecondsRealtime(forceMovementEnd);\n        CancelAbility();\n    }\n    public void OnAbilityCancel(InputAction.CallbackContext context)\n    {\n        Time.timeScale = _originalTimeScale;\n        if (abilitySelectionHUD != null) abilitySelectionHUD.gameObject.SetActive(false);\n        if (selectionArea != null) selectionArea.gameObject.SetActive(false);\n        _abilityStageIndex = 0;\n        CancelAbility();\n    }\n    void CancelAbility()\n    {\n        if (_objectRigidbodies == null) return;\n        foreach (var objectRb in _objectRigidbodies)\n        {\n            objectRb.transform.parent = null;\n            Rigidbody rb = objectRb.GetComponent&lt;Rigidbody&gt;();\n            rb.useGravity = true;\n        }\n        _objectRigidbodies.Clear();\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Interactables.cs<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>using UnityEngine;\nusing UnityEngine.Serialization;\n\npublic class Interactables : MonoBehaviour\n{\n    Vector3 _originalPosition;\n    Quaternion _originalRotation;\n    public bool canMove;\n    private float _movementSpeed = 7.5f;\n    private Vector2 _movementDirection;\n    PlayerMovement _playerMovement;\n    Rigidbody _rb;\n    Camera _camera;\n    \/\/ Start is called before the first frame update\n    private void Start()\n    {\n        _playerMovement = FindObjectOfType&lt;PlayerMovement&gt;();\n        _originalPosition = transform.position;\n        _originalRotation = transform.rotation;\n        _rb = GetComponent&lt;Rigidbody&gt;();\n        _camera = Camera.main;\n    }\n\n    private void Update()\n    {\n        GetMovement();\n    }\n\n    private void FixedUpdate()\n    {\n        MoveObject();\n    }\n\n    void MoveObject()\n    {\n        if (_rb == null) return;\n        if (!canMove)\n        {\n            _rb.isKinematic = false;\n            return;\n        }\n        _rb.isKinematic = true;\n        Vector3 movement = _camera.transform.right * _movementDirection.x + _camera.transform.forward * _movementDirection.y;\n        movement.y = 0f;\n        _rb.MovePosition(_rb.position + movement * (_movementSpeed * Time.fixedDeltaTime));\n    }\n    \n    void GetMovement()\n    {\n        _movementDirection = _playerMovement.direction;\n    }\n\n    public void ResetPosition()\n    {\n        transform.position = _originalPosition;\n        transform.rotation = _originalRotation;\n    }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">AbilitySelectionHUD<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>using System;\nusing TMPro;\nusing UnityEngine;\nusing UnityEngine.InputSystem;\nusing UnityEngine.UI;\n\npublic class AbilitySelectionHUD : MonoBehaviour\n{\n    &#091;SerializeField] private Image abilityImageSlot;\n    &#091;SerializeField] private Sprite possessionAbilityIcon;\n    &#091;SerializeField] private Sprite gravityAbilityIcon;\n    private PlayerAbility _ability;\n    &#091;SerializeField] private TextMeshProUGUI abilityMultiplierText;\n    private float _currentAbilitySelection;\n    &#091;SerializeField] TextMeshProUGUI useAbilityText;\n    &#091;SerializeField] private String useAbilityTextController;\n    &#091;SerializeField] private String useAbilityTextKbm;\n    GameManager _gameManager;\n    private void OnEnable()\n    {\n        _gameManager = FindObjectOfType&lt;GameManager&gt;();\n        _ability = GameObject.FindGameObjectWithTag(\"Player\").GetComponent&lt;PlayerAbility&gt;();\n        ChangeSelectedAbility(0);\n    }\n\n    private void Update()\n    {\n        useAbilityText.text = _gameManager.isUsingController ? useAbilityTextController : useAbilityTextKbm;\n    }\n\n    public void OnDetectInputDevice(InputAction.CallbackContext context)\n    {\n        switch (context.control.device)\n        {\n            case Gamepad:\n                useAbilityText.text = useAbilityTextController;\n                break;\n            case Keyboard:\n                useAbilityText.text = useAbilityTextKbm;\n                break;\n        }\n    }\n\n    public void OnChangeAbility(InputAction.CallbackContext context)\n    {\n        if (!context.performed) return;\n        _currentAbilitySelection = _currentAbilitySelection switch\n        {\n            0 =&gt; 1,\n            1 =&gt; 0,\n            _ =&gt; _currentAbilitySelection\n        };\n        ChangeSelectedAbility(_currentAbilitySelection);\n    }\n    void ChangeSelectedAbility(Single ability)\n    {\n        abilityImageSlot.sprite = ability switch\n        {\n            0 =&gt; gravityAbilityIcon,\n            1 =&gt; possessionAbilityIcon,\n            _ =&gt; abilityImageSlot.sprite\n        };\n        _ability.currentAbility = (int)ability;\n    }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can find more examples of code on Sam&#8217;s blog: <a href=\"https:\/\/sites.wsagames.com\/sc18g23\/\">METAL MADNESS by Sam Cox \u2013 Metal Madness<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sam was tasked as the Technical Designer for the project and produced the game&#8217;s code including the player&#8217;s movement, jumping, and abilities. The following are some examples of that code: PlayerAbility.cs Interactables.cs AbilitySelectionHUD You can find more examples of code on Sam&#8217;s blog: METAL MADNESS by Sam Cox \u2013 Metal Madness<\/p>\n","protected":false},"author":3,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-53","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/sites.wsagames.com\/kineticpanic\/wp-json\/wp\/v2\/pages\/53","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sites.wsagames.com\/kineticpanic\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/sites.wsagames.com\/kineticpanic\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/sites.wsagames.com\/kineticpanic\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/sites.wsagames.com\/kineticpanic\/wp-json\/wp\/v2\/comments?post=53"}],"version-history":[{"count":2,"href":"https:\/\/sites.wsagames.com\/kineticpanic\/wp-json\/wp\/v2\/pages\/53\/revisions"}],"predecessor-version":[{"id":225,"href":"https:\/\/sites.wsagames.com\/kineticpanic\/wp-json\/wp\/v2\/pages\/53\/revisions\/225"}],"wp:attachment":[{"href":"https:\/\/sites.wsagames.com\/kineticpanic\/wp-json\/wp\/v2\/media?parent=53"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}