177 lines
6.2 KiB
C#
177 lines
6.2 KiB
C#
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
|
||
}
|