Files
MRSnowWhite/Assets/_SnowWhite/Scripts/ForestEvent/Bow.cs

225 lines
7.0 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;
using System.Collections;
using System.Collections.Generic;
using DarkTonic.MasterAudio;
using UnityEngine;
public class Bow : MonoBehaviour
{
public Transform bowString;
public Transform bulletPoint;
public Arrow nowArrow;
[SoundGroup] public string arrowShoot;
[Header("箭矢类型管理")]
private float specialArrowTimeRemaining = 0f;
[Header("箭矢预制体")]
public GameObject normalArrowPrefab;
[Header("弓弦力度参数")]
public float minForce = 20f;//最小力度
public float maxForce = 45f;//最大力度
public float forceMultiplier = 1.2f;//力度倍增系数
public float maxPullDistance = 0.5f;//弓弦可拉最大距离
public float forceCurveExponent = 1.5f;//力度曲线指数
public float currentDrawStrength = 0f;//当前力度0-1
[Header("弓弦视觉反馈")]
public LineRenderer bowStringRenderer;//弓弦的LineRenderer
public Transform stringTopPoint;//弓弦顶端点
public Transform stringBottomPoint;//弓弦末端点
public Transform stringMiddlePoint;//弓弦中间点(手拉动弓弦的位置)
private Vector3 middlePointPos;//初始弓弦初始点
public virtual void Start()
{
//初始化弓弦位置
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);
}
}
//更新箭矢状态
public virtual void UpdateArrowStatus()
{
if (nowArrow != null)
{
// 箭尾固定点(通常是弓上挂箭点)
Transform tailPoint = bulletPoint; // 保证箭尾贴在弓上
// 箭头方向:从箭尾指向左手
Vector3 direction = (transform.position - tailPoint.position).normalized;
// 更新箭的位置与旋转
nowArrow.transform.position = tailPoint.position;
nowArrow.transform.rotation = Quaternion.LookRotation(direction, transform.up);
}
}
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 = transform.InverseTransformPoint(projectedPoint);
// 限制拉弓距离,防止超过最大拉伸范围
float pullDistance = Mathf.Clamp(localPos.z, -maxPullDistance, 0);
// 更新弓弦视觉
if (stringMiddlePoint != null)
{
Vector3 midPos = middlePointPos;
midPos.z = pullDistance;
stringMiddlePoint.localPosition = midPos;
}
// 更新箭尾点位置
if (bulletPoint != null)
{
bulletPoint.localPosition = new Vector3(0, 0, pullDistance);
}
// 力度计算
currentDrawStrength = Mathf.Clamp01(Mathf.Abs(pullDistance) / maxPullDistance);
// -------------------------
// ✅ 新增保护逻辑:超过最大拉距时自动复位
// -------------------------
// if (Mathf.Approximately(pullDistance, -maxPullDistance))
// {
// Reset();
// }
}
/// <summary>
/// 弓弦复位
/// </summary>
public void Reset()
{
// // 拉弓点归零
// if (bulletPoint != null)
// bulletPoint.localPosition = Vector3.zero;
// 弓弦归位
if (stringMiddlePoint != null)
stringMiddlePoint.localPosition = middlePointPos;
// 当前力度清零
currentDrawStrength = 0f;
}
/// <summary>
/// 创建箭矢
/// </summary>
public void CreateArrowInHand()
{
//如果已有箭矢,先销毁
if (nowArrow != null)
{
Destroy(nowArrow.gameObject);
}
//根据当前箭矢类型选择预制体
GameObject arrowPrefab = normalArrowPrefab;
GameObject arrowObj = Instantiate(arrowPrefab, bulletPoint);
nowArrow = arrowObj.GetComponent<Arrow>();
//初始禁用物理,知道射出
if (nowArrow.TryGetComponent<Rigidbody>(out var rb))
{
rb.isKinematic = true;
}
}
/// <summary>
/// 射出箭矢
/// </summary>
public void ShootArrow()
{
if (nowArrow != null)
{
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.OnSpawn(bulletPoint.forward, actualForce);
//清楚引用
nowArrow = null;
Reset();
}
}
}