-
Notifications
You must be signed in to change notification settings - Fork 459
Expand file tree
/
Copy pathMoverScriptNoRigidbody.cs
More file actions
380 lines (335 loc) · 13.6 KB
/
MoverScriptNoRigidbody.cs
File metadata and controls
380 lines (335 loc) · 13.6 KB
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
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
using System;
using Unity.Netcode.Components;
using Unity.Netcode;
using UnityEngine;
using Random = UnityEngine.Random;
using Debug = UnityEngine.Debug;
#region MoverScriptNoRigidbody Custom Editor
#if UNITY_EDITOR
using Unity.Netcode.Editor;
using UnityEditor;
/// <summary>
/// The custom editor for the <see cref="MoverScriptNoRigidbody"/> component.
/// </summary>
[CustomEditor(typeof(MoverScriptNoRigidbody), true)]
[CanEditMultipleObjects]
public class MoverScriptNoRigidbodyEditor : NetworkTransformEditor
{
private SerializedProperty m_Radius;
private SerializedProperty m_Increment;
private SerializedProperty m_RotateSpeed;
private SerializedProperty m_MovementSpeed;
private SerializedProperty m_AirSpeedFactor;
private SerializedProperty m_Gravity;
private SerializedProperty m_ContinualChildMotion;
public override void OnEnable()
{
m_Radius = serializedObject.FindProperty(nameof(MoverScriptNoRigidbody.SpawnRadius));
m_Increment = serializedObject.FindProperty(nameof(MoverScriptNoRigidbody.Increment));
m_RotateSpeed = serializedObject.FindProperty(nameof(MoverScriptNoRigidbody.RotationSpeed));
m_MovementSpeed = serializedObject.FindProperty(nameof(MoverScriptNoRigidbody.MovementSpeed));
m_AirSpeedFactor = serializedObject.FindProperty(nameof(MoverScriptNoRigidbody.AirSpeedFactor));
m_Gravity = serializedObject.FindProperty(nameof(MoverScriptNoRigidbody.Gravity));
m_ContinualChildMotion = serializedObject.FindProperty(nameof(MoverScriptNoRigidbody.ContinualChildMotion));
base.OnEnable();
}
private void DisplayerMoverScriptNoRigidbodyProperties()
{
EditorGUILayout.PropertyField(m_Radius);
EditorGUILayout.PropertyField(m_Increment);
EditorGUILayout.PropertyField(m_RotateSpeed);
EditorGUILayout.PropertyField(m_MovementSpeed);
EditorGUILayout.PropertyField(m_AirSpeedFactor);
EditorGUILayout.PropertyField(m_Gravity);
EditorGUILayout.PropertyField(m_ContinualChildMotion);
}
public override void OnInspectorGUI()
{
var moverScriptNoRigidbody = target as MoverScriptNoRigidbody;
void SetExpanded(bool expanded) { moverScriptNoRigidbody.MoverScriptNoRigidbodyExpanded = expanded; };
DrawFoldOutGroup<MoverScriptNoRigidbody>(moverScriptNoRigidbody.GetType(), DisplayerMoverScriptNoRigidbodyProperties, moverScriptNoRigidbody.MoverScriptNoRigidbodyExpanded, SetExpanded);
base.OnInspectorGUI();
}
}
#endif
#endregion
/// <summary>
/// The player controller for the player prefab
/// </summary>
public class MoverScriptNoRigidbody : NetworkTransform
{
#if UNITY_EDITOR
// Inspector view expand/collapse settings for this derived child class
[HideInInspector]
public bool MoverScriptNoRigidbodyExpanded;
#endif
private static bool s_EnablePlayerParentingText = true;
[Tooltip("Radius range a player will spawn within.")]
[Range(1.0f, 40.0f)]
public float SpawnRadius = 10.0f;
[Range(0.001f, 10.0f)]
public float Increment = 1.0f;
[Tooltip("The rotation speed multiplier.")]
[Range(0.01f, 2.0f)]
public float RotationSpeed = 1.0f;
[Tooltip("The forward movement speed.")]
[Range(0.01f, 30.0f)]
public float MovementSpeed = 15.0f;
[Tooltip("The jump launching speed.")]
[Range(1.0f, 20f)]
public float JumpSpeed = 10.0f;
[Tooltip("Determines how much the player's motion is applied when in the air.")]
[Range(0.01f, 1.0f)]
public float AirSpeedFactor = 0.35f;
[Range(-20.0f, 20.0f)]
public float Gravity = -9.8f;
[Tooltip("When enabled, the child spheres will continually move. When disabled, the child spheres will only move when the player moves.")]
public bool ContinualChildMotion = true;
private TextMesh m_ParentedText;
private PlayerColor m_PlayerColor;
private float m_JumpDelay;
private Vector3 m_WorldMotion = Vector3.zero;
private Vector3 m_CameraOriginalPosition;
private Quaternion m_CameraOriginalRotation;
private CharacterController m_CharacterController;
private PlayerBallMotion m_PlayerBallMotion;
public event Action<bool> NotifySpawnStatusChanged;
protected override void Awake()
{
m_ParentedText = GetComponentInChildren<TextMesh>();
m_ParentedText?.gameObject.SetActive(false);
m_PlayerColor = GetComponent<PlayerColor>();
m_PlayerBallMotion = GetComponentInChildren<PlayerBallMotion>();
base.Awake();
}
/// <summary>
/// Invoked after being instantiated, we can do other pre-spawn related
/// initilization tasks here.
/// </summary>
/// <remarks>
/// This provides you with a reference to the current <see cref="NetworkManager"/>
/// since that is not set on the <see cref="NetworkBehaviour"/> until it is spawned.
/// </remarks>
/// <param name="networkManager"></param>
protected override void OnNetworkPreSpawn(ref NetworkManager networkManager)
{
m_CharacterController = GetComponent<CharacterController>();
// By default, we always disable the CharacterController and only enable it on the
// owner/authority side.
m_CharacterController.enabled = false;
base.OnNetworkPreSpawn(ref networkManager);
}
/// <summary>
/// We are using post spawn to handle any final spawn initializations.
/// At this point we know all NetworkBehaviours on this instance have been spawned.
/// </summary>
protected override void OnNetworkPostSpawn()
{
// Authority of this object sends local notifications to any non-networkbehaviour subscribers
NotifySpawnStatusChanged?.Invoke(true);
m_CharacterController.enabled = CanCommitToTransform;
if (CanCommitToTransform)
{
m_PlayerBallMotion.SetContinualMotion(ContinualChildMotion);
Random.InitState((int)DateTime.Now.Ticks);
transform.position += new Vector3(Random.Range(-SpawnRadius, SpawnRadius), 1.25f, Random.Range(0, SpawnRadius));
SetState(transform.position, null, null, false);
if (IsLocalPlayer)
{
NetworkObject.DontDestroyWithOwner = false;
m_CameraOriginalPosition = Camera.main.transform.position;
m_CameraOriginalRotation = Camera.main.transform.rotation;
Camera.main.transform.SetParent(transform, false);
}
}
if (NetworkObject.IsPlayerObject)
{
gameObject.name = $"Player-{OwnerClientId}";
}
m_ParentedText?.gameObject.SetActive(true);
#if !DEDICATED_SERVER
UpdateParentedText();
#endif
base.OnNetworkPostSpawn();
}
public override void OnNetworkDespawn()
{
// Notify any client or server specific component that this instance has despawned.
NotifySpawnStatusChanged?.Invoke(false);
if (IsLocalPlayer)
{
m_CharacterController.enabled = false;
Camera.main.transform.SetParent(null, false);
Camera.main.transform.SetPositionAndRotation(m_CameraOriginalPosition, m_CameraOriginalRotation);
}
base.OnNetworkDespawn();
}
/// <summary>
/// Bypass NetworkTransform's OnNetworkObjectParentChanged
/// </summary>
public override void OnNetworkObjectParentChanged(NetworkObject parentNetworkObject)
{
if (parentNetworkObject != null)
{
Debug.Log($"Parented under {parentNetworkObject.name}");
}
#if !DEDICATED_SERVER
UpdateParentedText();
#endif
base.OnNetworkObjectParentChanged(parentNetworkObject);
}
/// <summary>
/// This method handles both client-server and distributed authority network topologies
/// client-server: If we are not the server, then we need to send an Rpc to the server to handle parenting since the Character controller is disabled on the server for all client CharacterControllers (i.e. won't trigger).
/// distributed authority: If we are the authority, then handle parenting locally.
/// </summary>
/// <param name="parent"></param>
public void SetParent(NetworkObject parent)
{
if ((!NetworkManager.DistributedAuthorityMode && (IsServer || (NetworkObject.AllowOwnerToParent && IsOwner))) || (NetworkManager.DistributedAuthorityMode && HasAuthority))
{
if (parent != null)
{
NetworkObject.TrySetParent(parent);
}
else
{
NetworkObject.TryRemoveParent();
}
}
else if (!NetworkManager.DistributedAuthorityMode && !IsServer)
{
SetParentRpc(new NetworkObjectReference(parent));
}
}
[Rpc(SendTo.Server)]
public void SetParentRpc(NetworkObjectReference parentReference, RpcParams rpcParams = default)
{
var parent = (NetworkObject)null;
parentReference.TryGet(out parent, NetworkManager);
if (parent != null)
{
NetworkObject.TrySetParent(parent);
}
else
{
NetworkObject.TryRemoveParent();
}
}
private void Update()
{
if (!IsSpawned || !CanCommitToTransform)
{
return;
}
ApplyInput();
}
private Vector3 m_PushMotion = Vector3.zero;
/// <summary>
/// Since <see cref="CharacterController"/> has issues with collisions and rotating bodies,
/// we have to simulate the collision using triggers.
/// </summary>
/// <remarks>
/// <see cref="TriggerPush"/>
/// </remarks>
/// <param name="normal">direction to push away from</param>
public void PushAwayFrom(Vector3 normal)
{
m_PushMotion += normal * MovementSpeed * 0.10f * Time.deltaTime;
}
/// <summary>
/// Handles player input
/// </summary>
private void ApplyInput()
{
// Simple rotation:
// Since the forward vector is perpendicular to the right vector of the player, we can just
// apply the +/- value to our forward direction and lerp our right vector towards that direction
// in order to get a reasonably smooth rotation.
var rotation = transform.forward;
m_WorldMotion = Vector3.Lerp(m_WorldMotion, m_CharacterController.isGrounded ? Vector3.zero : Vector3.up * Gravity, Time.deltaTime * 2f);
var motion = m_WorldMotion * Time.deltaTime + m_PushMotion;
var moveMotion = 0.0f;
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
{
motion += transform.forward * MovementSpeed * Time.deltaTime * (m_CharacterController.isGrounded ? 1.0f : AirSpeedFactor);
moveMotion = 1.0f;
m_CharacterController.Move(motion);
}
if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow))
{
motion += (transform.forward * -MovementSpeed) * Time.deltaTime * (m_CharacterController.isGrounded ? 1.0f : AirSpeedFactor);
moveMotion = -1.0f;
m_CharacterController.Move(motion);
}
if (!m_CharacterController.isGrounded || m_JumpDelay > Time.realtimeSinceStartup || m_PushMotion.magnitude > 0.01f)
{
m_CharacterController.Move(motion);
}
if (Input.GetKeyDown(KeyCode.Space) && m_CharacterController.isGrounded)
{
m_JumpDelay = Time.realtimeSinceStartup + 0.5f;
m_WorldMotion = motion + Vector3.up * JumpSpeed;
}
if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
{
transform.right = Vector3.Lerp(transform.right, rotation * RotationSpeed, Time.deltaTime).normalized;
}
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
{
transform.right = Vector3.Lerp(transform.right, rotation * -RotationSpeed, Time.deltaTime).normalized;
}
// Enabled/Disable player name, transform space, and parent TextMesh
if (Input.GetKeyDown(KeyCode.P))
{
s_EnablePlayerParentingText = !s_EnablePlayerParentingText;
}
if (Input.GetKeyDown(KeyCode.C))
{
ContinualChildMotion = !ContinualChildMotion;
m_PlayerBallMotion.SetContinualMotion(ContinualChildMotion);
}
m_PushMotion = Vector3.Lerp(m_PushMotion, Vector3.zero, 0.35f);
m_PlayerBallMotion.HasMotion(moveMotion);
}
#if !DEDICATED_SERVER
/// <summary>
/// Updates player TextMesh relative to each client's camera view
/// </summary>
private void OnGUI()
{
if (m_ParentedText != null)
{
if (m_ParentedText.gameObject.activeInHierarchy != s_EnablePlayerParentingText)
{
m_ParentedText.gameObject.SetActive(s_EnablePlayerParentingText);
}
if (s_EnablePlayerParentingText)
{
var position = Camera.main.transform.position;
position.y = m_ParentedText.transform.position.y;
m_ParentedText.transform.LookAt(position, transform.up);
m_ParentedText.transform.forward = -m_ParentedText.transform.forward;
}
}
}
/// <summary>
/// Updates the contents of the parented <see cref="TextMesh"/>
/// </summary>
private void UpdateParentedText()
{
if (m_ParentedText)
{
m_ParentedText.color = m_PlayerColor.Color;
if (transform.parent)
{
m_ParentedText.text = $"{gameObject.name}\n Local Space\n Parent: {transform.parent.name}";
}
else
{
m_ParentedText.text = $"{gameObject.name}\n WorldSpace\n Parent: None";
}
}
}
#endif
}