1230 lines
46 KiB
C#
1230 lines
46 KiB
C#
using UnityEngine;
|
||
using Shapes;
|
||
using System.Collections.Generic;
|
||
using UnityEngine.UI;
|
||
using TMPro;
|
||
|
||
[ExecuteAlways]
|
||
public class LakeLineDrawer : ImmediateModeShapeDrawer
|
||
{
|
||
[Header("场设置")]
|
||
public float fieldSize = 80f;
|
||
public Vector2 fieldOffset = Vector2.zero;
|
||
public int pointCount = 80;
|
||
public float pointSize = 0.1f;
|
||
public float lineWidth = 0.03f;
|
||
|
||
[Header("颜色设置")]
|
||
public Color lakeDarkColor = new Color(0f, 0.5f, 1f); // 湖面低处颜色
|
||
public Color lakeLightColor = new Color(0.5f, 1f, 1f); // 湖面高处颜色
|
||
public Color edgeDarkColor = new Color(0f, 0.5f, 1f); // 边缘低处颜色
|
||
public Color edgeLightColor = new Color(0.5f, 1f, 1f, 0.3f); // 边缘高处颜色
|
||
public float edgeLineLength = 1f; // 边缘线的长度
|
||
|
||
[Header("UI控制")]
|
||
public Button upButton;
|
||
public Button downButton;
|
||
public Button leftButton;
|
||
public Button rightButton;
|
||
public float moveStep = 1f; // 每次移动的步长
|
||
public Transform targetButtonsParent; // 目标按钮的父物体(带有Layout组件的容器)
|
||
public GameObject targetButtonPrefab; // 目标按钮预制体
|
||
public float buttonSpacing = 10f; // 按钮之间的间距
|
||
|
||
[Header("目标点设置")]
|
||
public List<TargetPoint> targetPoints = new List<TargetPoint>();
|
||
public float targetSize = 0.5f;
|
||
public float rotationSpeed = 30f; // 旋转速度(度/秒)
|
||
public float floatAmplitude = 0.2f; // 起伏幅度
|
||
public float floatFrequency = 2f; // 起伏频率
|
||
public float targetLineWidth = 0.02f; // 目标点线框的宽度
|
||
public float targetHeightOffset = 0.5f; // 目标点距离湖面的高度偏移
|
||
public Color targetFillColor = Color.red; // 目标点实体颜色
|
||
public Color targetWireColor = Color.white; // 目标点线框颜色
|
||
public float wireScale = 1.05f; // 线框相对于实体的大小比例
|
||
public float targetColorInfluenceRange = 5f; // 目标点颜色影响范围
|
||
public float targetColorInfluenceStrength = 1f; // 目标点颜色影响强度
|
||
|
||
[Header("可视区域")]
|
||
public float visibleAreaSize = 20f;
|
||
|
||
// 波动参数,用于生成波动效果
|
||
public float noiseScale = 0.3f;
|
||
public float waveHeight = 1.5f;
|
||
public float waveSpeed = 1f;
|
||
public float timeScale = 1f;
|
||
[Header("涟漪参数")]
|
||
public float rippleStrength = 4f; // 增加涟漪强度
|
||
public float rippleDuration = 0.8f; // 缩短持续时间使效果更活跃
|
||
public float rippleSpreadSpeed = 6f; // 增加传播速度
|
||
public float rippleSharpness = 3f; // 增加锐度
|
||
public float rippleMaxRadius = 4f; // 增加最大半径
|
||
public float rippleFadeStartTime = 0.3f; // 提前开始淡出的时间点
|
||
public float rippleFadeCurve = 2f; // 增加淡出曲线的指数,使消失更快
|
||
|
||
[Header("障碍物设置")]
|
||
public List<Obstacle> obstacles = new List<Obstacle>();
|
||
public float obstacleHeight = 2f; // 障碍物高度
|
||
public float obstacleWidth = 0.5f; // 障碍物宽度
|
||
public Color obstacleColor = Color.white; // 障碍物颜色
|
||
public float collisionEffectDuration = 0.5f; // 碰撞效果持续时间
|
||
public float collisionEffectSize = 1f; // 碰撞效果大小
|
||
public Color collisionEffectColor = Color.white; // 碰撞效果颜色
|
||
public int collisionEffectRingCount = 3; // 涟漪环的数量
|
||
public int collisionEffectDashCount = 12; // 每个环的虚线数量
|
||
public int maxObstacleCount = 20; // 最大障碍物数量
|
||
public float minDistanceToTarget = 3f; // 障碍物到目标的最小距离
|
||
public float maxDistanceToTarget = 10f; // 障碍物到目标的最大距离
|
||
|
||
[Header("目标触发效果")]
|
||
public float targetTriggerDuration = 1f; // 目标触发效果的持续时间
|
||
public float targetTriggerStrength = 3f; // 目标触发效果的强度
|
||
public float targetTriggerSpreadSpeed = 6f; // 目标触发效果的传播速度
|
||
public Color targetTriggerColor = Color.red; // 目标触发效果的颜色
|
||
public float targetFadeInSpeed = 2f; // 目标淡入速度
|
||
public float targetScaleSpeed = 2f; // 目标大小变化速度
|
||
public float targetMaxScale = 1.5f; // 目标最大大小
|
||
|
||
[Header("文字效果设置")]
|
||
public GameObject textPrefab; // TMP文本预制体
|
||
public Canvas worldSpaceCanvas; // 世界空间Canvas
|
||
public float textFloatSpeed = 2f; // 文字上浮速度
|
||
public float textFadeInDuration = 0.3f; // 文字淡入时间
|
||
public float textFadeOutDuration = 0.5f; // 文字淡出时间
|
||
public float textScaleDuration = 0.5f; // 文字缩放时间
|
||
public float textMaxScale = 1.5f; // 文字最大缩放
|
||
public float textLifeTime = 1.5f; // 文字总生命周期
|
||
|
||
private Vector3[] points;
|
||
// 存储每个点是否在可视区域内的标记
|
||
private bool[] validPoints;
|
||
private float time;
|
||
private List<Ripple> ripples = new List<Ripple>();
|
||
private List<Ripple> targetTriggerRipples = new List<Ripple>(); // 存储目标触发的涟漪
|
||
|
||
private Dictionary<TargetPoint, Button> targetButtons = new Dictionary<TargetPoint, Button>();
|
||
|
||
private List<FloatingText> floatingTexts = new List<FloatingText>();
|
||
|
||
private class FloatingText
|
||
{
|
||
public TextMeshProUGUI textComponent;
|
||
public float startTime;
|
||
public Vector3 startPosition;
|
||
public Color targetColor;
|
||
public float currentScale = 1f;
|
||
public float currentAlpha = 0f;
|
||
}
|
||
|
||
[System.Serializable]
|
||
public class TargetPoint
|
||
{
|
||
public string name = "未命名目标"; // 目标的名字
|
||
public Vector2 gridPosition; // 网格位置(0到pointCount-1)
|
||
public TargetType type;
|
||
public Color color = Color.white;
|
||
[HideInInspector]
|
||
public float visibility = 0f; // 可见度
|
||
[HideInInspector]
|
||
public float scale = 1f; // 当前大小
|
||
[HideInInspector]
|
||
public float lastTriggerTime = -1f; // 最后一次被触发的时间
|
||
[HideInInspector]
|
||
public bool isFound = false; // 是否已被找到
|
||
}
|
||
|
||
public enum TargetType
|
||
{
|
||
Sphere,
|
||
Cube,
|
||
Torus
|
||
}
|
||
|
||
private struct Ripple
|
||
{
|
||
public Vector2 position;
|
||
public float startTime;
|
||
}
|
||
|
||
private struct RippleResult
|
||
{
|
||
public float heightOffset;
|
||
public float brightnessBoost;
|
||
public Color targetColor;
|
||
public float targetInfluence;
|
||
public float shapeDistortion;
|
||
}
|
||
|
||
[System.Serializable]
|
||
public class Obstacle
|
||
{
|
||
public Vector2 gridPosition; // 网格位置(0到pointCount-1)
|
||
public string displayText = "障碍"; // 碰撞时显示的文字
|
||
[HideInInspector]public float visibility = 0f; // 可见度
|
||
[HideInInspector]public float lastRippleTime = -1f; // 最后一次被涟漪影响的时间
|
||
[HideInInspector]public float riseProgress = 0f; // 升起进度(0-1)
|
||
[HideInInspector]public float colorFadeProgress = 0f; // 颜色淡入进度(0-1)
|
||
[HideInInspector]public float lastTextTime = -1f; // 最后一次生成文字的时间
|
||
}
|
||
|
||
private struct CollisionEffect
|
||
{
|
||
public Vector2 position;
|
||
public float startTime;
|
||
}
|
||
|
||
private List<CollisionEffect> collisionEffects = new List<CollisionEffect>();
|
||
|
||
// 涟漪效果相关常量
|
||
private const float RIPPLE_SPREAD_FACTOR = 0.3f;
|
||
private const float RIPPLE_DISTANCE_FACTOR = 2f;
|
||
private const float RIPPLE_BRIGHTNESS_MULTIPLIER = 1.5f;
|
||
private const float RIPPLE_TIME_FACTOR = 0.5f;
|
||
private const float RIPPLE_EASE_FACTOR = 0.5f;
|
||
|
||
private void Start()
|
||
{
|
||
// 添加按钮点击事件监听
|
||
if (upButton != null) upButton.onClick.AddListener(MoveUp);
|
||
if (downButton != null) downButton.onClick.AddListener(MoveDown);
|
||
if (leftButton != null) leftButton.onClick.AddListener(MoveLeft);
|
||
if (rightButton != null) rightButton.onClick.AddListener(MoveRight);
|
||
|
||
// 生成随机障碍物
|
||
GenerateRandomObstacles();
|
||
|
||
// 初始化目标按钮
|
||
InitializeTargetButtons();
|
||
}
|
||
|
||
private void InitializeTargetButtons()
|
||
{
|
||
if (targetButtonPrefab == null || targetButtonsParent == null) return;
|
||
|
||
// 清除现有的按钮
|
||
ClearTargetButtons();
|
||
}
|
||
|
||
private void CreateTargetButton(TargetPoint target)
|
||
{
|
||
if (targetButtonPrefab == null || targetButtonsParent == null) return;
|
||
|
||
// 如果按钮已经存在,直接显示
|
||
if (targetButtons.ContainsKey(target))
|
||
{
|
||
targetButtons[target].gameObject.SetActive(true);
|
||
return;
|
||
}
|
||
|
||
// 创建新按钮
|
||
var buttonObj = Instantiate(targetButtonPrefab, targetButtonsParent);
|
||
var button = buttonObj.GetComponent<Button>();
|
||
|
||
// 设置按钮文本
|
||
var text = buttonObj.GetComponentInChildren<Text>();
|
||
if (text != null)
|
||
{
|
||
text.text = string.IsNullOrEmpty(target.name) ? "目标" : target.name;
|
||
}
|
||
|
||
// 设置按钮颜色
|
||
var image = buttonObj.GetComponent<Image>();
|
||
if (image != null)
|
||
{
|
||
image.color = target.color;
|
||
}
|
||
|
||
// 添加点击事件
|
||
button.onClick.AddListener(() => MoveToTarget(target));
|
||
|
||
// 存储按钮引用
|
||
targetButtons[target] = button;
|
||
}
|
||
|
||
private void MoveToTarget(TargetPoint target)
|
||
{
|
||
float step = fieldSize / (pointCount - 1);
|
||
float halfField = fieldSize * 0.5f;
|
||
float halfVisible = visibleAreaSize * 0.5f;
|
||
|
||
// 计算目标的世界坐标
|
||
float targetX = -halfField + target.gridPosition.x * step;
|
||
float targetZ = -halfField + target.gridPosition.y * step;
|
||
|
||
// 设置相机位置,使目标在视野中心
|
||
fieldOffset.x = -targetX;
|
||
fieldOffset.y = -targetZ;
|
||
|
||
// 确保不会超出边界
|
||
float maxOffset = fieldSize * 0.5f - halfVisible * 0.8f;
|
||
fieldOffset.x = Mathf.Clamp(fieldOffset.x, -maxOffset, maxOffset);
|
||
fieldOffset.y = Mathf.Clamp(fieldOffset.y, -maxOffset, maxOffset);
|
||
}
|
||
|
||
private void ClearTargetButtons()
|
||
{
|
||
// 清除所有目标按钮
|
||
foreach (var button in targetButtons.Values)
|
||
{
|
||
if (button != null)
|
||
{
|
||
button.onClick.RemoveAllListeners();
|
||
Destroy(button.gameObject);
|
||
}
|
||
}
|
||
targetButtons.Clear();
|
||
|
||
// 清除目标按钮父物体下的所有子物体
|
||
if (targetButtonsParent != null)
|
||
{
|
||
foreach (Transform child in targetButtonsParent)
|
||
{
|
||
Destroy(child.gameObject);
|
||
}
|
||
}
|
||
|
||
// 重置所有目标的状态
|
||
foreach (var target in targetPoints)
|
||
{
|
||
target.isFound = false;
|
||
target.visibility = 0f;
|
||
target.scale = 1f;
|
||
target.lastTriggerTime = -1f;
|
||
}
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
// 移除按钮点击事件监听
|
||
if (upButton != null) upButton.onClick.RemoveListener(MoveUp);
|
||
if (downButton != null) downButton.onClick.RemoveListener(MoveDown);
|
||
if (leftButton != null) leftButton.onClick.RemoveListener(MoveLeft);
|
||
if (rightButton != null) rightButton.onClick.RemoveListener(MoveRight);
|
||
|
||
// 清除所有按钮
|
||
ClearTargetButtons();
|
||
|
||
// 清除所有漂浮文字
|
||
ClearFloatingTexts();
|
||
}
|
||
|
||
private void ClearFloatingTexts()
|
||
{
|
||
foreach (var text in floatingTexts)
|
||
{
|
||
if (text.textComponent != null)
|
||
{
|
||
Destroy(text.textComponent.gameObject);
|
||
}
|
||
}
|
||
floatingTexts.Clear();
|
||
}
|
||
|
||
private void MoveUp()
|
||
{
|
||
float halfVisible = visibleAreaSize * 0.5f;
|
||
float maxOffset = fieldSize * 0.5f - halfVisible * 0.6f; // 允许超出20%
|
||
if (fieldOffset.y < maxOffset)
|
||
{
|
||
fieldOffset.y += moveStep;
|
||
fieldOffset.y = Mathf.Clamp(fieldOffset.y, -maxOffset, maxOffset);
|
||
}
|
||
}
|
||
|
||
private void MoveDown()
|
||
{
|
||
float halfVisible = visibleAreaSize * 0.5f;
|
||
float maxOffset = fieldSize * 0.5f - halfVisible * 0.8f; // 允许超出20%
|
||
if (fieldOffset.y > -maxOffset)
|
||
{
|
||
fieldOffset.y -= moveStep;
|
||
fieldOffset.y = Mathf.Clamp(fieldOffset.y, -maxOffset, maxOffset);
|
||
}
|
||
}
|
||
|
||
private void MoveLeft()
|
||
{
|
||
float halfVisible = visibleAreaSize * 0.5f;
|
||
float maxOffset = fieldSize * 0.5f - halfVisible * 0.8f; // 允许超出20%
|
||
if (fieldOffset.x > -maxOffset)
|
||
{
|
||
fieldOffset.x -= moveStep;
|
||
fieldOffset.x = Mathf.Clamp(fieldOffset.x, -maxOffset, maxOffset);
|
||
}
|
||
}
|
||
|
||
private void MoveRight()
|
||
{
|
||
float halfVisible = visibleAreaSize * 0.5f;
|
||
float maxOffset = fieldSize * 0.5f - halfVisible * 0.8f; // 允许超出20%
|
||
if (fieldOffset.x < maxOffset)
|
||
{
|
||
fieldOffset.x += moveStep;
|
||
fieldOffset.x = Mathf.Clamp(fieldOffset.x, -maxOffset, maxOffset);
|
||
}
|
||
}
|
||
|
||
private void HandleUIInput()
|
||
{
|
||
// 由于现在使用按钮事件,这个方法可以留空或删除
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
// 如果是播放状态,则更新时间;否则设为0
|
||
time = Application.isPlaying ? Time.time * timeScale : 0f;
|
||
|
||
HandleMouseInput();
|
||
HandleUIInput();
|
||
CleanExpiredRipples();
|
||
CleanExpiredCollisionEffects();
|
||
UpdateObstacleVisibility();
|
||
UpdateTargetVisibility();
|
||
CleanExpiredTargetTriggerRipples();
|
||
UpdateFloatingTexts(); // 添加更新文字效果
|
||
|
||
// 检查所有涟漪的碰撞
|
||
if (Application.isPlaying)
|
||
{
|
||
float currentTime = Time.time;
|
||
foreach (var ripple in ripples)
|
||
{
|
||
float elapsed = currentTime - ripple.startTime;
|
||
float spread = elapsed * rippleSpreadSpeed;
|
||
CheckRippleCollisions(ripple.position, spread, currentTime);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void HandleMouseInput()
|
||
{
|
||
if (Application.isPlaying && Input.GetMouseButtonDown(0))
|
||
{
|
||
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
||
|
||
if (Physics.Raycast(ray, out RaycastHit hit))
|
||
{
|
||
Vector3 hitPos = hit.point;
|
||
|
||
ripples.Add(new Ripple
|
||
{
|
||
position = new Vector2(hitPos.x, hitPos.z),
|
||
startTime = Time.time
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
private void CleanExpiredRipples()
|
||
{
|
||
ripples.RemoveAll(r => Time.time - r.startTime > rippleDuration);
|
||
}
|
||
|
||
private void CleanExpiredCollisionEffects()
|
||
{
|
||
collisionEffects.RemoveAll(e => Time.time - e.startTime > collisionEffectDuration);
|
||
}
|
||
|
||
private void CleanExpiredTargetTriggerRipples()
|
||
{
|
||
targetTriggerRipples.RemoveAll(r => Time.time - r.startTime > targetTriggerDuration);
|
||
}
|
||
|
||
// 计算涟漪对某个点的影响,返回该点的偏移量和亮度提升
|
||
private RippleResult GetRippleEffect(float x, float z, float currentTime)
|
||
{
|
||
float rippleOffset = 0f;
|
||
float brightness = 0f;
|
||
Color targetColor = Color.white;
|
||
float maxTargetInfluence = 0f;
|
||
float shapeDistortion = 0f;
|
||
|
||
// 处理普通涟漪
|
||
foreach (var ripple in ripples)
|
||
{
|
||
ProcessRippleEffect(ripple, x, z, currentTime, ref rippleOffset, ref brightness, ref targetColor, ref maxTargetInfluence, ref shapeDistortion);
|
||
}
|
||
|
||
// 处理目标触发的涟漪
|
||
foreach (var ripple in targetTriggerRipples)
|
||
{
|
||
ProcessRippleEffect(ripple, x, z, currentTime, ref rippleOffset, ref brightness, ref targetColor, ref maxTargetInfluence, ref shapeDistortion, true);
|
||
}
|
||
|
||
return new RippleResult
|
||
{
|
||
heightOffset = rippleOffset,
|
||
brightnessBoost = brightness,
|
||
targetColor = targetColor,
|
||
targetInfluence = maxTargetInfluence,
|
||
shapeDistortion = shapeDistortion
|
||
};
|
||
}
|
||
|
||
private void ProcessRippleEffect(Ripple ripple, float x, float z, float currentTime,
|
||
ref float rippleOffset, ref float brightness, ref Color targetColor,
|
||
ref float maxTargetInfluence, ref float shapeDistortion, bool isTargetTrigger = false)
|
||
{
|
||
// 检查涟漪是否有效
|
||
if (currentTime - ripple.startTime > (isTargetTrigger ? targetTriggerDuration : rippleDuration))
|
||
return;
|
||
|
||
// 计算基础参数
|
||
float elapsed = currentTime - ripple.startTime;
|
||
float spreadSpeed = isTargetTrigger ? targetTriggerSpreadSpeed : rippleSpreadSpeed;
|
||
float spread = elapsed * spreadSpeed;
|
||
float dist = Vector2.Distance(new Vector2(x, z), ripple.position);
|
||
|
||
// 计算时间相关参数
|
||
float duration = isTargetTrigger ? targetTriggerDuration : rippleDuration;
|
||
float t = Mathf.Clamp01(elapsed / duration);
|
||
float fadeT = Mathf.Clamp01((t - rippleFadeStartTime) / (1f - rippleFadeStartTime));
|
||
float fadeCurve = Mathf.Pow(1f - fadeT, rippleFadeCurve);
|
||
|
||
// 计算缓动效果
|
||
float ease = CalculateEase(t, fadeCurve);
|
||
|
||
// 计算效果因子
|
||
float finalFactor = CalculateEffectFactor(dist, spread, t);
|
||
|
||
// 应用涟漪效果
|
||
float strength = isTargetTrigger ? targetTriggerStrength : rippleStrength;
|
||
ApplyRippleEffect(finalFactor, ease, strength, ref rippleOffset, ref brightness);
|
||
}
|
||
|
||
private float CalculateEase(float t, float fadeCurve)
|
||
{
|
||
return Mathf.SmoothStep(0, 1, Mathf.Sin(t * Mathf.PI * RIPPLE_EASE_FACTOR)) * fadeCurve;
|
||
}
|
||
|
||
private float CalculateEffectFactor(float dist, float spread, float t)
|
||
{
|
||
float maxRadius = rippleMaxRadius;
|
||
float distRatio = dist / maxRadius;
|
||
float spreadRatio = (dist - spread) / (maxRadius * RIPPLE_SPREAD_FACTOR);
|
||
float timeFactor = 1f - t * RIPPLE_TIME_FACTOR;
|
||
|
||
// 计算距离衰减
|
||
float distanceFactor = Mathf.Exp(-distRatio * distRatio * RIPPLE_DISTANCE_FACTOR);
|
||
|
||
// 计算扩散衰减
|
||
float spreadFactor = Mathf.Exp(-spreadRatio * spreadRatio * rippleSharpness * timeFactor);
|
||
|
||
return distanceFactor * spreadFactor;
|
||
}
|
||
|
||
private void ApplyRippleEffect(float finalFactor, float ease, float strength,
|
||
ref float rippleOffset, ref float brightness)
|
||
{
|
||
rippleOffset += finalFactor * strength * ease;
|
||
brightness += finalFactor * ease * RIPPLE_BRIGHTNESS_MULTIPLIER;
|
||
}
|
||
|
||
private void CheckRippleCollisions(Vector2 ripplePos, float spread, float currentTime)
|
||
{
|
||
float step = fieldSize / (pointCount - 1);
|
||
float halfField = fieldSize * 0.5f;
|
||
|
||
// 检查障碍物碰撞
|
||
foreach (var obstacle in obstacles)
|
||
{
|
||
float x = -halfField + obstacle.gridPosition.x * step + fieldOffset.x;
|
||
float z = -halfField + obstacle.gridPosition.y * step + fieldOffset.y;
|
||
Vector2 worldPos = new Vector2(x, z);
|
||
|
||
float dist = Vector2.Distance(ripplePos, worldPos);
|
||
if (dist <= rippleMaxRadius && Mathf.Abs(dist - spread) < obstacleWidth * 0.5f)
|
||
{
|
||
obstacle.lastRippleTime = currentTime;
|
||
|
||
if (obstacle.visibility <= 0f)
|
||
{
|
||
obstacle.visibility = 0.2f;
|
||
obstacle.riseProgress = 0f;
|
||
obstacle.colorFadeProgress = 0f;
|
||
}
|
||
else if (obstacle.visibility < 1f)
|
||
{
|
||
obstacle.riseProgress = Mathf.Max(obstacle.riseProgress, obstacle.visibility);
|
||
}
|
||
|
||
collisionEffects.Add(new CollisionEffect
|
||
{
|
||
position = worldPos,
|
||
startTime = currentTime
|
||
});
|
||
|
||
// 检查是否可以生成新的文字(冷却时间为2秒)
|
||
if (currentTime - obstacle.lastTextTime > 2f)
|
||
{
|
||
Vector3 textPosition = new Vector3(x, GetHeight(x - fieldOffset.x, z - fieldOffset.y, time) + obstacleHeight * 0.5f, z);
|
||
CreateFloatingText(textPosition, obstacle.displayText, obstacleColor);
|
||
obstacle.lastTextTime = currentTime;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检查目标碰撞
|
||
foreach (var target in targetPoints)
|
||
{
|
||
float x = -halfField + target.gridPosition.x * step + fieldOffset.x;
|
||
float z = -halfField + target.gridPosition.y * step + fieldOffset.y;
|
||
Vector2 worldPos = new Vector2(x, z);
|
||
|
||
float dist = Vector2.Distance(ripplePos, worldPos);
|
||
if (dist <= rippleMaxRadius && Mathf.Abs(dist - spread) < 0.5f && target.lastTriggerTime < currentTime - targetTriggerDuration)
|
||
{
|
||
// 确保目标点之前没有被触发过
|
||
if (target.lastTriggerTime <= 0)
|
||
{
|
||
target.lastTriggerTime = currentTime;
|
||
target.isFound = true;
|
||
target.visibility = 0.2f; // 初始可见度
|
||
targetTriggerRipples.Add(new Ripple
|
||
{
|
||
position = worldPos,
|
||
startTime = currentTime
|
||
});
|
||
|
||
// 创建目标按钮
|
||
CreateTargetButton(target);
|
||
|
||
Vector3 textPosition = new Vector3(x, GetHeight(x - fieldOffset.x, z - fieldOffset.y, time) + targetHeightOffset, z);
|
||
CreateFloatingText(textPosition, target.name, target.color);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 修改GetHeight方法,使波浪更自然
|
||
private float GetHeight(float x, float z, float currentTime)
|
||
{
|
||
float noise = 0;
|
||
float amp = 1f;
|
||
float freq = 1f;
|
||
|
||
// 生成多个频率的噪声,模拟更复杂的波动
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
// 基于当前时间和位置生成噪声值
|
||
float nx = x * freq * noiseScale;
|
||
float nz = z * freq * noiseScale;
|
||
float nt = currentTime * waveSpeed * freq;
|
||
|
||
// 使用平滑的插值
|
||
float noise1 = Mathf.PerlinNoise(nx + nt, nz + nt);
|
||
float noise2 = Mathf.PerlinNoise(nx + nt + 0.1f, nz + nt + 0.1f);
|
||
noise += Mathf.Lerp(noise1, noise2, Mathf.SmoothStep(0, 1, (Mathf.Sin(currentTime * 0.5f) + 1) * 0.5f)) * amp;
|
||
|
||
amp *= 0.5f; // 每次衰减幅度
|
||
freq *= 2f; // 每次增加频率
|
||
}
|
||
|
||
return noise * waveHeight;
|
||
}
|
||
|
||
// 绘制湖面
|
||
public override void DrawShapes(Camera cam)
|
||
{
|
||
using (Draw.Command(cam))
|
||
{
|
||
Draw.ResetAllDrawStates();
|
||
Draw.LineGeometry = LineGeometry.Volumetric3D;
|
||
Draw.Thickness = lineWidth;
|
||
|
||
float step = fieldSize / (pointCount - 1);
|
||
float halfField = fieldSize * 0.5f;
|
||
float halfVisible = visibleAreaSize * 0.5f;
|
||
|
||
if (points == null || points.Length != pointCount * pointCount)
|
||
points = new Vector3[pointCount * pointCount];
|
||
if (validPoints == null || validPoints.Length != pointCount * pointCount)
|
||
validPoints = new bool[pointCount * pointCount];
|
||
|
||
CalculatePointPositions(step, halfField, halfVisible);
|
||
DrawPointsAndLines(step, halfField, halfVisible);
|
||
DrawEdgeLines(halfField, halfVisible);
|
||
DrawTargetPoints();
|
||
DrawObstacles(); // 绘制障碍物
|
||
DrawCollisionEffects(); // 绘制碰撞效果
|
||
}
|
||
}
|
||
|
||
// 计算每个点的位置和涟漪效果
|
||
private void CalculatePointPositions(float step, float halfField, float halfVisible)
|
||
{
|
||
// 遍历每个点,计算其位置和涟漪影响
|
||
for (int i = 0; i < pointCount; i++)
|
||
{
|
||
for (int j = 0; j < pointCount; j++)
|
||
{
|
||
// 计算当前点的位置(x,z)
|
||
float x = -halfField + j * step + fieldOffset.x;
|
||
float z = -halfField + i * step + fieldOffset.y;
|
||
|
||
// 判断该点是否在可视区域内
|
||
bool inside = Mathf.Abs(x) <= halfVisible && Mathf.Abs(z) <= halfVisible;
|
||
int index = i * pointCount + j; // 计算当前点的索引
|
||
validPoints[index] = inside; // 设置该点是否有效
|
||
|
||
if (!inside)
|
||
continue;
|
||
|
||
// 获取基础波浪高度,并计算涟漪影响
|
||
float baseHeight = GetHeight(x, z, time);
|
||
RippleResult ripple = GetRippleEffect(x, z, Time.time);
|
||
|
||
float finalY = baseHeight + ripple.heightOffset;
|
||
|
||
// 创建点的坐标
|
||
Vector3 pos = new Vector3(x, finalY, z);
|
||
points[index] = pos; // 存储计算得到的点
|
||
|
||
// 计算亮度:基于高度,越高越亮
|
||
float heightT = Mathf.InverseLerp(-waveHeight, waveHeight, finalY);
|
||
float brightness = Mathf.Clamp01(1f + ripple.brightnessBoost); // 计算亮度,防止超出范围
|
||
|
||
// 使用与 LakeVisualizer 相同的颜色渐变
|
||
Color baseColor = Color.Lerp(new Color(0.5f, 1f, 1f), new Color(0f, 0.5f, 1f), heightT);
|
||
baseColor *= brightness;
|
||
|
||
// 如果受到目标影响,混合目标颜色
|
||
if (ripple.targetInfluence > 0)
|
||
{
|
||
baseColor = Color.Lerp(baseColor, ripple.targetColor, ripple.targetInfluence);
|
||
}
|
||
|
||
// 绘制当前点
|
||
Draw.Sphere(pos, pointSize * (1f - heightT * 0.5f), baseColor);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 绘制点与点之间的连线
|
||
private void DrawPointsAndLines(float step, float halfField, float halfVisible)
|
||
{
|
||
for (int i = 0; i < pointCount; i++)
|
||
{
|
||
for (int j = 0; j < pointCount; j++)
|
||
{
|
||
int index = i * pointCount + j;
|
||
if (!validPoints[index])
|
||
continue;
|
||
|
||
Vector3 current = points[index];
|
||
|
||
if (j < pointCount - 1 && validPoints[index + 1])
|
||
{
|
||
Vector3 next = points[index + 1];
|
||
DrawLineBetweenPoints(current, next, halfField, halfVisible);
|
||
}
|
||
|
||
// 绘制与下边点的连线
|
||
if (i < pointCount - 1 && validPoints[index + pointCount])
|
||
{
|
||
Vector3 next = points[index + pointCount];
|
||
DrawLineBetweenPoints(current, next, halfField, halfVisible);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 绘制两点之间的连线,加入深度和颜色的渐变
|
||
private void DrawLineBetweenPoints(Vector3 current, Vector3 next, float halfField, float halfVisible)
|
||
{
|
||
// 计算连线的平均高度
|
||
float avgY = (current.y + next.y) * 0.5f;
|
||
float heightT = Mathf.InverseLerp(-waveHeight, waveHeight, avgY);
|
||
|
||
// 计算连线的颜色,依赖于高度
|
||
Color lineColor = Color.Lerp(Color.cyan, new Color(0, 0.5f, 1f, 0.3f), heightT);
|
||
|
||
// 绘制连线
|
||
Draw.Line(current, next, lineWidth * (1f - heightT * 0.5f), lineColor);
|
||
}
|
||
|
||
private void DrawEdgeLines(float halfField, float halfVisible)
|
||
{
|
||
// 绘制湖面边缘的向下线条(无扰动)
|
||
float step = fieldSize / (pointCount - 1);
|
||
float halfEdgeLineLength = edgeLineLength * 0.5f; // 设置边缘线长度
|
||
|
||
// 1. 绘制 X 轴的边缘线(左边和右边)
|
||
for (float z = -halfField; z <= halfField; z += step)
|
||
{
|
||
float worldZ = z + fieldOffset.y;
|
||
float x = halfField + fieldOffset.x; // 右边
|
||
DrawEdgeLine(x, worldZ, halfVisible);
|
||
x = -halfField + fieldOffset.x; // 左边
|
||
DrawEdgeLine(x, worldZ, halfVisible);
|
||
}
|
||
|
||
// 2. 绘制 Z 轴的边缘线(上边和下边)
|
||
for (float x = -halfField; x <= halfField; x += step)
|
||
{
|
||
float worldX = x + fieldOffset.x;
|
||
float z = halfField + fieldOffset.y; // 上边
|
||
DrawEdgeLine(worldX, z, halfVisible);
|
||
z = -halfField + fieldOffset.y; // 下边
|
||
DrawEdgeLine(worldX, z, halfVisible);
|
||
}
|
||
}
|
||
|
||
private void DrawEdgeLine(float x, float z, float halfVisible)
|
||
{
|
||
// 检查点是否在可视区域内
|
||
if (Mathf.Abs(x) > halfVisible || Mathf.Abs(z) > halfVisible)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 计算湖面高度作为起始点的Y坐标
|
||
float startY = GetHeight(x - fieldOffset.x, z - fieldOffset.y, time); // 获取湖面高度
|
||
|
||
// 设置边缘线的起始点
|
||
Vector3 start = new Vector3(x, startY, z);
|
||
|
||
// 设置边缘线的终止点,向下延伸一定的长度
|
||
Vector3 end = new Vector3(x, startY - edgeLineLength, z); // 向下延伸的线条
|
||
|
||
// 设置颜色,基于高度
|
||
float heightT = Mathf.InverseLerp(-waveHeight, waveHeight, startY);
|
||
Color lineColor = Color.Lerp(edgeDarkColor, edgeLightColor, heightT);
|
||
lineColor.a *= 0.8f; // 设置透明度
|
||
|
||
// 绘制边缘线
|
||
Draw.Line(start, end, lineWidth * 0.5f, lineColor);
|
||
}
|
||
|
||
// 绘制目标点
|
||
private void DrawTargetPoints()
|
||
{
|
||
float step = fieldSize / (pointCount - 1);
|
||
float halfField = fieldSize * 0.5f;
|
||
float halfVisible = visibleAreaSize * 0.5f;
|
||
|
||
foreach (var target in targetPoints)
|
||
{
|
||
// 将网格位置转换为世界坐标
|
||
float x = -halfField + target.gridPosition.x * step + fieldOffset.x;
|
||
float z = -halfField + target.gridPosition.y * step + fieldOffset.y;
|
||
|
||
// 检查是否在可视区域内
|
||
if (Mathf.Abs(x) > halfVisible || Mathf.Abs(z) > halfVisible)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
// 获取该点的高度
|
||
float height = GetHeight(x, z, time);
|
||
|
||
// 添加额外的起伏效果和高度偏移
|
||
float floatOffset = Mathf.Sin(time * floatFrequency) * floatAmplitude;
|
||
Vector3 position = new Vector3(x, height + floatOffset + targetHeightOffset, z);
|
||
|
||
// 计算旋转
|
||
float rotation = time * rotationSpeed;
|
||
Quaternion rot = Quaternion.Euler(0, rotation, 0);
|
||
|
||
// 计算当前颜色和大小
|
||
Color currentColor = Color.Lerp(Color.clear, target.color, target.visibility);
|
||
float currentSize = targetSize * target.scale;
|
||
|
||
// 绘制外层立方体线框
|
||
DrawWireCube(position, rot, currentSize * wireScale, currentColor);
|
||
|
||
// 根据类型绘制内部形状
|
||
switch (target.type)
|
||
{
|
||
case TargetType.Sphere:
|
||
Draw.Sphere(position, currentSize * 0.8f, currentColor);
|
||
break;
|
||
case TargetType.Cube:
|
||
Draw.Cube(position, rot, currentSize * 0.8f, currentColor);
|
||
break;
|
||
case TargetType.Torus:
|
||
Draw.Torus(position, rot, currentSize * 0.8f, currentSize * 0.3f, currentColor);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void DrawWireCube(Vector3 position, Quaternion rotation, float size, Color color)
|
||
{
|
||
Vector3[] vertices = new Vector3[]
|
||
{
|
||
new Vector3(-size, -size, -size),
|
||
new Vector3(size, -size, -size),
|
||
new Vector3(size, size, -size),
|
||
new Vector3(-size, size, -size),
|
||
new Vector3(-size, -size, size),
|
||
new Vector3(size, -size, size),
|
||
new Vector3(size, size, size),
|
||
new Vector3(-size, size, size)
|
||
};
|
||
|
||
// 应用旋转
|
||
for (int i = 0; i < vertices.Length; i++)
|
||
{
|
||
vertices[i] = rotation * vertices[i] + position;
|
||
}
|
||
|
||
// 绘制边
|
||
int[] edges = new int[]
|
||
{
|
||
0,1, 1,2, 2,3, 3,0, // 底面
|
||
4,5, 5,6, 6,7, 7,4, // 顶面
|
||
0,4, 1,5, 2,6, 3,7 // 连接线
|
||
};
|
||
|
||
for (int i = 0; i < edges.Length; i += 2)
|
||
{
|
||
Draw.Line(vertices[edges[i]], vertices[edges[i+1]], targetLineWidth, color);
|
||
}
|
||
}
|
||
|
||
private void DrawObstacles()
|
||
{
|
||
float step = fieldSize / (pointCount - 1);
|
||
float halfField = fieldSize * 0.5f;
|
||
float halfVisible = visibleAreaSize * 0.5f;
|
||
|
||
foreach (var obstacle in obstacles)
|
||
{
|
||
// 在编辑器中始终显示
|
||
if (Application.isPlaying && obstacle.visibility <= 0f)
|
||
continue;
|
||
|
||
// 将网格位置转换为世界坐标
|
||
float x = -halfField + obstacle.gridPosition.x * step + fieldOffset.x;
|
||
float z = -halfField + obstacle.gridPosition.y * step + fieldOffset.y;
|
||
|
||
// 检查是否在可视区域内
|
||
if (Mathf.Abs(x) > halfVisible || Mathf.Abs(z) > halfVisible)
|
||
continue;
|
||
|
||
// 获取该点的高度
|
||
float height = GetHeight(x, z, time);
|
||
|
||
// 计算颜色和升起效果
|
||
Color currentColor = obstacleColor;
|
||
if (Application.isPlaying)
|
||
{
|
||
currentColor.a *= obstacle.visibility * obstacle.colorFadeProgress;
|
||
}
|
||
float riseHeight = obstacleHeight * (Application.isPlaying ? obstacle.riseProgress : 1f);
|
||
|
||
// 绘制障碍物
|
||
Vector3 basePos = new Vector3(x, height, z);
|
||
float halfWidth = obstacleWidth * 0.5f;
|
||
|
||
// 绘制底部
|
||
Draw.Line(basePos + new Vector3(-halfWidth, 0, -halfWidth), basePos + new Vector3(halfWidth, 0, -halfWidth), lineWidth, currentColor);
|
||
Draw.Line(basePos + new Vector3(halfWidth, 0, -halfWidth), basePos + new Vector3(halfWidth, 0, halfWidth), lineWidth, currentColor);
|
||
Draw.Line(basePos + new Vector3(halfWidth, 0, halfWidth), basePos + new Vector3(-halfWidth, 0, halfWidth), lineWidth, currentColor);
|
||
Draw.Line(basePos + new Vector3(-halfWidth, 0, halfWidth), basePos + new Vector3(-halfWidth, 0, -halfWidth), lineWidth, currentColor);
|
||
|
||
// 绘制顶部(考虑升起效果)
|
||
Vector3 topPos = basePos + Vector3.up * riseHeight;
|
||
Draw.Line(topPos + new Vector3(-halfWidth, 0, -halfWidth), topPos + new Vector3(halfWidth, 0, -halfWidth), lineWidth, currentColor);
|
||
Draw.Line(topPos + new Vector3(halfWidth, 0, -halfWidth), topPos + new Vector3(halfWidth, 0, halfWidth), lineWidth, currentColor);
|
||
Draw.Line(topPos + new Vector3(halfWidth, 0, halfWidth), topPos + new Vector3(-halfWidth, 0, halfWidth), lineWidth, currentColor);
|
||
Draw.Line(topPos + new Vector3(-halfWidth, 0, halfWidth), topPos + new Vector3(-halfWidth, 0, -halfWidth), lineWidth, currentColor);
|
||
|
||
// 绘制连接线
|
||
Draw.Line(basePos + new Vector3(-halfWidth, 0, -halfWidth), topPos + new Vector3(-halfWidth, 0, -halfWidth), lineWidth, currentColor);
|
||
Draw.Line(basePos + new Vector3(halfWidth, 0, -halfWidth), topPos + new Vector3(halfWidth, 0, -halfWidth), lineWidth, currentColor);
|
||
Draw.Line(basePos + new Vector3(halfWidth, 0, halfWidth), topPos + new Vector3(halfWidth, 0, halfWidth), lineWidth, currentColor);
|
||
Draw.Line(basePos + new Vector3(-halfWidth, 0, halfWidth), topPos + new Vector3(-halfWidth, 0, halfWidth), lineWidth, currentColor);
|
||
}
|
||
}
|
||
|
||
private void DrawCollisionEffects()
|
||
{
|
||
foreach (var effect in collisionEffects)
|
||
{
|
||
float elapsed = Time.time - effect.startTime;
|
||
float t = elapsed / collisionEffectDuration;
|
||
|
||
// 计算当前效果的大小和透明度
|
||
float scale = Mathf.Lerp(0f, collisionEffectSize, t);
|
||
float alpha = Mathf.Lerp(1f, 0f, t);
|
||
Color effectColor = collisionEffectColor;
|
||
effectColor.a *= alpha;
|
||
|
||
// 计算效果位置
|
||
Vector3 effectPos = new Vector3(effect.position.x,
|
||
GetHeight(effect.position.x - fieldOffset.x, effect.position.y - fieldOffset.y, time),
|
||
effect.position.y);
|
||
|
||
// 设置虚线样式
|
||
Draw.UseDashes = true;
|
||
Draw.DashStyle = DashStyle.RelativeDashes(DashType.Basic, 0.5f, 0.5f, DashSnapping.Tiling, 0f, 0f);
|
||
|
||
// 绘制同心圆涟漪
|
||
for (int i = 0; i < collisionEffectRingCount; i++)
|
||
{
|
||
float ringScale = scale * (i + 1) / collisionEffectRingCount;
|
||
float ringAlpha = alpha * (1f - (float)i / collisionEffectRingCount);
|
||
Color ringColor = effectColor;
|
||
ringColor.a *= ringAlpha;
|
||
|
||
// 使用Shapes的Ring方法绘制虚线圆环
|
||
Draw.Ring(effectPos, Vector3.up, ringScale, lineWidth * (1f - t), ringColor);
|
||
}
|
||
|
||
// 恢复默认样式
|
||
Draw.UseDashes = false;
|
||
}
|
||
}
|
||
|
||
private void UpdateObstacleVisibility()
|
||
{
|
||
float currentTime = Application.isPlaying ? Time.time : 0f;
|
||
foreach (var obstacle in obstacles)
|
||
{
|
||
// 在编辑器中,如果没有被涟漪影响过,保持可见
|
||
if (!Application.isPlaying)
|
||
{
|
||
// obstacle.visibility = 1f;
|
||
// obstacle.riseProgress = 1f;
|
||
// obstacle.colorFadeProgress = 1f;
|
||
continue;
|
||
}
|
||
|
||
// 在游戏模式下,如果从未被涟漪影响过,保持不可见
|
||
if (obstacle.lastRippleTime <= 0f)
|
||
{
|
||
obstacle.visibility = 0f;
|
||
obstacle.riseProgress = 0f;
|
||
obstacle.colorFadeProgress = 0f;
|
||
continue;
|
||
}
|
||
|
||
// 如果超过6秒没有被涟漪影响,开始逐渐消失
|
||
if (currentTime - obstacle.lastRippleTime > 6f)
|
||
{
|
||
float fadeSpeed = Time.deltaTime;
|
||
obstacle.visibility = Mathf.Max(0f, obstacle.visibility - fadeSpeed);
|
||
// 同时降低升起高度
|
||
obstacle.riseProgress = Mathf.Max(0f, obstacle.riseProgress - fadeSpeed);
|
||
}
|
||
else if (obstacle.visibility > 0f)
|
||
{
|
||
// 如果正在显现,继续增加可见度
|
||
obstacle.visibility = Mathf.Min(1f, obstacle.visibility + Time.deltaTime * 0.5f);
|
||
// 同时增加升起高度
|
||
obstacle.riseProgress = Mathf.Min(1f, obstacle.riseProgress + Time.deltaTime * 2f);
|
||
}
|
||
|
||
// 更新颜色淡入进度
|
||
if (obstacle.visibility > 0f)
|
||
{
|
||
obstacle.colorFadeProgress = Mathf.Min(1f, obstacle.colorFadeProgress + Time.deltaTime * 3f);
|
||
}
|
||
else
|
||
{
|
||
obstacle.colorFadeProgress = 0f;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void UpdateTargetVisibility()
|
||
{
|
||
float currentTime = Time.time;
|
||
foreach (var target in targetPoints)
|
||
{
|
||
// 如果目标已经被触发过
|
||
if (target.lastTriggerTime > 0)
|
||
{
|
||
// 更新可见度
|
||
target.visibility = Mathf.Min(1f, target.visibility + Time.deltaTime * targetFadeInSpeed);
|
||
|
||
// 更新大小
|
||
float targetScale = 1f + (targetMaxScale - 1f) * Mathf.Sin(currentTime * targetScaleSpeed);
|
||
target.scale = Mathf.Lerp(target.scale, targetScale, Time.deltaTime * targetScaleSpeed);
|
||
}
|
||
else
|
||
{
|
||
// 如果从未被触发过,确保保持不可见
|
||
target.visibility = 0f;
|
||
target.scale = 1f;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void GenerateRandomObstacles()
|
||
{
|
||
obstacles.Clear();
|
||
float step = fieldSize / (pointCount - 1);
|
||
float halfField = fieldSize * 0.5f;
|
||
|
||
// 为每个目标生成周围的障碍物
|
||
foreach (var target in targetPoints)
|
||
{
|
||
// 计算目标的世界坐标
|
||
float targetX = -halfField + target.gridPosition.x * step;
|
||
float targetZ = -halfField + target.gridPosition.y * step;
|
||
Vector2 targetWorldPos = new Vector2(targetX, targetZ);
|
||
|
||
// 计算这个目标周围应该生成的障碍物数量
|
||
int obstacleCount = Mathf.CeilToInt(maxObstacleCount / targetPoints.Count);
|
||
|
||
for (int i = 0; i < obstacleCount; i++)
|
||
{
|
||
// 生成随机角度和距离
|
||
float angle = Random.Range(0f, 360f) * Mathf.Deg2Rad;
|
||
float distance = Random.Range(minDistanceToTarget, maxDistanceToTarget);
|
||
|
||
// 计算障碍物的世界坐标
|
||
float obstacleX = targetX + Mathf.Cos(angle) * distance;
|
||
float obstacleZ = targetZ + Mathf.Sin(angle) * distance;
|
||
|
||
// 转换为网格坐标
|
||
int gridX = Mathf.RoundToInt((obstacleX + halfField) / step);
|
||
int gridZ = Mathf.RoundToInt((obstacleZ + halfField) / step);
|
||
|
||
// 确保坐标在有效范围内
|
||
if (gridX >= 0 && gridX < pointCount && gridZ >= 0 && gridZ < pointCount)
|
||
{
|
||
// 检查是否与现有障碍物重叠
|
||
bool isOverlapping = false;
|
||
foreach (var existingObstacle in obstacles)
|
||
{
|
||
float dist = Vector2.Distance(
|
||
new Vector2(gridX, gridZ),
|
||
existingObstacle.gridPosition
|
||
);
|
||
if (dist < 2f) // 最小间距
|
||
{
|
||
isOverlapping = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!isOverlapping)
|
||
{
|
||
obstacles.Add(new Obstacle
|
||
{
|
||
gridPosition = new Vector2(gridX, gridZ),
|
||
visibility = 0f,
|
||
lastRippleTime = -1f,
|
||
riseProgress = 0f,
|
||
colorFadeProgress = 0f
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void OnDrawGizmos()
|
||
{
|
||
if (!Application.isPlaying)
|
||
{
|
||
float step = fieldSize / (pointCount - 1);
|
||
float halfField = fieldSize * 0.5f;
|
||
|
||
foreach (var target in targetPoints)
|
||
{
|
||
float x = -halfField + target.gridPosition.x * step + fieldOffset.x;
|
||
float z = -halfField + target.gridPosition.y * step + fieldOffset.y;
|
||
float height = GetHeight(x, z, time);
|
||
|
||
// 绘制目标点
|
||
Gizmos.color = target.color;
|
||
Gizmos.DrawSphere(new Vector3(x, height + targetHeightOffset, z), targetSize * 0.5f);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 添加一个公共方法,可以在需要时手动清除按钮
|
||
public void ResetGame()
|
||
{
|
||
ClearTargetButtons();
|
||
ClearFloatingTexts();
|
||
// 重新生成障碍物
|
||
GenerateRandomObstacles();
|
||
// 重新初始化按钮
|
||
InitializeTargetButtons();
|
||
}
|
||
|
||
private void UpdateFloatingTexts()
|
||
{
|
||
float currentTime = Time.time;
|
||
for (int i = floatingTexts.Count - 1; i >= 0; i--)
|
||
{
|
||
var text = floatingTexts[i];
|
||
float elapsed = currentTime - text.startTime;
|
||
|
||
if (elapsed >= textLifeTime)
|
||
{
|
||
if (text.textComponent != null)
|
||
{
|
||
Destroy(text.textComponent.gameObject);
|
||
}
|
||
floatingTexts.RemoveAt(i);
|
||
continue;
|
||
}
|
||
|
||
// 更新位置
|
||
float floatOffset = elapsed * textFloatSpeed;
|
||
text.textComponent.transform.position = text.startPosition + Vector3.up * floatOffset;
|
||
|
||
// 更新透明度
|
||
if (elapsed < textFadeInDuration)
|
||
{
|
||
text.currentAlpha = elapsed / textFadeInDuration;
|
||
}
|
||
else if (elapsed > textLifeTime - textFadeOutDuration)
|
||
{
|
||
text.currentAlpha = (textLifeTime - elapsed) / textFadeOutDuration;
|
||
}
|
||
else
|
||
{
|
||
text.currentAlpha = 1f;
|
||
}
|
||
|
||
// 更新缩放
|
||
if (elapsed < textScaleDuration)
|
||
{
|
||
text.currentScale = Mathf.Lerp(1f, textMaxScale, elapsed / textScaleDuration);
|
||
}
|
||
else
|
||
{
|
||
text.currentScale = textMaxScale;
|
||
}
|
||
|
||
// 应用颜色和透明度
|
||
Color currentColor = text.targetColor;
|
||
currentColor.a = text.currentAlpha;
|
||
text.textComponent.color = currentColor;
|
||
text.textComponent.transform.localScale = Vector3.one * text.currentScale;
|
||
}
|
||
}
|
||
|
||
private void CreateFloatingText(Vector3 position, string text, Color color)
|
||
{
|
||
if (textPrefab == null || worldSpaceCanvas == null) return;
|
||
|
||
GameObject textObj = Instantiate(textPrefab, worldSpaceCanvas.transform);
|
||
TextMeshProUGUI tmp = textObj.GetComponent<TextMeshProUGUI>();
|
||
if (tmp != null)
|
||
{
|
||
tmp.text = text;
|
||
tmp.color = new Color(color.r, color.g, color.b, 0f); // 初始透明
|
||
|
||
// 设置世界空间位置
|
||
textObj.transform.position = position;
|
||
|
||
floatingTexts.Add(new FloatingText
|
||
{
|
||
textComponent = tmp,
|
||
startTime = Time.time,
|
||
startPosition = position,
|
||
targetColor = color
|
||
});
|
||
}
|
||
}
|
||
}
|