Player, player movement, camera control
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53973a8cf32b015409a2d3177dedce5b
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244870
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4fc217c32ad7f464ca8b41993f129f21
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244931
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c38e6dcd67013f24ea1d964eb7158bcf
|
||||
folderAsset: yes
|
||||
timeCreated: 1507248137
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51116ce509f204c439bf161c3492f5f2
|
||||
timeCreated: 1506988233
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 59c94ba19ccaba442811b429355d9346
|
||||
timeCreated: 1517755563
|
||||
licenseType: Store
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 25800000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,78 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6444c446b97c3949a4e91a10723a1db
|
||||
timeCreated: 1517755562
|
||||
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:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a34100067b7ebb4a987b58e8b8243a6
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244924
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,70 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using KinematicCharacterController;
|
||||
using System;
|
||||
|
||||
namespace KinematicCharacterController.Walkthrough.PlayerCameraCharacterSetup
|
||||
{
|
||||
public class MyCharacterController : MonoBehaviour, ICharacterController
|
||||
{
|
||||
public KinematicCharacterMotor Motor;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Assign to motor
|
||||
Motor.CharacterController = this;
|
||||
}
|
||||
|
||||
public void BeforeCharacterUpdate(float deltaTime)
|
||||
{
|
||||
// This is called before the motor does anything
|
||||
}
|
||||
|
||||
public void UpdateRotation(ref Quaternion currentRotation, float deltaTime)
|
||||
{
|
||||
// This is called when the motor wants to know what its rotation should be right now
|
||||
}
|
||||
|
||||
public void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
|
||||
{
|
||||
// This is called when the motor wants to know what its velocity should be right now
|
||||
}
|
||||
|
||||
public void AfterCharacterUpdate(float deltaTime)
|
||||
{
|
||||
// This is called after the motor has finished everything in its update
|
||||
}
|
||||
|
||||
public bool IsColliderValidForCollisions(Collider coll)
|
||||
{
|
||||
// This is called after when the motor wants to know if the collider can be collided with (or if we just go through it)
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnGroundHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, ref HitStabilityReport hitStabilityReport)
|
||||
{
|
||||
// This is called when the motor's ground probing detects a ground hit
|
||||
}
|
||||
|
||||
public void OnMovementHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, ref HitStabilityReport hitStabilityReport)
|
||||
{
|
||||
// This is called when the motor's movement logic detects a hit
|
||||
}
|
||||
|
||||
public void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, Vector3 atCharacterPosition, Quaternion atCharacterRotation, ref HitStabilityReport hitStabilityReport)
|
||||
{
|
||||
// This is called after every hit detected in the motor, to give you a chance to modify the HitStabilityReport any way you want
|
||||
}
|
||||
|
||||
public void PostGroundingUpdate(float deltaTime)
|
||||
{
|
||||
// This is called after the motor has finished its ground probing, but before PhysicsMover/Velocity/etc.... handling
|
||||
}
|
||||
|
||||
public void OnDiscreteCollisionDetected(Collider hitCollider)
|
||||
{
|
||||
// This is called by the motor when it is detecting a collision that did not result from a "movement hit".
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b647278478c8694ab6b00ac51347de7
|
||||
timeCreated: 1507245023
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using KinematicCharacterController;
|
||||
using KinematicCharacterController.Examples;
|
||||
using System.Linq;
|
||||
|
||||
namespace KinematicCharacterController.Walkthrough.PlayerCameraCharacterSetup
|
||||
{
|
||||
public class MyPlayer : MonoBehaviour
|
||||
{
|
||||
public ExampleCharacterCamera OrbitCamera;
|
||||
public Transform CameraFollowPoint;
|
||||
public MyCharacterController Character;
|
||||
|
||||
private Vector3 _lookInputVector = Vector3.zero;
|
||||
|
||||
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 = Character.GetComponentsInChildren<Collider>().ToList();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetMouseButtonDown(0))
|
||||
{
|
||||
Cursor.lockState = CursorLockMode.Locked;
|
||||
}
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
HandleCameraInput();
|
||||
}
|
||||
|
||||
private void HandleCameraInput()
|
||||
{
|
||||
// Create the look input vector for the camera
|
||||
float mouseLookAxisUp = Input.GetAxisRaw("Mouse Y");
|
||||
float mouseLookAxisRight = Input.GetAxisRaw("Mouse X");
|
||||
_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("Mouse ScrollWheel");
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0659d469a6cbf3448a556b714972454c
|
||||
timeCreated: 1507245023
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11d7d978add07be4c9617f8d1cb466aa
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244870
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 480b31aff4f74ba419894201813d618e
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244931
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2951d7a8e1623046968adae6c693e2c
|
||||
folderAsset: yes
|
||||
timeCreated: 1507250394
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2500a88107e4f1f469181f22f53c0b3f
|
||||
timeCreated: 1506988233
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dcd109c9b01494f49b5a633a2a184afd
|
||||
timeCreated: 1517665930
|
||||
licenseType: Store
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 25800000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,78 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 577436d930a350c4b98a31b783f58237
|
||||
timeCreated: 1517665930
|
||||
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:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8ad7519b5fde5c44894fcfe68fcd128
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244924
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,439 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using KinematicCharacterController;
|
||||
using System;
|
||||
|
||||
namespace KinematicCharacterController.Walkthrough.MultipleMovementStates
|
||||
{
|
||||
public enum CharacterState
|
||||
{
|
||||
Default,
|
||||
}
|
||||
|
||||
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;
|
||||
public float MaxStableDistanceFromLedge = 5f;
|
||||
[Range(0f, 180f)]
|
||||
public float MaxStableDenivelationAngle = 180f;
|
||||
|
||||
[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 List<Collider> IgnoredColliders = new List<Collider>();
|
||||
public bool OrientTowardsGravity = false;
|
||||
public Vector3 Gravity = new Vector3(0, -30f, 0);
|
||||
public Transform MeshRoot;
|
||||
|
||||
public CharacterState CurrentCharacterState { get; private set; }
|
||||
|
||||
private Collider[] _probedColliders = new Collider[8];
|
||||
private Vector3 _moveInputVector;
|
||||
private Vector3 _lookInputVector;
|
||||
private bool _jumpRequested = false;
|
||||
private bool _jumpConsumed = false;
|
||||
private bool _doubleJumpConsumed = false;
|
||||
private bool _jumpedThisFrame = false;
|
||||
private bool _canWallJump = false;
|
||||
private Vector3 _wallJumpNormal;
|
||||
private float _timeSinceJumpRequested = Mathf.Infinity;
|
||||
private float _timeSinceLastAbleToJump = 0f;
|
||||
private Vector3 _internalVelocityAdd = Vector3.zero;
|
||||
private bool _shouldBeCrouching = false;
|
||||
private bool _isCrouching = false;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Assign to motor
|
||||
Motor.CharacterController = this;
|
||||
|
||||
// Handle initial state
|
||||
TransitionToState(CharacterState.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles movement state transitions and enter/exit callbacks
|
||||
/// </summary>
|
||||
public void TransitionToState(CharacterState newState)
|
||||
{
|
||||
CharacterState tmpInitialState = CurrentCharacterState;
|
||||
OnStateExit(tmpInitialState, newState);
|
||||
CurrentCharacterState = newState;
|
||||
OnStateEnter(newState, tmpInitialState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event when entering a state
|
||||
/// </summary>
|
||||
public void OnStateEnter(CharacterState state, CharacterState fromState)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event when exiting a state
|
||||
/// </summary>
|
||||
public void OnStateExit(CharacterState state, CharacterState toState)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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);
|
||||
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (OrientTowardsGravity)
|
||||
{
|
||||
// Rotate from current up to invert gravity
|
||||
currentRotation = Quaternion.FromToRotation((currentRotation * Vector3.up), -Gravity) * currentRotation;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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.CharacterOverlap(
|
||||
Motor.TransientPosition,
|
||||
Motor.TransientRotation,
|
||||
_probedColliders,
|
||||
Motor.CollidableLayers,
|
||||
QueryTriggerInteraction.Ignore) > 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;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsColliderValidForCollisions(Collider coll)
|
||||
{
|
||||
if (IgnoredColliders.Contains(coll))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddVelocity(Vector3 velocity)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
_internalVelocityAdd += velocity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, Vector3 atCharacterPosition, Quaternion atCharacterRotation, ref HitStabilityReport hitStabilityReport)
|
||||
{
|
||||
}
|
||||
|
||||
public void PostGroundingUpdate(float deltaTime)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnDiscreteCollisionDetected(Collider hitCollider)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cdd0dd2408e42ba47b026e5583efd762
|
||||
timeCreated: 1507247652
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,94 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using KinematicCharacterController;
|
||||
using KinematicCharacterController.Examples;
|
||||
using System.Linq;
|
||||
|
||||
namespace KinematicCharacterController.Walkthrough.MultipleMovementStates
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7bad51a62556c6f4692fd8537d6e2c20
|
||||
timeCreated: 1507247652
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e269b81cba6bb824b8219057530f5311
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244870
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b1d4204ac28112448cff21d3c13fe71
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244931
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d59a1529d74d7046b420743b414f480
|
||||
folderAsset: yes
|
||||
timeCreated: 1507327445
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 080c10a3703549c47b115b24b77e16ea
|
||||
timeCreated: 1506988233
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ca75fae4cd9a2d478cc927f4e81d91b
|
||||
timeCreated: 1517767069
|
||||
licenseType: Store
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 25800000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,78 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bfe22d4c784ad9e47a9cdd1582da2c26
|
||||
timeCreated: 1517767068
|
||||
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:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b9538658113d104fb4d5a77e8656327
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244924
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,533 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using KinematicCharacterController;
|
||||
using System;
|
||||
|
||||
namespace KinematicCharacterController.Walkthrough.ChargingState
|
||||
{
|
||||
public enum CharacterState
|
||||
{
|
||||
Default,
|
||||
Charging,
|
||||
}
|
||||
|
||||
public struct PlayerCharacterInputs
|
||||
{
|
||||
public float MoveAxisForward;
|
||||
public float MoveAxisRight;
|
||||
public Quaternion CameraRotation;
|
||||
public bool JumpDown;
|
||||
public bool CrouchDown;
|
||||
public bool CrouchUp;
|
||||
public bool ChargingDown;
|
||||
}
|
||||
|
||||
public class MyCharacterController : MonoBehaviour, ICharacterController
|
||||
{
|
||||
public KinematicCharacterMotor Motor;
|
||||
|
||||
[Header("Stable Movement")]
|
||||
public float MaxStableMoveSpeed = 10f;
|
||||
public float StableMovementSharpness = 15;
|
||||
public float OrientationSharpness = 10;
|
||||
public float MaxStableDistanceFromLedge = 5f;
|
||||
[Range(0f, 180f)]
|
||||
public float MaxStableDenivelationAngle = 180f;
|
||||
|
||||
[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("Charging")]
|
||||
public float ChargeSpeed = 15f;
|
||||
public float MaxChargeTime = 1.5f;
|
||||
public float StoppedTime = 1f;
|
||||
|
||||
[Header("Misc")]
|
||||
public List<Collider> IgnoredColliders = new List<Collider>();
|
||||
public bool OrientTowardsGravity = false;
|
||||
public Vector3 Gravity = new Vector3(0, -30f, 0);
|
||||
public Transform MeshRoot;
|
||||
|
||||
public CharacterState CurrentCharacterState { get; private set; }
|
||||
|
||||
private Collider[] _probedColliders = new Collider[8];
|
||||
private Vector3 _moveInputVector;
|
||||
private Vector3 _lookInputVector;
|
||||
private bool _jumpRequested = false;
|
||||
private bool _jumpConsumed = false;
|
||||
private bool _doubleJumpConsumed = false;
|
||||
private bool _jumpedThisFrame = false;
|
||||
private bool _canWallJump = false;
|
||||
private Vector3 _wallJumpNormal;
|
||||
private float _timeSinceJumpRequested = Mathf.Infinity;
|
||||
private float _timeSinceLastAbleToJump = 0f;
|
||||
private Vector3 _internalVelocityAdd = Vector3.zero;
|
||||
private bool _shouldBeCrouching = false;
|
||||
private bool _isCrouching = false;
|
||||
|
||||
private Vector3 _currentChargeVelocity;
|
||||
private bool _isStopped;
|
||||
private bool _mustStopVelocity = false;
|
||||
private float _timeSinceStartedCharge = 0;
|
||||
private float _timeSinceStopped = 0;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Assign to motor
|
||||
Motor.CharacterController = this;
|
||||
|
||||
// Handle initial state
|
||||
TransitionToState(CharacterState.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles movement state transitions and enter/exit callbacks
|
||||
/// </summary>
|
||||
public void TransitionToState(CharacterState newState)
|
||||
{
|
||||
CharacterState tmpInitialState = CurrentCharacterState;
|
||||
OnStateExit(tmpInitialState, newState);
|
||||
CurrentCharacterState = newState;
|
||||
OnStateEnter(newState, tmpInitialState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event when entering a state
|
||||
/// </summary>
|
||||
public void OnStateEnter(CharacterState state, CharacterState fromState)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case CharacterState.Charging:
|
||||
{
|
||||
_currentChargeVelocity = Motor.CharacterForward * ChargeSpeed;
|
||||
_isStopped = false;
|
||||
_timeSinceStartedCharge = 0f;
|
||||
_timeSinceStopped = 0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event when exiting a state
|
||||
/// </summary>
|
||||
public void OnStateExit(CharacterState state, CharacterState toState)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
// Handle state transition from input
|
||||
if (inputs.ChargingDown)
|
||||
{
|
||||
TransitionToState(CharacterState.Charging);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (Called by KinematicCharacterMotor during its update cycle)
|
||||
/// This is called before the character begins its movement update
|
||||
/// </summary>
|
||||
public void BeforeCharacterUpdate(float deltaTime)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case CharacterState.Charging:
|
||||
{
|
||||
// Update times
|
||||
_timeSinceStartedCharge += deltaTime;
|
||||
if (_isStopped)
|
||||
{
|
||||
_timeSinceStopped += deltaTime;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (OrientTowardsGravity)
|
||||
{
|
||||
// Rotate from current up to invert gravity
|
||||
currentRotation = Quaternion.FromToRotation((currentRotation * Vector3.up), -Gravity) * currentRotation;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CharacterState.Charging:
|
||||
{
|
||||
// If we have stopped and need to cancel velocity, do it here
|
||||
if (_mustStopVelocity)
|
||||
{
|
||||
currentVelocity = Vector3.zero;
|
||||
_mustStopVelocity = false;
|
||||
}
|
||||
|
||||
if (_isStopped)
|
||||
{
|
||||
// When stopped, do no velocity handling except gravity
|
||||
currentVelocity += Gravity * deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
// When charging, velocity is always constant
|
||||
float previousY = currentVelocity.y;
|
||||
currentVelocity = _currentChargeVelocity;
|
||||
currentVelocity.y = previousY;
|
||||
currentVelocity += Gravity * deltaTime;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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.CharacterOverlap(
|
||||
Motor.TransientPosition,
|
||||
Motor.TransientRotation,
|
||||
_probedColliders,
|
||||
Motor.CollidableLayers,
|
||||
QueryTriggerInteraction.Ignore) > 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;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CharacterState.Charging:
|
||||
{
|
||||
// Detect being stopped by elapsed time
|
||||
if (!_isStopped && _timeSinceStartedCharge > MaxChargeTime)
|
||||
{
|
||||
_mustStopVelocity = true;
|
||||
_isStopped = true;
|
||||
}
|
||||
|
||||
// Detect end of stopping phase and transition back to default movement state
|
||||
if (_timeSinceStopped > StoppedTime)
|
||||
{
|
||||
TransitionToState(CharacterState.Default);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsColliderValidForCollisions(Collider coll)
|
||||
{
|
||||
if (IgnoredColliders.Contains(coll))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CharacterState.Charging:
|
||||
{
|
||||
// Detect being stopped by obstructions
|
||||
if (!_isStopped && !hitStabilityReport.IsStable && Vector3.Dot(-hitNormal, _currentChargeVelocity.normalized) > 0.5f)
|
||||
{
|
||||
_mustStopVelocity = true;
|
||||
_isStopped = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddVelocity(Vector3 velocity)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
_internalVelocityAdd += velocity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, Vector3 atCharacterPosition, Quaternion atCharacterRotation, ref HitStabilityReport hitStabilityReport)
|
||||
{
|
||||
}
|
||||
|
||||
public void PostGroundingUpdate(float deltaTime)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnDiscreteCollisionDetected(Collider hitCollider)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 235082069226ea94baef1f813ba0f943
|
||||
timeCreated: 1507247652
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,95 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using KinematicCharacterController;
|
||||
using KinematicCharacterController.Examples;
|
||||
using System.Linq;
|
||||
|
||||
namespace KinematicCharacterController.Walkthrough.ChargingState
|
||||
{
|
||||
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);
|
||||
characterInputs.ChargingDown = Input.GetKeyDown(KeyCode.Q);
|
||||
|
||||
// Apply inputs to character
|
||||
Character.SetInputs(ref characterInputs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 391e1ac2a4b71dc42ae1a24fabdc143a
|
||||
timeCreated: 1507247652
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b09c4e9590714b44087adb248613d4fe
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244870
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8065b31a2eff1c4a988910720215dad
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244931
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01ac238be53bbf94785b2b52befc0ad1
|
||||
folderAsset: yes
|
||||
timeCreated: 1507339265
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c51b3a6a6cbf9e478da8e6dc6e68697
|
||||
timeCreated: 1506988233
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eeeef78adc5f0f948ae5ce452c42d9bf
|
||||
timeCreated: 1517767440
|
||||
licenseType: Store
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 25800000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,78 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60c6db21975c662438028db7f6265ec9
|
||||
timeCreated: 1517767440
|
||||
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:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e152823b359956843a17a711b6ad257a
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244924
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,501 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using KinematicCharacterController;
|
||||
using System;
|
||||
|
||||
namespace KinematicCharacterController.Walkthrough.NoClipState
|
||||
{
|
||||
public enum CharacterState
|
||||
{
|
||||
Default,
|
||||
NoClip,
|
||||
}
|
||||
|
||||
public struct PlayerCharacterInputs
|
||||
{
|
||||
public float MoveAxisForward;
|
||||
public float MoveAxisRight;
|
||||
public Quaternion CameraRotation;
|
||||
public bool JumpDown;
|
||||
public bool JumpHeld;
|
||||
public bool CrouchDown;
|
||||
public bool CrouchUp;
|
||||
public bool CrouchHeld;
|
||||
public bool NoClipDown;
|
||||
}
|
||||
|
||||
public class MyCharacterController : MonoBehaviour, ICharacterController
|
||||
{
|
||||
public KinematicCharacterMotor Motor;
|
||||
|
||||
[Header("Stable Movement")]
|
||||
public float MaxStableMoveSpeed = 10f;
|
||||
public float StableMovementSharpness = 15;
|
||||
public float OrientationSharpness = 10;
|
||||
public float MaxStableDistanceFromLedge = 5f;
|
||||
[Range(0f, 180f)]
|
||||
public float MaxStableDenivelationAngle = 180f;
|
||||
|
||||
[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("NoClip")]
|
||||
public float NoClipMoveSpeed = 10f;
|
||||
public float NoClipSharpness = 15;
|
||||
|
||||
[Header("Misc")]
|
||||
public List<Collider> IgnoredColliders = new List<Collider>();
|
||||
public bool OrientTowardsGravity = false;
|
||||
public Vector3 Gravity = new Vector3(0, -30f, 0);
|
||||
public Transform MeshRoot;
|
||||
|
||||
public CharacterState CurrentCharacterState { get; private set; }
|
||||
|
||||
private Collider[] _probedColliders = new Collider[8];
|
||||
private Vector3 _moveInputVector;
|
||||
private Vector3 _lookInputVector;
|
||||
private bool _jumpInputIsHeld = false;
|
||||
private bool _crouchInputIsHeld = false;
|
||||
private bool _jumpRequested = false;
|
||||
private bool _jumpConsumed = false;
|
||||
private bool _doubleJumpConsumed = false;
|
||||
private bool _jumpedThisFrame = false;
|
||||
private bool _canWallJump = false;
|
||||
private Vector3 _wallJumpNormal;
|
||||
private float _timeSinceJumpRequested = Mathf.Infinity;
|
||||
private float _timeSinceLastAbleToJump = 0f;
|
||||
private Vector3 _internalVelocityAdd = Vector3.zero;
|
||||
private bool _shouldBeCrouching = false;
|
||||
private bool _isCrouching = false;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Assign to motor
|
||||
Motor.CharacterController = this;
|
||||
|
||||
// Handle initial state
|
||||
TransitionToState(CharacterState.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles movement state transitions and enter/exit callbacks
|
||||
/// </summary>
|
||||
public void TransitionToState(CharacterState newState)
|
||||
{
|
||||
CharacterState tmpInitialState = CurrentCharacterState;
|
||||
OnStateExit(tmpInitialState, newState);
|
||||
CurrentCharacterState = newState;
|
||||
OnStateEnter(newState, tmpInitialState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event when entering a state
|
||||
/// </summary>
|
||||
public void OnStateEnter(CharacterState state, CharacterState fromState)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case CharacterState.NoClip:
|
||||
{
|
||||
Motor.SetCapsuleCollisionsActivation(false);
|
||||
Motor.SetMovementCollisionsSolvingActivation(false);
|
||||
Motor.SetGroundSolvingActivation(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event when exiting a state
|
||||
/// </summary>
|
||||
public void OnStateExit(CharacterState state, CharacterState toState)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case CharacterState.NoClip:
|
||||
{
|
||||
Motor.SetCapsuleCollisionsActivation(true);
|
||||
Motor.SetMovementCollisionsSolvingActivation(true);
|
||||
Motor.SetGroundSolvingActivation(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
// Handle state transition from input
|
||||
if (inputs.NoClipDown)
|
||||
{
|
||||
if (CurrentCharacterState == CharacterState.Default)
|
||||
{
|
||||
TransitionToState(CharacterState.NoClip);
|
||||
}
|
||||
else if (CurrentCharacterState == CharacterState.NoClip)
|
||||
{
|
||||
TransitionToState(CharacterState.Default);
|
||||
}
|
||||
}
|
||||
|
||||
_jumpInputIsHeld = inputs.JumpHeld;
|
||||
_crouchInputIsHeld = inputs.CrouchHeld;
|
||||
|
||||
// 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);
|
||||
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CharacterState.NoClip:
|
||||
{
|
||||
_moveInputVector = inputs.CameraRotation * moveInputVector;
|
||||
_lookInputVector = cameraPlanarDirection;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (Called by KinematicCharacterMotor during its update cycle)
|
||||
/// This is called before the character begins its movement update
|
||||
/// </summary>
|
||||
public void BeforeCharacterUpdate(float deltaTime)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
case CharacterState.NoClip:
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (OrientTowardsGravity)
|
||||
{
|
||||
// Rotate from current up to invert gravity
|
||||
currentRotation = Quaternion.FromToRotation((currentRotation * Vector3.up), -Gravity) * currentRotation;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CharacterState.NoClip:
|
||||
{
|
||||
float verticalInput = 0f + (_jumpInputIsHeld ? 1f : 0f) + (_crouchInputIsHeld ? -1f : 0f);
|
||||
|
||||
// Smoothly interpolate to target velocity
|
||||
Vector3 targetMovementVelocity = (_moveInputVector + (Motor.CharacterUp * verticalInput)).normalized * NoClipMoveSpeed;
|
||||
currentVelocity = Vector3.Lerp(currentVelocity, targetMovementVelocity, 1 - Mathf.Exp(-NoClipSharpness * deltaTime));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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.CharacterOverlap(
|
||||
Motor.TransientPosition,
|
||||
Motor.TransientRotation,
|
||||
_probedColliders,
|
||||
Motor.CollidableLayers,
|
||||
QueryTriggerInteraction.Ignore) > 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;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsColliderValidForCollisions(Collider coll)
|
||||
{
|
||||
if (IgnoredColliders.Contains(coll))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddVelocity(Vector3 velocity)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
_internalVelocityAdd += velocity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, Vector3 atCharacterPosition, Quaternion atCharacterRotation, ref HitStabilityReport hitStabilityReport)
|
||||
{
|
||||
}
|
||||
|
||||
public void PostGroundingUpdate(float deltaTime)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnDiscreteCollisionDetected(Collider hitCollider)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78ff0994cb11f364795e0da470aceef8
|
||||
timeCreated: 1507247652
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,97 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using KinematicCharacterController;
|
||||
using KinematicCharacterController.Examples;
|
||||
using System.Linq;
|
||||
|
||||
namespace KinematicCharacterController.Walkthrough.NoClipState
|
||||
{
|
||||
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.JumpHeld = Input.GetKey(KeyCode.Space);
|
||||
characterInputs.CrouchDown = Input.GetKeyDown(KeyCode.C);
|
||||
characterInputs.CrouchUp = Input.GetKeyUp(KeyCode.C);
|
||||
characterInputs.CrouchHeld = Input.GetKey(KeyCode.C);
|
||||
characterInputs.NoClipDown = Input.GetKeyUp(KeyCode.Q);
|
||||
|
||||
// Apply inputs to character
|
||||
Character.SetInputs(ref characterInputs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04d7db6fb3ef0f548bf93754f0bbe224
|
||||
timeCreated: 1507247652
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 87dd83d8ec8c03c42afc80953c7627c2
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244870
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 605fa2dbc40a9154ca2a1078e18d9b36
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244931
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f6b779dc8fff1e49a9dfa708f9a9efb
|
||||
folderAsset: yes
|
||||
timeCreated: 1507343875
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd899887b2fc18b46b5bd2b474774a2d
|
||||
timeCreated: 1506988233
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad04f42b5c282df45b6cb76c9d5d0ce7
|
||||
timeCreated: 1517768625
|
||||
licenseType: Store
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 25800000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,78 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46581c5748209e84fb9336a40afd83c4
|
||||
timeCreated: 1517768625
|
||||
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:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5343829f56067844db4fdb42ba8ff333
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244924
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,527 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using KinematicCharacterController;
|
||||
using System;
|
||||
|
||||
namespace KinematicCharacterController.Walkthrough.SwimmingState
|
||||
{
|
||||
public enum CharacterState
|
||||
{
|
||||
Default,
|
||||
Swimming,
|
||||
}
|
||||
|
||||
public struct PlayerCharacterInputs
|
||||
{
|
||||
public float MoveAxisForward;
|
||||
public float MoveAxisRight;
|
||||
public Quaternion CameraRotation;
|
||||
public bool JumpDown;
|
||||
public bool JumpHeld;
|
||||
public bool CrouchDown;
|
||||
public bool CrouchUp;
|
||||
public bool CrouchHeld;
|
||||
}
|
||||
|
||||
public class MyCharacterController : MonoBehaviour, ICharacterController
|
||||
{
|
||||
public KinematicCharacterMotor Motor;
|
||||
|
||||
[Header("Stable Movement")]
|
||||
public float MaxStableMoveSpeed = 10f;
|
||||
public float StableMovementSharpness = 15;
|
||||
public float OrientationSharpness = 10;
|
||||
public float MaxStableDistanceFromLedge = 5f;
|
||||
[Range(0f, 180f)]
|
||||
public float MaxStableDenivelationAngle = 180f;
|
||||
|
||||
[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("Swimming")]
|
||||
public Transform SwimmingReferencePoint;
|
||||
public LayerMask WaterLayer;
|
||||
public float SwimmingSpeed = 4f;
|
||||
public float SwimmingMovementSharpness = 3;
|
||||
public float SwimmingOrientationSharpness = 2f;
|
||||
|
||||
[Header("Misc")]
|
||||
public List<Collider> IgnoredColliders = new List<Collider>();
|
||||
public bool OrientTowardsGravity = false;
|
||||
public Vector3 Gravity = new Vector3(0, -30f, 0);
|
||||
public Transform MeshRoot;
|
||||
|
||||
public CharacterState CurrentCharacterState { get; private set; }
|
||||
|
||||
private Collider[] _probedColliders = new Collider[8];
|
||||
private Vector3 _moveInputVector;
|
||||
private Vector3 _lookInputVector;
|
||||
private bool _jumpInputIsHeld = false;
|
||||
private bool _crouchInputIsHeld = false;
|
||||
private bool _jumpRequested = false;
|
||||
private bool _jumpConsumed = false;
|
||||
private bool _doubleJumpConsumed = false;
|
||||
private bool _jumpedThisFrame = false;
|
||||
private bool _canWallJump = false;
|
||||
private Vector3 _wallJumpNormal;
|
||||
private float _timeSinceJumpRequested = Mathf.Infinity;
|
||||
private float _timeSinceLastAbleToJump = 0f;
|
||||
private Vector3 _internalVelocityAdd = Vector3.zero;
|
||||
private bool _shouldBeCrouching = false;
|
||||
private bool _isCrouching = false;
|
||||
private Collider _waterZone;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Assign to motor
|
||||
Motor.CharacterController = this;
|
||||
|
||||
// Handle initial state
|
||||
TransitionToState(CharacterState.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles movement state transitions and enter/exit callbacks
|
||||
/// </summary>
|
||||
public void TransitionToState(CharacterState newState)
|
||||
{
|
||||
CharacterState tmpInitialState = CurrentCharacterState;
|
||||
OnStateExit(tmpInitialState, newState);
|
||||
CurrentCharacterState = newState;
|
||||
OnStateEnter(newState, tmpInitialState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event when entering a state
|
||||
/// </summary>
|
||||
public void OnStateEnter(CharacterState state, CharacterState fromState)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
Motor.SetGroundSolvingActivation(true);
|
||||
break;
|
||||
}
|
||||
case CharacterState.Swimming:
|
||||
{
|
||||
Motor.SetGroundSolvingActivation(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event when exiting a state
|
||||
/// </summary>
|
||||
public void OnStateExit(CharacterState state, CharacterState toState)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
_jumpInputIsHeld = inputs.JumpHeld;
|
||||
_crouchInputIsHeld = inputs.CrouchHeld;
|
||||
|
||||
// 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);
|
||||
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CharacterState.Swimming:
|
||||
{
|
||||
_jumpRequested = inputs.JumpHeld;
|
||||
|
||||
_moveInputVector = inputs.CameraRotation * moveInputVector;
|
||||
_lookInputVector = cameraPlanarDirection;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (Called by KinematicCharacterMotor during its update cycle)
|
||||
/// This is called before the character begins its movement update
|
||||
/// </summary>
|
||||
public void BeforeCharacterUpdate(float deltaTime)
|
||||
{
|
||||
// Handle detecting water surfaces
|
||||
{
|
||||
// Do a character overlap test to detect water surfaces
|
||||
if (Motor.CharacterOverlap(Motor.TransientPosition, Motor.TransientRotation, _probedColliders, WaterLayer, QueryTriggerInteraction.Collide) > 0)
|
||||
{
|
||||
// If a water surface was detected
|
||||
if (_probedColliders[0] != null)
|
||||
{
|
||||
// If the swimming reference point is inside the box, make sure we are in swimming state
|
||||
if (Physics.ClosestPoint(SwimmingReferencePoint.position, _probedColliders[0], _probedColliders[0].transform.position, _probedColliders[0].transform.rotation) == SwimmingReferencePoint.position)
|
||||
{
|
||||
if (CurrentCharacterState == CharacterState.Default)
|
||||
{
|
||||
TransitionToState(CharacterState.Swimming);
|
||||
_waterZone = _probedColliders[0];
|
||||
}
|
||||
}
|
||||
// otherwise; default state
|
||||
else
|
||||
{
|
||||
if (CurrentCharacterState == CharacterState.Swimming)
|
||||
{
|
||||
TransitionToState(CharacterState.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
case CharacterState.Swimming:
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (OrientTowardsGravity)
|
||||
{
|
||||
// Rotate from current up to invert gravity
|
||||
currentRotation = Quaternion.FromToRotation((currentRotation * Vector3.up), -Gravity) * currentRotation;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CharacterState.Swimming:
|
||||
{
|
||||
float verticalInput = 0f + (_jumpInputIsHeld ? 1f : 0f) + (_crouchInputIsHeld ? -1f : 0f);
|
||||
|
||||
// Smoothly interpolate to target swimming velocity
|
||||
Vector3 targetMovementVelocity = (_moveInputVector + (Motor.CharacterUp * verticalInput)).normalized * SwimmingSpeed;
|
||||
Vector3 smoothedVelocity = Vector3.Lerp(currentVelocity, targetMovementVelocity, 1 - Mathf.Exp(-SwimmingMovementSharpness * deltaTime));
|
||||
|
||||
// See if our swimming reference point would be out of water after the movement from our velocity has been applied
|
||||
{
|
||||
Vector3 resultingSwimmingReferancePosition = Motor.TransientPosition + (smoothedVelocity * deltaTime) + (SwimmingReferencePoint.position - Motor.TransientPosition);
|
||||
Vector3 closestPointWaterSurface = Physics.ClosestPoint(resultingSwimmingReferancePosition, _waterZone, _waterZone.transform.position, _waterZone.transform.rotation);
|
||||
|
||||
// if our position would be outside the water surface on next update, project the velocity on the surface normal so that it would not take us out of the water
|
||||
if (closestPointWaterSurface != resultingSwimmingReferancePosition)
|
||||
{
|
||||
Vector3 waterSurfaceNormal = (resultingSwimmingReferancePosition - closestPointWaterSurface).normalized;
|
||||
smoothedVelocity = Vector3.ProjectOnPlane(smoothedVelocity, waterSurfaceNormal);
|
||||
|
||||
// Jump out of water
|
||||
if (_jumpRequested)
|
||||
{
|
||||
smoothedVelocity += (Motor.CharacterUp * JumpSpeed) - Vector3.Project(currentVelocity, Motor.CharacterUp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentVelocity = smoothedVelocity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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.CharacterOverlap(
|
||||
Motor.TransientPosition,
|
||||
Motor.TransientRotation,
|
||||
_probedColliders,
|
||||
Motor.CollidableLayers,
|
||||
QueryTriggerInteraction.Ignore) > 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;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsColliderValidForCollisions(Collider coll)
|
||||
{
|
||||
if (IgnoredColliders.Contains(coll))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddVelocity(Vector3 velocity)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
_internalVelocityAdd += velocity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, Vector3 atCharacterPosition, Quaternion atCharacterRotation, ref HitStabilityReport hitStabilityReport)
|
||||
{
|
||||
}
|
||||
|
||||
public void PostGroundingUpdate(float deltaTime)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnDiscreteCollisionDetected(Collider hitCollider)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0ea9886d276c4240a64a52df00bff13
|
||||
timeCreated: 1507247652
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,96 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using KinematicCharacterController;
|
||||
using KinematicCharacterController.Examples;
|
||||
using System.Linq;
|
||||
|
||||
namespace KinematicCharacterController.Walkthrough.SwimmingState
|
||||
{
|
||||
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.JumpHeld = Input.GetKey(KeyCode.Space);
|
||||
characterInputs.CrouchDown = Input.GetKeyDown(KeyCode.C);
|
||||
characterInputs.CrouchUp = Input.GetKeyUp(KeyCode.C);
|
||||
characterInputs.CrouchHeld = Input.GetKey(KeyCode.C);
|
||||
|
||||
// Apply inputs to character
|
||||
Character.SetInputs(ref characterInputs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4713c253e1b718441b5183e3e7a50f24
|
||||
timeCreated: 1507247652
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84b904d2472431d4fb4f2c86676d2850
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244870
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8cf0ba00c38c13643bd89fbef469aeaa
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244931
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76e122927b58e3246a3e63612c919c43
|
||||
folderAsset: yes
|
||||
timeCreated: 1507409469
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 99217385d36556848a4019cc1e7fa0d8
|
||||
timeCreated: 1506988233
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd0fdcc34d728ac4db527844b498d09b
|
||||
timeCreated: 1517686306
|
||||
licenseType: Store
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 25800000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,78 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0adf4a148ffe70438e744834048a5c0
|
||||
timeCreated: 1517686306
|
||||
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:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e1ce3088977c1447b528a3a6cb06dd5
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244924
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,604 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using KinematicCharacterController;
|
||||
using System;
|
||||
|
||||
namespace KinematicCharacterController.Walkthrough.ClimbingLadders
|
||||
{
|
||||
public enum CharacterState
|
||||
{
|
||||
Default,
|
||||
Climbing,
|
||||
}
|
||||
|
||||
public enum ClimbingState
|
||||
{
|
||||
Anchoring,
|
||||
Climbing,
|
||||
DeAnchoring
|
||||
}
|
||||
|
||||
public struct PlayerCharacterInputs
|
||||
{
|
||||
public float MoveAxisForward;
|
||||
public float MoveAxisRight;
|
||||
public Quaternion CameraRotation;
|
||||
public bool JumpDown;
|
||||
public bool CrouchDown;
|
||||
public bool CrouchUp;
|
||||
public bool ClimbLadder;
|
||||
}
|
||||
|
||||
public class MyCharacterController : MonoBehaviour, ICharacterController
|
||||
{
|
||||
public KinematicCharacterMotor Motor;
|
||||
|
||||
[Header("Stable Movement")]
|
||||
public float MaxStableMoveSpeed = 10f;
|
||||
public float StableMovementSharpness = 15;
|
||||
public float OrientationSharpness = 10;
|
||||
public float MaxStableDistanceFromLedge = 5f;
|
||||
[Range(0f, 180f)]
|
||||
public float MaxStableDenivelationAngle = 180f;
|
||||
|
||||
[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("Ladder Climbing")]
|
||||
public float ClimbingSpeed = 4f;
|
||||
public float AnchoringDuration = 0.25f;
|
||||
public LayerMask InteractionLayer;
|
||||
|
||||
[Header("Misc")]
|
||||
public List<Collider> IgnoredColliders = new List<Collider>();
|
||||
public bool OrientTowardsGravity = false;
|
||||
public Vector3 Gravity = new Vector3(0, -30f, 0);
|
||||
public Transform MeshRoot;
|
||||
|
||||
public CharacterState CurrentCharacterState { get; private set; }
|
||||
|
||||
private Collider[] _probedColliders = new Collider[8];
|
||||
private Vector3 _moveInputVector;
|
||||
private Vector3 _lookInputVector;
|
||||
private bool _jumpRequested = false;
|
||||
private bool _jumpConsumed = false;
|
||||
private bool _doubleJumpConsumed = false;
|
||||
private bool _jumpedThisFrame = false;
|
||||
private bool _canWallJump = false;
|
||||
private Vector3 _wallJumpNormal;
|
||||
private float _timeSinceJumpRequested = Mathf.Infinity;
|
||||
private float _timeSinceLastAbleToJump = 0f;
|
||||
private Vector3 _internalVelocityAdd = Vector3.zero;
|
||||
private bool _shouldBeCrouching = false;
|
||||
private bool _isCrouching = false;
|
||||
|
||||
// Ladder vars
|
||||
private float _ladderUpDownInput;
|
||||
private MyLadder _activeLadder { get; set; }
|
||||
private ClimbingState _internalClimbingState;
|
||||
private ClimbingState _climbingState
|
||||
{
|
||||
get
|
||||
{
|
||||
return _internalClimbingState;
|
||||
}
|
||||
set
|
||||
{
|
||||
_internalClimbingState = value;
|
||||
_anchoringTimer = 0f;
|
||||
_anchoringStartPosition = Motor.TransientPosition;
|
||||
_anchoringStartRotation = Motor.TransientRotation;
|
||||
}
|
||||
}
|
||||
private Vector3 _ladderTargetPosition;
|
||||
private Quaternion _ladderTargetRotation;
|
||||
private float _onLadderSegmentState = 0;
|
||||
private float _anchoringTimer = 0f;
|
||||
private Vector3 _anchoringStartPosition = Vector3.zero;
|
||||
private Quaternion _anchoringStartRotation = Quaternion.identity;
|
||||
private Quaternion _rotationBeforeClimbing = Quaternion.identity;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Assign to motor
|
||||
Motor.CharacterController = this;
|
||||
|
||||
// Handle initial state
|
||||
TransitionToState(CharacterState.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles movement state transitions and enter/exit callbacks
|
||||
/// </summary>
|
||||
public void TransitionToState(CharacterState newState)
|
||||
{
|
||||
CharacterState tmpInitialState = CurrentCharacterState;
|
||||
OnStateExit(tmpInitialState, newState);
|
||||
CurrentCharacterState = newState;
|
||||
OnStateEnter(newState, tmpInitialState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event when entering a state
|
||||
/// </summary>
|
||||
public void OnStateEnter(CharacterState state, CharacterState fromState)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case CharacterState.Climbing:
|
||||
{
|
||||
_rotationBeforeClimbing = Motor.TransientRotation;
|
||||
|
||||
Motor.SetMovementCollisionsSolvingActivation(false);
|
||||
Motor.SetGroundSolvingActivation(false);
|
||||
_climbingState = ClimbingState.Anchoring;
|
||||
|
||||
// Store the target position and rotation to snap to
|
||||
_ladderTargetPosition = _activeLadder.ClosestPointOnLadderSegment(Motor.TransientPosition, out _onLadderSegmentState);
|
||||
_ladderTargetRotation = _activeLadder.transform.rotation;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event when exiting a state
|
||||
/// </summary>
|
||||
public void OnStateExit(CharacterState state, CharacterState toState)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case CharacterState.Climbing:
|
||||
{
|
||||
Motor.SetMovementCollisionsSolvingActivation(true);
|
||||
Motor.SetGroundSolvingActivation(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
// Handle ladder transitions
|
||||
_ladderUpDownInput = inputs.MoveAxisForward;
|
||||
if (inputs.ClimbLadder)
|
||||
{
|
||||
if (Motor.CharacterOverlap(Motor.TransientPosition, Motor.TransientRotation, _probedColliders, InteractionLayer, QueryTriggerInteraction.Collide) > 0)
|
||||
{
|
||||
if (_probedColliders[0] != null)
|
||||
{
|
||||
// Handle ladders
|
||||
MyLadder ladder = _probedColliders[0].gameObject.GetComponent<MyLadder>();
|
||||
if (ladder)
|
||||
{
|
||||
// Transition to ladder climbing state
|
||||
if (CurrentCharacterState == CharacterState.Default)
|
||||
{
|
||||
_activeLadder = ladder;
|
||||
TransitionToState(CharacterState.Climbing);
|
||||
}
|
||||
// Transition back to default movement state
|
||||
else if (CurrentCharacterState == CharacterState.Climbing)
|
||||
{
|
||||
_climbingState = ClimbingState.DeAnchoring;
|
||||
_ladderTargetPosition = Motor.TransientPosition;
|
||||
_ladderTargetRotation = _rotationBeforeClimbing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (OrientTowardsGravity)
|
||||
{
|
||||
// Rotate from current up to invert gravity
|
||||
currentRotation = Quaternion.FromToRotation((currentRotation * Vector3.up), -Gravity) * currentRotation;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CharacterState.Climbing:
|
||||
{
|
||||
switch (_climbingState)
|
||||
{
|
||||
case ClimbingState.Climbing:
|
||||
currentRotation = _activeLadder.transform.rotation;
|
||||
break;
|
||||
case ClimbingState.Anchoring:
|
||||
case ClimbingState.DeAnchoring:
|
||||
currentRotation = Quaternion.Slerp(_anchoringStartRotation, _ladderTargetRotation, (_anchoringTimer / AnchoringDuration));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CharacterState.Climbing:
|
||||
{
|
||||
currentVelocity = Vector3.zero;
|
||||
|
||||
switch (_climbingState)
|
||||
{
|
||||
case ClimbingState.Climbing:
|
||||
currentVelocity = (_ladderUpDownInput * _activeLadder.transform.up).normalized * ClimbingSpeed;
|
||||
break;
|
||||
case ClimbingState.Anchoring:
|
||||
case ClimbingState.DeAnchoring:
|
||||
Vector3 tmpPosition = Vector3.Lerp(_anchoringStartPosition, _ladderTargetPosition, (_anchoringTimer / AnchoringDuration));
|
||||
currentVelocity = Motor.GetVelocityForMovePosition(Motor.TransientPosition, tmpPosition, deltaTime);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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.CharacterOverlap(
|
||||
Motor.TransientPosition,
|
||||
Motor.TransientRotation,
|
||||
_probedColliders,
|
||||
Motor.CollidableLayers,
|
||||
QueryTriggerInteraction.Ignore) > 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;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CharacterState.Climbing:
|
||||
{
|
||||
switch (_climbingState)
|
||||
{
|
||||
case ClimbingState.Climbing:
|
||||
// Detect getting off ladder during climbing
|
||||
_activeLadder.ClosestPointOnLadderSegment(Motor.TransientPosition, out _onLadderSegmentState);
|
||||
if (Mathf.Abs(_onLadderSegmentState) > 0.05f)
|
||||
{
|
||||
_climbingState = ClimbingState.DeAnchoring;
|
||||
|
||||
// If we're higher than the ladder top point
|
||||
if (_onLadderSegmentState > 0)
|
||||
{
|
||||
_ladderTargetPosition = _activeLadder.TopReleasePoint.position;
|
||||
_ladderTargetRotation = _activeLadder.TopReleasePoint.rotation;
|
||||
}
|
||||
// If we're lower than the ladder bottom point
|
||||
else if (_onLadderSegmentState < 0)
|
||||
{
|
||||
_ladderTargetPosition = _activeLadder.BottomReleasePoint.position;
|
||||
_ladderTargetRotation = _activeLadder.BottomReleasePoint.rotation;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ClimbingState.Anchoring:
|
||||
case ClimbingState.DeAnchoring:
|
||||
// Detect transitioning out from anchoring states
|
||||
if (_anchoringTimer >= AnchoringDuration)
|
||||
{
|
||||
if (_climbingState == ClimbingState.Anchoring)
|
||||
{
|
||||
_climbingState = ClimbingState.Climbing;
|
||||
}
|
||||
else if (_climbingState == ClimbingState.DeAnchoring)
|
||||
{
|
||||
TransitionToState(CharacterState.Default);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep track of time since we started anchoring
|
||||
_anchoringTimer += deltaTime;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsColliderValidForCollisions(Collider coll)
|
||||
{
|
||||
if (IgnoredColliders.Contains(coll))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
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)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddVelocity(Vector3 velocity)
|
||||
{
|
||||
switch (CurrentCharacterState)
|
||||
{
|
||||
case CharacterState.Default:
|
||||
{
|
||||
_internalVelocityAdd += velocity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, Vector3 atCharacterPosition, Quaternion atCharacterRotation, ref HitStabilityReport hitStabilityReport)
|
||||
{
|
||||
}
|
||||
|
||||
public void PostGroundingUpdate(float deltaTime)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnDiscreteCollisionDetected(Collider hitCollider)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d44422eef151e74c8387f4da8838cf6
|
||||
timeCreated: 1507247652
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace KinematicCharacterController.Walkthrough.ClimbingLadders
|
||||
{
|
||||
public class MyLadder : MonoBehaviour
|
||||
{
|
||||
// Ladder segment
|
||||
public Vector3 LadderSegmentBottom;
|
||||
public float LadderSegmentLength;
|
||||
|
||||
// Points to move to when reaching one of the extremities and moving off of the ladder
|
||||
public Transform BottomReleasePoint;
|
||||
public Transform TopReleasePoint;
|
||||
|
||||
// Gets the position of the bottom point of the ladder segment
|
||||
public Vector3 BottomAnchorPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
return transform.position + transform.TransformVector(LadderSegmentBottom);
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the position of the top point of the ladder segment
|
||||
public Vector3 TopAnchorPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
return transform.position + transform.TransformVector(LadderSegmentBottom) + (transform.up * LadderSegmentLength);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 ClosestPointOnLadderSegment(Vector3 fromPoint, out float onSegmentState)
|
||||
{
|
||||
Vector3 segment = TopAnchorPoint - BottomAnchorPoint;
|
||||
Vector3 segmentPoint1ToPoint = fromPoint - BottomAnchorPoint;
|
||||
float pointProjectionLength = Vector3.Dot(segmentPoint1ToPoint, segment.normalized);
|
||||
|
||||
// When higher than bottom point
|
||||
if(pointProjectionLength > 0)
|
||||
{
|
||||
// If we are not higher than top point
|
||||
if (pointProjectionLength <= segment.magnitude)
|
||||
{
|
||||
onSegmentState = 0;
|
||||
return BottomAnchorPoint + (segment.normalized * pointProjectionLength);
|
||||
}
|
||||
// If we are higher than top point
|
||||
else
|
||||
{
|
||||
onSegmentState = pointProjectionLength - segment.magnitude;
|
||||
return TopAnchorPoint;
|
||||
}
|
||||
}
|
||||
// When lower than bottom point
|
||||
else
|
||||
{
|
||||
onSegmentState = pointProjectionLength;
|
||||
return BottomAnchorPoint;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawLine(BottomAnchorPoint, TopAnchorPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c9bfe760a258784bac7a3dfe4c2018d
|
||||
timeCreated: 1507424829
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,95 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using KinematicCharacterController;
|
||||
using KinematicCharacterController.Examples;
|
||||
using System.Linq;
|
||||
|
||||
namespace KinematicCharacterController.Walkthrough.ClimbingLadders
|
||||
{
|
||||
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);
|
||||
characterInputs.ClimbLadder = Input.GetKeyUp(KeyCode.E);
|
||||
|
||||
// Apply inputs to character
|
||||
Character.SetInputs(ref characterInputs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 73ad718951a54cd4da442fee50532dd1
|
||||
timeCreated: 1507247652
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb1cd6e6c068fd946aa4b4a5906b78b3
|
||||
folderAsset: yes
|
||||
timeCreated: 1507244870
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6f661d0a947caf48ad6182cb6bbb21a
|
||||
folderAsset: yes
|
||||
timeCreated: 1507408249
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45734a45f9072fd4f8f580e87f79bafc
|
||||
folderAsset: yes
|
||||
timeCreated: 1507408249
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user