304 lines
8.9 KiB
C#
304 lines
8.9 KiB
C#
using UnityEngine;
|
||
using TMPro;
|
||
using UnityEngine.InputSystem;
|
||
|
||
public class IncenseController : MonoBehaviour
|
||
{
|
||
[Header("References")]
|
||
public Transform incensePivot;
|
||
public Transform incenseStick;
|
||
public ParticleSystem smokeParticles;
|
||
|
||
[Header("Light (URP only)")]
|
||
public UnityEngine.Rendering.Universal.Light2D glowLight; // URP 版本
|
||
// 如果用 Built-in,用這個:
|
||
// public SpriteRenderer glowSprite;
|
||
|
||
[Header("Falling Settings")]
|
||
public float startHeight = 3f;
|
||
public float targetHeight = -2.5f;
|
||
public float fallDuration = 30f;
|
||
|
||
[Header("Struggle Settings")]
|
||
public float windForceMin = 20f;
|
||
public float windForceMax = 60f;
|
||
public float windChangeSpeed = 5f;
|
||
public float playerControlStrength = 100f;
|
||
|
||
[Header("Visual Settings")]
|
||
public float maxGlowIntensity = 3f;
|
||
|
||
[Header("UI")]
|
||
public TextMeshProUGUI messageText;
|
||
public TextMeshProUGUI angleText;
|
||
public TextMeshProUGUI timerText;
|
||
|
||
[Header("Audio (Optional)")]
|
||
public AudioSource backgroundMusic;
|
||
|
||
private float currentAngle = 90f;
|
||
private float targetWindAngle = 90f;
|
||
private float lastAngleValue = 90f;
|
||
private float stability = 1f;
|
||
private float fallTimer = 0f;
|
||
private bool hasLanded = false;
|
||
private float finalSincerity = 0f;
|
||
|
||
public System.Action<int> OnCountdownFinished;
|
||
|
||
void Start()
|
||
{
|
||
if (incensePivot != null)
|
||
{
|
||
Vector3 pos = incensePivot.position;
|
||
pos.y = startHeight;
|
||
incensePivot.position = pos;
|
||
}
|
||
|
||
currentAngle = Random.Range(60f, 120f);
|
||
targetWindAngle = currentAngle;
|
||
|
||
if (glowLight != null)
|
||
{
|
||
glowLight.intensity = 1f;
|
||
}
|
||
|
||
Debug.Log($"遊戲開始!時長: {fallDuration} 秒");
|
||
}
|
||
|
||
void Update()
|
||
{
|
||
// 按 R 重新開始
|
||
if (Keyboard.current.rKey.wasPressedThisFrame)
|
||
{
|
||
UnityEngine.SceneManagement.SceneManager.LoadScene(
|
||
UnityEngine.SceneManagement.SceneManager.GetActiveScene().name
|
||
);
|
||
}
|
||
|
||
if (!hasLanded)
|
||
{
|
||
UpdateFalling();
|
||
HandleInput();
|
||
CalculateStability();
|
||
UpdateVisuals();
|
||
UpdateUI();
|
||
|
||
// 觸發時間到事件
|
||
if (hasLanded)
|
||
{
|
||
int angle = Mathf.RoundToInt(currentAngle);
|
||
OnCountdownFinished?.Invoke(angle);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DisplayFinalResult();
|
||
}
|
||
}
|
||
|
||
void UpdateFalling()
|
||
{
|
||
fallTimer += Time.deltaTime;
|
||
|
||
if (fallTimer >= fallDuration)
|
||
{
|
||
hasLanded = true;
|
||
fallTimer = fallDuration;
|
||
|
||
float angleDeviation = Mathf.Abs(currentAngle - 90f);
|
||
finalSincerity = 1f - (angleDeviation / 30f);
|
||
finalSincerity = Mathf.Clamp01(finalSincerity);
|
||
|
||
Debug.Log($"角度: {currentAngle:F1}°, 誠意度: {finalSincerity * 100:F0}%");
|
||
return;
|
||
}
|
||
|
||
// 計算下降
|
||
float t = fallTimer / fallDuration;
|
||
float currentHeight = Mathf.Lerp(startHeight, targetHeight, t);
|
||
|
||
if (incensePivot != null)
|
||
{
|
||
Vector3 pos = incensePivot.position;
|
||
pos.y = currentHeight;
|
||
incensePivot.position = pos;
|
||
}
|
||
|
||
// 風力計算
|
||
float windMultiplier = 1f - (t * 0.5f);
|
||
float currentWindMax = windForceMax * windMultiplier;
|
||
|
||
float windChange = Random.Range(-currentWindMax, currentWindMax) * Time.deltaTime;
|
||
targetWindAngle += windChange;
|
||
targetWindAngle = Mathf.Clamp(targetWindAngle, 30f, 150f);
|
||
|
||
currentAngle = Mathf.Lerp(currentAngle, targetWindAngle, windChangeSpeed * Time.deltaTime);
|
||
currentAngle = Mathf.Clamp(currentAngle, 30f, 150f);
|
||
|
||
// 2D 旋轉(Z 軸)
|
||
if (incensePivot != null)
|
||
{
|
||
incensePivot.rotation = Quaternion.Euler(0, 0, currentAngle - 90f);
|
||
}
|
||
}
|
||
|
||
void HandleInput()
|
||
{
|
||
if (Mouse.current.leftButton.isPressed)
|
||
{
|
||
Vector2 mouseDelta = Mouse.current.delta.ReadValue();
|
||
float mouseX = mouseDelta.x;
|
||
currentAngle += mouseX * playerControlStrength * Time.deltaTime * 0.1f; // 可能需要調整係數
|
||
currentAngle = Mathf.Clamp(currentAngle, 30f, 150f);
|
||
}
|
||
}
|
||
void CalculateStability()
|
||
{
|
||
float angleChange = Mathf.Abs(currentAngle - lastAngleValue);
|
||
stability = Mathf.Clamp01(1f - angleChange / 10f);
|
||
lastAngleValue = currentAngle;
|
||
}
|
||
|
||
void UpdateVisuals()
|
||
{
|
||
float angleDeviation = Mathf.Abs(currentAngle - 90f);
|
||
float sincerity = 1f - (angleDeviation / 30f);
|
||
sincerity = Mathf.Clamp01(sincerity);
|
||
|
||
// 更新光線(URP)
|
||
if (glowLight != null)
|
||
{
|
||
Color lightColor = Color.Lerp(Color.red, Color.yellow, sincerity);
|
||
glowLight.color = lightColor;
|
||
glowLight.intensity = Mathf.Lerp(0.5f, maxGlowIntensity, sincerity);
|
||
}
|
||
|
||
// 更新煙霧
|
||
if (smokeParticles != null)
|
||
{
|
||
var emission = smokeParticles.emission;
|
||
emission.rateOverTime = Mathf.Lerp(5f, 30f, 1f - stability);
|
||
}
|
||
|
||
// 更新音樂音量(如果有)
|
||
if (backgroundMusic != null)
|
||
{
|
||
backgroundMusic.volume = Mathf.Lerp(0.2f, 0.8f, sincerity);
|
||
}
|
||
}
|
||
|
||
void UpdateUI()
|
||
{
|
||
// 角度顯示
|
||
if (angleText != null)
|
||
{
|
||
angleText.text = $"角度: {currentAngle:F0}°";
|
||
}
|
||
|
||
// 倒數計時
|
||
if (timerText != null)
|
||
{
|
||
float remaining = fallDuration - fallTimer;
|
||
timerText.text = $"倒計時{remaining:F1}秒";
|
||
|
||
if (remaining <= 5f)
|
||
{
|
||
float flicker = Mathf.Sin(Time.time * 10f) * 0.5f + 0.5f;
|
||
timerText.color = Color.Lerp(Color.red, Color.yellow, flicker);
|
||
timerText.fontSize = 40 + Mathf.Sin(Time.time * 8f) * 5f;
|
||
}
|
||
else if (remaining <= 10f)
|
||
{
|
||
timerText.color = Color.yellow;
|
||
timerText.fontSize = 40;
|
||
}
|
||
else
|
||
{
|
||
timerText.color = Color.white;
|
||
timerText.fontSize = 40;
|
||
}
|
||
}
|
||
|
||
// 誠意訊息
|
||
if (messageText != null)
|
||
{
|
||
float angleDeviation = Mathf.Abs(currentAngle - 90f);
|
||
float sincerity = 1f - (angleDeviation / 30f);
|
||
sincerity = Mathf.Clamp01(sincerity);
|
||
|
||
string message = "";
|
||
Color messageColor = Color.white;
|
||
|
||
if (sincerity >= 0.95f)
|
||
{
|
||
message = "😊 完美!最高誠意!";
|
||
messageColor = new Color(0.2f, 1f, 0.2f);
|
||
}
|
||
else if (sincerity >= 0.75f)
|
||
{
|
||
message = "不錯!還算虔誠";
|
||
messageColor = new Color(0.5f, 1f, 0.5f);
|
||
}
|
||
else if (sincerity >= 0.60f)
|
||
{
|
||
message = "普通~可以更好";
|
||
messageColor = new Color(1f, 1f, 0.3f);
|
||
}
|
||
else if (sincerity >= 0.40f)
|
||
{
|
||
message = "誠意不足...需努力";
|
||
messageColor = new Color(1f, 0.6f, 0.2f);
|
||
}
|
||
else
|
||
{
|
||
message = "太歪了!需要反省";
|
||
messageColor = new Color(1f, 0.2f, 0.2f);
|
||
}
|
||
|
||
messageText.text = message;
|
||
messageText.color = messageColor;
|
||
}
|
||
}
|
||
|
||
void DisplayFinalResult()
|
||
{
|
||
// 光線閃爍
|
||
if (glowLight != null)
|
||
{
|
||
float flicker = 0.8f + Mathf.Sin(Time.time * 3f) * 0.2f;
|
||
glowLight.intensity = maxGlowIntensity * finalSincerity * flicker;
|
||
}
|
||
|
||
// UI 顯示
|
||
if (timerText != null)
|
||
{
|
||
timerText.text = "時間到!";
|
||
timerText.color = Color.yellow;
|
||
}
|
||
|
||
if (angleText != null)
|
||
{
|
||
angleText.text = $"最終角度: {currentAngle:F0}°";
|
||
}
|
||
|
||
if (messageText != null)
|
||
{
|
||
if (finalSincerity >= 0.95f)
|
||
{
|
||
messageText.text = "完美插香!最高誠意!";
|
||
messageText.color = Color.yellow;
|
||
}
|
||
else if (finalSincerity >= 0.75f)
|
||
{
|
||
messageText.text = $"不錯!誠意度: {finalSincerity * 100:F0}%";
|
||
messageText.color = new Color(0.5f, 1f, 0.5f);
|
||
}
|
||
else
|
||
{
|
||
messageText.text = $"可惜!誠意度: {finalSincerity * 100:F0}%";
|
||
messageText.color = new Color(1f, 0.6f, 0.2f);
|
||
}
|
||
}
|
||
}
|
||
} |