Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NaughtyAttributes;
#if UNITY_EDITOR
using UnityEditor;
#endif
/// <summary>
/// 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 )]
public class Character_Settings_SO : ScriptableObject {
[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;
[Header( "Movement Options" )]
[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;
[ShowIf("jumpSettingsType", eJumpSettingsType.CGSK_Time)]
public CGSK_JumpSettings_Time jumpSettingsTime;
[ShowIf("jumpSettingsType", eJumpSettingsType.CGSK_Distance)]
public CGSK_JumpSettings_Distance jumpSettingsDistance;
// [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" )]
public CSSO_FloatUpDown jumpDist, jumpDuration, jumpVel, jumpGrav;
[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;
[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;
[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;
private void OnValidate() {
switch ( jumpSettingsType ) {
case eJumpSettingsType.CGSK_Time:
CalculateDerivedCGSKJumpValues_Time();
break;
case eJumpSettingsType.CGSK_Distance:
CalculateDerivedCGSKJumpValues_Distance();
break;
case eJumpSettingsType.GMTK_GameMakersToolKit:
timeToJumpApex = scale( 1, 10, 0.2f, 1.25f, jumpDurationFromVideo );
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 void CalculateJumpLine() {
if ( jumpSettingsType == eJumpSettingsType.GMTK_GameMakersToolKit ) {
jumpLinePoints = null;
return;
}
maxJumpDistHeight = Vector2.zero;
Vector3 acc = new Vector3( 0, jumpGrav.up, 0 );
Vector3 p = Vector3.zero;
jumpLinePoints = new Vector3[jumpLineResolution];
jumpStartMidEndPoints = new Vector3[3];
jumpLinePoints[0] = p;
jumpStartMidEndPoints[0] = p;
Vector3 v = new Vector3( maxSpeed, jumpVel.up, 0 );
int numSteps = jumpLineResolution / 2;
// Jumping Up
float timeStep = jumpDuration.up / (float) numSteps;
int i = 1;
for ( ; i <= jumpLineResolution / 2; i++ ) {
SimplifiedVelocityVerletIntegration(ref p, ref v, acc, timeStep);
// p.x += v.x * timeStep;
// v.y += jumpGrav.up * timeStep;
// p.y += v.y * timeStep;
jumpLinePoints[i] = p;
}
jumpStartMidEndPoints[1] = p;
maxJumpDistHeight.y = p.y;
// Jumping Down
acc.y = jumpGrav.down;
timeStep = jumpDuration.down / (float) (numSteps-1);
for ( ; i < jumpLineResolution; i++ ) {
SimplifiedVelocityVerletIntegration(ref p, ref v, acc, timeStep);
// p.x += v.x * timeStep;
// v.y += jumpGrav.down * timeStep;
// p.y += v.y * timeStep;
jumpLinePoints[i] = p;
}
jumpStartMidEndPoints[2] = p;
maxJumpDistHeight.x = p.x;
}
// 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
vel += acc * deltaTime;
}
public float scale( float OldMin, float OldMax, float NewMin, float NewMax, float OldValue ) {
float OldRange = ( OldMax - OldMin );
float NewRange = ( NewMax - NewMin );
float NewValue = ( ( ( OldValue - OldMin ) * NewRange ) / OldRange ) + NewMin;
return ( NewValue );
}
[System.Serializable]
public class CGSK_JumpSettings_Time {
[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;
}
private void CalculateDerivedCGSKJumpValues_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;
jumpDist.down = jumpDuration.down * maxSpeed;
}
[System.Serializable]
public class CGSK_JumpSettings_Distance {
[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;
}
private void CalculateDerivedCGSKJumpValues_Distance() {
jumpDist.up = jumpSettingsDistance.fullJumpDistanceMax *
jumpSettingsDistance.jumpApexFraction;
jumpDist.down = jumpSettingsDistance.fullJumpDistanceMax - jumpDist.up;
// Th = Xh / Vh
jumpDuration.up = jumpDist.up / maxSpeed;
jumpDuration.down = jumpDist.down / maxSpeed;
// Vy = 2hVh / Xh
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 );
}
}
[System.Serializable]
public class CSSO_FloatUpDown {
public float up, down;
}
#if UNITY_EDITOR
[CustomPropertyDrawer( typeof( CSSO_FloatUpDown ) )]
public class CSSO_FloatUpDown_Drawer : PropertyDrawer {
static public GUIStyle styleLabelGray = null, styleLabelGrayBold = null;
// SerializedProperty m_stat;
// Draw the property inside the given rect
public override void OnGUI( Rect position, SerializedProperty property, GUIContent label ) {
// Init the SerializedProperty fields
//if ( m_show == null ) m_show = property.FindPropertyRelative( "show" );
//if ( m_recNum == null ) m_recNum = property.FindPropertyRelative( "recNum" );
//if ( m_playerName == null ) m_playerName = property.FindPropertyRelative( "playerName" );
//if ( m_dateTime == null ) m_dateTime = property.FindPropertyRelative( "dateTime" );
CSSO_FloatUpDown fud = fieldInfo.GetValue( property.serializedObject.targetObject ) as CSSO_FloatUpDown;
// Using BeginProperty / EndProperty on the parent property means that
// prefab override logic works on the entire property.
EditorGUI.BeginProperty( position, label, property );
// Draw label
//position = EditorGUI.PrefixLabel( position, GUIUtility.GetControlID( FocusType.Passive ), GUIContent.none );// label );
if ( styleLabelGray == null) {
styleLabelGray = new GUIStyle( EditorStyles.label );
styleLabelGray.richText = true;
}
string colorString = "#606060ff";
// Don't make child fields be indented
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 1;
EditorGUI.LabelField(position, $"<b><color={colorString}>{property.displayName}</color></b>", styleLabelGray );
EditorGUI.indentLevel = 8;
EditorGUI.LabelField( position, $"<color={colorString}>up: {fud.up:0.0###}</color>", styleLabelGray );
EditorGUI.indentLevel = 14;
EditorGUI.LabelField( position, $"<color={colorString}>down: {fud.down:0.0###}</color>", styleLabelGray );
// Set indent back to what it was
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
}
}
// This was a bad idea because the CSSO doesn't know who the character is. Moved to CharacterJump
// [CustomEditor( typeof(Character_Settings_SO) )]
// public class CSSO_Editor : Editor {
// private Character_Settings_SO csso;
//
// private void OnEnable() {
// csso = (Character_Settings_SO) target;
// }
//
//
// private void OnSceneGUI() {
// if ( csso == null || csso.jumpLinePoints == null ) return;
//
// // Handles.matrix = Matrix4x4.Translate(); // Not needed because jump will be shown at origin.
// Handles.color = Color.green;
// Handles.DrawAAPolyLine(4, csso.jumpLinePoints);
// }
// }
#endif