290 lines
12 KiB
C#
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);
|
|
}
|
|
} |