using System.Collections; using System.Collections.Generic; using UnityEngine; using DG.Tweening; using UnityEngine.SceneManagement; /* 拍摄箭飞行的摄像机 */ public class ArrowCamera : MonoBehaviour { [System.NonSerialized] public Arrow arrow; [System.NonSerialized] public ArrowCameraTemplate arrowCameraTemplate; [System.NonSerialized] public bool isArrowSync = false; [System.NonSerialized] public ArrowSync arrowSync; public void SetArrowSync(ArrowSync arrowSync) { isArrowSync = true; this.arrowSync = arrowSync; } void Awake() { BowCamera.ins.SetArrowFollowing(true); } void Start() { if (SceneManager.GetActiveScene().name == "Game") { if ((isArrowSync && arrowSync.canUserLockCamera) || (!isArrowSync && arrow.canUserLockCamera)) { //使用锁靶镜头 arrowCameraTemplate = new ArrowCameraTemplate_targetLock(this, TargetBody.ins.transform); } else { //射空就使用之前的镜头 //arrowCameraTemplate = new ArrowCameraTemplate1(this); arrowCameraTemplate = new ArrowCameraTemplate_targetLock(this, TargetBody.ins.transform,false); } //if ((isArrowSync && arrowSync.canUseSideCamera) || (!isArrowSync && arrow.canUseSideCamera)) { // arrowCameraTemplate = new ArrowCameraTemplate2(this); // Debug.Log("跟随镜头2"); //} else { // arrowCameraTemplate = new ArrowCameraTemplate1(this); // Debug.Log("跟随镜头1"); //} } else if (SceneManager.GetActiveScene().name == "GameChallenge") { arrowCameraTemplate = new ArrowCameraTemplate3(this); } } void OnDestroy() { try { BowCamera.ins.SetArrowFollowing(false); } catch (System.Exception) {} } void FixedUpdate() { arrowCameraTemplate.Update(); } } /* 模板3:跟着箭飞_打猎场景 */ class ArrowCameraTemplate3 : ArrowCameraTemplate { public ArrowCameraTemplate3(ArrowCamera arrowCamera) : base(arrowCamera) { if (!arrowCamera.isArrowSync) { this.arrowCamera.arrow.activeEffectCyclone(true); } else { this.arrowCamera.arrowSync.activeEffectCyclone(true); } Camera camera = arrowCamera.transform.GetComponent(); if (camera) camera.fieldOfView = 25; } private bool cameraMoveFinish = false; Vector3 cameraToRunPosition = new Vector3(0.4f, 0.5f, -1 - Mathf.Clamp(Arrow.speed / 20 * 6, 0, 6)); Vector3 cameraFinalPosition = new Vector3(0.4f, 0.8f, -5f); //若有树的遮挡,则使用以下视角坐标 Vector3 cameraFinalPosition_whenBlockByTree = new Vector3(0.15f, 0.3f, -1.2f); bool hasBlockByTree = false; Vector3 blockByTreeLookAtPoint; public override void Update() { if (cameraMoveFinish) { return; } Transform cameraT = this.arrowCamera.transform; Vector3 cameraPosition = cameraT.localPosition; if ((!this.arrowCamera.isArrowSync && this.arrowCamera.arrow.isHit) || (this.arrowCamera.isArrowSync && this.arrowCamera.arrowSync.isHit)) { cameraPosition = Vector3.Lerp(cameraPosition, cameraFinalPosition, Time.deltaTime * 6); cameraT.localPosition = cameraPosition; if (hasBlockByTree) { cameraT.LookAt(blockByTreeLookAtPoint); } float d = Vector3.Distance(cameraPosition, cameraFinalPosition); if (d < 0.001f) { cameraMoveFinish = true; Sequence seq = DOTween.Sequence(); if (!quicklyNextShoot) seq.AppendInterval(1f); seq.AppendCallback(delegate() { if (!this.arrowCamera.isArrowSync) { this.arrowCamera.arrow.nextShoot(); } GameObject.Destroy(container?container:this.arrowCamera.gameObject); }); } } else { cameraPosition = Vector3.Lerp(cameraPosition, cameraToRunPosition, Time.deltaTime * 6); cameraT.localPosition = cameraPosition; if (this.arrowCamera.isArrowSync) { cameraT.LookAt(this.arrowCamera.arrowSync.Head()); } else { cameraT.LookAt(this.arrowCamera.arrow.Head()); } } } GameObject container = null; public override void beforeHit() { //把镜头从箭中移除,放到一个固定的容器里,免得随着动物抖动 if (!container) { container = new GameObject("CameraContainer"); container.transform.position = this.arrowCamera.arrow.transform.position; container.transform.rotation = this.arrowCamera.arrow.transform.rotation; this.arrowCamera.transform.SetParent(container.transform); //检测有没有树遮挡 Quaternion rot = container.transform.rotation; Quaternion back = rot * Quaternion.AngleAxis(180, Vector3.up); //绕本地坐标轴旋转 for (int i = -36; i <= 36; i += 3) { float angle = i; Quaternion axis = back * Quaternion.AngleAxis(angle, Vector3.up); Vector3 direction = axis * Vector3.forward; RaycastHit[] raycastHits = Physics.RaycastAll(container.transform.position, direction, 6f); foreach (var raycastHit in raycastHits) { Transform tf = raycastHit.transform; if (tf && tf.name.StartsWith("TreeCollider")) { cameraFinalPosition = cameraFinalPosition_whenBlockByTree; hasBlockByTree = true; Arrow arrow = this.arrowCamera.arrow; blockByTreeLookAtPoint = arrow.Head().position - arrow.transform.forward * 0.5f; break; } } } } //记录需要同步的消息 if (!this.arrowCamera.isArrowSync) { if (this.arrowCamera.arrow.outputSyncData != null) { this.arrowCamera.arrow.outputSyncData.SetArrowCameraTemplate3(this.hasBlockByTree, this.quicklyNextShoot); } } } public void beforHitWhenSync(bool hasBlockByTree) { this.hasBlockByTree = hasBlockByTree; container = new GameObject("CameraContainer"); container.transform.position = this.arrowCamera.arrowSync.transform.position; container.transform.rotation = Quaternion.LookRotation(this.arrowCamera.arrowSync.transform.forward, Vector3.up); this.arrowCamera.transform.SetParent(container.transform); this.arrowCamera.transform.localRotation = Quaternion.identity; if (hasBlockByTree) { cameraFinalPosition = cameraFinalPosition_whenBlockByTree; ArrowSync arrowSync = this.arrowCamera.arrowSync; blockByTreeLookAtPoint = arrowSync.Head().position - arrowSync.transform.forward * 0.5f; } } public bool quicklyNextShoot = ChallengeGameMode.IsChallengeWolf(); public override void SendMsg(int id, object msg) { if (id == 0) { quicklyNextShoot = true; } //记录需要同步的消息 if (!this.arrowCamera.isArrowSync) { if (this.arrowCamera.arrow.outputSyncData != null) { this.arrowCamera.arrow.outputSyncData.SetArrowCameraTemplate3(this.hasBlockByTree, this.quicklyNextShoot); } } } } /* 模板2:从侧面看箭飞 */ class ArrowCameraTemplate2 : ArrowCameraTemplate { public ArrowCameraTemplate2(ArrowCamera arrowCamera) : base(arrowCamera) { this.arrowCamera.transform.parent = null; arrowCamera.transform.localPosition = new Vector3(8.33f, 2.45f, 6.4f); arrowCamera.transform.localEulerAngles = new Vector3(0, -42, 0); } bool isHit = false; public override void Update() { if (arrowCamera.isArrowSync) { if (!isHit) { isHit = arrowCamera.arrowSync.isHit; if (isHit) { this.arrowCamera.transform.SetParent(this.arrowCamera.arrowSync.transform); arrowCamera.transform.localPosition = new Vector3(-0.3f, 0.2f, -1.3f); arrowCamera.transform.LookAt(this.arrowCamera.arrowSync.Head()); Sequence seq = DOTween.Sequence(); seq.PrependInterval(2.2f); seq.AppendCallback(delegate() { GameObject.Destroy(this.arrowCamera.gameObject); }); return; } } if (!arrowCamera.arrowSync) { GameObject.Destroy(this.arrowCamera.gameObject); } } else { if (!isHit) { isHit = arrowCamera.arrow.isHit; if (isHit) { this.arrowCamera.transform.SetParent(this.arrowCamera.arrow.transform); arrowCamera.transform.localPosition = new Vector3(-0.3f, 0.2f, -1.3f); arrowCamera.transform.LookAt(this.arrowCamera.arrow.Head()); Sequence seq = DOTween.Sequence(); seq.PrependInterval(2.2f); seq.AppendCallback(delegate() { this.arrowCamera.arrow.nextShoot(); GameObject.Destroy(this.arrowCamera.gameObject); }); return; } } if (!arrowCamera.arrow) { GameObject.Destroy(this.arrowCamera.gameObject); } } } } /* 模板1:跟着箭飞 */ class ArrowCameraTemplate1 : ArrowCameraTemplate { public ArrowCameraTemplate1(ArrowCamera arrowCamera) : base(arrowCamera) { if (!arrowCamera.isArrowSync) { this.arrowCamera.arrow.activeEffectCyclone(true); } else { this.arrowCamera.arrowSync.activeEffectCyclone(true); } } private bool cameraMoveFinish = false; Vector3 cameraToRunPosition = new Vector3(0.4f, 0.3f, -1 - Mathf.Clamp(Arrow.speed / 20 * 6, 0, 6)); Vector3 cameraFinalPosition = new Vector3(-0.3f, 0.2f, -1.3f); public override void Update() { if (cameraMoveFinish) { return; } Transform cameraT = this.arrowCamera.transform; Vector3 cameraPosition = cameraT.localPosition; if ((!this.arrowCamera.isArrowSync && this.arrowCamera.arrow.isHit) || (this.arrowCamera.isArrowSync && this.arrowCamera.arrowSync.isHit)) { cameraPosition = Vector3.Lerp(cameraPosition, cameraFinalPosition, Time.deltaTime * 8); float d = Vector3.Distance(cameraPosition, cameraFinalPosition); if (d < 0.001f) { cameraMoveFinish = true; Sequence seq = DOTween.Sequence(); seq.AppendInterval(2.2f); seq.AppendCallback(delegate() { if (!arrowCamera.isArrowSync) { this.arrowCamera.arrow.nextShoot(); } GameObject.Destroy(this.arrowCamera.gameObject); }); } } else { cameraPosition = Vector3.Lerp(cameraPosition, cameraToRunPosition, Time.deltaTime * 6); } if (this.arrowCamera.isArrowSync) { cameraT.LookAt(this.arrowCamera.arrowSync.Head()); } else { cameraT.LookAt(this.arrowCamera.arrow.Head()); } cameraT.localPosition = cameraPosition; } } /// /// 箭相机模板:靶锁模式 /// 相机会跟随箭头运动,并在靠近目标时减速停下,支持缓动曲线控制 /// class ArrowCameraTemplate_targetLock : ArrowCameraTemplate { //当前操作状态,是否删除 private bool isDeleting = false; public bool IsDeleting => isDeleting; private bool cameraStopped = false; // 标记相机是否已停止(到达目标附近后停止更新位置) private Transform target; // 被锁定的目标(通常是靶心) private bool isFollowTarget = true; /// /// 相机相对箭的偏移量 /// x = 左右偏移(负数表示往左,正数往右) /// y = 高度偏移(正数表示高于箭,固定高度时候,和目标y值叠加) /// z = 前后偏移(负数表示在箭的后面,正数表示在箭的前面) /// private Vector3 offsetFromArrow = new Vector3(0, 0.8f, -2.2f); private float stopDistanceToTarget = 4f; // 相机与目标之间的最小停止距离(XZ 平面) private float totalDist = 0f; // 初始相机与目标之间的水平总距离(用于计算进度) private Vector3 lastArrowPos; // 记录箭的上一次位置 private float arrowSpeed = 0; // 调节参数 private const float extraSpeedMax = 100f; // 相机在箭方向上的额外冲击速度最大值(越大越“冲”) private const float minLerpSpeed = 2f; // 插值时的最小速度(进度小的时候) private const float maxLerpSpeed = 10f; // 插值时的最大速度(进度接近 1 时) public ArrowCameraTemplate_targetLock(ArrowCamera arrowCamera, Transform target,bool followTarget = true) : base(arrowCamera) { //在 ArmBow.ins 调试参数 offsetFromArrow = followTarget? ArmBow.ins.offsetFromArrow : ArmBow.ins.offsetFromArrowTemp; stopDistanceToTarget = ArmBow.ins.stopDistanceToTarget; this.target = target; this.isFollowTarget = followTarget; Transform cameraT = arrowCamera.transform; Transform arrowT = arrowCamera.isArrowSync ? arrowCamera.arrowSync.transform : arrowCamera.arrow.transform; this.lastArrowPos = arrowT.position; // 相机初始位置(箭位置 + 偏移,锁定 Y 高度) Vector3 offsetPos = arrowT.position + arrowT.TransformDirection(offsetFromArrow); offsetPos.y = target.position.y + offsetFromArrow.y; offsetPos.x = target.position.x; cameraT.position = offsetPos; // 记录初始总路程(XZ 平面,不考虑高度差) totalDist = GetXZDistance(offsetPos, target.position); arrowSpeed = arrowCamera.arrow.mySpeed; Debug.Log("当前弓箭速度:" + arrowSpeed); } public override void Update() { if (target == null) return; //弓箭删除 if ((arrowCamera.isArrowSync && arrowCamera.arrowSync == null) || (!arrowCamera.isArrowSync && arrowCamera.arrow == null))return; Transform cameraT = arrowCamera.transform; Transform arrowT = arrowCamera.isArrowSync ? arrowCamera.arrowSync.transform : arrowCamera.arrow.transform; if (!cameraStopped) { // 计算当前相机与目标的水平距离 float distToTarget = GetXZDistance(cameraT.position, target.position); if (distToTarget <= stopDistanceToTarget) { // 到达目标附近 -> 停止跟随 cameraStopped = true; cameraT.SetParent(null); } else { // progress = 相机已接近目标的进度(0 = 起点,1 = 停止点) float progress = Mathf.Clamp01( 1f - (distToTarget - stopDistanceToTarget) / (totalDist - stopDistanceToTarget) ); //Vector3 arrowScreenPos = cameraT.GetComponent().WorldToViewportPoint(arrowT.position); //bool isFollow = true; //// 如果箭接近屏幕边缘,就往后退 //if (arrowScreenPos.x < -0.13f || arrowScreenPos.x > 1f || // arrowScreenPos.y < -0.13f || arrowScreenPos.y > 1f) //{ // //UnityEditor.EditorApplication.isPaused = true; // // offsetFromArrow.z = ArmBow.ins.offsetFromArrow.z - 2; // // 用 Lerp 平滑过渡 // //float targetZ = ArmBow.ins.offsetFromArrow.z - 4f; // // offsetFromArrow.z = Mathf.Lerp(offsetFromArrow.z, targetZ, Time.deltaTime * 2f); // Debug.Log("箭离开屏幕!" + offsetFromArrow); // isFollow = false; //} //else //{ // //offsetFromArrow.z = ArmBow.ins.offsetFromArrow.z; // isFollow = true; //} // 用缓动曲线计算归一化进度(决定移动节奏) float easedT = ArmBow.ins.customCurveCamera.Evaluate(progress); // 箭位置 + 偏移(相机保持与箭的相对位置,并锁定到目标的高度) Vector3 offsetPos = arrowT.position + arrowT.TransformDirection(offsetFromArrow); //offsetPos.y = target.position.y + offsetFromArrow.y; // offsetPos.x = target.position.x; if (this.isFollowTarget) { // 箭的水平前进方向(忽略x, y 分量)arrowT.forward.x Vector3 arrowDirXZ = new Vector3(0f, 0f, arrowT.forward.z).normalized; // 相机额外的位移偏移量(随 easedT 增强,越靠近目标越快) Vector3 extraOffset = arrowDirXZ * Mathf.Lerp(0f, extraSpeedMax, easedT) * Time.deltaTime; // 最终目标位置 = 箭偏移位置 + 额外速度偏移 Vector3 targetPos = offsetPos + extraOffset; // 相机向目标位置平滑插值(随曲线加速) float speedFactor = Mathf.Lerp(minLerpSpeed, maxLerpSpeed, easedT); cameraT.position = Vector3.Lerp(cameraT.position, targetPos, Time.deltaTime * speedFactor); } else { // 最终目标位置 = 箭偏移位置 //offsetPos.z = arrowT.position.z - 5; // 箭的水平前进方向(忽略 y 分量)arrowT.forward.x //Vector3 arrowDirXZ = new Vector3(0f, 0f, arrowT.forward.z).normalized; // 相机额外的位移偏移量(随 easedT 增强,越靠近目标越快) // Vector3 extraOffset = arrowDirXZ * Mathf.Lerp(0f, extraSpeedMax, easedT) * Time.deltaTime; // 最终目标位置 = 箭偏移位置 + 额外速度偏移 //Vector3 targetPos = offsetPos + extraOffset; // 相机向目标位置平滑插值(随曲线加速) //float speedFactor = Mathf.Lerp(minLerpSpeed, maxLerpSpeed, easedT); //cameraT.position = offsetPos;// Vector3.Lerp(cameraT.position, targetPos, Time.deltaTime * speedFactor); cameraT.SetParent(null); // 箭的前进方向(XZ 平面)Z 分量 float arrowSpeedZ = arrowSpeed * arrowT.forward.z; // 箭速度 * 前方向 Z 分量 // 相机当前 Z 位置参考箭的 Z float targetZ = cameraT.position.z + arrowSpeedZ * Time.deltaTime; // 保证相机不要超过箭本身(可选) targetZ = Mathf.Min(targetZ, arrowT.position.z); // 目标位置 = 相机 X/Y 偏移不变,Z 方向用 targetZ Vector3 targetPos = new Vector3( target.position.x, // X 可以锁定到目标或保持 offsetPos.x offsetPos.y, // Y 维持箭高度偏移 targetZ ); // 直接插值平滑到目标位置(也可以加缓动) float speedFactor = 5f; // 可调节平滑程度 cameraT.position = Vector3.Lerp(cameraT.position, targetPos, Time.deltaTime * speedFactor); } // 最后强制 y = 靶子高度,不受lerp影响 Vector3 fixedPos = cameraT.position; fixedPos.y = target.position.y + offsetFromArrow.y; cameraT.position = fixedPos; //#if UNITY_EDITOR // Debug.Log($"progress:{progress:F2}, easedT:{easedT:F2}, speed:{speedFactor:F2}, extra:{extraOffset}"); //#endif } } // 命中处理(箭命中目标后,1.5 秒后销毁相机对象) if ((!arrowCamera.isArrowSync && arrowCamera.arrow.isHit) || (arrowCamera.isArrowSync && arrowCamera.arrowSync.isHit)) { if (isDeleting) return; isDeleting = true; cameraStopped = true; Sequence seq = DOTween.Sequence(); seq.AppendInterval(1.5f); seq.AppendCallback(() => { if (!arrowCamera.isArrowSync) arrowCamera.arrow.nextShoot(); // 下一发准备 GameObject.Destroy(arrowCamera.gameObject); // 销毁相机 }); } } /// /// 计算两个点在 XZ 平面的水平距离(忽略 Y 高度差) /// private float GetXZDistance(Vector3 a, Vector3 b) { float dx = a.x - b.x; float dz = a.z - b.z; return Mathf.Sqrt(dx * dx + dz * dz); } } public class ArrowCameraTemplate { public ArrowCamera arrowCamera; public ArrowCameraTemplate(ArrowCamera arrowCamera) { this.arrowCamera = arrowCamera; } public virtual void Update() {} public virtual void beforeHit() {} public virtual void SendMsg(int id, object msg) {} }