Files
Loong/Assets/_Loong/Scripts/Guns/Bow.cs
2025-10-15 15:27:45 +08:00

256 lines
7.8 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 DarkTonic.MasterAudio;
using UnityEngine;
public class Bow : Launcher
{
public Transform bowString;
public GameObject arrowPre;
private Arrow _nowArrow;
[SoundGroup] public string arrowShoot;
[Header("箭矢类型管理")]
public BulletType currentArrowType = BulletType.Arrow;
private float specialArrowTimeRemaining = 0f;
[Header("箭矢预制体")]
public GameObject normalArrowPrefab;
public GameObject fireArrowPrefab;
[Header("弓弦力度参数")]
public float minForce = 20f;//最小力度
public float maxForce = 45f;//最大力度
public float forceMultiplier = 1.2f;//力度倍增系数
public float maxPullDistance = 0.5f;//弓弦可拉最大距离
public float forceCurveExponent = 1.5f;//力度曲线指数
private float currentDrawStrength = 0f;//当前力度0-1
[Header("弓弦视觉反馈")]
public LineRenderer bowStringRenderer;//弓弦的LineRenderer
public Transform stringTopPoint;//弓弦顶端点
public Transform stringBottomPoint;//弓弦末端点
public Transform stringMiddlePoint;//弓弦中间点(手拉动弓弦的位置)
private Vector3 middlePointPos;//初始弓弦初始点
public void Start()
{
type = GunType.Bow;
GunInfo gunInfo = GameManager.Ins.GunInfos[GunType.Bow][1];
shootRate = gunInfo.ShootRate;
recoil = maxForce;//使用最大力度作为基础后坐力
//初始化弓弦位置
if (bowStringRenderer != null && stringTopPoint != null && stringBottomPoint != null)
{
//设置LineRenderer位置
bowStringRenderer.positionCount = 3;
bowStringRenderer.SetPosition(0, stringTopPoint.position);
bowStringRenderer.SetPosition(1, stringMiddlePoint.position);
bowStringRenderer.SetPosition(2, stringBottomPoint.position);
//保存中间点初始位置
middlePointPos = stringMiddlePoint.localPosition;
}
}
public void Update()
{
UpdateArrowStatus();
UpdateBowStringVisual();
}
//更新弓弦视觉效果
private void UpdateBowStringVisual()
{
if (bowStringRenderer != null && stringTopPoint != null && stringBottomPoint != null && stringMiddlePoint != null)
{
//更新LineRenderer
bowStringRenderer.SetPosition(0, stringTopPoint.position);
bowStringRenderer.SetPosition(1, stringMiddlePoint.position);
bowStringRenderer.SetPosition(2, stringBottomPoint.position);
}
}
//更新箭矢状态
private void UpdateArrowStatus()
{
if (specialArrowTimeRemaining > 0)
{
specialArrowTimeRemaining -= Time.deltaTime;
if (specialArrowTimeRemaining <= 0)
{
currentArrowType = BulletType.Arrow;
//TODO:UI特殊箭矢已失效或不做
}
}
}
//激活特殊箭矢
//public void ActivateSpecialArrow(ArrowPickup.ArrowType arrowType, float duration)
//{
// switch (arrowType)
// {
// case ArrowPickup.ArrowType.Fire:
// currentArrowType = BulletType.FireArrow;
// specialArrowTimeRemaining = duration;
// //TODO:UI获得火焰箭或不做
// break;
// //TODO添加其他特殊箭矢类型
// }
//}
Vector3 ProjectPointOnPlane(Vector3 point, Vector3 planeOrigin, Vector3 planeNormal)
{
// 单位化法向量
planeNormal.Normalize();
// 计算点到平面上任意一点的向量
Vector3 vectorToPlane = point - planeOrigin;
// 计算投影距离
float distance = Vector3.Dot(vectorToPlane, planeNormal);
// 得到垂足(即点在平面上的投影)
Vector3 projection = point - distance * planeNormal;
return projection;
}
/// <summary>
/// 拉弓弦
/// </summary>
public void Pull(Transform point)
{
// 定义投影平面的原点和法向量
Vector3 planeOrigin = bowString.position; // 平面经过的点
Vector3 planeNormal = bowString.right; // 平面的法向量
// 调用投影函数
Vector3 projectedPoint = ProjectPointOnPlane(point.position, planeOrigin, planeNormal);
Vector3 localPos = bowString.InverseTransformPoint(projectedPoint);
//限制拉弓距离
float pullDistance = Mathf.Clamp(localPos.z, -maxPullDistance, 0);
bulletPoint.localPosition = new Vector3(0, 0, localPos.z);
//更新中间点位置(弓弦弯曲效果)
if (stringMiddlePoint != null)
{
Vector3 midPos = middlePointPos;
midPos.z = pullDistance;
stringMiddlePoint.localPosition = midPos;
}
// 计算拉弓力度 (0-1)
currentDrawStrength = Mathf.Clamp01(Mathf.Abs(pullDistance) / maxPullDistance);
// 应用非线性曲线(初始松,后段紧)
float curvedStrength = Mathf.Pow(currentDrawStrength, 1.8f);
}
/// <summary>
/// 弓弦复位
/// </summary>
public void Reset()
{
bulletPoint.localPosition = Vector3.zero;
currentDrawStrength = 0f;
//重置中间点位置
if (stringMiddlePoint != null)
{
stringMiddlePoint.localPosition = middlePointPos;
}
}
/// <summary>
/// 创建箭矢
/// </summary>
public void CreateArrowInHand()
{
//如果已有箭矢,先销毁
if (_nowArrow != null)
{
Destroy(_nowArrow.gameObject);
}
//根据当前箭矢类型选择预制体
GameObject arrowPrefab = GetCurrentArrowPrefab();
GameObject arrowObj = Instantiate(arrowPrefab, bulletPoint);
_nowArrow = arrowObj.GetComponent<Arrow>();
//初始禁用物理,知道射出
if (_nowArrow.TryGetComponent<Rigidbody>(out var rb))
{
rb.isKinematic = true;
}
}
//获取当前箭矢预制体
private GameObject GetCurrentArrowPrefab()
{
switch (currentArrowType)
{
case BulletType.FireArrow:
return fireArrowPrefab;
default:
return normalArrowPrefab;
}
}
/// <summary>
/// 射出箭矢
/// </summary>
public void ShootArrow()
{
//Shoot();
//Reset();
if (_nowArrow != null)
{
//检查游戏状态
if (GameManager.Ins.gameState == GameState.Introduce)
{
GameManager.Ins.GameStart();
}
MasterAudio.PlaySound(arrowShoot);
// 根据拉弓力度计算实际力度(应用非线性曲线)
float curvedStrength = Mathf.Pow(currentDrawStrength, 1.8f);
float actualForce = Mathf.Lerp(minForce, maxForce, curvedStrength) * forceMultiplier;
//发射手上已有的箭矢
_nowArrow.transform.SetParent(null);
//应用物理效果
if (_nowArrow.TryGetComponent<Rigidbody>(out var rb))
{
rb.isKinematic = false;//确保物理启用
rb.velocity = Vector3.zero;//重置速度
rb.angularVelocity= Vector3.zero;//重置角速度
//设置初始速度
rb.AddForce(bulletPoint.forward * actualForce, ForceMode.VelocityChange);
}
//设置箭矢类型
_nowArrow.type = currentArrowType;
//调用箭矢初始化(传递力度值)
_nowArrow.OnSpawn(bulletPoint.forward, actualForce);
//清楚引用
_nowArrow = null;
Reset();
}
}
}