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
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();
}
}
}
}