using System;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
//Wallrunning
public LayerMask whatIsWall;
public float wallrunForce,maxWallrunTime, maxWallSpeed;
bool isWallRight, isWallLeft;
bool isWallRunning;
public float maxWallRunCameraTilt, wallRunCameraTilt;
private void WallRunInput() //make sure to call in void Update
{
//Wallrun
if (Input.GetKey(KeyCode.D) && isWallRight) StartWallrun();
if (Input.GetKey(KeyCode.A) && isWallLeft) StartWallrun();
}
private void StartWallrun()
{
rb.useGravity = false;
isWallRunning = true;
allowDashForceCounter = false;
if (rb.velocity.magnitude <= maxWallSpeed)
{
rb.AddForce(orientation.forward * wallrunForce * Time.deltaTime);
//Make sure char sticks to wall
if (isWallRight)
rb.AddForce(orientation.right * wallrunForce / 5 * Time.deltaTime);
else
rb.AddForce(-orientation.right * wallrunForce / 5 * Time.deltaTime);
}
}
private void StopWallRun()
{
isWallRunning = false;
rb.useGravity = true;
}
private void CheckForWall() //make sure to call in void Update
{
isWallRight = Physics.Raycast(transform.position, orientation.right, 1f, whatIsWall);
isWallLeft = Physics.Raycast(transform.position, -orientation.right, 1f, whatIsWall);
//leave wall run
if (!isWallLeft && !isWallRight) StopWallRun();
//reset double jump (if you have one :smile:)
if (isWallLeft || isWallRight) doubleJumpsLeft = startDoubleJumps;
}
/// <summary>
/// Wall run done, here comes the rest of the movement script
/// </summary>
//Assingables
public Transform playerCam;
public Transform orientation;
//Other
private Rigidbody rb;
//Rotation and look
private float xRotation;
private float sensitivity = 50f;
private float sensMultiplier = 1f;
//Movement
public float moveSpeed = 4500;
public float maxSpeed = 20;
private float startMaxSpeed;
public bool grounded;
public LayerMask whatIsGround;
public float counterMovement = 0.175f;
private float threshold = 0.01f;
public float maxSlopeAngle = 35f;
//Crouch & Slide
private Vector3 crouchScale = new Vector3(1, 0.5f, 1);
private Vector3 playerScale;
public float slideForce = 400;
public float slideCounterMovement = 0.2f;
public float crouchGravityMultiplier;
//Jumping
private bool readyToJump = true;
private float jumpCooldown = 0.25f;
public float jumpForce = 550f;
public int startDoubleJumps = 1;
int doubleJumpsLeft;
//Input
public float x, y;
bool jumping, sprinting, crouching;
//AirDash
public float dashForce;
public float dashCooldown;
public float dashTime;
bool allowDashForceCounter;
bool readyToDash;
int wTapTimes = 0;
Vector3 dashStartVector;
//RocketBoost
public float maxRocketTime;
public float rocketForce;
bool rocketActive, readyToRocket;
bool alreadyInvokedRockedStop;
float rocketTimer;
//Sliding
private Vector3 normalVector = Vector3.up;
//SonicSpeed
public float maxSonicSpeed;
public float sonicSpeedForce;
public float timeBetweenNextSonicBoost;
float timePassedSonic;
//flash
public float flashCooldown, flashRange;
public int maxFlashesLeft;
bool alreadySubtractedFlash;
public int flashesLeft = 3;
//Climbing
public float climbForce, maxClimbSpeed;
public LayerMask whatIsLadder;
bool alreadyStoppedAtLadder;
void Awake()
{
rb = GetComponent<Rigidbody>();
startMaxSpeed = maxSpeed;
}
void Start()
{
playerScale = transform.localScale;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
private void FixedUpdate()
{
Movement();
}
private void Update()
{
MyInput();
Look();
CheckForWall();
SonicSpeed();
WallRunInput();
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
/// <summary>
/// Find user input. Should put this in its own class but im lazy
/// </summary>
private void MyInput()
{
x = Input.GetAxisRaw("Horizontal");
y = Input.GetAxisRaw("Vertical");
jumping = Input.GetButton("Jump");
crouching = Input.GetKey(KeyCode.LeftShift);
//Crouching
if (Input.GetKeyDown(KeyCode.LeftShift))
StartCrouch();
if (Input.GetKeyUp(KeyCode.LeftShift))
StopCrouch();
//Double Jumping
if (Input.GetButtonDown("Jump") && !grounded && doubleJumpsLeft >= 1)
{
Jump();
doubleJumpsLeft--;
}
//Dashing
if (Input.GetKeyDown(KeyCode.W) && wTapTimes <= 1)
{
wTapTimes++;
Invoke("ResetTapTimes", 0.3f);
}
if (wTapTimes == 2 && readyToDash) Dash();
//SideFlash
if (Input.GetKeyDown(KeyCode.Mouse1) && flashesLeft > 0 && x > 0) SideFlash(true);
if (Input.GetKeyDown(KeyCode.Mouse1) && flashesLeft > 0 && x < 0) SideFlash(false);
//RocketFlight
if (Input.GetKeyDown(KeyCode.LeftControl) && readyToRocket)
{
//Dampens velocity
rb.velocity = rb.velocity / 3;
}
if (Input.GetKey(KeyCode.LeftControl) && readyToRocket)
StartRocketBoost();
//Climbing
if (Physics.Raycast(transform.position, orientation.forward, 1, whatIsLadder) && y > .9f)
Climb();
else alreadyStoppedAtLadder = false;
}
private void ResetTapTimes()
{
wTapTimes = 0;
}
private void StartCrouch()
{
transform.localScale = crouchScale;
transform.position = new Vector3(transform.position.x, transform.position.y - 0.5f, transform.position.z);
if (rb.velocity.magnitude > 0.5f)
{
if (grounded)
{
rb.AddForce(orientation.transform.forward * slideForce);
}
}
}
private void StopCrouch()
{
transform.localScale = playerScale;
transform.position = new Vector3(transform.position.x, transform.position.y + 0.5f, transform.position.z);
}
private void Movement()
{
//Extra gravity
//Needed that the Ground Check works better!
float gravityMultiplier = 10f;
if (crouching) gravityMultiplier = crouchGravityMultiplier;
rb.AddForce(Vector3.down * Time.deltaTime * gravityMultiplier);
//Find actual velocity relative to where player is looking
Vector2 mag = FindVelRelativeToLook();
float xMag = mag.x, yMag = mag.y;
//Counteract sliding and sloppy movement
CounterMovement(x, y, mag);
//If holding jump && ready to jump, then jump
if (readyToJump && jumping && grounded && !rocketActive) Jump();
//ResetStuff when touching ground
if (grounded)
{
readyToDash = true;
readyToRocket = true;
doubleJumpsLeft = startDoubleJumps;
}
//Set max speed
float maxSpeed = this.maxSpeed;
//If sliding down a ramp, add force down so player stays grounded and also builds speed
if (crouching && grounded && readyToJump)
{
rb.AddForce(Vector3.down * Time.deltaTime * 3000);
return;
}
//If speed is larger than maxspeed, cancel out the input so you don't go over max speed
if (x > 0 && xMag > maxSpeed) x = 0;
if (x < 0 && xMag < -maxSpeed) x = 0;
if (y > 0 && yMag > maxSpeed) y = 0;
if (y < 0 && yMag < -maxSpeed) y = 0;
//Some multipliers
float multiplier = 1f, multiplierV = 1f;
// Movement in air
if (!grounded)
{
multiplier = 0.5f;
multiplierV = 0.5f;
}
// Movement while sliding
if (grounded && crouching) multiplierV = 0f;
//Apply forces to move player
rb.AddForce(orientation.transform.forward * y * moveSpeed * Time.deltaTime * multiplier * multiplierV);
rb.AddForce(orientation.transform.right * x * moveSpeed * Time.deltaTime * multiplier);
}
private void Jump()
{
if (grounded)
{
readyToJump = false;
//Add jump forces
rb.AddForce(Vector2.up * jumpForce * 1.5f);
rb.AddForce(normalVector * jumpForce * 0.5f);
//If jumping while falling, reset y velocity.
Vector3 vel = rb.velocity;
if (rb.velocity.y < 0.5f)
rb.velocity = new Vector3(vel.x, 0, vel.z);
else if (rb.velocity.y > 0)
rb.velocity = new Vector3(vel.x, vel.y / 2, vel.z);
Invoke(nameof(ResetJump), jumpCooldown);
}
if (!grounded)
{
readyToJump = false;
//Add jump forces
rb.AddForce(orientation.forward * jumpForce * 1f);
rb.AddForce(Vector2.up * jumpForce * 1.5f);
rb.AddForce(normalVector * jumpForce * 0.5f);
//Reset Velocity
rb.velocity = Vector3.zero;
//Disable dashForceCounter if doublejumping while dashing
allowDashForceCounter = false;
Invoke(nameof(ResetJump), jumpCooldown);
}
//Walljump
if (isWallRunning)
{
readyToJump = false;
//normal jump
if (isWallLeft && !Input.GetKey(KeyCode.D) || isWallRight && !Input.GetKey(KeyCode.A))
{
rb.AddForce(Vector2.up * jumpForce * 1.5f);
rb.AddForce(normalVector * jumpForce * 0.5f);
}
//sidwards wallhop
if (isWallRight || isWallLeft && Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D)) rb.AddForce(-orientation.up * jumpForce * 1f);
if (isWallRight && Input.GetKey(KeyCode.A)) rb.AddForce(-orientation.right * jumpForce * 3.2f);
if (isWallLeft && Input.GetKey(KeyCode.D)) rb.AddForce(orientation.right * jumpForce * 3.2f);
//Always add forward force
rb.AddForce(orientation.forward * jumpForce * 1f);
//Disable dashForceCounter if doublejumping while dashing
allowDashForceCounter = false;
Invoke(nameof(ResetJump), jumpCooldown);
}
}
private void ResetJump()
{
readyToJump = true;
}
private void Dash()
{
//saves current velocity
dashStartVector = orientation.forward;
allowDashForceCounter = true;
readyToDash = false;
wTapTimes = 0;
//Deactivate gravity
rb.useGravity = false;
//Add force
rb.velocity = Vector3.zero;
rb.AddForce(orientation.forward * dashForce);
Invoke("ActivateGravity", dashTime);
}
private void ActivateGravity()
{
rb.useGravity = true;
//Counter currentForce
if (allowDashForceCounter)
{
rb.AddForce(dashStartVector * -dashForce * 0.5f);
}
}
private void SonicSpeed()
{
//If running builds up speed
if (grounded && y >= 0.99f)
{
timePassedSonic += Time.deltaTime;
}
else
{
timePassedSonic = 0;
maxSpeed = startMaxSpeed;
}
if (timePassedSonic >= timeBetweenNextSonicBoost)
{
if (maxSpeed <= maxSonicSpeed)
{
maxSpeed += 5;
rb.AddForce(orientation.forward * Time.deltaTime * sonicSpeedForce);
}
timePassedSonic = 0;
}
}
private void SideFlash(bool isRight)
{
RaycastHit hit;
//Flash Right
if (Physics.Raycast(orientation.position, orientation.right, out hit, flashRange) && isRight)
{
transform.position = hit.point;
}
else if (!Physics.Raycast(orientation.position, orientation.right, out hit, flashRange) && isRight)
transform.position = new Vector3(transform.position.x + flashRange, transform.position.y, transform.position.z);
//Flash Left
if (Physics.Raycast(orientation.position, -orientation.right, out hit, flashRange) && !isRight)
{
transform.position = hit.point;
}
else if (!Physics.Raycast(orientation.position, -orientation.right, out hit, flashRange) && !isRight)
transform.position = new Vector3(transform.position.x - flashRange, transform.position.y, transform.position.z);
//Dampen falldown
Vector3 vel = rb.velocity;
if (rb.velocity.y < 0.5f && !alreadyStoppedAtLadder)
{
rb.velocity = new Vector3(vel.x, 0, vel.z);
}
flashesLeft--;
if (!alreadySubtractedFlash)
{
Invoke("ResetFlash", flashCooldown);
alreadySubtractedFlash = true;
}
}
private void ResetFlash()
{
alreadySubtractedFlash = false;
Invoke("ResetFlash", flashCooldown);
if (flashesLeft < maxFlashesLeft)
flashesLeft++;
}
private void StartRocketBoost()
{
if (!alreadyInvokedRockedStop)
{
Invoke("StopRocketBoost", maxRocketTime);
alreadyInvokedRockedStop = true;
}
rocketTimer += Time.deltaTime;
rocketActive = true;
//Disable dashForceCounter if doublejumping while dashing
allowDashForceCounter = false;
/*Boost all Forces
Vector3 vel = velocityToBoost;
Vector3 velBoosted = vel * rocketBoostMultiplier;
rb.velocity = velBoosted;
*/
//Boost forwards and upwards
rb.AddForce(orientation.forward * rocketForce * Time.deltaTime * 1f);
rb.AddForce(Vector3.up * rocketForce * Time.deltaTime * 2f);
}
private void StopRocketBoost()
{
alreadyInvokedRockedStop = false;
rocketActive = false;
readyToRocket = false;
if (rocketTimer >= maxRocketTime - 0.2f)
{
rb.AddForce(orientation.forward * rocketForce * -.2f);
rb.AddForce(Vector3.up * rocketForce * -.4f);
}
else
{
rb.AddForce(orientation.forward * rocketForce * -.2f * rocketTimer);
rb.AddForce(Vector3.up * rocketForce * -.4f * rocketTimer);
}
rocketTimer = 0;
}
private void Climb()
{
//Makes possible to climb even when falling down fast
Vector3 vel = rb.velocity;
if (rb.velocity.y < 0.5f && !alreadyStoppedAtLadder)
{
rb.velocity = new Vector3(vel.x, 0, vel.z);
//Make sure char get's at wall
alreadyStoppedAtLadder = true;
rb.AddForce(orientation.forward * 500 * Time.deltaTime);
}
//Push character up
if (rb.velocity.magnitude < maxClimbSpeed)
rb.AddForce(orientation.up * climbForce * Time.deltaTime);
//Doesn't Push into the wall
if (!Input.GetKey(KeyCode.S)) y = 0;
}
private float desiredX;
private void Look()
{
float mouseX = Input.GetAxis("Mouse X") * sensitivity * Time.fixedDeltaTime * sensMultiplier;
float mouseY = Input.GetAxis("Mouse Y") * sensitivity * Time.fixedDeltaTime * sensMultiplier;
//Find current look rotation
Vector3 rot = playerCam.transform.localRotation.eulerAngles;
desiredX = rot.y + mouseX;
//Rotate, and also make sure we dont over- or under-rotate.
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -90f, 90f);
//Perform the rotations
playerCam.transform.localRotation = Quaternion.Euler(xRotation, desiredX, wallRunCameraTilt);
orientation.transform.localRotation = Quaternion.Euler(0, desiredX, 0);
//While Wallrunning
//Tilts camera in .5 second
if (Math.Abs(wallRunCameraTilt) < maxWallRunCameraTilt && isWallRunning && isWallRight)
wallRunCameraTilt += Time.deltaTime * maxWallRunCameraTilt * 2;
if (Math.Abs(wallRunCameraTilt) < maxWallRunCameraTilt && isWallRunning && isWallLeft)
wallRunCameraTilt -= Time.deltaTime * maxWallRunCameraTilt * 2;
//Tilts camera back again
if (wallRunCameraTilt > 0 && !isWallRight && !isWallLeft)
wallRunCameraTilt -= Time.deltaTime * maxWallRunCameraTilt * 2;
if (wallRunCameraTilt < 0 && !isWallRight && !isWallLeft)
wallRunCameraTilt += Time.deltaTime * maxWallRunCameraTilt * 2;
}
private void CounterMovement(float x, float y, Vector2 mag)
{
if (!grounded || jumping) return;
//Slow down sliding
if (crouching)
{
rb.AddForce(moveSpeed * Time.deltaTime * -rb.velocity.normalized * slideCounterMovement);
return;
}
//Counter movement
if (Math.Abs(mag.x) > threshold && Math.Abs(x) < 0.05f || (mag.x < -threshold && x > 0) || (mag.x > threshold && x < 0))
{
rb.AddForce(moveSpeed * orientation.transform.right * Time.deltaTime * -mag.x * counterMovement);
}
if (Math.Abs(mag.y) > threshold && Math.Abs(y) < 0.05f || (mag.y < -threshold && y > 0) || (mag.y > threshold && y < 0))
{
rb.AddForce(moveSpeed * orientation.transform.forward * Time.deltaTime * -mag.y * counterMovement);
}
//Limit diagonal running. This will also cause a full stop if sliding fast and un-crouching, so not optimal.
if (Mathf.Sqrt((Mathf.Pow(rb.velocity.x, 2) + Mathf.Pow(rb.velocity.z, 2))) > maxSpeed)
{
float fallspeed = rb.velocity.y;
Vector3 n = rb.velocity.normalized * maxSpeed;
rb.velocity = new Vector3(n.x, fallspeed, n.z);
}
}
/// <summary>
/// Find the velocity relative to where the player is looking
/// Useful for vectors calculations regarding movement and limiting movement
/// </summary>
public Vector2 FindVelRelativeToLook()
{
float lookAngle = orientation.transform.eulerAngles.y;
float moveAngle = Mathf.Atan2(rb.velocity.x, rb.velocity.z) * Mathf.Rad2Deg;
float u = Mathf.DeltaAngle(lookAngle, moveAngle);
float v = 90 - u;
float magnitue = rb.velocity.magnitude;
float yMag = magnitue * Mathf.Cos(u * Mathf.Deg2Rad);
float xMag = magnitue * Mathf.Cos(v * Mathf.Deg2Rad);
return new Vector2(xMag, yMag);
}
private bool IsFloor(Vector3 v)
{
float angle = Vector3.Angle(Vector3.up, v);
return angle < maxSlopeAngle;
}
private bool cancellingGrounded;
/// <summary>
/// Handle ground detection
/// </summary>
private void OnCollisionStay(Collision other)
{
//Make sure we are only checking for walkable layers
int layer = other.gameObject.layer;
if (whatIsGround != (whatIsGround | (1 << layer))) return;
//Iterate through every collision in a physics update
for (int i = 0; i < other.contactCount; i++)
{
Vector3 normal = other.contacts[i].normal;
//FLOOR
if (IsFloor(normal))
{
grounded = true;
cancellingGrounded = false;
normalVector = normal;
CancelInvoke(nameof(StopGrounded));
}
}
//Invoke ground/wall cancel, since we can't check normals with CollisionExit
float delay = 3f;
if (!cancellingGrounded)
{
cancellingGrounded = true;
Invoke(nameof(StopGrounded), Time.deltaTime * delay);
}
}
private void StopGrounded()
{
grounded = false;
}
}
Do you mean convert the above single-player script to work as multiplayer?
yes exactly! That would be the help I need.
It will help if you share how far you’ve got on your own. If you’re completely new the video’s on this channel are a good place to start. https://www.youtube.com/c/ShrineApp/videos
Mirror documentation: Mirror Networking | Mirror
I already watched all of his videos. what i need is make that code able to sync movement between player and that this code works only for the owner of the GO. As of right now this code control all the player GO no matter what you own.
Anyways thanks for the reply!