nightward/Assets/ResWork/zTest/06/BreastJigglePhysics.cs

290 lines
12 KiB
C#

using UnityEngine;
using UnityEditor;
/**
* Free body 설정,
* 각각의 Radius는 값이 크면, 묵직하고 관성이 강해짐, 외부 힘에 덜 민감하고, 흔들리더라도 천천히 크게 출렁이며 진동이 오래 남아. 가슴의 중심부에 배치해서 무거운 느낌을 줄 때 사용.
* 값이 작으면, 매우 가볍고 외부 힘에 민감하지만 관성이 없어 금방 제자리로 돌아오려고 함. Kinematic 바디는 아니지만, 고정된 경계 근처에 배치해서 그 부분의 움직임을 최소화하고 싶을 때 사용.
*
*/
[ExecuteInEditMode]
[RequireComponent(typeof(JellySprite))]
public class BreastJigglePhysics : MonoBehaviour
{
[Header("=== JellySprite 물리 설정 ===")]
[Tooltip("JellySprite 물리 속성을 이 스크립트 값으로 자동 적용. (기본: True)")]
public bool autoConfigureJellySprite = true;
[Header("JellySprite 강성/감쇠 설정")]
[Tooltip("스프링 강성. 높을수록 단단하고 빠르게 진동. 낮을수록 물렁하고 느림.")]
//높게 (3.0 이상): 단단하고 탄력 있는(Firm) 느낌. 진동 주기가 빠르고 출렁임이 작음.
//낮게(1.5 이하) : 물렁하고 부드러운(Soft/Jelly) 느낌.출렁임이 느리고 크게 늘어남.
[Range(0.5f, 5f)] public float stiffness = 2.2f;
[Tooltip("감쇠 계수. 높을수록 흔들림이 빨리 멈춤. 낮을수록 오래 지속.")]
//높게 (0.5 이상): 흔들림이 빨리 멈추고 잔진동이 적어 깔끔함.
//낮게 (0.3 이하): 흔들림이 오래 지속되고 잔진동이 많아 물컹거리는 느낌이 강함.
[Range(0f, 1f)] public float dampingRatio = 0.4f;
[Header("JellySprite 질량/중력/드래그")]
[Tooltip("전체 질량. 높을수록 둔하고, 낮을수록 민감하게 반응.(높을스록 흔들림 주기가 길어짐)")]
//높게 (1.0 이상): 관성이 커져 외부 힘에 둔하게 반응하며, 흔들림 주기가 길어짐.
//낮게 (0.5 이하): 외부 힘에 민감하게 반응하고, 가벼워 보임.
[Range(0.1f, 2f)] public float mass = 0.8f;
[Tooltip("중력 배율. 높을수록 아래로 처지는(Sag) 느낌이 강해짐.")]
//높게 (0.5 이상): 아래로 처지는(Sag) 효과가 강해져 더 육중한 느낌을 줌.
//0에 가깝게: 처짐 효과가 거의 없어 매우 가볍게 출렁거림.
[Range(0f, 2f)] public float gravityScale = 0f; //0.25f;
[Tooltip("이동 시 공기 저항(선형 드래그). 움직임 전반을 둔화시킴.")]
//높게: 움직임이 전반적으로 둔화되고 속도가 느려짐. 물리 폭주를 막는 보조 수단으로도 활용.
[Range(0f, 2f)] public float drag = 0f; //0.35f;
[Tooltip("회전 저항(Angular Drag). 회전 움직임을 둔화시킴.")]
//높게: 움직임이 전반적으로 둔화되고 속도가 느려짐. 물리 폭주를 막는 보조 수단으로도 활용.
[Range(0f, 2f)] public float angularDrag = 0.6f;
[Header("=== Player(Blade) 흔들림 설정 ===")]
[Tooltip("Player(터치) 충돌 시 가슴에 가해지는 힘의 강도. 높을수록 크게 출렁임.")]
//높게: 외부 터치에 가슴이 더 크게 밀려나고 출렁이게 한다. (강한 상호작용)
[Range(1f, 30f)] public float playerJiggleStrength = 26f;
[Tooltip("Player 흔들림 힘에 적용되는 랜덤 변동 비율. (0.3 이상 권장)")]
//높게 (0.3 이상): 충돌마다 가해지는 힘의 크기가 불규칙해져 매번 다른 흔들림을 연출하여 자연스러움을 높인다.
[Range(0f, 1f)] public float randomVariation = 0.5f;
[Header("흔들림 방향 설정")]
[Tooltip("흔들림 발생 시 좌우로 퍼지는 범위. 0이면 수직으로만 흔들림. (0.2~0.5 권장)")]
//적절한 양수 (0.2~0.5): 힘이 좌우로도 퍼져서 흔들림이 수직을 넘어 옆으로도 퍼지게 하여 더 풍부한 움직임을 만든다.
[Range(-1f, 1f)] public float horizontalSpread = 0.5f;
[Tooltip("위아래 편향. 높을수록 아래로 힘이 쏠려 처짐/출렁임을 강조. (1.0 이상 권장)")]
//높게 (1.0 이상): 힘의 방향이 아래쪽으로 강하게 쏠려 중력에 의한 처짐과 아래 방향 출렁임을 강조한다. (자연스러운 처짐 연출)
[Range(0f, 3f)] public float verticalBias = 0.8f;
[Header("=== Ball 흔들림 설정 (물리엔진 사용) ===")]
[Tooltip("Ball 충돌 시 기본 물리 반응에 더해지는 추가 힘.")]
//0 이상: Ball 충돌 시 기본 물리 반응에 이 추가 힘을 더해 더욱 강력한 충격 반응을 줄 수 있다.
[Range(0f, 30f)] public float ballJiggleStrength = 26f;
[Tooltip("충돌 지점 반발력이 전체 힘에 미치는 영향도. 높을수록 밀리는 느낌이 강해짐.")]
//높게 (1.5 이상): 충돌한 지점의 반발력이 전체 힘 방향에 강하게 반영되도록 하여, verticalBias보다 국소적인 반응을 강조한다.
[Range(0f, 5f)] public float contactPointInfluence = 3f;
[Header("=== 힘 제한 ===")]
[Tooltip("JellySprite에 가할 수 있는 최대 힘 제한. 물리 폭주 방지용. (10.0~20.0 권장)")]
//양수 (10.0~20.0): 값이 너무 크면 물리 폭주로 가슴 메쉬가 과도하게 늘어나거나 튕겨 나갈 수 있으므로, 안정적인 값으로 제한한다.
public float maxJellyForce = 26f;
[Header("=== 충돌 위치 기반 반응 ===")]
[Tooltip("충돌 지점 기반의 반발력 방향 사용 여부. (가장 현실적인 반응, 권장: True)")]
public bool useContactPointPhysics = true;
[Header("=== 디버그 옵션 ===")]
[Tooltip("충돌/힘 적용 방향을 Scene 뷰에 시각화")]
public bool showDebugGizmos = false;
[Tooltip("충돌 및 힘 적용 로그를 콘솔에 표시")]
public bool showDebugLog = false;
// 연속 충돌 무시 시간
private float minCollisionInterval = 0.18f;// 0.08f;//0.2f;
private float lastCollisionTime = -999f;
private JellySprite jelly;
private Vector2 lastContactPoint;
private void Awake()
{
jelly = GetComponent<JellySprite>();
if (jelly == null)
{
Debug.LogError("JellySprite 컴포넌트를 찾을 수 없습니다!");
return;
}
if (autoConfigureJellySprite)
ApplyJellySpriteSettings();
}
/// <summary>JellySprite 물리 속성 적용</summary>
private void ApplyJellySpriteSettings()
{
jelly.m_Stiffness = stiffness;
jelly.m_DampingRatio = dampingRatio;
jelly.m_Mass = mass;
jelly.m_GravityScale = gravityScale;
jelly.m_Drag = drag;
jelly.m_AngularDrag = angularDrag;
if (Application.isPlaying)
{
jelly.InitMass();
jelly.UpdateJoints();
jelly.WakeUp();
}
jelly.m_UseGravity = true;
/*
#if UNITY_EDITOR
SerializedObject so = new SerializedObject(jelly);
so.Update();
so.ApplyModifiedProperties();
#endif
*/
if (showDebugLog)
Debug.Log($"[BreastJiggle] JellySprite 설정 적용 - Stiffness: {stiffness}, Damping: {dampingRatio}");
}
private void OnValidate()
{
if (jelly != null && autoConfigureJellySprite && Application.isPlaying)
ApplyJellySpriteSettings();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
if (Time.time - lastCollisionTime < minCollisionInterval)
{
if (showDebugLog)
Debug.Log("[BreastJiggle] 충돌 간격이 너무 짧아서 무시됨");
return;
}
lastCollisionTime = Time.time;
//jelly.m_Stiffness = 2.6f;
ApplyJellyForceForPlayer(DSUtil.RandomVector(0.01f, 0.1f), playerJiggleStrength);
}
// 멈추기 실패
//if (Time.time - lastCollisionTime > 1f)
//{
// lastCollisionTime = Time.time;
// jelly.m_Stiffness = 0f;
//}
}
private void HandleCollision(Collider2D other)
{
//if (!other.CompareTag("Player") && !other.CompareTag("Ball")) return;
if (!other.CompareTag("Player")) return;
if (Time.time - lastCollisionTime < minCollisionInterval)
{
if (showDebugLog)
Debug.Log("[BreastJiggle] 충돌 간격이 너무 짧아서 무시됨");
return;
}
lastCollisionTime = Time.time;
Vector2 contactPoint = other.ClosestPoint(transform.position);
lastContactPoint = contactPoint;
if (showDebugLog)
Debug.Log($"[BreastJiggle] 충돌 감지: {other.name} (Tag: {other.tag})");
// Player 터치일 경우 AddForce로 흔들림 적용
if (other.CompareTag("Player"))
{
ApplyJellyForceForPlayer(contactPoint, playerJiggleStrength);
//GameManager.Instance.StartRandomReaction();
}
/*
// Ball일 경우 물리엔진 적용 + Ball용 힘 추가
if (other.CompareTag("Ball"))
{
ApplyJellyForceForBall(contactPoint, ballJiggleStrength);
}
*/
}
/// <summary>Player/Blade 전용 힘 적용</summary>
private void ApplyJellyForceForPlayer(Vector2 contactPoint, float strength)
{
Vector2 direction = CalculateJiggleDirection(contactPoint);
float force = strength;
if (maxJellyForce > 0) force = Mathf.Min(force, maxJellyForce);
jelly.AddForce(direction * force);
if (showDebugLog)
Debug.Log($"[BreastJiggle] Player 힘 적용: {force:F2} (방향: {direction})");
if (showDebugGizmos)
Debug.DrawRay(transform.position, direction * force * 0.5f, Color.yellow, 1f);
}
/// <summary>
/// Ball이 닿았을 때 AddForce로 가슴에 가해지는 힘, 기본 물리엔진에+Add
/// </summary>
/// <param name="contactPoint"></param>
/// <param name="strength"></param>
private void ApplyJellyForceForBall(Vector2 contactPoint, float strength)
{
if (strength <= 0.0f) return;
Vector2 direction = CalculateJiggleDirection(contactPoint);
float force = strength; // Ball은 랜덤 variation 없이 일정 힘
if (maxJellyForce > 0) force = Mathf.Min(force, maxJellyForce);
jelly.AddForce(direction * force);
if (showDebugLog)
Debug.Log($"[BreastJiggle] Ball 힘 적용: {force:F2} (방향: {direction})");
if (showDebugGizmos)
Debug.DrawRay(transform.position, direction * force * 0.5f, Color.green, 1f);
}
/// <summary>충돌 지점 기반 힘 방향 계산</summary>
private Vector2 CalculateJiggleDirection(Vector2 contactPoint)
{
if (useContactPointPhysics)
{
Vector2 offset = contactPoint - (Vector2)transform.position;
Vector2 pushDir = -offset.normalized;
Vector2 gravityBiasVec = Vector2.down * verticalBias;
float horizWave = Random.Range(-horizontalSpread, horizontalSpread);
Vector2 lateralMove = Vector2.right * horizWave;
return (pushDir * contactPointInfluence + gravityBiasVec + lateralMove).normalized;
}
else
{
return new Vector2(Random.Range(-horizontalSpread, horizontalSpread),
-verticalBias + Random.Range(-0.3f, 0.2f)).normalized;
}
}
/// <summary>
/// Player(Blade)와 충돌시 이벤트 발생
/// </summary>
/// <param name="trigger"></param>
void OnJellyTriggerEnter2D(JellySprite.JellyCollider2D trigger) => HandleCollision(trigger.Collider2D);
/// <summary>
/// Ball과 충돌시 이벤트 발생
/// </summary>
/// <param name="collision"></param>
void OnJellyCollisionEnter2D(JellySprite.JellyCollision2D collision) => HandleCollision(collision.Collision2D.collider);
private void OnDrawGizmos()
{
if (!showDebugGizmos || !Application.isPlaying) return;
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(lastContactPoint, 0.15f);
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(transform.position, 0.1f);
Gizmos.color = Color.cyan;
Gizmos.DrawLine(transform.position, lastContactPoint);
}
}