coyote time

master
Jordan Orelli 5 years ago
parent c0513e07e2
commit 1acf3cd3e2

@ -498,13 +498,15 @@ RectTransform:
- {fileID: 2864439304583037120}
- {fileID: 5992904181450710442}
- {fileID: 1513821230345505978}
- {fileID: 7929369720254856305}
- {fileID: 1178666589773800033}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 240, y: 24}
m_SizeDelta: {x: 240, y: 96}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &6864253409301352422
MonoBehaviour:
@ -523,6 +525,7 @@ MonoBehaviour:
rightDisplay: {fileID: 44462917099111718}
aboveDisplay: {fileID: 8497902669476004809}
belowDisplay: {fileID: 4292523660552820178}
jumpStateDisplay: {fileID: 1374171197480281285}
--- !u!1 &4452007896964695948
GameObject:
m_ObjectHideFlags: 0
@ -600,6 +603,83 @@ MonoBehaviour:
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: False
--- !u!1 &5835746257598859911
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1178666589773800033}
- component: {fileID: 8448032696549684776}
- component: {fileID: 1374171197480281285}
m_Layer: 5
m_Name: JumpState Status
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1178666589773800033
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5835746257598859911}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 4296878337294020281}
m_RootOrder: 9
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 180, y: -108}
m_SizeDelta: {x: 120, y: 24}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &8448032696549684776
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5835746257598859911}
m_CullTransparentMesh: 0
--- !u!114 &1374171197480281285
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5835746257598859911}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 14
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 10
m_MaxSize: 40
m_Alignment: 3
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: False
--- !u!1 &8087166239981300898
GameObject:
m_ObjectHideFlags: 0
@ -677,3 +757,80 @@ MonoBehaviour:
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: False
--- !u!1 &8177252236619225150
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7929369720254856305}
- component: {fileID: 1633306603050821040}
- component: {fileID: 1453528757066824372}
m_Layer: 5
m_Name: JumpState Label
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &7929369720254856305
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8177252236619225150}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 4296878337294020281}
m_RootOrder: 8
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 60, y: -108}
m_SizeDelta: {x: 120, y: 24}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &1633306603050821040
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8177252236619225150}
m_CullTransparentMesh: 0
--- !u!114 &1453528757066824372
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8177252236619225150}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 14
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 10
m_MaxSize: 40
m_Alignment: 3
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: Jump State

@ -545,7 +545,9 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
player: {fileID: 1313500787}
camera: {fileID: 0}
maxAcceleration: 1
maxVelocity: 10
smoothTime: 0.3
--- !u!20 &519420031
Camera:
m_ObjectHideFlags: 0

@ -2,25 +2,32 @@
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class CameraController : MonoBehaviour {
public Transform player;
public Camera camera;
public float maxAcceleration = 1f;
public float maxVelocity = 10f;
private Frame frame;
private Vector3 velocity;
private Vector3 lastVelocity;
private Vector3 acceleration;
private Vector3 lastAcceleration;
// the camera's ideal position, which we will continually move towards but
// not necessarily snap to (to avoid jerky camera motion)
private Vector3 targetPosition;
private Vector3 velocity = Vector3.zero;
public float smoothTime = 0.0025f;
// private Vector3 acceleration = Vector3.zero;
// the values of position, velocity, and acceleration last frame
// private Vector3 lastPosition = Vector3.zero;
// private Vector3 lastVelocity = Vector3.zero;
// private Vector3 lastAcceleration = Vector3.zero;
private Camera cam;
// Start is called before the first frame update
void Start() {
camera = gameObject.GetComponent<Camera>();
velocity.x = 0;
velocity.y = 0;
velocity.z = 0;
cam = gameObject.GetComponent<Camera>();
}
void Update() {
@ -33,23 +40,26 @@ public class CameraController : MonoBehaviour {
}
setupFrame();
Vector3 targetPosition = transform.position;
targetPosition = transform.position;
BoxCollider2D collider = player.GetComponent<BoxCollider2D>();
if (collider.bounds.max.x > frame.topRight.x) {
targetPosition.x = collider.bounds.max.x - frame.topRight.x;
}
if (collider.bounds.min.x < frame.topLeft.x) {
targetPosition.x = collider.bounds.min.x - frame.topLeft.x;
targetPosition.x += collider.bounds.max.x - frame.topRight.x;
} else if (collider.bounds.min.x < frame.topLeft.x) {
targetPosition.x += collider.bounds.min.x - frame.topLeft.x;
}
if (collider.bounds.max.y > frame.topLeft.y) {
targetPosition.y = collider.bounds.max.y - frame.topLeft.y;
}
if (collider.bounds.min.y < frame.bottomLeft.y) {
targetPosition.y = collider.bounds.min.y - frame.bottomLeft.y;
targetPosition.y += collider.bounds.max.y - frame.topLeft.y;
} else if (collider.bounds.min.y < frame.bottomLeft.y) {
targetPosition.y += collider.bounds.min.y - frame.bottomLeft.y;
}
transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref velocity, smoothTime);
// lastPosition = transform.position;
// lastVelocity = velocity;
// lastAcceleration = acceleration;
}
void OnDrawGizmosSelected() {
@ -83,10 +93,10 @@ public class CameraController : MonoBehaviour {
int height = Screen.height;
int width = Screen.width;
frame.topLeft = camera.ScreenToWorldPoint(new Vector3(width / 2 - width / 6, height / 2 + height / 3, 1));
frame.topRight = camera.ScreenToWorldPoint(new Vector3(width / 2 + width / 6, height / 2 + height / 3, 1));
frame.bottomLeft = camera.ScreenToWorldPoint(new Vector3(width / 2 - width / 6, height / 2 - height / 3, 1));
frame.bottomRight = camera.ScreenToWorldPoint(new Vector3(width / 2 + width / 6, height / 2 - height / 3, 1));
frame.topLeft = cam.ScreenToWorldPoint(new Vector3(width / 2 - width / 9, height / 2 + height / 3, 1));
frame.topRight = cam.ScreenToWorldPoint(new Vector3(width / 2 + width / 9, height / 2 + height / 3, 1));
frame.bottomLeft = cam.ScreenToWorldPoint(new Vector3(width / 2 - width / 9, height / 2 - height / 3, 1));
frame.bottomRight = cam.ScreenToWorldPoint(new Vector3(width / 2 + width / 9, height / 2 - height / 3, 1));
}
struct Frame {

@ -9,13 +9,16 @@ public class MoveControllerDebugDisplay : MonoBehaviour {
public Text rightDisplay;
public Text aboveDisplay;
public Text belowDisplay;
public Text jumpStateDisplay;
private PlayerController player;
private MoveController moveController;
// Start is called before the first frame update
void Start() {
if (target) {
moveController = target.GetComponent<MoveController>();
player = target.GetComponent<PlayerController>();
}
}
@ -27,6 +30,32 @@ public class MoveControllerDebugDisplay : MonoBehaviour {
showBool(leftDisplay, moveController.collisions.left);
showBool(rightDisplay, moveController.collisions.right);
}
if (player) {
switch (player.jumpState) {
case PlayerController.JumpState.Grounded:
jumpStateDisplay.text = "Grounded";
break;
case PlayerController.JumpState.CoyoteTime:
jumpStateDisplay.text = "CoyoteTime";
break;
case PlayerController.JumpState.Ascending:
jumpStateDisplay.text = "Ascending";
break;
case PlayerController.JumpState.Apex:
jumpStateDisplay.text = "Apex";
break;
case PlayerController.JumpState.Descending:
jumpStateDisplay.text = "Descending";
break;
case PlayerController.JumpState.Falling:
jumpStateDisplay.text = "Falling";
break;
default:
jumpStateDisplay.text = "???";
break;
}
}
}
void showBool(Text label, bool value) {

@ -11,24 +11,31 @@ public class PlayerController : MonoBehaviour {
public float maxFallSpeed = -20f;
public float timeToMaxRunSpeed = 0.15f;
public float timeToMaxAirmoveSpeed = 0.25f;
public float floatTime = 0.025f; // amount of time spent at max jump height before falling again
public float coyoteTime = 0.025f;
// this has to be public to be readable by the display, which is a code
// smell.
public JumpState jumpState;
private float jumpStateStart;
private float jumpVelocity = 8f;
private float gravity = -20f;
private Vector3 velocity;
private MoveController moveController;
private float velocityXSmoothing;
private int frameCount = 0;
void Start() {
setJumpState(JumpState.Falling);
moveController = GetComponent<MoveController>();
gravity = -(2* jumpHeight) / Mathf.Pow(timeToJumpApex, 2);
jumpVelocity = Mathf.Abs(gravity) * timeToJumpApex;
}
void Update() {
frameCount++;
Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
float targetX = input.x * moveSpeed;
Vector3 initialVelocity = velocity;
if (moveController.collisions.above) {
velocity.y = 0;
@ -38,22 +45,84 @@ public class PlayerController : MonoBehaviour {
velocity.x = 0;
}
if (moveController.isGrounded) {
switch (jumpState) {
case JumpState.Grounded:
if (Input.GetButtonDown("Jump")) {
setJumpState(JumpState.Ascending);
velocity.y = jumpVelocity;
} else {
velocity.y = gravity * Time.deltaTime;
}
velocity.x = Mathf.SmoothDamp(velocity.x, targetX, ref velocityXSmoothing, timeToMaxRunSpeed);
break;
// case JumpState.Ascending:
// velocity.x = Mathf.SmoothDamp(velocity.x, targetX, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
// velocity.y += gravity * Time.deltaTime;
// if (velocity.y < maxFallSpeed) {
// velocity.y = maxFallSpeed;
// }
// // if we were rising in the last frame but will be falling in this
// // frame, we should zero out the velocity to float instead.
// if (initialVelocity.y >= 0 && velocity.y <= 0) {
// velocity.y = 0;
// setJumpState(JumpState.Apex);
// }
// break;
case JumpState.Apex:
velocity.x = Mathf.SmoothDamp(velocity.x, targetX, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
float timeFloating = Time.time - jumpStateStart;
float floatTimeRemaining = floatTime - timeFloating;
if (floatTimeRemaining < 0) {
velocity.y += gravity * -floatTimeRemaining;
setJumpState(JumpState.Descending);
} else {
velocity.y = 0;
}
break;
case JumpState.CoyoteTime:
velocity.x = Mathf.SmoothDamp(velocity.x, targetX, ref velocityXSmoothing, timeToMaxRunSpeed);
if (Input.GetButtonDown("Jump")) {
setJumpState(JumpState.Ascending);
velocity.y = jumpVelocity;
} else {
float elapsedCoyoteTime = Time.time - jumpStateStart;
float coyoteTimeRemaining = coyoteTime - elapsedCoyoteTime;
if (coyoteTimeRemaining < 0) {
velocity.y += gravity * -coyoteTimeRemaining;
setJumpState(JumpState.Falling);
} else {
velocity.y = 0;
}
}
break;
default:
velocity.x = Mathf.SmoothDamp(velocity.x, targetX, ref velocityXSmoothing, timeToMaxAirmoveSpeed);
velocity.y += gravity * Time.deltaTime;
if (velocity.y < maxFallSpeed) {
velocity.y = maxFallSpeed;
}
// if we were rising in the last frame but will be falling in this
// frame, we should zero out the velocity to float instead.
if (initialVelocity.y >= 0 && velocity.y <= 0) {
velocity.y = 0;
setJumpState(JumpState.Apex);
}
break;
}
moveController.Move(velocity * Time.deltaTime);
if (jumpState == JumpState.Grounded && !moveController.isGrounded) {
setJumpState(JumpState.CoyoteTime);
}
if (jumpState != JumpState.Grounded && moveController.isGrounded) {
setJumpState(JumpState.Grounded);
}
}
void OnDestroy() {
@ -64,4 +133,45 @@ public class PlayerController : MonoBehaviour {
void OnTriggerEnter(Collider other) {
}
void setJumpState(JumpState state) {
jumpState = state;
jumpStateStart = Time.time;
}
/*
Possible JumpState transitions:
Grounded -> Ascending : a normal jump
Grounded -> CoyoteTime : player has walked off ledge
CoyoteTime -> Ascending : player has jumped after leaving a ledge
CoyoteTime -> Falling : player has walked off of a ledge and is now falling
Ascending -> Apex : player has reached the top of their jump normally
Apex -> Descending : player has reached the top of their jump and is now falling
*/
public enum JumpState {
// The player is on the ground
Grounded,
// The player has not jumped; they have walked off of a platform without
// jumping
CoyoteTime,
// The player is rising in a jump
Ascending,
// The player has reached the apex of their jump, where they will
// briefly remain to give a feeling of lightness
Apex,
// The player, having jumped, is in a controlled descent following the
// jump
Descending,
// The player is descending but without control; they are falling but
// did not initially jump.
Falling,
}
}

@ -6,7 +6,7 @@
"com.unity.analytics": "3.3.5",
"com.unity.collab-proxy": "1.2.16",
"com.unity.ide.rider": "1.1.4",
"com.unity.ide.vscode": "1.1.4",
"com.unity.ide.vscode": "1.1.3",
"com.unity.multiplayer-hlapi": "1.0.4",
"com.unity.purchasing": "2.0.6",
"com.unity.quicksearch": "1.6.0-preview.6",

Loading…
Cancel
Save