diff --git a/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/CharacterJump.cs b/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/CharacterJump.cs index cf441521b9deb4225c251b7b4ea6f1bdbf30d056..9b977b33fbf4900abc8ead98d894f01747e7e202 100644 --- a/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/CharacterJump.cs +++ b/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/CharacterJump.cs @@ -1,3 +1,4 @@ +using NaughtyAttributes; using UnityEngine; using UnityEngine.InputSystem; @@ -10,7 +11,8 @@ [RequireComponent(typeof(CharacterGround))] public class CharacterJump : MonoBehaviour { - [Header("Components")] + // [Header("Components")] + [InfoBox("If you want to see a prediction of the jump when this GameObject is selected, check showJumpLine in the Character_Settings_SO that you're using.", EInfoBoxType.Normal)] [XnTools.Hidden] public Rigidbody2D rigid; [XnTools.Hidden] public Vector2 velocity; @@ -33,7 +35,7 @@ public class CharacterJump : MonoBehaviour // [SerializeField, Range(0, 1)][Tooltip("How many times can you jump in the air?")] public int maxAirJumps = 0; // [Header("Options")] -// [Tooltip("Should the character drop when you let go of jump?")] public bool variablejumpHeight; +// [Tooltip("Should the character drop when you let go of jump?")] public bool variableJumpHeight; // [SerializeField, Range(1f, 10f)][Tooltip("Gravity multiplier when you let go of jump")] public float jumpCutOff; // [SerializeField][Tooltip("The fastest speed the character can fall")] public float speedLimit; // [SerializeField, Range(0f, 0.3f)][Tooltip("How long should coyote time last?")] public float coyoteTime = 0.15f; @@ -82,7 +84,7 @@ public void OnJump(InputAction.CallbackContext context) private void SetPhysics() { //Determine the character's gravity scale, using the stats provided. Multiply it by a gravMultiplier, used later - Vector2 newGravity = new Vector2( 0, ( -2 * characterSettingsSO.jumpHeight ) / ( characterSettingsSO.timeToJumpApex * characterSettingsSO.timeToJumpApex ) ); + Vector2 newGravity = new Vector2( 0, ( -2 * characterSettingsSO.jumpHeight ) / ( characterSettingsSO.jumpDuration.up * characterSettingsSO.jumpDuration.up ) ); rigid.gravityScale = ( newGravity.y / Physics2D.gravity.y ) * gravMultiplier; } @@ -165,22 +167,20 @@ private void CalculateGravity() else { //If we're using variable jump height...) - if ( characterSettingsSO.variablejumpHeight ) + if ( characterSettingsSO.jumpSettingsVariableHeight.useVariableJumpHeight ) { //Apply upward multiplier if player is rising and holding jump - if (pressingJump && currentlyJumping) - { - gravMultiplier = characterSettingsSO.upwardMovementMultiplier; + if (pressingJump && currentlyJumping) { + gravMultiplier = characterSettingsSO.jumpGrav.up;// characterSettingsSO.upwardMovementMultiplier; } //But apply a special downward multiplier if the player lets go of jump - else - { - gravMultiplier = characterSettingsSO.jumpCutOff; + else { + gravMultiplier = characterSettingsSO.jumpGrav.up * characterSettingsSO.jumpSettingsVariableHeight.gravUpMultiplierOnRelease;// characterSettingsSO.jumpCutOff; } } else { - gravMultiplier = characterSettingsSO.upwardMovementMultiplier; + gravMultiplier = characterSettingsSO.jumpGrav.up;// characterSettingsSO.upwardMovementMultiplier; } } } @@ -197,7 +197,7 @@ private void CalculateGravity() else { //Otherwise, apply the downward gravity multiplier as Kit comes back to Earth - gravMultiplier = characterSettingsSO.downwardMovementMultiplier; + gravMultiplier = characterSettingsSO.jumpGrav.down; //characterSettingsSO.downwardMovementMultiplier; } } @@ -233,8 +233,9 @@ private void DoAJump() SetPhysics(); //If we have double jump on, allow us to jump again (but only once) - canJumpAgain = ( characterSettingsSO.maxAirJumps == 1 && canJumpAgain == false); - + // canJumpAgain = ( characterSettingsSO.maxAirJumps == 1 && canJumpAgain == false); + canJumpAgain = ( characterSettingsSO.jumpsBetweenGrounding > 1 && canJumpAgain == false); + //Determine the power of the jump, based on our gravity and stats jumpSpeed = Mathf.Sqrt(-2f * Physics2D.gravity.y * rigid.gravityScale * characterSettingsSO.jumpHeight ); if (jumpSpeed > 100) { @@ -308,22 +309,39 @@ private void OnSceneGUI() { if ( !csso.showJumpLine ) return; if ( csso.jumpSettingsType == Character_Settings_SO.eJumpSettingsType.GMTK_GameMakersToolKit ) return; if (csso.jumpLinePoints == null) cMove.characterSettingsSO.CalculateJumpLine(); + + GUIStyle labelStyle = new GUIStyle( EditorStyles.foldoutHeader ); + labelStyle.imagePosition = ImagePosition.TextOnly; // NOTE: This didn't seem to do anything. + labelStyle.richText = true; - Handles.matrix = Matrix4x4.Translate(cJump.transform.position); // Not needed because jump will be shown at origin. + Handles.matrix = Matrix4x4.Translate(cJump.transform.position); Handles.color = Color.green; Handles.DrawAAPolyLine(4, csso.jumpLinePoints); + Vector3 tVec; Vector3[] jSME = csso.jumpStartMidEndPoints; if ( jSME != null && jSME.Length == 3 ) { Vector3 offset = Vector3.up * 0.2f; Handles.DrawDottedLine( jSME[0] + offset, jSME[2] + offset, dashSize ); - Vector3 tVec = ( jSME[0] + jSME[2] ) / 2f + offset * 4 + Vector3.left * 0.4f; - Handles.Label( tVec, $"Dist: {csso.maxJumpDistHeight.x:0.##}" ); + tVec = ( jSME[0] + jSME[2] ) / 2f + offset * 4 + Vector3.left * 0.4f; + Handles.Label( tVec, $"<b>Dist: {csso.maxJumpDistHeight.x:0.##}</b>", labelStyle ); tVec = jSME[1]; tVec.y = 0; Handles.DrawDottedLine( tVec, jSME[1], dashSize ); tVec = ( tVec + jSME[1] ) / 2f + Vector3.left * 0.4f; - Handles.Label( tVec, $"Height: {csso.maxJumpDistHeight.y:0.##}" ); + Handles.Label( tVec, $"<b>Height: {csso.maxJumpDistHeight.y:0.##}</b>", labelStyle ); } + + if ( csso.jumpSettingsVariableHeight.useVariableJumpHeight ) { + Handles.color = Color.magenta; + Handles.DrawAAPolyLine( 8, csso.minJumpLinePoints.ToArray() ); + if ( csso.minJumpStartMidEndPoints != null && + csso.minJumpStartMidEndPoints.Length == 3 ) { + tVec = csso.minJumpStartMidEndPoints[0] + Vector3.down * 0.25f; + Handles.Label( tVec, $"<b>Min: Ht: {csso.minJumpDistHeight.y:0.##} Dst: {csso.minJumpDistHeight.x:0.##}" + + $" tApex: {csso.minTimeApexFull.x:0.##} tFull: {csso.minTimeApexFull.y:0.##}</b>", labelStyle ); + } + } + } } diff --git a/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/Character_Settings_SO.cs b/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/Character_Settings_SO.cs index 5b69a794eb9cf7c84c0b9ade2894fc483eb340c7..fc1906a46fb27c30b8bd16b3bbe2680a23f56b41 100644 --- a/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/Character_Settings_SO.cs +++ b/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/Character_Settings_SO.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using UnityEngine; using NaughtyAttributes; +using UnityEngine.Timeline; #if UNITY_EDITOR using UnityEditor; #endif @@ -11,130 +12,180 @@ /// Created by Jeremy Bond for MI 231 at Michigan State University /// Built to work with a modified version of the GMTK Platformer Toolkit /// </summary> -[CreateAssetMenu( fileName = "GMTK_Settings_[GameName]", menuName = "ScriptableObjects/GMTK_Settings", order = 1 )] +[CreateAssetMenu( fileName = "GMTK_Settings_[GameName]", + menuName = "ScriptableObjects/GMTK_Settings", order = 1 )] public class Character_Settings_SO : ScriptableObject { + static bool DEBUG_JUMP_LINE_CALCULATION = false; [Header( "Movement Stats" )] - [SerializeField, Range( 0f, 20f )] [Tooltip( "Maximum movement speed" )] public float maxSpeed = 10f; - [SerializeField, Range( 0f, 100f )] [Tooltip( "How fast to reach max speed" )] public float maxAcceleration = 52f; - [SerializeField, Range( 0f, 100f )] [Tooltip( "How fast to stop after letting go" )] public float maxDeceleration = 52f; - [SerializeField, Range( 0f, 100f )] [Tooltip( "How fast to stop when changing direction" )] public float maxTurnSpeed = 80f; - [SerializeField, Range( 0f, 100f )] [Tooltip( "How fast to reach max speed when in mid-air" )] public float maxAirAcceleration = 0; - [SerializeField, Range( 0f, 100f )] [Tooltip( "How fast to stop in mid-air when no direction is used" )] public float maxAirDeceleration = 0; - [SerializeField, Range( 0f, 100f )] [Tooltip( "How fast to stop when changing direction when in mid-air" )] public float maxAirTurnSpeed = 80f; - [SerializeField] [Tooltip( "Friction to apply against movement on stick" )] public float friction = 0; + [SerializeField, Range( 0f, 20f )] + [Tooltip( "Maximum movement speed" )] + public float maxSpeed = 10f; + [SerializeField, Range( 0f, 100f )] + [Tooltip( "How fast to reach max speed" )] + public float maxAcceleration = 52f; + [SerializeField, Range( 0f, 100f )] + [Tooltip( "How fast to stop after letting go" )] + public float maxDeceleration = 52f; + [SerializeField, Range( 0f, 100f )] + [Tooltip( "How fast to stop when changing direction" )] + public float maxTurnSpeed = 80f; + [SerializeField, Range( 0f, 100f )] + [Tooltip( "How fast to reach max speed when in mid-air" )] + public float maxAirAcceleration = 0; + [SerializeField, Range( 0f, 100f )] + [Tooltip( "How fast to stop in mid-air when no direction is used" )] + public float maxAirDeceleration = 0; + [SerializeField, Range( 0f, 100f )] + [Tooltip( "How fast to stop when changing direction when in mid-air" )] + public float maxAirTurnSpeed = 80f; + [SerializeField] + [Tooltip( "Friction to apply against movement on stick" )] + public float friction = 0; [Header( "Movement Options" )] - [Tooltip( "When false, the charcter will skip acceleration and deceleration and instantly move and stop" )] public bool useAcceleration = true; + [Tooltip( + "When false, the charcter will skip acceleration and deceleration and instantly move and stop" )] + public bool useAcceleration = true; // NOTE: CGSK jummp math comes from Math for Game Programmers: Building a Better Jump // https://www.youtube.com/watch?v=hG9SzQxaCm8&t=9m35s & https://www.youtube.com/watch?v=hG9SzQxaCm8&t=784s // Th = Xh/Vx V0 = 2H / Th G = -2H / (Th * Th) V0 = 2HVx / Xh G = -2H(Vx*Vx) / (Xh*Xh) - + [Header( "Jump Settings" )] public eJumpSettingsType jumpSettingsType = eJumpSettingsType.CGSK_Time; public enum eJumpSettingsType { CGSK_Distance, CGSK_Time, GMTK_GameMakersToolKit }; public bool showJumpLine = true; - + [Tooltip( "Maximum jump height" )] - [Range( 1f, 10f )] public float jumpHeight = 4f; + [Range( 1f, 10f )] + public float jumpHeight = 4f; - [ShowIf("jumpSettingsType", eJumpSettingsType.CGSK_Time)] + [ShowIf( "jumpSettingsType", eJumpSettingsType.CGSK_Time )] public CGSK_JumpSettings_Time jumpSettingsTime; - - [ShowIf("jumpSettingsType", eJumpSettingsType.CGSK_Distance)] + + [ShowIf( "jumpSettingsType", eJumpSettingsType.CGSK_Distance )] public CGSK_JumpSettings_Distance jumpSettingsDistance; + [HideIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )] + public CGSK_JumpSettings_VariableHeight jumpSettingsVariableHeightCGSK; + [ShowIf("jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )] + public CGSK_JumpSettings_VariableHeight jumpSettingsVariableHeightGMTK; + [HideInInspector] internal CGSK_JumpSettings_VariableHeight jumpSettingsVariableHeight; + // [HideIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )] // [XnTools.ReadOnly][BoxGroup("CGSK Derived Jump Properties")] // public float jumpDistUp, jumpDurationUp, jumpVelUp, jumpGravUp, jumpDistDown, jumpDurationDown, jumpVelDown, jumpGravDown; [HideIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )] - [BoxGroup( "CGSK Derived Jump Properties" )] + [BoxGroup( "Derived Jump Properties" )] public CSSO_FloatUpDown jumpDist, jumpDuration, jumpVel, jumpGrav; + [BoxGroup( "Derived Jump Properties" )] [SerializeField] [XnTools.ReadOnly] + internal Vector2 maxJumpDistHeight, minJumpDistHeight; + + + + [ShowIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )] + public GMTK_JumpSettings jumpSettingsGMTK; - - - - [Header("Jump Settings - GameMakers ToolKit")] - [ShowIf("jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit)] - [SerializeField, Range( 1f, 10f )] [Tooltip( "How long it takes to reach that height before coming back down" )] - public float jumpDurationFromVideo = 5; - //If you're using your stats from Platformer Toolkit with this character controller, - // please note that the number on the Jump Duration handle does not match this stat - // It is re-scaled, from 0.2f - 1.25f, to 1 - 10. - [XnTools.ReadOnly] - [SerializeField, Range( 0.1f, 1.25f )][ShowIf("jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit)] - [Tooltip( "For some reason, the GMTK version of the value in the video is different from the one here, so I calculate this from the other value." )] - public float timeToJumpApex; - [ShowIf("jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit)] - [SerializeField, Range( 0f, 5f )] [Tooltip( "Gravity multiplier to apply when going up" )] public float upwardMovementMultiplier = 1f; - [ShowIf("jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit)] - [SerializeField, Range( 1f, 10f )] [Tooltip( "Gravity multiplier to apply when coming down" )] public float downwardMovementMultiplier = 6.17f; - [ShowIf("jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit)] - [SerializeField, Range( 0, 1 )] [Tooltip( "How many times can you jump in the air?" )] public int maxAirJumps = 0; - [ShowIf("jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit)] - [Tooltip( "Should the character drop when you let go of jump?" )] public bool variablejumpHeight; - [ShowIf("jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit)] - [SerializeField, Range( 1f, 10f )] [Tooltip( "Gravity multiplier when you let go of jump" )] public float jumpCutOff; - [Header( "Jump Options" )] - [SerializeField] [Tooltip( "The fastest speed the character can fall" )] public float speedLimit = 26.45f; - [SerializeField, Range( 0f, 0.3f )] [Tooltip( "How long should coyote time last?" )] public float coyoteTime = 0.15f; - [SerializeField, Range( 0f, 0.3f )] [Tooltip( "How far from ground should we cache your jump?" )] public float jumpBuffer = 0.15f; + [SerializeField] + [Tooltip( "The fastest speed the character can fall" )] + public float speedLimit = 26.45f; + [SerializeField, Range( 0f, 0.3f )] + [Tooltip( "How long should coyote time last?" )] + public float coyoteTime = 0.15f; + [SerializeField, Range( 0f, 0.3f )] + [Tooltip( "How far from ground should we cache your jump?" )] + public float jumpBuffer = 0.15f; + [Tooltip( "Max jumps between grounding. (2 for Double Jump, 3 for Triple Jump, etc.) " )] + [Range( 1, 10 )] + public int jumpsBetweenGrounding = 1; [Header( "Juice Settings - Squash and Stretch" )] - [SerializeField] public bool squashAndStretch; - [SerializeField, Tooltip( "Width Squeeze, Height Squeeze, Duration" )] public Vector3 jumpSquashSettings; - [SerializeField, Tooltip( "Width Squeeze, Height Squeeze, Duration" )] public Vector3 landSquashSettings; - [SerializeField, Tooltip( "How powerful should the effect be?" )] public float landSqueezeMultiplier; - [SerializeField, Tooltip( "How powerful should the effect be?" )] public float jumpSqueezeMultiplier; - [SerializeField] public float landDrop = 1; + [SerializeField] + public bool squashAndStretch; + [SerializeField, Tooltip( "Width Squeeze, Height Squeeze, Duration" )] + public Vector3 jumpSquashSettings; + [SerializeField, Tooltip( "Width Squeeze, Height Squeeze, Duration" )] + public Vector3 landSquashSettings; + [SerializeField, Tooltip( "How powerful should the effect be?" )] + public float landSqueezeMultiplier; + [SerializeField, Tooltip( "How powerful should the effect be?" )] + public float jumpSqueezeMultiplier; + [SerializeField] + public float landDrop = 1; [Header( "Juice Settings - Tilting" )] - [SerializeField] public bool leanForward; - [SerializeField, Tooltip( "How far should the character tilt?" )] public float maxTilt; - [SerializeField, Tooltip( "How fast should the character tilt?" )] public float tiltSpeed; + [SerializeField] + public bool leanForward; + [SerializeField, Tooltip( "How far should the character tilt?" )] + public float maxTilt; + [SerializeField, Tooltip( "How fast should the character tilt?" )] + public float tiltSpeed; + // public float timeToJumpApex; + // [ShowIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )] + // [SerializeField, Range( 0f, 5f )] + // [Tooltip( "Gravity multiplier to apply when going up" )] + // public float upwardMovementMultiplier = 1f; + // [ShowIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )] + // [SerializeField, Range( 1f, 10f )] + // [Tooltip( "Gravity multiplier to apply when coming down" )] + // public float downwardMovementMultiplier = 6.17f; + // [ShowIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )] + // [SerializeField, Range( 0, 1 )] + // [Tooltip( "How many times can you jump in the air?" )] + // public int maxAirJumps = 0; + // [ShowIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )] + // [Tooltip( "Should the character drop when you let go of jump?" )] + // public bool variableJumpHeight; + // [ShowIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )] + // [SerializeField, Range( 1f, 10f )] + // [Tooltip( "Gravity multiplier when you let go of jump" )] + // public float jumpCutOff; + + private void OnValidate() { switch ( jumpSettingsType ) { case eJumpSettingsType.CGSK_Time: - CalculateDerivedCGSKJumpValues_Time(); + CalculateDerivedJumpValues_Time(); break; - + case eJumpSettingsType.CGSK_Distance: - CalculateDerivedCGSKJumpValues_Distance(); + CalculateDerivedJumpValues_Distance(); break; - + case eJumpSettingsType.GMTK_GameMakersToolKit: - timeToJumpApex = scale( 1, 10, 0.2f, 1.25f, jumpDurationFromVideo ); + CalculateDerivedJumpValues_GMTK(); break; } - + CalculateJumpLine(); } - + static private int jumpLineResolution = 64; // NOTE: This must be a positive even number internal Vector3[] jumpLinePoints; - [HideIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )] - [BoxGroup( "CGSK Derived Jump Properties" )][SerializeField][XnTools.ReadOnly] - internal Vector2 maxJumpDistHeight; - internal Vector3[] jumpStartMidEndPoints; + internal List<Vector3> minJumpLinePoints; + // [HideIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )] + internal Vector3[] jumpStartMidEndPoints, minJumpStartMidEndPoints; + internal Vector2 minTimeApexFull; + internal void CalculateJumpLine() { if ( jumpSettingsType == eJumpSettingsType.GMTK_GameMakersToolKit ) { jumpLinePoints = null; @@ -142,6 +193,7 @@ internal void CalculateJumpLine() { } maxJumpDistHeight = Vector2.zero; Vector3 acc = new Vector3( 0, jumpGrav.up, 0 ); + Vector3 newAcc = acc; Vector3 p = Vector3.zero; jumpLinePoints = new Vector3[jumpLineResolution]; jumpStartMidEndPoints = new Vector3[3]; @@ -150,10 +202,10 @@ internal void CalculateJumpLine() { Vector3 v = new Vector3( maxSpeed, jumpVel.up, 0 ); int numSteps = jumpLineResolution / 2; // Jumping Up - float timeStep = jumpDuration.up / (float) numSteps; + float timeStepUp = jumpDuration.up / (float) numSteps; int i = 1; for ( ; i <= jumpLineResolution / 2; i++ ) { - SimplifiedVelocityVerletIntegration(ref p, ref v, acc, timeStep); + SimplifiedVelocityVerletIntegration( ref p, ref v, acc, timeStepUp ); // p.x += v.x * timeStep; // v.y += jumpGrav.up * timeStep; // p.y += v.y * timeStep; @@ -163,9 +215,9 @@ internal void CalculateJumpLine() { maxJumpDistHeight.y = p.y; // Jumping Down acc.y = jumpGrav.down; - timeStep = jumpDuration.down / (float) (numSteps-1); + float timeStepDown = jumpDuration.down / (float) ( numSteps - 1 ); for ( ; i < jumpLineResolution; i++ ) { - SimplifiedVelocityVerletIntegration(ref p, ref v, acc, timeStep); + SimplifiedVelocityVerletIntegration( ref p, ref v, acc, timeStepDown ); // p.x += v.x * timeStep; // v.y += jumpGrav.down * timeStep; // p.y += v.y * timeStep; @@ -173,15 +225,93 @@ internal void CalculateJumpLine() { } jumpStartMidEndPoints[2] = p; maxJumpDistHeight.x = p.x; + + // Calculate jump line if jump button is released immediately + if ( !jumpSettingsVariableHeight.useVariableJumpHeight ) { + minJumpLinePoints = null; + return; + } + minJumpDistHeight = Vector2.zero; + minTimeApexFull = Vector2.zero; + v = new Vector3( maxSpeed, jumpVel.up, 0 ); + newAcc = acc = new Vector3( 0, jumpGrav.up, 0 ); + p = Vector3.zero; + minJumpLinePoints = new List<Vector3>(); + minJumpStartMidEndPoints = new Vector3[3]; + minJumpLinePoints.Add( p ); + minJumpStartMidEndPoints[0] = p; + // We'll use timeStepUp, the timeStep from jumpDuration.up + i = 0; + float time = 0; + int minJumpPhase = 0; // 0=up and button held, 1=button released, 2=down + Vector3 debugAcc = new Vector3(acc.y, -1, -1); + float minTimeStep = 0.01f; + for ( ; time < 10; i++ ) { // time<10 or i<jumpLineResolution to keep it from getting out of hand + time += minTimeStep; + if ( minJumpPhase == 0 ) { // Up and button held + if ( v.y <= 0 || time >= jumpSettingsVariableHeight.minJumpButtonHeldTime ) { + if ( v.y <= 0 || jumpSettingsVariableHeight.upwardVelocityZeroing ) { + v.y = 0; + newAcc.y = jumpGrav.down; + debugAcc.z = newAcc.y; + minJumpStartMidEndPoints[1] = p; + minJumpPhase = 2; + minTimeApexFull.x = time; + } else { + newAcc.y = jumpGrav.up * jumpSettingsVariableHeight.gravUpMultiplierOnRelease; + debugAcc.y = newAcc.y; + minJumpPhase = 1; + } + } + } else if ( minJumpPhase == 1 ) { // Still up, but button has been released + if ( v.y <= 0 ) { // We're starting down + newAcc.y = jumpGrav.down; + debugAcc.z = newAcc.y; + minJumpStartMidEndPoints[1] = p; + minJumpDistHeight.y = p.y; + minJumpPhase = 2; + minTimeApexFull.x = time; + } + } else { // minJumpPhase == 2 // Moving down + if ( p.y < 0 ) break; // This shouldn't ever happen because it should be caught after last SVVI call in minJumpPhase 2 + } + + VelocityVerletIntegration( ref p, ref v, ref acc, newAcc, minTimeStep ); + if ( p.y < 0 ) { + p.y = 0; // This is a fudging of the numbers, but it should be ok. - JGB 2023-03-12 + minJumpStartMidEndPoints[2] = p; + minJumpLinePoints.Add( p ); + minJumpDistHeight.x = p.x; + minTimeApexFull.y = time; + break; + } + minJumpLinePoints.Add( p ); + } + if (DEBUG_JUMP_LINE_CALCULATION) Debug.LogWarning( + $"gUMOR: {jumpSettingsVariableHeight.gravUpMultiplierOnRelease:0.##}" + + $"\tp0acc: {debugAcc.x:#,0.##}" + + $"\tp1acc: {debugAcc.y:#,0.##}" + + $"\tp2acc: {debugAcc.z:#,0.##}"); + } - + // NOTE: Simplified Velocity Verlet Integration from Math for Game Programmers: Building a Better Jump // https://www.youtube.com/watch?v=hG9SzQxaCm8&t=23m2s - void SimplifiedVelocityVerletIntegration( ref Vector3 pos, ref Vector3 vel, Vector3 acc, float deltaTime ) { - pos += vel * deltaTime + acc * ( 0.5f * deltaTime * deltaTime ); // pos += vel*dT + 1/2*acc*dT*dT + void SimplifiedVelocityVerletIntegration( ref Vector3 pos, ref Vector3 vel, Vector3 acc, + float deltaTime ) { + pos += vel * deltaTime + + acc * ( 0.5f * deltaTime * deltaTime ); // pos += vel*dT + 1/2*acc*dT*dT vel += acc * deltaTime; } + void VelocityVerletIntegration( ref Vector3 pos, ref Vector3 vel, ref Vector3 acc, + Vector3 newAcc, float deltaTime ) { + pos += vel * deltaTime + + acc * ( 0.5f * deltaTime * deltaTime ); // pos += vel*dT + 1/2*acc*dT*dT + vel += (acc + newAcc) * (0.5f * deltaTime); + acc = newAcc; + } + public float scale( float OldMin, float OldMax, float NewMin, float NewMax, float OldValue ) { float OldRange = ( OldMax - OldMin ); float NewRange = ( NewMax - NewMin ); @@ -193,40 +323,46 @@ public float scale( float OldMin, float OldMax, float NewMin, float NewMax, floa [System.Serializable] public class CGSK_JumpSettings_Time { - [Header("Classic Game Starter Kit - Time Jump Settings")] + [Header( "Classic Game Starter Kit - Time Jump Settings" )] [Tooltip( "The full duration of the shortest jump possible (by tapping the button)" )] public float fullJumpDurationMin = 0.5f; [Tooltip( "The full duration of the longest jump possible (by holding the button)" )] public float fullJumpDurationMax = 1f; - [Tooltip("The fraction of the jump that is going up")] - [Range( 0.05f, 0.95f )] public float jumpApexFraction = 0.6f; + [Tooltip( "The fraction of the jump that is going up" )] + [Range( 0.05f, 0.95f )] + public float jumpApexFraction = 0.6f; } - - private void CalculateDerivedCGSKJumpValues_Time() { + + private void CalculateDerivedJumpValues_Time() { jumpDuration.up = jumpSettingsTime.fullJumpDurationMax * jumpSettingsTime.jumpApexFraction; jumpDuration.down = jumpSettingsTime.fullJumpDurationMax - jumpDuration.up; - jumpVel.up = jumpHeight * 2 / jumpDuration.up; - jumpVel.down = jumpHeight * 2 / jumpDuration.down; // This is the velocity when the character lands. - GB 2023-03-10 - jumpGrav.up = -2 * jumpHeight / ( jumpDuration.up * jumpDuration.up ); - jumpGrav.down = -2 * jumpHeight / ( jumpDuration.down * jumpDuration.down ); - jumpDist.up = jumpDuration.up * maxSpeed; + jumpVel.up = jumpHeight * 2 / jumpDuration.up; + jumpVel.down = + jumpHeight * 2 / + jumpDuration.down; // This is the velocity when the character lands. - GB 2023-03-10 + jumpGrav.up = -2 * jumpHeight / ( jumpDuration.up * jumpDuration.up ); + jumpGrav.down = -2 * jumpHeight / ( jumpDuration.down * jumpDuration.down ); + jumpDist.up = jumpDuration.up * maxSpeed; jumpDist.down = jumpDuration.down * maxSpeed; + + jumpSettingsVariableHeight = jumpSettingsVariableHeightCGSK; } - + [System.Serializable] public class CGSK_JumpSettings_Distance { - [Header("Classic Game Starter Kit - Distance Jump Settings")] + [Header( "Classic Game Starter Kit - Distance Jump Settings" )] [Tooltip( "The horizontal distance at full run speed of the shortest jump possible (by tapping the button)" )] public float fullJumpDistanceMin = 0.5f; [Tooltip( "The horizontal distance at full run speed of the longest jump possible (by holding the button)" )] public float fullJumpDistanceMax = 1f; - [Tooltip("The fraction of the jump that is going up")] - [Range( 0.05f, 0.95f )] public float jumpApexFraction = 0.6f; + [Tooltip( "The fraction of the jump that is going up" )] + [Range( 0.05f, 0.95f )] + public float jumpApexFraction = 0.6f; } - - private void CalculateDerivedCGSKJumpValues_Distance() { + + private void CalculateDerivedJumpValues_Distance() { jumpDist.up = jumpSettingsDistance.fullJumpDistanceMax * - jumpSettingsDistance.jumpApexFraction; + jumpSettingsDistance.jumpApexFraction; jumpDist.down = jumpSettingsDistance.fullJumpDistanceMax - jumpDist.up; // Th = Xh / Vh jumpDuration.up = jumpDist.up / maxSpeed; @@ -235,8 +371,80 @@ private void CalculateDerivedCGSKJumpValues_Distance() { jumpVel.up = 2 * jumpHeight * maxSpeed / jumpDist.up; jumpVel.down = 2 * jumpHeight * maxSpeed / jumpDist.down; // G = -2h(Vx*Vx) / (Xh*Xh) - jumpGrav.up = -2 * jumpHeight * ( maxSpeed * maxSpeed ) / ( jumpDist.up * jumpDist.up ); - jumpGrav.down = -2 * jumpHeight * ( maxSpeed * maxSpeed ) / ( jumpDist.down * jumpDist.down ); + jumpGrav.up = -2 * jumpHeight * ( maxSpeed * maxSpeed ) / ( jumpDist.up * jumpDist.up ); + jumpGrav.down = -2 * jumpHeight * ( maxSpeed * maxSpeed ) / + ( jumpDist.down * jumpDist.down ); + + jumpSettingsVariableHeight = jumpSettingsVariableHeightCGSK; + } + + [System.Serializable] + public class CGSK_JumpSettings_VariableHeight { + [Header( "Classic Game Starter Kit - Variable Jump Height" )] + + [Tooltip("Should the character jump differently based on how long the jump button is held?")] + public bool useVariableJumpHeight = true; + [Tooltip( "Should upward velocity be set to 0 when the jump button is released? (Like in Metroid for NES)" )] + public bool upwardVelocityZeroing = false; + [Tooltip( "The minimum amount of time that the jump button will be forced to be held" + + " Set this to 0.1f if you want to ensure that the player can't release the button before 0.1 seconds have passed." + + " 0.05f is the default value because 100ms is a typical shortest time for a button to be held." )] + [Range( 0.05f, 2f )] + public float minJumpButtonHeldTime = 0.05f; // 100ms is a typical shortest time for a button to be held.; + [Tooltip( "The multiplier applied to jumpGrav.up to slow upward velocity faster after the jump button has been released." + + "\nIf this is set to 1 and upwardVelocityZeroing=false, then it is the same as useVariableJumpHeight=false." + + "\nIf this were extremely high, it would similar to upwardVelocityZeroing=true." )] + [Range(1,20)] + public float gravUpMultiplierOnRelease = 1; + } + + // NOTE: CGSK jummp math comes from Math for Game Programmers: Building a Better Jump + // https://www.youtube.com/watch?v=hG9SzQxaCm8&t=9m35s & https://www.youtube.com/watch?v=hG9SzQxaCm8&t=784s + // Th = Xh/Vx V0 = 2H / Th G = -2H / (Th * Th) V0 = 2HVx / Xh G = -2H(Vx*Vx) / (Xh*Xh) + + [System.Serializable] + public class GMTK_JumpSettings { + [Header( "Jump Settings - GameMakers ToolKit" )] + [SerializeField, Range( 1f, 10f )] + [Tooltip( "This number is converted from the rather meaningless [1..10] to a time to jump apex of [0.2sec..1.25sec]" )] + public float jumpDuration = 5; + [SerializeField, Range( 1f, 10f )] + [Tooltip( "Gravity multiplier to apply when coming down" )] + public float downGravity = 6.17f; + public bool doubleJump = false; + [Tooltip( "Should the character drop when you let go of jump?" )] + public bool variableJumpHeight; + [SerializeField, Range( 1f, 10f )] + [ShowIf("useVariableJumpHeight")] + [Tooltip( "Gravity multiplier when you let go of jump and character is still moving up" )] + public float jumpCutOff; + } + + void CalculateDerivedJumpValues_GMTK() { + // Jump Duration up is set by the [1..10] value from the GMTK app + jumpDuration.up = scale( 1, 10, 0.2f, 1.25f, jumpSettingsGMTK.jumpDuration ); + // These are the only derived values where the initial gravity is based on Physics2D.gravity - JGB 2023-03-12 + jumpGrav.up = Physics2D.gravity.y; + // downGravity is a multiplier on the up gravity + jumpGrav.down = jumpGrav.up * jumpSettingsGMTK.downGravity; + // Calculate jumpDuration.down from G = -2H / (Th * Th), which solves for Th to Th = √(-2H / G) + jumpDuration.down = Mathf.Sqrt( -2 * jumpHeight / jumpGrav.down ); + // Calculate jumpVel from V = 2H / Th + jumpVel.up = 2 * jumpHeight / jumpDuration.up; + jumpVel.down = 2 * jumpHeight / jumpDuration.down; + // Calculate jumpDist from Th = Xh/Vx which is Vx = Xh/Th + jumpDist.up = maxSpeed / jumpDuration.up; + jumpDist.down = maxSpeed / jumpDuration.down; + + // Set double jump + jumpsBetweenGrounding = jumpSettingsGMTK.doubleJump ? 2 : 1; + + jumpSettingsVariableHeightGMTK.useVariableJumpHeight = jumpSettingsGMTK.variableJumpHeight; + jumpSettingsVariableHeightGMTK.gravUpMultiplierOnRelease = jumpSettingsGMTK.jumpCutOff; + jumpSettingsVariableHeightGMTK.upwardVelocityZeroing = false; + jumpSettingsVariableHeightGMTK.minJumpButtonHeldTime = 0.05f; // 100ms is a typical shortest time for a button to be held. + + jumpSettingsVariableHeight = jumpSettingsVariableHeightGMTK; } } diff --git a/Assets/_Classic Game Starter Kit/__Scripts/XnTools/InfoComponent.cs b/Assets/_Classic Game Starter Kit/__Scripts/XnTools/InfoComponent.cs index 8d5928a61d86ea1db406e91766acebeee826dc2a..f116eafaf5b9d6b946bf243df826e5938fba428b 100644 --- a/Assets/_Classic Game Starter Kit/__Scripts/XnTools/InfoComponent.cs +++ b/Assets/_Classic Game Starter Kit/__Scripts/XnTools/InfoComponent.cs @@ -25,9 +25,9 @@ public class InfoComponent : MonoBehaviour { //[CanEditMultipleObjects] public class InfoComponentEditor : Editor { static private bool TURN_ON_DEFAULT_INSPECTORS = false; + static private bool DEBUG_GUI_ERRORS = false; - private const int delayGUIStart = 2; - private int delayGUI = 0; + private double failedGUITimeStamp = -1f; //static float kSpace = 16f; InfoComponent info; @@ -51,7 +51,6 @@ public class InfoComponentEditor : Editor { void OnEnable() { - delayGUI = delayGUIStart; info = (InfoComponent)target; Init(); } @@ -67,13 +66,15 @@ static void EnableEdit(MenuCommand command) { } public override void OnInspectorGUI() { + if ( failedGUITimeStamp == EditorApplication.timeSinceStartup ) return; if (info == null) { info = (InfoComponent)target; Init(); } - if ( !m_Initialized ) return; - if ( delayGUI > 0 ) { - delayGUI--; + if ( !m_Initialized ) { + failedGUITimeStamp = EditorApplication.timeSinceStartup; + if ( DEBUG_GUI_ERRORS ) + Debug.LogWarning( $"InfoComponent Init() failed but it didn't cause an error message!)" ); //at {failedGUITimeStamp:#,0.###}."); return; } diff --git a/Assets/_Classic Game Starter Kit/___Demo Level/CGSK_Settings_SuperMarioBros.asset b/Assets/_Classic Game Starter Kit/___Demo Level/CGSK_Settings_SuperMarioBros.asset index 200452f5c4d0304ca6a6f838b3fa6af919d7ea4c..6dd3fc6ebf45d24b24847808828a51b1cbc68a77 100644 --- a/Assets/_Classic Game Starter Kit/___Demo Level/CGSK_Settings_SuperMarioBros.asset +++ b/Assets/_Classic Game Starter Kit/___Demo Level/CGSK_Settings_SuperMarioBros.asset @@ -32,6 +32,16 @@ MonoBehaviour: fullJumpDistanceMin: 0.5 fullJumpDistanceMax: 5.5 jumpApexFraction: 0.6 + jumpSettingsVariableHeightCGSK: + useVariableJumpHeight: 1 + upwardVelocityZeroing: 0 + minJumpButtonHeldTime: 0.05 + gravUpMultiplierOnRelease: 2.54 + jumpSettingsVariableHeightGMTK: + useVariableJumpHeight: 1 + upwardVelocityZeroing: 0 + minJumpButtonHeldTime: 0.05 + gravUpMultiplierOnRelease: 1 jumpDist: up: 3.3000002 down: 2.1999998 @@ -44,16 +54,18 @@ MonoBehaviour: jumpGrav: up: -22.22222 down: -50.000008 - jumpDurationFromVideo: 5 - timeToJumpApex: 0.6666666 - upwardMovementMultiplier: 1 - downwardMovementMultiplier: 1.4 - maxAirJumps: 0 - variablejumpHeight: 1 - jumpCutOff: 2.5 + maxJumpDistHeight: {x: 5.499999, y: 4} + minJumpDistHeight: {x: 3.0800004, y: 1.9994661} + jumpSettingsGMTK: + jumpDuration: 5 + downGravity: 6.17 + doubleJump: 0 + variableJumpHeight: 0 + jumpCutOff: 0 speedLimit: 26.45 coyoteTime: 0.15 jumpBuffer: 0.15 + jumpsBetweenGrounding: 1 squashAndStretch: 0 jumpSquashSettings: {x: 0, y: 0, z: 0} landSquashSettings: {x: 0, y: 0, z: 0} @@ -63,4 +75,3 @@ MonoBehaviour: leanForward: 0 maxTilt: 0 tiltSpeed: 0 - maxJumpDistHeight: {x: 5.499999, y: 4}