Player, player movement, camera control

This commit is contained in:
2024-12-07 21:48:47 +01:00
parent 54fe327198
commit 8969435fda
1052 changed files with 166612 additions and 6768 deletions

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 0db8bae1b96dfff42a7205ba3214f627
folderAsset: yes
timeCreated: 1507244931
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 853e50eb779ed35449d1b791b29f4bbb
folderAsset: yes
timeCreated: 1507248157
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5cf941e64de8d2f4db87a028ca727a28
timeCreated: 1506988233
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3693a0216eec70942afc42b4c63cb5e6
timeCreated: 1517763936
licenseType: Store
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 25800000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,78 @@
fileFormatVersion: 2
guid: 7845e0b558d50c041b91b4794f8d3d79
timeCreated: 1517763936
licenseType: Store
TextureImporter:
fileIDToRecycleName:
8900000: generatedCubemap
externalObjects: {}
serializedVersion: 4
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 1
seamlessCubemap: 1
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 2
aniso: 0
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 2
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 100
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 8c1c9601b5e06224eb992e7af6f78109
folderAsset: yes
timeCreated: 1507244924
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,331 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using KinematicCharacterController;
using System;
namespace KinematicCharacterController.Walkthrough.Crouching
{
public struct PlayerCharacterInputs
{
public float MoveAxisForward;
public float MoveAxisRight;
public Quaternion CameraRotation;
public bool JumpDown;
public bool CrouchDown;
public bool CrouchUp;
}
public class MyCharacterController : MonoBehaviour, ICharacterController
{
public KinematicCharacterMotor Motor;
[Header("Stable Movement")]
public float MaxStableMoveSpeed = 10f;
public float StableMovementSharpness = 15;
public float OrientationSharpness = 10;
[Header("Air Movement")]
public float MaxAirMoveSpeed = 10f;
public float AirAccelerationSpeed = 5f;
public float Drag = 0.1f;
[Header("Jumping")]
public bool AllowJumpingWhenSliding = false;
public bool AllowDoubleJump = false;
public bool AllowWallJump = false;
public float JumpSpeed = 10f;
public float JumpPreGroundingGraceTime = 0f;
public float JumpPostGroundingGraceTime = 0f;
[Header("Misc")]
public Vector3 Gravity = new Vector3(0, -30f, 0);
public Transform MeshRoot;
private Collider[] _probedColliders = new Collider[8];
private Vector3 _moveInputVector;
private Vector3 _lookInputVector;
private bool _jumpRequested = false;
private bool _jumpConsumed = false;
private bool _jumpedThisFrame = false;
private float _timeSinceJumpRequested = Mathf.Infinity;
private float _timeSinceLastAbleToJump = 0f;
private bool _doubleJumpConsumed = false;
private bool _canWallJump = false;
private Vector3 _wallJumpNormal;
private Vector3 _internalVelocityAdd = Vector3.zero;
private bool _shouldBeCrouching = false;
private bool _isCrouching = false;
private void Start()
{
// Assign to motor
Motor.CharacterController = this;
}
/// <summary>
/// This is called every frame by MyPlayer in order to tell the character what its inputs are
/// </summary>
public void SetInputs(ref PlayerCharacterInputs inputs)
{
// Clamp input
Vector3 moveInputVector = Vector3.ClampMagnitude(new Vector3(inputs.MoveAxisRight, 0f, inputs.MoveAxisForward), 1f);
// Calculate camera direction and rotation on the character plane
Vector3 cameraPlanarDirection = Vector3.ProjectOnPlane(inputs.CameraRotation * Vector3.forward, Motor.CharacterUp).normalized;
if (cameraPlanarDirection.sqrMagnitude == 0f)
{
cameraPlanarDirection = Vector3.ProjectOnPlane(inputs.CameraRotation * Vector3.up, Motor.CharacterUp).normalized;
}
Quaternion cameraPlanarRotation = Quaternion.LookRotation(cameraPlanarDirection, Motor.CharacterUp);
// Move and look inputs
_moveInputVector = cameraPlanarRotation * moveInputVector;
_lookInputVector = cameraPlanarDirection;
// Jumping input
if (inputs.JumpDown)
{
_timeSinceJumpRequested = 0f;
_jumpRequested = true;
}
// Crouching input
if (inputs.CrouchDown)
{
_shouldBeCrouching = true;
if (!_isCrouching)
{
_isCrouching = true;
Motor.SetCapsuleDimensions(0.5f, 1f, 0.5f);
MeshRoot.localScale = new Vector3(1f, 0.5f, 1f);
}
}
else if (inputs.CrouchUp)
{
_shouldBeCrouching = false;
}
}
/// <summary>
/// (Called by KinematicCharacterMotor during its update cycle)
/// This is called before the character begins its movement update
/// </summary>
public void BeforeCharacterUpdate(float deltaTime)
{
}
/// <summary>
/// (Called by KinematicCharacterMotor during its update cycle)
/// This is where you tell your character what its rotation should be right now.
/// This is the ONLY place where you should set the character's rotation
/// </summary>
public void UpdateRotation(ref Quaternion currentRotation, float deltaTime)
{
if (_lookInputVector != Vector3.zero && OrientationSharpness > 0f)
{
// Smoothly interpolate from current to target look direction
Vector3 smoothedLookInputDirection = Vector3.Slerp(Motor.CharacterForward, _lookInputVector, 1 - Mathf.Exp(-OrientationSharpness * deltaTime)).normalized;
// Set the current rotation (which will be used by the KinematicCharacterMotor)
currentRotation = Quaternion.LookRotation(smoothedLookInputDirection, Motor.CharacterUp);
}
}
/// <summary>
/// (Called by KinematicCharacterMotor during its update cycle)
/// This is where you tell your character what its velocity should be right now.
/// This is the ONLY place where you can set the character's velocity
/// </summary>
public void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
{
Vector3 targetMovementVelocity = Vector3.zero;
if (Motor.GroundingStatus.IsStableOnGround)
{
// Reorient velocity on slope
currentVelocity = Motor.GetDirectionTangentToSurface(currentVelocity, Motor.GroundingStatus.GroundNormal) * currentVelocity.magnitude;
// Calculate target velocity
Vector3 inputRight = Vector3.Cross(_moveInputVector, Motor.CharacterUp);
Vector3 reorientedInput = Vector3.Cross(Motor.GroundingStatus.GroundNormal, inputRight).normalized * _moveInputVector.magnitude;
targetMovementVelocity = reorientedInput * MaxStableMoveSpeed;
// Smooth movement Velocity
currentVelocity = Vector3.Lerp(currentVelocity, targetMovementVelocity, 1 - Mathf.Exp(-StableMovementSharpness * deltaTime));
}
else
{
// Add move input
if (_moveInputVector.sqrMagnitude > 0f)
{
targetMovementVelocity = _moveInputVector * MaxAirMoveSpeed;
// Prevent climbing on un-stable slopes with air movement
if (Motor.GroundingStatus.FoundAnyGround)
{
Vector3 perpenticularObstructionNormal = Vector3.Cross(Vector3.Cross(Motor.CharacterUp, Motor.GroundingStatus.GroundNormal), Motor.CharacterUp).normalized;
targetMovementVelocity = Vector3.ProjectOnPlane(targetMovementVelocity, perpenticularObstructionNormal);
}
Vector3 velocityDiff = Vector3.ProjectOnPlane(targetMovementVelocity - currentVelocity, Gravity);
currentVelocity += velocityDiff * AirAccelerationSpeed * deltaTime;
}
// Gravity
currentVelocity += Gravity * deltaTime;
// Drag
currentVelocity *= (1f / (1f + (Drag * deltaTime)));
}
// Handle jumping
{
_jumpedThisFrame = false;
_timeSinceJumpRequested += deltaTime;
if (_jumpRequested)
{
// Handle double jump
if (AllowDoubleJump)
{
if (_jumpConsumed && !_doubleJumpConsumed && (AllowJumpingWhenSliding ? !Motor.GroundingStatus.FoundAnyGround : !Motor.GroundingStatus.IsStableOnGround))
{
Motor.ForceUnground(0.1f);
// Add to the return velocity and reset jump state
currentVelocity += (Motor.CharacterUp * JumpSpeed) - Vector3.Project(currentVelocity, Motor.CharacterUp);
_jumpRequested = false;
_doubleJumpConsumed = true;
_jumpedThisFrame = true;
}
}
// See if we actually are allowed to jump
if (_canWallJump ||
(!_jumpConsumed && ((AllowJumpingWhenSliding ? Motor.GroundingStatus.FoundAnyGround : Motor.GroundingStatus.IsStableOnGround) || _timeSinceLastAbleToJump <= JumpPostGroundingGraceTime)))
{
// Calculate jump direction before ungrounding
Vector3 jumpDirection = Motor.CharacterUp;
if (_canWallJump)
{
jumpDirection = _wallJumpNormal;
}
else if (Motor.GroundingStatus.FoundAnyGround && !Motor.GroundingStatus.IsStableOnGround)
{
jumpDirection = Motor.GroundingStatus.GroundNormal;
}
// Makes the character skip ground probing/snapping on its next update.
// If this line weren't here, the character would remain snapped to the ground when trying to jump. Try commenting this line out and see.
Motor.ForceUnground(0.1f);
// Add to the return velocity and reset jump state
currentVelocity += (jumpDirection * JumpSpeed) - Vector3.Project(currentVelocity, Motor.CharacterUp);
_jumpRequested = false;
_jumpConsumed = true;
_jumpedThisFrame = true;
}
}
// Reset wall jump
_canWallJump = false;
}
// Take into account additive velocity
if (_internalVelocityAdd.sqrMagnitude > 0f)
{
currentVelocity += _internalVelocityAdd;
_internalVelocityAdd = Vector3.zero;
}
}
/// <summary>
/// (Called by KinematicCharacterMotor during its update cycle)
/// This is called after the character has finished its movement update
/// </summary>
public void AfterCharacterUpdate(float deltaTime)
{
// Handle jump-related values
{
// Handle jumping pre-ground grace period
if (_jumpRequested && _timeSinceJumpRequested > JumpPreGroundingGraceTime)
{
_jumpRequested = false;
}
if (AllowJumpingWhenSliding ? Motor.GroundingStatus.FoundAnyGround : Motor.GroundingStatus.IsStableOnGround)
{
// If we're on a ground surface, reset jumping values
if (!_jumpedThisFrame)
{
_doubleJumpConsumed = false;
_jumpConsumed = false;
}
_timeSinceLastAbleToJump = 0f;
}
else
{
// Keep track of time since we were last able to jump (for grace period)
_timeSinceLastAbleToJump += deltaTime;
}
}
// Handle uncrouching
if (_isCrouching && !_shouldBeCrouching)
{
// Do an overlap test with the character's standing height to see if there are any obstructions
Motor.SetCapsuleDimensions(0.5f, 2f, 1f);
if (Motor.CharacterCollisionsOverlap(
Motor.TransientPosition,
Motor.TransientRotation,
_probedColliders) > 0)
{
// If obstructions, just stick to crouching dimensions
Motor.SetCapsuleDimensions(0.5f, 1f, 0.5f);
}
else
{
// If no obstructions, uncrouch
MeshRoot.localScale = new Vector3(1f, 1f, 1f);
_isCrouching = false;
}
}
}
public bool IsColliderValidForCollisions(Collider coll)
{
return true;
}
public void OnGroundHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, ref HitStabilityReport hitStabilityReport)
{
}
public void OnMovementHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, ref HitStabilityReport hitStabilityReport)
{
// We can wall jump only if we are not stable on ground and are moving against an obstruction
if (AllowWallJump && !Motor.GroundingStatus.IsStableOnGround && !hitStabilityReport.IsStable)
{
_canWallJump = true;
_wallJumpNormal = hitNormal;
}
}
public void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, Vector3 atCharacterPosition, Quaternion atCharacterRotation, ref HitStabilityReport hitStabilityReport)
{
}
public void PostGroundingUpdate(float deltaTime)
{
}
public void AddVelocity(Vector3 velocity)
{
_internalVelocityAdd += velocity;
}
public void OnDiscreteCollisionDetected(Collider hitCollider)
{
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6219781d6ef1ea5479df78d516db814d
timeCreated: 1507246770
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,93 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using KinematicCharacterController;
using KinematicCharacterController.Examples;
namespace KinematicCharacterController.Walkthrough.Crouching
{
public class MyPlayer : MonoBehaviour
{
public ExampleCharacterCamera OrbitCamera;
public Transform CameraFollowPoint;
public MyCharacterController Character;
private const string MouseXInput = "Mouse X";
private const string MouseYInput = "Mouse Y";
private const string MouseScrollInput = "Mouse ScrollWheel";
private const string HorizontalInput = "Horizontal";
private const string VerticalInput = "Vertical";
private void Start()
{
Cursor.lockState = CursorLockMode.Locked;
// Tell camera to follow transform
OrbitCamera.SetFollowTransform(CameraFollowPoint);
// Ignore the character's collider(s) for camera obstruction checks
OrbitCamera.IgnoredColliders.Clear();
OrbitCamera.IgnoredColliders.AddRange(Character.GetComponentsInChildren<Collider>());
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
Cursor.lockState = CursorLockMode.Locked;
}
HandleCharacterInput();
}
private void LateUpdate()
{
HandleCameraInput();
}
private void HandleCameraInput()
{
// Create the look input vector for the camera
float mouseLookAxisUp = Input.GetAxisRaw(MouseYInput);
float mouseLookAxisRight = Input.GetAxisRaw(MouseXInput);
Vector3 lookInputVector = new Vector3(mouseLookAxisRight, mouseLookAxisUp, 0f);
// Prevent moving the camera while the cursor isn't locked
if (Cursor.lockState != CursorLockMode.Locked)
{
lookInputVector = Vector3.zero;
}
// Input for zooming the camera (disabled in WebGL because it can cause problems)
float scrollInput = -Input.GetAxis(MouseScrollInput);
#if UNITY_WEBGL
scrollInput = 0f;
#endif
// Apply inputs to the camera
OrbitCamera.UpdateWithInput(Time.deltaTime, scrollInput, lookInputVector);
// Handle toggling zoom level
if (Input.GetMouseButtonDown(1))
{
OrbitCamera.TargetDistance = (OrbitCamera.TargetDistance == 0f) ? OrbitCamera.DefaultDistance : 0f;
}
}
private void HandleCharacterInput()
{
PlayerCharacterInputs characterInputs = new PlayerCharacterInputs();
// Build the CharacterInputs struct
characterInputs.MoveAxisForward = Input.GetAxisRaw(VerticalInput);
characterInputs.MoveAxisRight = Input.GetAxisRaw(HorizontalInput);
characterInputs.CameraRotation = OrbitCamera.Transform.rotation;
characterInputs.JumpDown = Input.GetKeyDown(KeyCode.Space);
characterInputs.CrouchDown = Input.GetKeyDown(KeyCode.C);
characterInputs.CrouchUp = Input.GetKeyUp(KeyCode.C);
// Apply inputs to character
Character.SetInputs(ref characterInputs);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7ee0baee98708fb428704385c47f518e
timeCreated: 1507246770
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: