fr.jeanf.universal.player 0.8.43 → 0.8.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,111 @@
1
+ using UnityEngine;
2
+ using System.Reflection;
3
+ using System.Collections.Generic;
4
+
5
+ namespace jeanf.vrplayer
6
+ {
7
+ /// <summary>
8
+ /// Detects and logs missing references in MonoBehaviour components using reflection.
9
+ /// Attach this to any GameObject to check its hierarchy for missing references.
10
+ /// </summary>
11
+ public class MissingReferenceDetector : MonoBehaviour
12
+ {
13
+ [Header("Detection Settings")]
14
+ [Tooltip("Check this GameObject and all its children")]
15
+ [SerializeField] private bool checkChildren = true;
16
+
17
+ [Tooltip("Log even if no missing references are found")]
18
+ [SerializeField] private bool verboseLogging = true;
19
+
20
+ [Tooltip("Run check on Start")]
21
+ [SerializeField] private bool checkOnStart = true;
22
+
23
+ private void Start()
24
+ {
25
+ if (checkOnStart)
26
+ {
27
+ CheckForMissingReferences();
28
+ }
29
+ }
30
+
31
+ [ContextMenu("Check For Missing References")]
32
+ public void CheckForMissingReferences()
33
+ {
34
+ int missingCount = 0;
35
+ List<string> missingReferencesList = new List<string>();
36
+
37
+ Transform[] transforms = checkChildren ? GetComponentsInChildren<Transform>(true) : new Transform[] { transform };
38
+
39
+ Debug.Log($"<color=cyan>Starting missing reference check on: <b>{gameObject.name}</b></color>");
40
+
41
+ foreach (Transform t in transforms)
42
+ {
43
+ MonoBehaviour[] components = t.GetComponents<MonoBehaviour>();
44
+
45
+ foreach (MonoBehaviour component in components)
46
+ {
47
+ if (component == null)
48
+ {
49
+ string msg = $"[MISSING SCRIPT] on GameObject: {t.name}";
50
+ Debug.LogError(msg, t.gameObject);
51
+ missingReferencesList.Add(msg);
52
+ missingCount++;
53
+ continue;
54
+ }
55
+
56
+ // Use reflection to check all serialized fields
57
+ FieldInfo[] fields = component.GetType().GetFields(
58
+ BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
59
+
60
+ foreach (FieldInfo field in fields)
61
+ {
62
+ // Check if field is serialized
63
+ if (!field.IsPublic && field.GetCustomAttribute<SerializeField>() == null)
64
+ continue;
65
+
66
+ // Check if it's a Unity Object reference type
67
+ if (!typeof(Object).IsAssignableFrom(field.FieldType))
68
+ continue;
69
+
70
+ // Get the field value
71
+ object fieldValue = field.GetValue(component);
72
+
73
+ // Check if it's null
74
+ if (fieldValue == null || fieldValue.Equals(null))
75
+ {
76
+ // Try to determine if this should be assigned (not just optional)
77
+ // We'll log it and let the user decide
78
+ string msg = $"[NULL REFERENCE] GameObject: {t.name} | " +
79
+ $"Component: {component.GetType().Name} | " +
80
+ $"Field: {field.Name} (Type: {field.FieldType.Name})";
81
+ Debug.LogWarning(msg, t.gameObject);
82
+ missingReferencesList.Add(msg);
83
+ missingCount++;
84
+ }
85
+ // Special check for destroyed Unity objects
86
+ else if (fieldValue is Object unityObj && unityObj == null)
87
+ {
88
+ string msg = $"[DESTROYED REFERENCE] GameObject: {t.name} | " +
89
+ $"Component: {component.GetType().Name} | " +
90
+ $"Field: {field.Name}";
91
+ Debug.LogError(msg, t.gameObject);
92
+ missingReferencesList.Add(msg);
93
+ missingCount++;
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+ // Summary
100
+ if (missingCount > 0)
101
+ {
102
+ Debug.LogError($"MISSING/NULL REFERENCES DETECTED: {missingCount} issues found on {gameObject.name}\n" +
103
+ "Check console for details.", gameObject);
104
+ }
105
+ else if (verboseLogging)
106
+ {
107
+ Debug.Log($"<color=green>No missing references found in {gameObject.name} hierarchy</color>", gameObject);
108
+ }
109
+ }
110
+ }
111
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: 86a8bd9ca25a9aa479d5fc5c9fb65749
@@ -0,0 +1,8 @@
1
+ fileFormatVersion: 2
2
+ guid: 16cd441c33a822941a488eadfbdeab18
3
+ folderAsset: yes
4
+ DefaultImporter:
5
+ externalObjects: {}
6
+ userData:
7
+ assetBundleName:
8
+ assetBundleVariant:
@@ -458,7 +458,7 @@ namespace jeanf.universalplayer
458
458
  return;
459
459
  }
460
460
 
461
- switch (fadeType)
461
+ switch (fadeType)
462
462
  {
463
463
  case FadeType.Loading:
464
464
  SetVolumeTo_FadeToBlack(); // Black fade
@@ -1,6 +1,5 @@
1
1
  using System;
2
2
  using System.Collections.Generic;
3
- using System.Linq;
4
3
  using UnityEngine;
5
4
  using jeanf.EventSystem;
6
5
  using LitMotion;
@@ -57,33 +56,30 @@ namespace jeanf.universalplayer
57
56
  [ReadOnly] [SerializeField] private bool lastHandVisibility = true;
58
57
  [ReadOnly] [SerializeField] private bool canUpdate = false;
59
58
 
60
-
61
59
  private float tolerance = 0.01f;
60
+
61
+ private List<int> _nullHandIndices = new List<int>(4);
62
+ private int _frameCounter = 0;
63
+ private const int CleanupInterval = 300;
62
64
 
63
65
  [Header("Action binding")]
64
66
  [SerializeField] private InputActionReference shiftTypeHandAction;
65
67
 
66
- //[SerializeField] private Material skin;
67
- //[SerializeField] private Material nail;
68
68
  private static readonly int SkinBaseColor = Shader.PropertyToID("_BaseColor");
69
- //private static readonly int SkinDarness = Shader.PropertyToID("_SkinDarkness");
70
69
  private static readonly int _gloveValue = Shader.PropertyToID("_Switch_Gloves");
71
-
72
70
  [SerializeField] private readonly int _genderValue = Shader.PropertyToID("_Switch_Woman");
73
- //[Range(0,100)]
74
- //[SerializeField] private float nailDarkness = 10f;
75
-
76
71
 
77
72
  [Header("Listening on:")]
78
73
  [SerializeField] private BoolEventChannelSO gloveStateChannel;
79
74
 
80
- //[SerializeField] private BoolEventChannelSO hmdStateChannel;
81
75
  private void OnEnable()
82
76
  {
83
77
  BlendableHand.AddHand += AddHand;
84
78
  BlendableHand.RemoveHand += RemoveHand;
85
- gloveStateChannel.OnEventRaised += SetGloveState;
86
- //hmdStateChannel.OnEventRaised += SetUpdateState;
79
+ if (gloveStateChannel != null)
80
+ {
81
+ gloveStateChannel.OnEventRaised += SetGloveState;
82
+ }
87
83
  }
88
84
 
89
85
  private void OnDisable() => Unsubscribe();
@@ -95,12 +91,21 @@ namespace jeanf.universalplayer
95
91
  _hands.TrimExcess();
96
92
  BlendableHand.AddHand -= AddHand;
97
93
  BlendableHand.RemoveHand -= RemoveHand;
98
- gloveStateChannel.OnEventRaised -= SetGloveState;
99
- //hmdStateChannel.OnEventRaised -= SetUpdateState;
94
+ if (gloveStateChannel != null)
95
+ {
96
+ gloveStateChannel.OnEventRaised -= SetGloveState;
97
+ }
100
98
  }
101
99
 
102
100
  private void Update()
103
101
  {
102
+ _frameCounter++;
103
+ if (_frameCounter >= CleanupInterval)
104
+ {
105
+ _frameCounter = 0;
106
+ CleanupNullHands_GCFree();
107
+ }
108
+
104
109
  if(BroadcastControlsStatus.controlScheme == BroadcastControlsStatus.ControlScheme.XR)
105
110
  {
106
111
  SetHandsVisibility(true);
@@ -110,84 +115,244 @@ namespace jeanf.universalplayer
110
115
  SetBodyMass(_hands, bodyMass);
111
116
  SetSkinDarkness(_hands, skinDarkness);
112
117
  }
113
- //SetBlendValueFromGender(gender);
114
118
  }
119
+
120
+ private void CleanupNullHands_GCFree()
121
+ {
122
+ if (_hands is null || _hands.Count is 0) return;
123
+
124
+ _nullHandIndices.Clear();
125
+
126
+ for (int i = 0; i < _hands.Count; i++)
127
+ {
128
+ if (_hands[i] is null)
129
+ {
130
+ _nullHandIndices.Add(i);
131
+ }
132
+ }
133
+
134
+ for (int i = _nullHandIndices.Count - 1; i >= 0; i--)
135
+ {
136
+ _hands.RemoveAt(_nullHandIndices[i]);
137
+ }
138
+ }
139
+
115
140
  private void AddHand(SkinnedMeshRenderer hand)
116
141
  {
142
+ if (hand is null)
143
+ {
144
+ if (isDebug) Debug.LogWarning("[HandsAppearanceManager] Attempted to add null hand!");
145
+ return;
146
+ }
147
+
148
+ if (hand.sharedMesh is null)
149
+ {
150
+ if (isDebug)
151
+ {
152
+ Debug.LogWarning("[HandsAppearanceManager] Hand has no mesh: " + hand.name);
153
+ }
154
+ return;
155
+ }
156
+
117
157
  if (!_hands.Contains(hand))
118
158
  {
119
159
  _hands.Add(hand);
120
- SetGender(_hands, gender);
160
+
161
+ if (ValidateBlendShapes(hand))
162
+ {
163
+ SetGender(_hands, gender);
164
+ }
165
+ else
166
+ {
167
+ if (isDebug)
168
+ {
169
+ Debug.LogWarning("[HandsAppearanceManager] Hand missing blend shapes: " + hand.name);
170
+ }
171
+ }
172
+
121
173
  SetHandsVisibility(isHandVisible);
122
174
  }
123
-
124
175
  }
176
+
177
+ private bool ValidateBlendShapes(SkinnedMeshRenderer hand)
178
+ {
179
+ if (hand is null || hand.sharedMesh is null) return false;
180
+
181
+ int blendShapeCount = hand.sharedMesh.blendShapeCount;
182
+
183
+ if (blendShapeCount < 1)
184
+ {
185
+ if (isDebug)
186
+ {
187
+ Debug.LogWarning("[HandsAppearanceManager] Hand has no blend shapes: " + hand.name);
188
+ }
189
+ return false;
190
+ }
191
+
192
+ if (blendShapeCount < 3)
193
+ {
194
+ if (isDebug)
195
+ {
196
+ Debug.LogWarning("[HandsAppearanceManager] Hand missing body mass blend shape: " + hand.name);
197
+ }
198
+ }
199
+
200
+ return true;
201
+ }
202
+
125
203
  private void RemoveHand(SkinnedMeshRenderer hand)
126
204
  {
127
- if(_hands.Count > 0 && _hands.Contains(hand)) _hands.Remove(hand);
205
+ if(_hands != null && _hands.Count > 0 && _hands.Contains(hand))
206
+ {
207
+ _hands.Remove(hand);
208
+ }
128
209
  }
129
210
 
130
-
131
211
  private void SetBlendValueFromGender(bool gender)
132
212
  {
133
213
  _blendValue = gender ? 100f : 0f;
134
214
  }
135
215
 
136
-
137
216
  private void SetGender(List<SkinnedMeshRenderer> hands, float value)
138
217
  {
139
- foreach (var hand in hands)
218
+ if (hands is null || hands.Count is 0) return;
219
+
220
+ for (int i = 0; i < hands.Count; i++)
140
221
  {
141
- hand.SetBlendShapeWeight(0, value);
222
+ var hand = hands[i];
223
+ if (hand is null || hand.sharedMesh is null) continue;
224
+
225
+ if (hand.sharedMesh.blendShapeCount < 1)
226
+ {
227
+ if (isDebug)
228
+ {
229
+ Debug.LogWarning("[HandsAppearanceManager] Hand has no blend shapes for gender: " + hand.name);
230
+ }
231
+ continue;
232
+ }
233
+
234
+ try
235
+ {
236
+ hand.SetBlendShapeWeight(0, value);
237
+ }
238
+ catch (Exception e)
239
+ {
240
+ Debug.LogError("[HandsAppearanceManager] Error setting gender blend shape on " + hand.name + ": " + e.Message);
241
+ }
142
242
  }
143
243
  }
144
244
 
145
245
  private void SetBodyMass(List<SkinnedMeshRenderer> hands, float value)
146
246
  {
147
- foreach (var hand in hands)
247
+ if (hands is null || hands.Count is 0) return;
248
+
249
+ for (int i = 0; i < hands.Count; i++)
148
250
  {
149
- hand.SetBlendShapeWeight(2, value);
251
+ var hand = hands[i];
252
+ if (hand is null || hand.sharedMesh is null) continue;
253
+
254
+ if (hand.sharedMesh.blendShapeCount < 3)
255
+ {
256
+ if (isDebug)
257
+ {
258
+ Debug.LogWarning("[HandsAppearanceManager] Hand doesn't have body mass blend shape: " + hand.name);
259
+ }
260
+ continue;
261
+ }
262
+
263
+ try
264
+ {
265
+ hand.SetBlendShapeWeight(2, value);
266
+ }
267
+ catch (Exception e)
268
+ {
269
+ Debug.LogError("[HandsAppearanceManager] Error setting body mass blend shape on " + hand.name + ": " + e.Message);
270
+ }
150
271
  }
151
272
  }
152
273
 
153
-
154
274
  private void SetHandMaterials(List<SkinnedMeshRenderer> hands, float value)
155
275
  {
156
- if(hands.Count < 1) return;
157
- foreach (var hand in hands)
276
+ if (hands is null || hands.Count < 1) return;
277
+
278
+ for (int i = 0; i < hands.Count; i++)
158
279
  {
159
- hand.sharedMaterial.SetFloat(_genderValue, value);
280
+ var hand = hands[i];
281
+ if (hand is null || hand.sharedMaterial is null) continue;
282
+
283
+ try
284
+ {
285
+ hand.sharedMaterial.SetFloat(_genderValue, value);
286
+ }
287
+ catch (Exception e)
288
+ {
289
+ Debug.LogError("[HandsAppearanceManager] Error setting material on " + hand.name + ": " + e.Message);
290
+ }
160
291
  }
161
292
  }
162
293
 
163
294
  private void SetSkinDarkness(List<SkinnedMeshRenderer> hands, float skinDarness)
164
295
  {
165
- if(hands.Count < 1) return;
296
+ if (hands is null || hands.Count < 1) return;
297
+
166
298
  var blend = Color.Lerp(lightSkinColor, darkSkinColor, skinDarkness);
167
- foreach (var hand in hands)
299
+
300
+ for (int i = 0; i < hands.Count; i++)
168
301
  {
169
- hand.sharedMaterials[0].SetColor(SkinBaseColor, blend);
302
+ var hand = hands[i];
303
+ if (hand is null || hand.sharedMaterials is null || hand.sharedMaterials.Length is 0) continue;
304
+
305
+ if (hand.sharedMaterials[0] is null) continue;
306
+
307
+ try
308
+ {
309
+ hand.sharedMaterials[0].SetColor(SkinBaseColor, blend);
310
+ }
311
+ catch (Exception e)
312
+ {
313
+ Debug.LogError("[HandsAppearanceManager] Error setting skin darkness on " + hand.name + ": " + e.Message);
314
+ }
170
315
  }
171
316
  }
172
317
 
173
318
  private void SetGloveValue(List<SkinnedMeshRenderer> hands, float value)
174
319
  {
175
- if(hands.Count < 1) return;
176
- foreach (var mat in hands.SelectMany(hand => hand.sharedMaterials))
320
+ if (hands is null || hands.Count < 1) return;
321
+
322
+ for (int i = 0; i < hands.Count; i++)
177
323
  {
178
- mat.SetFloat(_gloveValue, value);
324
+ var hand = hands[i];
325
+ if (hand is null || hand.sharedMaterials is null) continue;
326
+
327
+ for (int j = 0; j < hand.sharedMaterials.Length; j++)
328
+ {
329
+ var mat = hand.sharedMaterials[j];
330
+ if (mat is null) continue;
331
+
332
+ try
333
+ {
334
+ mat.SetFloat(_gloveValue, value);
335
+ }
336
+ catch (Exception e)
337
+ {
338
+ Debug.LogError("[HandsAppearanceManager] Error setting glove value on " + hand.name + ": " + e.Message);
339
+ }
340
+ }
179
341
  }
180
342
  }
181
343
 
182
344
  private void LerpGloveTowardsValue(float goalValue, float blendTime)
183
345
  {
184
- _gloveHandle = LMotion.Create(gloveValue,goalValue,blendTime)
346
+ _gloveHandle = LMotion.Create(gloveValue, goalValue, blendTime)
185
347
  .Bind(x => gloveValue = x);
186
348
  }
187
349
 
188
350
  public void SetGloveState(bool state)
189
351
  {
190
- if(isDebug) Debug.Log($"glove state {state}");
352
+ if(isDebug)
353
+ {
354
+ Debug.Log("[HandsAppearanceManager] Glove state: " + state);
355
+ }
191
356
  isGlove = state;
192
357
  var goalValue = isGlove ? 1 : 0;
193
358
  LerpGloveTowardsValue(goalValue, blendTime);
@@ -201,9 +366,21 @@ namespace jeanf.universalplayer
201
366
 
202
367
  public void SetHandsVisibility(bool state)
203
368
  {
204
- foreach (var hand in _hands)
369
+ if (_hands is null) return;
370
+
371
+ for (int i = 0; i < _hands.Count; i++)
205
372
  {
206
- hand.enabled = state;
373
+ var hand = _hands[i];
374
+ if (hand is null) continue;
375
+
376
+ try
377
+ {
378
+ hand.enabled = state;
379
+ }
380
+ catch (Exception e)
381
+ {
382
+ Debug.LogError("[HandsAppearanceManager] Error setting hand visibility: " + e.Message);
383
+ }
207
384
  }
208
385
 
209
386
  lastHandVisibility = state;
@@ -24,19 +24,16 @@ namespace jeanf.universalplayer
24
24
  bool isMoving;
25
25
  Vector2 moveValue;
26
26
 
27
- // Cache camera transform to avoid Camera.main lookups
28
27
  private Transform cameraTransform;
29
28
  private const float INPUT_MULTIPLIER = 50f;
30
29
 
31
30
  private void Awake()
32
31
  {
33
- // Cache the camera transform once
34
32
  cameraTransform = Camera.main.transform;
35
33
  }
36
34
 
37
35
  private void OnEnable()
38
36
  {
39
- // Use proper method references instead of lambdas
40
37
  fpsMoveAction.action.performed += OnFpsMovePerformed;
41
38
  fpsMoveAction.action.canceled += OnFpsMoveCanceled;
42
39
  xrMoveAction.action.performed += OnXrMovePerformed;
@@ -61,7 +58,6 @@ namespace jeanf.universalplayer
61
58
  }
62
59
  }
63
60
 
64
- // Event handler methods
65
61
  private void OnFpsMovePerformed(InputAction.CallbackContext ctx)
66
62
  {
67
63
  SetMoveValue(ctx.ReadValue<Vector2>() * Time.smoothDeltaTime * INPUT_MULTIPLIER);
@@ -104,7 +100,6 @@ namespace jeanf.universalplayer
104
100
 
105
101
  private void Move(Vector2 move)
106
102
  {
107
- // Use cached camera transform
108
103
  Vector3 forward = cameraTransform.forward;
109
104
  Vector3 right = cameraTransform.right;
110
105
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fr.jeanf.universal.player",
3
3
 
4
- "version": "0.8.43",
4
+ "version": "0.8.45",
5
5
 
6
6
  "displayName": "Universal Player",
7
7
  "description": "This package contains a universal player working in URP & HDRP for Mouse+Keyboard or VR",
@@ -21,14 +21,14 @@
21
21
  "dependencies": {
22
22
  "com.unity.modules.vr": "1.0.0",
23
23
  "com.unity.modules.xr": "1.0.0",
24
- "com.unity.inputsystem": "1.4.4",
25
- "com.unity.vectorgraphics": "2.0.0-preview.20",
26
- "com.unity.xr.openxr": "1.6.0",
27
- "com.unity.xr.interaction.toolkit": "2.3.2",
28
- "com.unity.xr.hands": "1.1.0-pre.3",
29
- "com.unity.xr.management": "4.3.3",
30
- "com.unity.xr.oculus": "3.2.3",
31
- "fr.jeanf.eventsystem": "0.1.96",
24
+ "com.unity.inputsystem": "1.14.2",
25
+ "com.unity.vectorgraphics": "2.0.0-preview.25",
26
+ "com.unity.xr.openxr": "1.15.1",
27
+ "com.unity.xr.interaction.toolkit": "3.0.9",
28
+ "com.unity.xr.hands": "1.5.1",
29
+ "com.unity.xr.management": "4.5.3",
30
+ "com.unity.xr.oculus": "4.5.2",
31
+ "fr.jeanf.eventsystem": "0.1.124",
32
32
  "fr.jeanf.propertydrawer": "1.1.6",
33
33
  "fr.jeanf.tooltipsystem": "0.0.7"
34
34
  },