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 Vector3 targetCenterPos;//目标的中心点位置,靶子的射击靶中点 //private float stopDistanceToTarget = 4f; // 相机与目标之间的最小停止距离(XZ 平面) private float totalDist = 0f; // 初始相机与目标之间的水平总距离(用于计算进度) private Vector3 lastArrowPos; // 记录箭的上一次位置 private float arrowSpeed = 0; public float maxVerticalOffset = -2f; // 相机最多比箭低 2 public ArrowCameraTemplate_targetLock(ArrowCamera arrowCamera, Transform target,bool followTarget = true) : base(arrowCamera) { //在 ArmBow.ins 调试参数 offsetFromArrow = ArmBow.ins.offsetFromArrow; //stopDistanceToTarget = ArmBow.ins.stopDistanceToTarget; //靶子的位置是 y = 1.3 中心点高度 targetCenterPos = new Vector3(target.position.x, 1.3f, target.position.z); 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, targetCenterPos); arrowSpeed = arrowCamera.isArrowSync ? arrowCamera.arrowSync.mySyncSpeed: 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, targetCenterPos); if (this.isFollowTarget) { //if (distToTarget <= stopDistanceToTarget) //{ // // 到达目标附近 -> 停止跟随 // cameraStopped = true; // cameraT.SetParent(null); // Debug.Log("distToTarget:" + distToTarget + ",stopDistanceToTarget:"+ stopDistanceToTarget); // Debug.DrawLine(cameraT.position, targetCenterPos, Color.blue, 2f); //} //else //{ //// progress = 相机已接近目标的进度(0 = 起点,1 = 停止点) //float progress = Mathf.Clamp01( // 1f - (distToTarget - stopDistanceToTarget) / (totalDist - stopDistanceToTarget) //); //} // progress = 相机已接近目标的进度(0 = 起点,1 = 停止点) float progress = Mathf.Clamp01( 1f - (distToTarget) / (totalDist) ); // 用缓动曲线计算归一化进度(决定移动节奏) float easedT = ArmBow.ins.customCurveCamera.Evaluate(progress); // ----------------------- // 插值偏移量 (从 offsetFromArrow -> offsetFromArrowEnd) // ----------------------- Vector3 lerpedOffset = Vector3.Lerp(offsetFromArrow, ArmBow.ins.offsetFromArrowEnd, easedT); // 箭位置 + 偏移(相机保持与箭的相对位置,并锁定到目标的高度) Vector3 offsetPos = arrowT.position + arrowT.TransformDirection(lerpedOffset); // ----------------------- // 修改:保持 x 对齐靶子 // ----------------------- offsetPos.x = target.position.x; // y 固定为靶子高度 offsetPos.y = target.position.y + offsetFromArrow.y; cameraT.position = offsetPos; } else { //如果没射中目标靶,不用判断目标距离, //需要判断一下射出目标高度,比如现在是固定y和目标靶的,如何飞高之后,相机不再参考目标靶高度,跟随箭飞出去, //也给一个能看到的最低水平距离,比如箭飞出相机的距离是10,那就是需要相机和箭保持5的相对高度 // ---------------------------- // 自由跟随模式(非目标锁定,下方视角 + 阶段性切换) // ---------------------------- Vector3 offsetPos = arrowT.position + arrowT.TransformDirection(offsetFromArrow); // 相机的目标高度,初始参考靶子,后续逐渐切换为箭下方固定距离 float targetY = target.position.y + offsetFromArrow.y; // 箭当前位置的理想“下方位置” float arrowFollowY = arrowT.position.y + maxVerticalOffset; // 如果相机位置比箭下方限制还高,就参考靶子高度; // 一旦箭飞到比 maxVerticalOffset 更高的地方,相机高度固定在箭下方 maxVerticalOffset。 if (targetY > arrowFollowY) offsetPos.y = targetY; else offsetPos.y = arrowFollowY; // 保持相机与箭的最小水平距离,避免重叠 float minXZDistance = 4f; Vector3 flatCamPos = new Vector3(offsetPos.x, 0, offsetPos.z); Vector3 flatArrowPos = new Vector3(arrowT.position.x, 0, arrowT.position.z); float currentXZDist = Vector3.Distance(flatCamPos, flatArrowPos); if (currentXZDist < minXZDistance) { // 推开到最小水平距离 Vector3 dir = (flatCamPos - flatArrowPos).normalized; flatCamPos = flatArrowPos + dir * minXZDistance; offsetPos.x = flatCamPos.x; offsetPos.z = flatCamPos.z; } // 更新相机位置 cameraT.position = offsetPos; // 相机始终朝向箭头方向(可选,保证画面观感) //cameraT.LookAt(arrowT); } } // 命中处理(箭命中目标后,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) {} }