Skip to content

Instantly share code, notes, and snippets.

@verborghs
Created October 4, 2019 13:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save verborghs/b0e1e17aec7928b5b3df20f730bf151f to your computer and use it in GitHub Desktop.
Save verborghs/b0e1e17aec7928b5b3df20f730bf151f to your computer and use it in GitHub Desktop.
using UnityEngine;
public class EnvironmentQuery
{
private readonly CharacterController _controller;
private readonly float _characterRayCastHeight;
private readonly float _radius;
public EnvironmentQuery(float characterRayCastHeight, float radius)
{
_characterRayCastHeight = characterRayCastHeight;
_radius = radius;
}
public Vector3 FindGroundPoint(Vector3 characterCenter)
{
var groundLayer = LayerMask.GetMask("Walkable");
Vector3 groundPoint = Vector3.zero;
var groundPointRay = new Ray(characterCenter, Vector3.down);
if (Physics.SphereCast(groundPointRay, _radius, out var hitInfo, _characterRayCastHeight, groundLayer))
{
groundPoint = hitInfo.point;
}
return groundPoint;
}
public Vector3 FindGroundNormal(Vector3 groundPoint, Vector3 characterCenter)
{
Vector3 groundNormal = Vector3.up;
var groundNormalRay = new Ray(new Vector3(groundPoint.x, characterCenter.y, groundPoint.z), Vector3.down);
if (Physics.Raycast(groundNormalRay, out var hitInfo, _characterRayCastHeight))
{
groundNormal = hitInfo.normal;
}
return groundNormal;
}
public Collider FindInteractives()
{
var halfExtents = new Vector3(_controller.radius * 0.5f,
_controller.height / 2.0f, _controller.radius * 1.0f);
var forwardOffset = _controller.transform.forward * halfExtents.x;
var upOffset = _controller.transform.up * halfExtents.y;
var interactiveCenter = _controller.transform.position + forwardOffset + upOffset;
var interactiveLayer = LayerMask.GetMask("Interactive");
var interactives =
Physics.OverlapBox(interactiveCenter, halfExtents, _controller.transform.rotation, interactiveLayer);
if (interactives.Length > 0)
return interactives[0];
else
{
return null;
}
}
}
using UnityEngine;
public class Motor
{
private readonly CharacterController _controller;
private readonly float _jumpHeight;
private readonly float _speed;
private Vector3 _velocity;
public Motor(CharacterController controller, float jumpHeight, float speed)
{
_controller = controller;
_jumpHeight = jumpHeight;
_speed = speed;
}
public void Begin()
{
_velocity = _controller.velocity;
}
public void Commit()
{
_controller.Move(_velocity * Time.deltaTime);
}
public void ApplyJump()
{
//jump impulse
_velocity = new Vector3(
_velocity.x,
Mathf.Sqrt(2 * Physics.gravity.magnitude * _jumpHeight),
_velocity.z);
}
public void ApplyMovement(Vector3 worldDirection, Vector3 groundNormal)
{
//calculate ground orientation
Vector3 groundTangent = worldDirection;
Vector3.OrthoNormalize(ref groundNormal, ref groundTangent);
//add movement to velocity
_velocity += groundTangent * worldDirection.magnitude * _speed;
}
public void ApplyGround(Vector3 groundNormal)
{
//only keep vertical velocity
_velocity = Vector3.Scale(_velocity, new Vector3(0, 1, 0));
//transform velocity into pure velocity along plane.
_velocity = Vector3.ProjectOnPlane(_velocity, groundNormal);
}
public void ApplyGravity()
{
//gravity
_velocity += Physics.gravity * Time.deltaTime;
}
}
using System;
using UnityEngine;
public class PlayerBehaviour : MonoBehaviour
{
public float Speed;
public float JumpHeight;
private CharacterController _controller;
private Camera _camera;
private Motor _motor;
private EnvironmentQuery _environmentQuery;
private const string JoyStickVerticalTemplate = "{0}_Vertical";
private const string JoyStickHorizontalTemplate = "{0}_Horizontal";
void Start()
{
_controller = GetComponent<CharacterController>();
_camera = Camera.main;
_motor = new Motor(_controller, JumpHeight, Speed);
}
// Update is called once per frame
void Update()
{
_motor.Begin();
//Determine the player surface
var groundNormal = Vector3.up;
var groundPoint = Vector3.zero;
var characterCenter = transform.position + _controller.center;
groundPoint = _environmentQuery.FindGroundPoint(characterCenter);
groundNormal = _environmentQuery.FindGroundNormal(groundPoint, characterCenter);
var directionRight = GetJoyStickDirection("RightStick");
var worldDirectionRight = ToWorldSpaceXYPlaneDirection(directionRight);
if (IsZeroLengthVector(worldDirectionRight))
{
ApplyRotation(worldDirectionRight);
}
_motor.ApplyGravity();
var directionLeft = GetJoyStickDirection("LeftStick");
var worldDirectionLeft = ToWorldSpaceXYPlaneDirection(directionRight);
if (_controller.isGrounded)
{
_motor.ApplyGround(groundNormal);
_motor.ApplyMovement(worldDirectionLeft, groundNormal);
if (Input.GetButtonDown("A"))
{
_motor.ApplyJump();
}
else
{
if (Input.GetButtonDown("B"))
{
var interactive = _environmentQuery.FindInteractives();
if (interactive != null)
{
InteractWith(interactive);
}
}
}
}
_motor.Commit();
}
private void ApplyRotation(Vector3 worldDirection)
{
Quaternion worldOrientation = Quaternion.LookRotation(worldDirection, Vector3.up);
//rotate the player
transform.rotation = worldOrientation;
}
private bool IsZeroLengthVector(Vector3 worldDirectionRight)
{
return !Mathf.Approximately(worldDirectionRight.sqrMagnitude, 0.0f);
}
private void InteractWith(Collider interactive)
{
var physics = interactive.GetComponentInParent<Rigidbody>();
Vector3 direction = interactive.transform.position - transform.position;
direction.y = 0;
physics.AddForce(direction * 8.0f, ForceMode.Impulse);
}
private Vector2 GetJoyStickDirection(String joyStickName)
{
float horizontal = UnityEngine.Input.GetAxis(String.Format(JoyStickHorizontalTemplate, joyStickName));
float vertical = UnityEngine.Input.GetAxis(String.Format(JoyStickVerticalTemplate, joyStickName));
Vector3 direction = new Vector2(horizontal, vertical);
return direction.sqrMagnitude > 2 ? direction.normalized : direction;
}
private Vector3 ToWorldSpaceXYPlaneDirection(Vector2 direction)
{
Vector3 cameraForward = Vector3.Scale(_camera.transform.forward, new Vector3(1, 0, 1)).normalized;
Vector3 cameraRight = Vector3.Scale(_camera.transform.right, new Vector3(1, 0, 1)).normalized;
return cameraRight * direction.x + cameraForward * direction.y;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment