Rework diapason with octo arms

This commit is contained in:
2026-02-19 17:16:54 +01:00
parent ec89813b1b
commit 5b1bdaeb90
48 changed files with 2075 additions and 583 deletions
+461
View File
@@ -0,0 +1,461 @@
using UnityEngine;
public enum ArmState
{
Environmental,
TransitionToEquipped,
Equipped,
TransitionToEnvironmental
}
[System.Serializable]
public class ArmEquipment
{
[Header("Arm References")]
public OctoIkLimb IkLimb;
public Transform HandSocket;
[Header("Bone References (for procedural animation)")]
[Tooltip("Shoulder bone (optional)")]
public Transform ShoulderBone;
[Tooltip("Elbow bone (optional)")]
public Transform ElbowBone;
[Tooltip("Wrist bone (required for rotation)")]
public Transform WristBone;
[Header("Equipped Pose Settings")]
[Tooltip("Rotation to apply to shoulder when equipped")]
public Vector3 ShoulderRotationOffset = Vector3.zero;
[Tooltip("Rotation to apply to elbow when equipped")]
public Vector3 ElbowRotationOffset = Vector3.zero;
[Tooltip("Rotation to apply to wrist when equipped")]
public Vector3 WristRotationOffset = new Vector3(0, 0, 90);
[Tooltip("Speed of rotation lerp")]
[Range(1f, 20f)]
public float RotationSpeed = 10f;
[Tooltip("Duration of IK blend after unequip transition")]
[Range(0.1f, 1f)]
public float IkBlendDuration = 0.3f;
[Header("Transition Settings")]
[Tooltip("Position offset where arm exits camera view")]
public Vector3 TransitionOffset = new Vector3(0.5f, -0.3f, 0.2f);
[Tooltip("Duration for arm to move out of view")]
public float TransitionOutDuration = 0.3f;
[Tooltip("Duration for arm to return to view")]
public float TransitionInDuration = 0.3f;
[Tooltip("Curve for transition movement")]
public AnimationCurve TransitionCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
[Header("Tap Animation")]
[Tooltip("Elbow rotation during windup (degrees)")]
public float TapWindupRotation = 15f;
[Tooltip("Duration of windup (pull back)")]
public float TapWindupDuration = 0.1f;
[Tooltip("Duration of strike (forward snap)")]
public float TapStrikeDuration = 0.05f;
[Tooltip("Wrist snap rotation during strike (degrees)")]
public float TapWristSnapRotation = 10f;
[Tooltip("Shake intensity during vibration")]
public float ShakeIntensity = 2f;
[Tooltip("Shake speed (frequency)")]
public float ShakeSpeed = 30f;
[HideInInspector] public ArmState State = ArmState.Environmental;
[HideInInspector] public Vector3 OriginalTargetPosition;
[HideInInspector] public Quaternion OriginalTargetRotation;
[HideInInspector] public GameObject EquippedObject;
[HideInInspector] public float IkWeight = 1f;
[HideInInspector] public Quaternion OriginalShoulderRotation;
[HideInInspector] public Quaternion OriginalElbowRotation;
[HideInInspector] public Quaternion OriginalWristRotation;
private int _transitionTweenId = -1;
private int _ikWeightTweenId = -1;
private int _ikBlendTweenId = -1;
private bool _isBlendingToIk = false;
private int _tapTweenId = -1;
private bool _isShaking = false;
private Quaternion _transitionStartShoulder;
private Quaternion _transitionStartElbow;
private Quaternion _transitionStartWrist;
public void Initialize()
{
if (IkLimb != null && IkLimb.Target != null)
{
OriginalTargetPosition = IkLimb.Target.localPosition;
OriginalTargetRotation = IkLimb.Target.localRotation;
}
if (ShoulderBone != null)
OriginalShoulderRotation = ShoulderBone.localRotation;
if (ElbowBone != null)
OriginalElbowRotation = ElbowBone.localRotation;
if (WristBone != null)
OriginalWristRotation = WristBone.localRotation;
State = ArmState.Environmental;
IkWeight = 1f;
}
public void StartTransitionToEquipped(GameObject objectToEquip, System.Action onTransitionComplete)
{
if (State != ArmState.Environmental)
return;
State = ArmState.TransitionToEquipped;
EquippedObject = objectToEquip;
CancelActiveTweens();
if (IkLimb == null || IkLimb.Target == null)
{
CompleteEquip(onTransitionComplete);
return;
}
Vector3 transitionTarget = OriginalTargetPosition + TransitionOffset;
_transitionTweenId = LeanTween.value(IkLimb.gameObject, 0f, 1f, TransitionOutDuration)
.setEase(TransitionCurve)
.setOnUpdate((float t) => {
if (IkLimb != null && IkLimb.Target != null)
{
IkLimb.Target.localPosition = Vector3.Lerp(IkLimb.Target.localPosition, transitionTarget, t);
}
})
.setOnComplete(() => {
_transitionTweenId = -1;
CompleteEquip(onTransitionComplete);
}).id;
_ikWeightTweenId = LeanTween.value(IkLimb.gameObject, IkWeight, 0f, TransitionOutDuration * 0.5f)
.setOnUpdate((float val) => {
IkWeight = val;
})
.setOnComplete(() => {
_ikWeightTweenId = -1;
}).id;
}
private void CompleteEquip(System.Action onComplete)
{
State = ArmState.Equipped;
if (IkLimb != null)
{
IkLimb.enabled = false;
}
if (EquippedObject != null && HandSocket != null)
{
EquippedObject.transform.SetParent(HandSocket, false);
EquippedObject.transform.localPosition = Vector3.zero;
EquippedObject.transform.localRotation = Quaternion.identity;
}
onComplete?.Invoke();
}
public void UpdateEquippedPose()
{
if (State != ArmState.Equipped && !_isBlendingToIk)
return;
if (_isShaking)
{
UpdateShake();
return;
}
if (State == ArmState.Equipped)
{
float deltaTime = UnityEngine.Time.deltaTime;
float lerpSpeed = 1f - Mathf.Exp(-RotationSpeed * deltaTime);
if (ShoulderBone != null)
{
Quaternion targetRot = OriginalShoulderRotation * Quaternion.Euler(ShoulderRotationOffset);
ShoulderBone.localRotation = Quaternion.Slerp(ShoulderBone.localRotation, targetRot, lerpSpeed);
}
if (ElbowBone != null)
{
Quaternion targetRot = OriginalElbowRotation * Quaternion.Euler(ElbowRotationOffset);
ElbowBone.localRotation = Quaternion.Slerp(ElbowBone.localRotation, targetRot, lerpSpeed);
}
if (WristBone != null)
{
Quaternion targetRot = OriginalWristRotation * Quaternion.Euler(WristRotationOffset);
WristBone.localRotation = Quaternion.Slerp(WristBone.localRotation, targetRot, lerpSpeed);
}
}
}
public void StartTransitionToEnvironmental(System.Action onTransitionComplete)
{
if (State != ArmState.Equipped)
return;
State = ArmState.TransitionToEnvironmental;
CancelActiveTweens();
_transitionStartShoulder = ShoulderBone != null ? ShoulderBone.localRotation : Quaternion.identity;
_transitionStartElbow = ElbowBone != null ? ElbowBone.localRotation : Quaternion.identity;
_transitionStartWrist = WristBone != null ? WristBone.localRotation : Quaternion.identity;
if (IkLimb == null || IkLimb.Target == null)
{
CompleteUnequip(onTransitionComplete);
return;
}
Vector3 currentPosition = IkLimb.Target.localPosition;
Vector3 transitionTarget = OriginalTargetPosition + TransitionOffset;
_ikWeightTweenId = LeanTween.value(IkLimb.gameObject, IkWeight, 0f, TransitionInDuration * 0.5f)
.setOnUpdate((float val) => {
IkWeight = val;
})
.setOnComplete(() => {
_ikWeightTweenId = -1;
}).id;
_transitionTweenId = LeanTween.value(IkLimb.gameObject, 0f, 1f, TransitionInDuration)
.setEase(TransitionCurve)
.setOnUpdate((float t) => {
if (IkLimb != null && IkLimb.Target != null)
{
IkLimb.Target.localPosition = Vector3.Lerp(currentPosition, transitionTarget, t);
}
if (ShoulderBone != null)
ShoulderBone.localRotation = Quaternion.Slerp(_transitionStartShoulder, OriginalShoulderRotation, t);
if (ElbowBone != null)
ElbowBone.localRotation = Quaternion.Slerp(_transitionStartElbow, OriginalElbowRotation, t);
if (WristBone != null)
WristBone.localRotation = Quaternion.Slerp(_transitionStartWrist, OriginalWristRotation, t);
})
.setOnComplete(() => {
_transitionTweenId = -1;
StartIkBlend(onTransitionComplete);
}).id;
}
private void StartIkBlend(System.Action onTransitionComplete)
{
if (IkLimb != null)
{
IkLimb.enabled = true;
}
if (EquippedObject != null)
{
if (HandSocket != null)
EquippedObject.transform.SetParent(null);
UnityEngine.Object.Destroy(EquippedObject);
EquippedObject = null;
}
_isBlendingToIk = true;
Quaternion startShoulder = ShoulderBone != null ? ShoulderBone.localRotation : Quaternion.identity;
Quaternion startElbow = ElbowBone != null ? ElbowBone.localRotation : Quaternion.identity;
Quaternion startWrist = WristBone != null ? WristBone.localRotation : Quaternion.identity;
Vector3 startTargetPos = IkLimb != null && IkLimb.Target != null ? IkLimb.Target.localPosition : Vector3.zero;
Quaternion startTargetRot = IkLimb != null && IkLimb.Target != null ? IkLimb.Target.localRotation : Quaternion.identity;
if (IkLimb != null && IkLimb.gameObject != null)
{
_ikBlendTweenId = LeanTween.value(IkLimb.gameObject, 0f, 1f, IkBlendDuration)
.setEase(LeanTweenType.easeOutQuad)
.setOnUpdate((float t) => {
if (ShoulderBone != null)
ShoulderBone.localRotation = Quaternion.Slerp(startShoulder, OriginalShoulderRotation, t);
if (ElbowBone != null)
ElbowBone.localRotation = Quaternion.Slerp(startElbow, OriginalElbowRotation, t);
if (WristBone != null)
WristBone.localRotation = Quaternion.Slerp(startWrist, OriginalWristRotation, t);
if (IkLimb != null && IkLimb.Target != null)
{
IkLimb.Target.localPosition = Vector3.Lerp(startTargetPos, OriginalTargetPosition, t);
IkLimb.Target.localRotation = Quaternion.Slerp(startTargetRot, OriginalTargetRotation, t);
}
IkWeight = t;
})
.setOnComplete(() => {
_ikBlendTweenId = -1;
CompleteUnequip(onTransitionComplete);
}).id;
}
else
{
CompleteUnequip(onTransitionComplete);
}
}
private void CompleteUnequip(System.Action onComplete)
{
State = ArmState.Environmental;
_isBlendingToIk = false;
if (IkLimb != null && IkLimb.Target != null)
{
IkLimb.Target.localPosition = OriginalTargetPosition;
IkLimb.Target.localRotation = OriginalTargetRotation;
}
if (ShoulderBone != null)
ShoulderBone.localRotation = OriginalShoulderRotation;
if (ElbowBone != null)
ElbowBone.localRotation = OriginalElbowRotation;
if (WristBone != null)
WristBone.localRotation = OriginalWristRotation;
onComplete?.Invoke();
}
public void CancelActiveTweens()
{
if (_transitionTweenId != -1)
{
LeanTween.cancel(_transitionTweenId);
_transitionTweenId = -1;
}
if (_ikWeightTweenId != -1)
{
LeanTween.cancel(_ikWeightTweenId);
_ikWeightTweenId = -1;
}
if (_ikBlendTweenId != -1)
{
LeanTween.cancel(_ikBlendTweenId);
_ikBlendTweenId = -1;
}
if (_tapTweenId != -1)
{
LeanTween.cancel(_tapTweenId);
_tapTweenId = -1;
}
_isBlendingToIk = false;
_isShaking = false;
}
public void PlayTapAnimation()
{
if (State != ArmState.Equipped)
return;
if (_tapTweenId != -1)
{
LeanTween.cancel(_tapTweenId);
}
Quaternion elbowStart = ElbowBone != null ? ElbowBone.localRotation : Quaternion.identity;
Quaternion wristStart = WristBone != null ? WristBone.localRotation : Quaternion.identity;
Quaternion elbowWindup = elbowStart * Quaternion.Euler(0, 0, -TapWindupRotation);
Quaternion elbowStrike = elbowStart * Quaternion.Euler(0, 0, TapWindupRotation * 0.3f);
Quaternion wristWindup = wristStart;
Quaternion wristStrike = wristStart * Quaternion.Euler(0, 0, -TapWristSnapRotation);
_tapTweenId = LeanTween.value(IkLimb.gameObject, 0f, 1f, TapWindupDuration)
.setOnUpdate((float t) => {
if (ElbowBone != null)
{
ElbowBone.localRotation = Quaternion.Slerp(elbowStart, elbowWindup, t);
}
if (WristBone != null)
{
WristBone.localRotation = Quaternion.Slerp(wristStart, wristWindup, t);
}
})
.setOnComplete(() => {
_tapTweenId = LeanTween.value(IkLimb.gameObject, 0f, 1f, TapStrikeDuration)
.setOnUpdate((float t) => {
if (ElbowBone != null)
{
ElbowBone.localRotation = Quaternion.Slerp(elbowWindup, elbowStrike, t);
}
if (WristBone != null)
{
WristBone.localRotation = Quaternion.Slerp(wristWindup, wristStrike, t);
}
})
.setOnComplete(() => {
_tapTweenId = LeanTween.value(IkLimb.gameObject, 0f, 1f, 0.1f)
.setOnUpdate((float t) => {
if (ElbowBone != null)
{
ElbowBone.localRotation = Quaternion.Slerp(elbowStrike, elbowStart, t);
}
if (WristBone != null)
{
WristBone.localRotation = Quaternion.Slerp(wristStrike, wristStart, t);
}
})
.setOnComplete(() => {
_tapTweenId = -1;
}).id;
}).id;
}).id;
}
public void StartShake()
{
_isShaking = true;
}
public void StopShake()
{
_isShaking = false;
}
public void UpdateShake()
{
if (!_isShaking || State != ArmState.Equipped || WristBone == null)
return;
float shake = Mathf.Sin(UnityEngine.Time.time * ShakeSpeed) * ShakeIntensity;
Quaternion shakeRotation = Quaternion.Euler(shake, shake * 0.5f, 0);
Quaternion targetRot = OriginalWristRotation * Quaternion.Euler(WristRotationOffset) * shakeRotation;
WristBone.localRotation = targetRot;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 80d59ec0ac09852429c56ca6c42464c2
+357
View File
@@ -0,0 +1,357 @@
using UnityEngine;
using UnityEngine.InputSystem;
using Unheimlich.Audio;
using System;
using System.Collections;
public class DiapasonEquipmentManager : MonoBehaviour
{
[Header("References")]
[SerializeField] private OctoIkArmsImproved _armsSystem;
[SerializeField] private GameObject _diapasonPrefab;
[SerializeField] private int _equipmentArmIndex = 1;
[Header("Detection Settings")]
[SerializeField] private LayerMask _soundEmitterLayer;
[SerializeField] private float _detectionRadius = 0.15f;
[SerializeField] private Transform[] _detectionPoints;
[Header("Visual Feedback")]
[SerializeField] private GameObject _contactFeedbackPrefab;
[Header("Diapason Animation")]
[SerializeField] private string _vibratingBoolParam = "IsVibrating";
[SerializeField] private string _tapTriggerParam = "Tap";
[Header("Tap Settings")]
[SerializeField] private AudioClip _tapSound;
[SerializeField] [Range(0f, 1f)] private float _tapVolume = 1f;
[SerializeField] private float _vibrationDuration = 15f;
public bool IsEquipped => _isEquipped;
public AudioListenable CurrentDetectedEmitter => _currentDetectedEmitter;
public bool CanTap => _isEquipped && _currentDetectedEmitter != null && !_isVibrating && !_currentDetectedEmitter.HasBeenTapped;
public event Action<AudioListenable> OnEmitterDetected;
public event Action<AudioListenable> OnEmitterLost;
private GameObject _spawnedDiapason;
private bool _isEquipped = false;
private AudioListenable _currentDetectedEmitter;
private GameObject _contactFeedback;
private bool _isVibrating = false;
private AudioSource _audioSource;
private Coroutine _vibrationCoroutine;
private Animator _diapasonAnimator;
private ParticleSystem _diapasonParticles;
void Awake()
{
if (_armsSystem == null)
{
_armsSystem = GetComponent<OctoIkArmsImproved>();
if (_armsSystem == null)
{
Debug.LogError("[DiapasonEquipment] OctoIkArmsImproved not found!");
}
}
_audioSource = GetComponent<AudioSource>();
if (_audioSource == null)
{
_audioSource = gameObject.AddComponent<AudioSource>();
}
}
void Update()
{
if (_isEquipped && _armsSystem.GetArmState(_equipmentArmIndex) == ArmState.Equipped)
{
CheckForSoundEmitters();
if (Mouse.current != null && Mouse.current.leftButton.wasPressedThisFrame)
{
if (CanTap)
{
TapDiapason();
}
}
}
}
public void EquipDiapason()
{
if (_isEquipped)
{
Debug.LogWarning("[DiapasonEquipment] Diapason already equipped!");
return;
}
if (_diapasonPrefab == null)
{
Debug.LogError("[DiapasonEquipment] Diapason prefab not assigned!");
return;
}
_spawnedDiapason = Instantiate(_diapasonPrefab);
_spawnedDiapason.SetActive(true);
_diapasonAnimator = _spawnedDiapason.GetComponentInChildren<Animator>(true);
if (_diapasonAnimator == null)
{
Debug.LogWarning("[DiapasonEquipment] No Animator found on spawned diapason");
}
else
{
_diapasonAnimator.enabled = false;
}
_diapasonParticles = _spawnedDiapason.GetComponentInChildren<ParticleSystem>(true);
if (_diapasonParticles == null)
{
Debug.LogWarning("[DiapasonEquipment] No ParticleSystem found on spawned diapason");
}
_armsSystem.EquipObjectOnArm(_equipmentArmIndex, _spawnedDiapason, () => {
_isEquipped = true;
OnDiapasonEquipped();
});
}
public void UnequipDiapason()
{
if (!_isEquipped)
{
Debug.LogWarning("[DiapasonEquipment] Diapason not equipped!");
return;
}
if (_vibrationCoroutine != null)
{
StopCoroutine(_vibrationCoroutine);
_vibrationCoroutine = null;
}
if (_currentDetectedEmitter != null)
{
_currentDetectedEmitter.SetConcentrating(false);
_currentDetectedEmitter.StopListening(cancelConcentrationFade: false);
_currentDetectedEmitter = null;
}
_armsSystem.UnequipObjectFromArm(_equipmentArmIndex, () => {
_isEquipped = false;
_spawnedDiapason = null;
OnDiapasonUnequipped();
});
}
private void CheckForSoundEmitters()
{
if (_detectionPoints == null || _detectionPoints.Length == 0)
return;
AudioListenable closestEmitter = null;
float closestDistance = float.MaxValue;
foreach (Transform detectionPoint in _detectionPoints)
{
if (detectionPoint == null)
continue;
Collider[] hits = Physics.OverlapSphere(detectionPoint.position, _detectionRadius, _soundEmitterLayer);
foreach (Collider hit in hits)
{
AudioListenable emitter = hit.GetComponent<AudioListenable>();
if (emitter != null)
{
float distance = Vector3.Distance(detectionPoint.position, hit.transform.position);
if (distance < closestDistance)
{
closestDistance = distance;
closestEmitter = emitter;
}
}
}
}
if (closestEmitter != _currentDetectedEmitter)
{
if (_currentDetectedEmitter != null)
{
OnEmitterExited(_currentDetectedEmitter);
}
_currentDetectedEmitter = closestEmitter;
if (_currentDetectedEmitter != null)
{
OnEmitterEntered(_currentDetectedEmitter);
}
}
}
private void OnDiapasonEquipped()
{
if (_diapasonAnimator != null)
{
_diapasonAnimator.enabled = true;
_diapasonAnimator.SetBool(_vibratingBoolParam, false);
}
if (_diapasonParticles != null)
{
_diapasonParticles.Stop();
var emission = _diapasonParticles.emission;
emission.enabled = false;
}
}
private void OnDiapasonUnequipped()
{
_isVibrating = false;
_diapasonAnimator = null;
_diapasonParticles = null;
}
public void TapDiapason()
{
if (!CanTap)
{
Debug.LogWarning("[DiapasonEquipment] Cannot tap: not equipped, no contact, or already tapped");
return;
}
ArmEquipment equipment = _armsSystem.GetArmEquipment(_equipmentArmIndex);
if (equipment != null)
{
equipment.PlayTapAnimation();
equipment.StartShake();
}
if (_tapSound != null && _audioSource != null)
{
_audioSource.PlayOneShot(_tapSound, _tapVolume);
}
if (_diapasonAnimator != null)
{
_diapasonAnimator.SetTrigger(_tapTriggerParam);
_diapasonAnimator.SetBool(_vibratingBoolParam, true);
}
if (_diapasonParticles != null)
{
var emission = _diapasonParticles.emission;
emission.enabled = true;
_diapasonParticles.Play();
}
_isVibrating = true;
if (_currentDetectedEmitter != null)
{
_currentDetectedEmitter.StartListening(transform.position);
}
if (_vibrationCoroutine != null)
StopCoroutine(_vibrationCoroutine);
_vibrationCoroutine = StartCoroutine(StopVibrationAfterDuration());
}
private IEnumerator StopVibrationAfterDuration()
{
yield return new WaitForSeconds(_vibrationDuration);
if (_diapasonAnimator != null)
{
_diapasonAnimator.SetBool(_vibratingBoolParam, false);
}
if (_diapasonParticles != null)
{
_diapasonParticles.Stop();
var emission = _diapasonParticles.emission;
emission.enabled = false;
}
if (_currentDetectedEmitter != null)
{
_currentDetectedEmitter.StopListening();
}
ArmEquipment equipment = _armsSystem.GetArmEquipment(_equipmentArmIndex);
if (equipment != null)
{
equipment.StopShake();
}
_isVibrating = false;
_vibrationCoroutine = null;
}
private void OnEmitterEntered(AudioListenable emitter)
{
emitter.SetConcentrating(true);
if (_contactFeedbackPrefab != null && _contactFeedback == null)
{
_contactFeedback = Instantiate(_contactFeedbackPrefab, emitter.transform);
_contactFeedback.transform.localPosition = Vector3.zero;
}
OnEmitterDetected?.Invoke(emitter);
}
private void OnEmitterExited(AudioListenable emitter)
{
emitter.SetConcentrating(false);
if (!emitter.HasBeenTapped)
{
emitter.StopListening();
}
if (_contactFeedback != null)
{
Destroy(_contactFeedback);
_contactFeedback = null;
}
if (_diapasonAnimator != null)
{
_diapasonAnimator.SetBool(_vibratingBoolParam, false);
}
if (_diapasonParticles != null)
{
_diapasonParticles.Stop();
var emission = _diapasonParticles.emission;
emission.enabled = false;
}
_isVibrating = false;
OnEmitterLost?.Invoke(emitter);
}
void OnDrawGizmosSelected()
{
if (_detectionPoints == null || !_isEquipped)
return;
Gizmos.color = Color.yellow;
foreach (Transform detectionPoint in _detectionPoints)
{
if (detectionPoint != null)
{
Gizmos.DrawWireSphere(detectionPoint.position, _detectionRadius);
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 38b80731eb88f834ab3d79a566e40b48
+41
View File
@@ -0,0 +1,41 @@
using UnityEngine;
using UnityEngine.InputSystem;
public class DiapasonEquipmentTester : MonoBehaviour
{
[Header("References")]
[SerializeField] private DiapasonEquipmentManager _equipmentManager;
[Header("Input")]
[SerializeField] private Key _equipKey = Key.E;
[SerializeField] private Key _unequipKey = Key.R;
void Update()
{
if (_equipmentManager == null)
return;
if (Keyboard.current[_equipKey].wasPressedThisFrame)
{
_equipmentManager.EquipDiapason();
}
if (Keyboard.current[_unequipKey].wasPressedThisFrame)
{
_equipmentManager.UnequipDiapason();
}
}
void OnGUI()
{
GUILayout.BeginArea(new Rect(10, 10, 350, 150));
GUILayout.Label("=== Diapason Equipment System ===");
GUILayout.Label($"Press {_equipKey} to Equip Diapason");
GUILayout.Label($"Press {_unequipKey} to Unequip Diapason");
GUILayout.Label("");
GUILayout.Label("When equipped:");
GUILayout.Label("• Move LEFT hand to touch AudioListenable");
GUILayout.Label("• LEFT CLICK to tap and activate");
GUILayout.EndArea();
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9279e0fb73118d14ba6f2cf05170a660
+175
View File
@@ -0,0 +1,175 @@
using UnityEngine;
[ExecuteInEditMode]
public class DiapasonPoseTweaker : MonoBehaviour
{
[Header("References")]
[SerializeField] private OctoIkArmsImproved _armsSystem;
[SerializeField] private int _armIndex = 1;
[Header("Quick Presets")]
[SerializeField] private bool _applyPreset1 = false;
[SerializeField] private bool _applyPreset2 = false;
[SerializeField] private bool _applyPreset3 = false;
[Header("Live Tweaking (Runtime Only)")]
[SerializeField] private Vector3 _shoulderRotation = new Vector3(20, 0, -10);
[SerializeField] private Vector3 _elbowRotation = new Vector3(0, 0, 0);
[SerializeField] private Vector3 _wristRotation = new Vector3(0, 0, 90);
[Header("Preview")]
[SerializeField] private bool _previewPose = false;
private ArmEquipment _equipment;
private bool _wasPreviewActive = false;
void Update()
{
if (!Application.isPlaying)
return;
if (_armsSystem == null)
return;
if (_equipment == null)
{
var armRigField = typeof(OctoIkArmsImproved).GetField("_rightArm",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (armRigField != null)
{
var armRig = armRigField.GetValue(_armsSystem);
var equipmentField = armRig.GetType().GetField("Equipment");
if (equipmentField != null)
{
_equipment = (ArmEquipment)equipmentField.GetValue(armRig);
}
}
}
if (_equipment == null)
return;
if (_previewPose && _equipment.State == ArmState.Environmental)
{
ApplyRotations();
_wasPreviewActive = true;
}
else if (_wasPreviewActive && !_previewPose)
{
ResetRotations();
_wasPreviewActive = false;
}
if (_equipment.State == ArmState.Equipped)
{
_equipment.ShoulderRotationOffset = _shoulderRotation;
_equipment.ElbowRotationOffset = _elbowRotation;
_equipment.WristRotationOffset = _wristRotation;
}
}
void OnValidate()
{
if (_applyPreset1)
{
_applyPreset1 = false;
ApplyPreset1();
}
if (_applyPreset2)
{
_applyPreset2 = false;
ApplyPreset2();
}
if (_applyPreset3)
{
_applyPreset3 = false;
ApplyPreset3();
}
}
private void ApplyPreset1()
{
_shoulderRotation = new Vector3(30, 0, -15);
_elbowRotation = new Vector3(0, 10, 0);
_wristRotation = new Vector3(0, 0, 90);
Debug.Log("[DiapasonPose] Applied Preset 1: Close to body, slight inward");
}
private void ApplyPreset2()
{
_shoulderRotation = new Vector3(20, -10, -20);
_elbowRotation = new Vector3(0, 0, 0);
_wristRotation = new Vector3(0, -15, 90);
Debug.Log("[DiapasonPose] Applied Preset 2: Further from left hand, angled");
}
private void ApplyPreset3()
{
_shoulderRotation = new Vector3(15, 0, -25);
_elbowRotation = new Vector3(0, 15, 0);
_wristRotation = new Vector3(0, 0, 85);
Debug.Log("[DiapasonPose] Applied Preset 3: Natural grip, centered");
}
private void ApplyRotations()
{
if (_equipment.ShoulderBone != null)
{
_equipment.ShoulderBone.localRotation = _equipment.OriginalShoulderRotation * Quaternion.Euler(_shoulderRotation);
}
if (_equipment.ElbowBone != null)
{
_equipment.ElbowBone.localRotation = _equipment.OriginalElbowRotation * Quaternion.Euler(_elbowRotation);
}
if (_equipment.WristBone != null)
{
_equipment.WristBone.localRotation = _equipment.OriginalWristRotation * Quaternion.Euler(_wristRotation);
}
}
private void ResetRotations()
{
if (_equipment.ShoulderBone != null)
{
_equipment.ShoulderBone.localRotation = _equipment.OriginalShoulderRotation;
}
if (_equipment.ElbowBone != null)
{
_equipment.ElbowBone.localRotation = _equipment.OriginalElbowRotation;
}
if (_equipment.WristBone != null)
{
_equipment.WristBone.localRotation = _equipment.OriginalWristRotation;
}
}
void OnGUI()
{
if (!Application.isPlaying)
return;
GUILayout.BeginArea(new Rect(10, 150, 350, 250));
GUILayout.Label("=== Diapason Pose Tweaker ===");
GUILayout.Label($"Shoulder: {_shoulderRotation}");
GUILayout.Label($"Elbow: {_elbowRotation}");
GUILayout.Label($"Wrist: {_wristRotation}");
GUILayout.Space(10);
GUILayout.Label("Tips:");
GUILayout.Label("• Shoulder X: + = down, - = up");
GUILayout.Label("• Shoulder Y: + = inward, - = outward");
GUILayout.Label("• Shoulder Z: + = twist out, - = twist in");
GUILayout.Label("• Elbow Y: + = bend out, - = bend in");
GUILayout.Label("• Wrist Z: rotation du diapason");
GUILayout.EndArea();
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8d1a1f500877b404fa6d23dff3bfa8c8
+32
View File
@@ -0,0 +1,32 @@
using UnityEngine;
[System.Serializable]
public class TapAnimationSettings
{
[Header("Tap Motion")]
[Tooltip("Elbow rotation during windup (degrees)")]
public float WindupRotation = 15f;
[Tooltip("Duration of windup (pull back)")]
public float WindupDuration = 0.1f;
[Tooltip("Duration of strike (forward snap)")]
public float StrikeDuration = 0.05f;
[Tooltip("Wrist snap rotation during strike (degrees)")]
public float WristSnapRotation = 10f;
[Header("Vibration Shake")]
[Tooltip("Shake intensity during vibration")]
public float ShakeIntensity = 2f;
[Tooltip("Shake speed (frequency)")]
public float ShakeSpeed = 30f;
}
public class EquippableTapSettings : MonoBehaviour
{
[SerializeField] private TapAnimationSettings _tapSettings = new TapAnimationSettings();
public TapAnimationSettings TapSettings => _tapSettings;
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b46af809fa8f34d49b3dec606a505c5e
+104 -11
View File
@@ -27,6 +27,9 @@ public class OctoIkArmsImproved : MonoBehaviour
public OctoIkLimb Arm;
public Vector3 RotationOffset;
public RaycastRig[] Raycasts;
[Header("Equipment System")]
public ArmEquipment Equipment;
[HideInInspector] public Vector3 TargetOriginPos;
[HideInInspector] public Quaternion TargetOriginRot;
@@ -47,6 +50,27 @@ public class OctoIkArmsImproved : MonoBehaviour
PoleOriginPos = Arm.Pole.localPosition;
}
}
if (Equipment != null)
{
Equipment.Initialize();
}
}
public bool IsEnvironmentalIkActive()
{
if (Equipment == null)
return Enabled;
return Enabled && Equipment.State == ArmState.Environmental;
}
public float GetEnvironmentalIkWeight()
{
if (Equipment == null)
return 1f;
return Equipment.IkWeight;
}
}
@@ -148,6 +172,19 @@ public class OctoIkArmsImproved : MonoBehaviour
PerformRaycasts();
FindNearestHits();
UpdateArmPositions();
UpdateEquippedPoses();
}
private void UpdateEquippedPoses()
{
for (int i = 0; i < _arms.Length; i++)
{
ArmRig armRig = _arms[i];
if (armRig.Equipment != null)
{
armRig.Equipment.UpdateEquippedPose();
}
}
}
// Cast all configured raycasts and store results
@@ -157,7 +194,7 @@ public class OctoIkArmsImproved : MonoBehaviour
{
ArmRig armRig = _arms[i];
if (!armRig.Enabled || armRig.Raycasts == null)
if (!armRig.IsEnvironmentalIkActive() || armRig.Raycasts == null)
continue;
for (int u = 0; u < armRig.Raycasts.Length; u++)
@@ -247,24 +284,23 @@ public class OctoIkArmsImproved : MonoBehaviour
{
ArmRig armRig = _arms[i];
if (armRig.Arm == null || armRig.Arm.Target == null) continue;
if (!armRig.IsEnvironmentalIkActive())
continue;
RaycastInfos raycastInfos = _nearestHitRaycasts[i];
// Default to origin position/rotation
Vector3 targetPos = armRig.TargetOriginPos;
Quaternion targetRot = armRig.TargetOriginRot;
bool shouldStick = false;
// Check if we should stick to surface
if (raycastInfos.HasHit && _noHitFrameCount[i] < _settings.StabilityFrames)
{
if (IsValidSurface(raycastInfos, i))
{
// Calculate target position on surface
targetPos = armRig.Arm.Target.parent.InverseTransformPoint(raycastInfos.Position);
// Calculate target rotation aligned to surface
targetRot = Quaternion.LookRotation(
-armRig.Arm.Target.parent.InverseTransformDirection(raycastInfos.Normal),
transform.up
@@ -274,12 +310,10 @@ public class OctoIkArmsImproved : MonoBehaviour
}
}
// Smooth stickiness transition
float targetStickiness = shouldStick ? 1f : 0f;
float stickinessSpeed = shouldStick ? _settings.StickSpeed : _settings.ReleaseSpeed;
_currentStickiness[i] = Mathf.MoveTowards(_currentStickiness[i], targetStickiness, stickinessSpeed * Time.deltaTime);
// Apply distance-based stickiness factor (hysteresis)
float distanceFactor = 0f;
if (shouldStick && raycastInfos.HasHit)
{
@@ -287,17 +321,14 @@ public class OctoIkArmsImproved : MonoBehaviour
distanceFactor = Mathf.Clamp01(distanceFactor);
}
float finalStickiness = _currentStickiness[i] * distanceFactor;
float finalStickiness = _currentStickiness[i] * distanceFactor * armRig.GetEnvironmentalIkWeight();
// Calculate interpolation speeds
float posSpeed = Mathf.Lerp(_settings.ReleaseSpeed, _settings.StickSpeed, finalStickiness);
float rotSpeed = _settings.RotationSpeed * Mathf.Deg2Rad;
// Exponential smoothing for natural movement
float tPos = 1f - Mathf.Exp(-posSpeed * Time.deltaTime);
float tRot = 1f - Mathf.Exp(-rotSpeed * Time.deltaTime);
// Apply smooth interpolation
armRig.Arm.Target.localPosition = Vector3.Lerp(armRig.Arm.Target.localPosition, targetPos, tPos);
armRig.Arm.Target.localRotation = Quaternion.Slerp(armRig.Arm.Target.localRotation, targetRot, tRot);
}
@@ -327,4 +358,66 @@ public class OctoIkArmsImproved : MonoBehaviour
return true;
}
public void EquipObjectOnArm(int armIndex, GameObject objectToEquip, System.Action onComplete = null)
{
if (armIndex < 0 || armIndex >= _arms.Length)
{
Debug.LogError($"[OctoIkArms] Invalid arm index: {armIndex}");
return;
}
ArmRig armRig = _arms[armIndex];
if (armRig.Equipment == null)
{
Debug.LogError($"[OctoIkArms] Arm {armIndex} has no Equipment system configured!");
return;
}
armRig.Equipment.StartTransitionToEquipped(objectToEquip, onComplete);
}
public void UnequipObjectFromArm(int armIndex, System.Action onComplete = null)
{
if (armIndex < 0 || armIndex >= _arms.Length)
{
Debug.LogError($"[OctoIkArms] Invalid arm index: {armIndex}");
return;
}
ArmRig armRig = _arms[armIndex];
if (armRig.Equipment == null)
{
Debug.LogError($"[OctoIkArms] Arm {armIndex} has no Equipment system configured!");
return;
}
armRig.Equipment.StartTransitionToEnvironmental(onComplete);
}
public ArmState GetArmState(int armIndex)
{
if (armIndex < 0 || armIndex >= _arms.Length)
return ArmState.Environmental;
if (_arms[armIndex].Equipment == null)
return ArmState.Environmental;
return _arms[armIndex].Equipment.State;
}
public bool IsArmInEnvironmentalMode(int armIndex)
{
return GetArmState(armIndex) == ArmState.Environmental;
}
public ArmEquipment GetArmEquipment(int armIndex)
{
if (armIndex < 0 || armIndex >= _arms.Length)
return null;
return _arms[armIndex].Equipment;
}
}