A project that me and a couple of students are developing. I started with creating most of the base prototype in Unity before the group decided to use Unreal Engine 5.
In the Unity version, I created:
Character swap mechanic.
Player controls, such as movement, double jump, go into stealth.
A character "merge" ability (they merge into one character).
The functionality of the laser and mirrors.
That enemies and objects can be destroyed by the laser.
A basic AI that would patrol and then follow the player if spotted.
In the Unreal Engine 5 version, I've made things such as:
The "disintigrate material" and the laser material.
The laser components and their functions (destroying, reflecting, spawning etc).
The pushable rock.
The mirrors for the laser puzzle.
A small glimps of the prototype
Pushing the puzzle rock
The disintigrating material
The reflection of the laser
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
Animator animator;
Rigidbody rb;
public int currentPlayer;
[Header("Camera Springarm")]
[SerializeField] private CameraSpringarm cameraSpringarm;
[Header("Movement")]
[SerializeField] public float movementSpeed = 10f;
[SerializeField] private float rotationSpeed = 10f;
[SerializeField] private float disabledTime = 0.4f;
private bool canMove = true;
[Header("Jumps")]
[SerializeField] private float jumpForce = 8f;
[SerializeField] private float doubleJumpForce = 8f;
[SerializeField] private float doubleJumpCooldown = 0.8f;
[SerializeField] private bool jumping = false;
[SerializeField] private bool doubleJumpAvailable = false;
private bool isFalling;
private bool isGrounded;
private int doubleJumpVariationIndex = 0;
private float doubleJumpCooldownTimer = 0;
[Header("Paraglider")]
[SerializeField] private float glidingGravity = 0.5f;
[SerializeField] private float glidingMass = 0.5f;
[SerializeField] private float glidingDrag = 15f;
[SerializeField] private float glidingAngularDrag = 15f;
private float originalDrag;
private float originalAngularDrag;
private float originalMass;
public bool gliding = false;
[Header("Stealth")]
[SerializeField] private float stealthCooldown = 5;
[SerializeField] private int detected = 0;
private bool stealthed = false;
private bool stealthButtonPressed = false;
private void Start()
{
originalDrag = rb.drag;
originalAngularDrag = rb.angularDrag;
originalMass = rb.mass;
}
private void Awake()
{
rb = GetComponent<Rigidbody>();
animator = GetComponent<Animator>();
}
private void FixedUpdate()
{
MoveCharacter();
DoubleJumpTimer();
}
private void Update()
{
if (rb.velocity.magnitude > 0) animator.SetFloat("walkBlend", 1);
if (rb.velocity.magnitude == 0) animator.SetFloat("walkBlend", 0);
}
private void MoveCharacter()
{
if (canMove)
{
Vector3 direction = Vector3.zero;
if (Input.GetKey(KeyCode.W))
{
rb.MovePosition(direction += cameraSpringarm.GetCameraForward());
}
if (Input.GetKey(KeyCode.S))
{
rb.MovePosition(direction -= cameraSpringarm.GetCameraForward());
}
if (Input.GetKey(KeyCode.A))
{
rb.MovePosition(direction -= cameraSpringarm.GetCameraRight());
}
if (Input.GetKey(KeyCode.D))
{
rb.MovePosition(direction += cameraSpringarm.GetCameraRight());
}
if (direction.magnitude > 0)
{
direction.Normalize();
Vector3 move = direction * movementSpeed * Time.deltaTime;
rb.MovePosition(transform.position + move);
// Rotate the player smoothly in the direction of movement
Quaternion toRotation = Quaternion.LookRotation(direction, Vector3.up);
rb.MoveRotation(Quaternion.Lerp(transform.rotation, toRotation, rotationSpeed * Time.deltaTime));
}
CheckVelocityForJumping();
if (Input.GetKey(KeyCode.Space))
{
if (!jumping && currentPlayer != 2 && isGrounded)
{
animator.SetTrigger("t_jump");
jumping = true;
Jump();
doubleJumpCooldownTimer = doubleJumpCooldown;
}
else if (jumping && currentPlayer == 1 && doubleJumpAvailable)
{
doubleJumpAvailable = false;
DoubleJump();
}
}
if (Input.GetKey(KeyCode.Q) && detected == 0 && currentPlayer == 2 && !stealthButtonPressed)
{
stealthButtonPressed = true;
if (!stealthed)
{
StartStealth();
}
else if (stealthed)
{
EndStealth();
}
}
if (Input.GetKeyDown(KeyCode.LeftControl))
{
if (currentPlayer == 1)
{
if (gliding)
{
rb.AddForce(Vector3.down * glidingGravity * Time.deltaTime);
}
if (!isGrounded)
{
if (!gliding)
{
StartGlide();
}
}
}
}
if (Input.GetKeyUp(KeyCode.LeftControl))
{
EndGlide();
}
}
}
private void StealthCooldown()
{
stealthButtonPressed = false;
}
public void DisableMovement()
{
canMove = false;
Invoke("StartMovement", disabledTime);
}
private void StartMovement()
{
canMove = true;
}
private void StartStealth()
{
stealthed = true;
Debug.Log("Start Stealth");
gameObject.layer = 9;
Invoke("StealthCooldown", stealthCooldown);
}
public void EndStealth()
{
stealthed = false;
Debug.Log("End Stealth");
gameObject.layer = 8;
Invoke("StealthCooldown", stealthCooldown);
}
public void Jump()
{
rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z); // Reset vertical velocity
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
animator.SetTrigger("t_jump");
isGrounded = false;
}
public void DoubleJump()
{
DoubleJumpAnimation();
rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z); // Reset vertical velocity
rb.AddForce(Vector3.up * doubleJumpForce, ForceMode.Impulse);
}
private void DoubleJumpTimer()
{
if (doubleJumpCooldownTimer >= 0 && doubleJumpCooldownTimer <= doubleJumpCooldown)
{
doubleJumpAvailable = false;
doubleJumpCooldownTimer -= Time.deltaTime;
}
if (doubleJumpCooldownTimer < 0)
{
doubleJumpAvailable = true;
doubleJumpCooldownTimer = doubleJumpCooldown + 2f;
}
}
private void DoubleJumpAnimation()
{
if (doubleJumpVariationIndex % 2 == 0) animator.SetTrigger("t_doubleJump");
else animator.SetTrigger("t_doubleJumpSpin");
doubleJumpVariationIndex++;
}
private void StartGlide()
{
animator.SetBool("b_isFlying", true);
gliding = true;
rb.velocity = Vector3.down * glidingGravity;
rb.drag = glidingDrag;
rb.angularDrag = glidingAngularDrag;
rb.mass = glidingMass;
}
private void EndGlide()
{
animator.SetBool("b_isFlying", false);
gliding = false;
rb.useGravity = true;
rb.drag = originalDrag;
rb.angularDrag = originalAngularDrag;
rb.mass = originalMass;
}
private void CheckVelocityForJumping()
{
// Check if player is falling (velocity.y < 0) and disable jump
if (rb.velocity.y < 0)
{
isGrounded = false;
isFalling = true;
}
// Check if player has landed (velocity.y approximately 0) and enable jump
if (Mathf.Approximately(rb.velocity.y, 0f) && isFalling)
{
isGrounded = true;
isFalling = false;
jumping = false;
EndGlide();
}
}
}