Files
DefendNJ/Assets/_DefendNJ/Scripts/Bullets/CleanBullet.cs
2025-09-28 18:24:09 +08:00

177 lines
6.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections;
using System.Collections.Generic;
using Mirror;
using UnityEngine;
public class CleanBullet : NetworkBehaviour
{
[Header("Flight")]
[Tooltip("水平前方飞行的目标距离(单位:米)")]
public float flightDistance = 30f;
[Tooltip("从发射到落地的飞行时间(单位:秒)。根据需要调整以改变弧度高低")]
public float flightTime = 2.0f;
[Tooltip("是否在发射时立即启用物理重力Rigidbody.useGravity")]
public bool enableGravity = true;
[Tooltip("爆炸特效存在多长时间之后会被销毁0 表示不自动销毁")]
public float explosionDuration = 3f;
[Tooltip("碰撞判断的层掩码(比如 Ground - 若为 0 则对任何碰撞都触发爆炸")]
public LayerMask groundLayer = 0;
[Tooltip("碰撞触发时是否只在第一次触发后执行爆炸(避免多次触发)")]
public bool onlyExplodeOnce = true;
[Header("Options")]
[Tooltip("是否在 Start 中自动发射(否则外部调用 Launch()")]
public bool autoLaunch = true;
[Tooltip("发射时额外的高度偏移local space用于把炮弹向上或向下偏移起始点")]
public Vector3 launchLocalOffset = Vector3.zero;
// 内部
Rigidbody rb;
bool launched = false;
float launchTime = 0f;
bool exploded = false;
void Awake()
{
rb = GetComponent<Rigidbody>();
}
void Start()
{
if (autoLaunch)
{
}
}
/// <summary>
/// 外部调用:发射炮弹(使用当前 transform.forward 方向)
/// </summary>
public void Launch()
{
if (launched) return;
launched = true;
launchTime = Time.time;
// 确保 Rigidbody 存在
if (rb == null) rb = GetComponent<Rigidbody>();
// 打开/关闭重力(通常使用真实重力)
rb.useGravity = enableGravity;
// 计算初速度:选定飞行时间 T使水平距离为 flightDistance。
// 令水平速度 vx = forward * (flightDistance / T)
// 垂直速度 vy = -0.5 * g * T (使得在时间 T 回到同一高度)
// 公式来源: y(T) = vy * T + 0.5 * g * T^2 = 0 -> vy = -0.5 * g * T
Vector3 worldForward = transform.forward;
Vector3 startPos = transform.position + transform.TransformVector(launchLocalOffset);
// 使用 Physics.gravity 而不是固定值,保持一致
Vector3 g = Physics.gravity; // 通常是 (0, -9.81, 0)
// 水平分量X-Z 面)
Vector3 forwardHorizontal = new Vector3(worldForward.x, 0f, worldForward.z);
float forwardHorizMag = forwardHorizontal.magnitude;
if (forwardHorizMag < 1e-6f)
{
// 如果 forward 几乎垂直,则用 transform.up 和 transform.right 组合,避免除零
forwardHorizontal = transform.right;
forwardHorizMag = forwardHorizontal.magnitude;
}
forwardHorizontal.Normalize();
float T = Mathf.Max(0.01f, flightTime); // 防止除零
// 计算水平速度大小
float horizontalSpeed = flightDistance / T;
// 计算垂直速度
float vy = -0.5f * g.y * T; // g.y 是负值,注意符号
Vector3 initialVelocity = forwardHorizontal * horizontalSpeed + Vector3.up * vy;
// 设置 Rigidbody 的速度
rb.velocity = initialVelocity;
// 如果需要,也可以使用 AddForce 而不是直接设置速度:
// rb.AddForce(initialVelocity - rb.velocity, ForceMode.VelocityChange);
// 启动一个超时监测,防止永远不触发碰撞
StartCoroutine(FlightTimeoutRoutine(T + 0.5f));
}
IEnumerator FlightTimeoutRoutine(float wait)
{
yield return new WaitForSeconds(wait);
if (!exploded)
{
Explode(transform.position, Vector3.up);
}
}
void OnCollisionEnter(Collision collision)
{
if (exploded && onlyExplodeOnce) return;
// 如果 groundLayer 为 0表示对任何碰撞都触发爆炸
if (groundLayer == 0 || ((groundLayer.value & (1 << collision.gameObject.layer)) != 0))
{
// 在碰撞点爆炸(取第一个接触点)
ContactPoint contact = collision.contacts[0];
Explode(contact.point, contact.normal);
}
}
/// <summary>
/// 播放爆炸特效并销毁炮弹
/// </summary>
/// <param name="pos">世界位置</param>
/// <param name="normal">法线,用于对齐特效朝向</param>
void Explode(Vector3 pos, Vector3 normal)
{
if (exploded && onlyExplodeOnce) return;
exploded = true;
GameManager.Ins.CreateExplosion(pos,EnemyType.Clean);
// 这里可以扩展伤害/力场效果,例如 Physics.OverlapSphere + 对敌人造成伤害
// 销毁炮弹对象(延迟一帧以保证碰撞回调完成)
Destroy(gameObject);
}
#if UNITY_EDITOR
// 编辑器辅助:在场景视图显示预测弧线(基于当前参数)
void OnDrawGizmosSelected()
{
if (!Application.isPlaying)
{
// 画出从 0 到 flightTime 的轨迹采样
int steps = 30;
Vector3 start = transform.position;
Vector3 worldForward = transform.forward;
Vector3 forwardHorizontal = new Vector3(worldForward.x, 0f, worldForward.z);
if (forwardHorizontal.sqrMagnitude < 1e-6f)
forwardHorizontal = transform.right;
forwardHorizontal.Normalize();
float T = Mathf.Max(0.01f, flightTime);
Vector3 g = Physics.gravity;
float horizontalSpeed = flightDistance / T;
float vy = -0.5f * g.y * T;
Vector3 v0 = forwardHorizontal * horizontalSpeed + Vector3.up * vy;
Vector3 prev = start;
for (int i = 1; i <= steps; i++)
{
float t = (T * i) / steps;
Vector3 pos = start + v0 * t + 0.5f * g * t * t;
Gizmos.color = Color.cyan;
Gizmos.DrawLine(prev, pos);
prev = pos;
}
}
}
#endif
}