Jade Friend

Sigma-Phoenix Productions

Home
Games 3D art and Animation Photography
Blog

Rennovator Rift - Specific Code Details

Unity C# group project for Ravensbourne University consisting of an entire class of Year 2 Game developers and Programmers (46 students) from February to March 2024

Return to the main game page here

As this project was for a large group module, we had a total of 8 programmers working together to produce all the features and mechanics. While primarily i focused on developing the shadergraph i also devised one of the key mobility mechanics used in the game, the grapple hook.

This is the primary portion for my grapple hook script. When the player enters the grapple anchor's radius it enables the use of the hook, and when the player activates the hook it creates a spring joint between the player and the anchor to use the physics engine to control the movement, with the feel of the rope being dialed in using the behaviour variables. I also used a line renderer to create the hook visually and set the start and end points to the player and the anchor

    //using late update to render the rope so that it does not lag behind the physics of the rope 
    private void LateUpdate()
    {
        DrawRope();
    }


    private void GrappleBegin()
    {
        if(grappleAnchor != Vector3.zero) //if the grapple point is not "Reset"
        {
            //Adds a springJoint component and connects it to the desired grappleAnchor
            joint = gameObject.AddComponent();
            joint.autoConfigureConnectedAnchor = false;
            joint.connectedAnchor = grappleAnchor;

            //gets the distance between the player and the grapple-point 
            float dist = Vector3.Distance(gameObject.transform.position, grappleAnchor);

            //distance the player can vary from grapple point
            joint.maxDistance = dist * ropeMaxScale; //smaller scale, the less it can stretch
            joint.minDistance = dist * ropeMinScale; //smaller scale, the less it can pull you in

            //setting the Variables for rope's behaviour
            joint.spring = ropeSpringyness;
            joint.damper = ropeDampen;
            joint.massScale = ropeMass;

            //amount of points the line render can render too for the rope
            lr.positionCount = 2;

            isGrappling = true;
        }
public class GrappleAnchor : MonoBehaviour
{
    private GameObject enteredPlayer; //ref to the player that enters the area
    private GrappleHook hook; //ref to the GrappleHook script on the player

    private void OnTriggerEnter(Collider other)
    {
        //if the object that entered the area is a player and there isnt already a player inside the array
        if (other.tag == "Player" && enteredPlayer == null)
        {
            enteredPlayer = other.gameObject; 
            hook = enteredPlayer.GetComponent(); 
            //tells the player's grapple hook the location of the anchor and enables the hook
            hook.EnterGrappleRadius(gameObject.transform.position);
        }
    }

    private void OnTriggerExit(Collider other)
    {
        //to prevent issues if theres multiple players, only the player who initially entered can trigger the exit script
        if(other.tag == "Player" && other.gameObject == enteredPlayer) 
        {
            //triggers the exit function in the player's script
            hook.ExitGrappleRadius(); 
            //clears the references
            hook = null;
            enteredPlayer = null;
        }
    }

}
        

The entire script for the anchor points. This script only stores references the player when they're within the trigger, sending the location of the anchor to the player so that their grapple hook script can handle the physics of the game.

Its a simple and easy method for other developers to be able to quickly understand when it comes to building the levels.

For this project i was also responsible for creating a checkpoint and saving system for the game, so that players could keep their progress when they die or close the game

This was the first time i had experimented with saving systems, so for the most part i focused on saving essential data as a class in a text document.
The class contains the index number for the last checkpoint they were at, whether this is a fresh save-file and the amount of health the player has.

When the level is loaded, the SaveManager checks whether it is loading a new save file or a pre-existing one and spawns the player at the corresponding checkpoint/spawnpoint and the player's data is saved every time they pass a new checkpoint to keep up with their progress. Whenever the player dies they are also respawned at the same checkpoint they would when the game is launched based on their save-file.

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.SceneManagement;

public class PlayerSettings 
{
    public int lastSavedPosition; //index of the last checkpoint the player hit
    public int playerHealth; 
    public bool isNewGame = true; //initialised to true so that fresh saveData is always considered a new game.
}

public class SaveManager : MonoBehaviour
{
    [SerializeField]
    private GameObject[] checkPoints; //list of checkpoints in the scene

    private GameObject 
        player, //reference to the player
        startPos; //first child of GameManager, where the player spawns when they have no save data/saved checkpoints

    private PlayerSettings saveData = new PlayerSettings(); //creates a saveData type initialised to default settings

    private int currentCheckPoint; //current checkPoint the player is at in the level

    void Awake()
    {
        startPos = gameObject.transform.GetChild(0).gameObject; //gets the startPos from child
        player = GameObject.FindGameObjectWithTag("Player"); //gets a reference to the player in the scene 
        LoadFromJson(); //runs the load function to check if there is save data and to set the player's position
    }

    private void Update()
    {
        //if the player wants to load the save
        if (Input.GetKeyDown(KeyCode.L)) 
        {
            //reload the scene to re-run the "loadFromJson()" in awake
            SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); 
        }

        /if the player wants to clear a save
        if (Input.GetKeyDown(KeyCode.C)) /
        {
            //Creates a new PlayerSettings & Overwrites pre-existing save with new one
            saveData = new PlayerSettings();
            SaveToJson();
        }
    }

    //Saves the game data to a .json file
    private void SaveToJson() 
    {
        //converts the saveData to a .json formatted string and saves it to the desired path
        string savDat = JsonUtility.ToJson(saveData); 
        string filePath = Application.persistentDataPath + "/PlayerData.json"; 
        Debug.Log(filePath);
        System.IO.File.WriteAllText(filePath, savDat); //writes the data to the file
    }

    private void LoadFromJson() 
    {
        //locates the filepath of the saveData and saves it to a temporary string variable
        string filepath = Application.persistentDataPath + "/PlayerData.json";   
        string savDat = System.IO.File.ReadAllText(filepath); 

        if(JsonUtility.FromJson(savDat) != null) 
        {
            //converts from .json to PlayerSettings class and saves in in the saveData
            saveData = JsonUtility.FromJson(savDat); 
            //checks if the loading save is from a new game
            if (!saveData.isNewGame)
            {
                //sets the players position to the last checkpoint
                player.transform.position = checkPoints[saveData.lastSavedPosition].transform.position;  
            }
            else 
            {
                //sends player to the start position
                player.transform.position = startPos.transform.position;
            }
        }
        else //if no saveData could be found, treats the game as loading from a fresh save
        {
            player.transform.position = startPos.transform.position;
        }
    }

    public void HitCheckPoint(GameObject checkPoint) 
    {
        //compares index of checkpoints against the checkpoint the player hit
        for(int i = 0; i < checkPoints.Length; i++) 
        {
            if (checkPoints[i].gameObject == checkPoint)
            {
                //sets the file's index number to the checkpoint the player hit 
                saveData.lastSavedPosition = i; 
                saveData.isNewGame = false;
                //saves the game
                SaveToJson();
            }
        }
    }
}