ArrowCamera.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using DG.Tweening;
  5. using UnityEngine.SceneManagement;
  6. /* 拍摄箭飞行的摄像机 */
  7. public class ArrowCamera : MonoBehaviour
  8. {
  9. [System.NonSerialized] public Arrow arrow;
  10. [System.NonSerialized] public ArrowCameraTemplate arrowCameraTemplate;
  11. [System.NonSerialized] public bool isArrowSync = false;
  12. [System.NonSerialized] public ArrowSync arrowSync;
  13. public void SetArrowSync(ArrowSync arrowSync) {
  14. isArrowSync = true;
  15. this.arrowSync = arrowSync;
  16. }
  17. void Awake()
  18. {
  19. BowCamera.ins.SetArrowFollowing(true);
  20. }
  21. void Start()
  22. {
  23. if (SceneManager.GetActiveScene().name == "Game") {
  24. if ((isArrowSync && arrowSync.canUserLockCamera) || (!isArrowSync && arrow.canUserLockCamera))
  25. {
  26. //使用锁靶镜头
  27. arrowCameraTemplate = new ArrowCameraTemplate_targetLock(this, TargetBody.ins.transform);
  28. }
  29. else {
  30. //射空就使用之前的镜头
  31. //arrowCameraTemplate = new ArrowCameraTemplate1(this);
  32. arrowCameraTemplate = new ArrowCameraTemplate_targetLock(this, TargetBody.ins.transform,false);
  33. }
  34. //if ((isArrowSync && arrowSync.canUseSideCamera) || (!isArrowSync && arrow.canUseSideCamera)) {
  35. // arrowCameraTemplate = new ArrowCameraTemplate2(this);
  36. // Debug.Log("跟随镜头2");
  37. //} else {
  38. // arrowCameraTemplate = new ArrowCameraTemplate1(this);
  39. // Debug.Log("跟随镜头1");
  40. //}
  41. } else if (SceneManager.GetActiveScene().name == "GameChallenge") {
  42. arrowCameraTemplate = new ArrowCameraTemplate3(this);
  43. }
  44. }
  45. void OnDestroy()
  46. {
  47. try
  48. {
  49. BowCamera.ins.SetArrowFollowing(false);
  50. }
  51. catch (System.Exception) {}
  52. }
  53. void FixedUpdate()
  54. {
  55. arrowCameraTemplate.Update();
  56. }
  57. }
  58. /* 模板3:跟着箭飞_打猎场景 */
  59. class ArrowCameraTemplate3 : ArrowCameraTemplate
  60. {
  61. public ArrowCameraTemplate3(ArrowCamera arrowCamera) : base(arrowCamera) {
  62. if (!arrowCamera.isArrowSync) {
  63. this.arrowCamera.arrow.activeEffectCyclone(true);
  64. } else {
  65. this.arrowCamera.arrowSync.activeEffectCyclone(true);
  66. }
  67. Camera camera = arrowCamera.transform.GetComponent<Camera>();
  68. if (camera) camera.fieldOfView = 25;
  69. }
  70. private bool cameraMoveFinish = false;
  71. Vector3 cameraToRunPosition = new Vector3(0.4f, 0.5f, -1 - Mathf.Clamp(Arrow.speed / 20 * 6, 0, 6));
  72. Vector3 cameraFinalPosition = new Vector3(0.4f, 0.8f, -5f);
  73. //若有树的遮挡,则使用以下视角坐标
  74. Vector3 cameraFinalPosition_whenBlockByTree = new Vector3(0.15f, 0.3f, -1.2f);
  75. bool hasBlockByTree = false;
  76. Vector3 blockByTreeLookAtPoint;
  77. public override void Update() {
  78. if (cameraMoveFinish) {
  79. return;
  80. }
  81. Transform cameraT = this.arrowCamera.transform;
  82. Vector3 cameraPosition = cameraT.localPosition;
  83. if ((!this.arrowCamera.isArrowSync && this.arrowCamera.arrow.isHit) ||
  84. (this.arrowCamera.isArrowSync && this.arrowCamera.arrowSync.isHit)) {
  85. cameraPosition = Vector3.Lerp(cameraPosition, cameraFinalPosition, Time.deltaTime * 6);
  86. cameraT.localPosition = cameraPosition;
  87. if (hasBlockByTree) {
  88. cameraT.LookAt(blockByTreeLookAtPoint);
  89. }
  90. float d = Vector3.Distance(cameraPosition, cameraFinalPosition);
  91. if (d < 0.001f) {
  92. cameraMoveFinish = true;
  93. Sequence seq = DOTween.Sequence();
  94. if (!quicklyNextShoot) seq.AppendInterval(1f);
  95. seq.AppendCallback(delegate() {
  96. if (!this.arrowCamera.isArrowSync) {
  97. this.arrowCamera.arrow.nextShoot();
  98. }
  99. GameObject.Destroy(container?container:this.arrowCamera.gameObject);
  100. });
  101. }
  102. } else {
  103. cameraPosition = Vector3.Lerp(cameraPosition, cameraToRunPosition, Time.deltaTime * 6);
  104. cameraT.localPosition = cameraPosition;
  105. if (this.arrowCamera.isArrowSync) {
  106. cameraT.LookAt(this.arrowCamera.arrowSync.Head());
  107. } else {
  108. cameraT.LookAt(this.arrowCamera.arrow.Head());
  109. }
  110. }
  111. }
  112. GameObject container = null;
  113. public override void beforeHit() {
  114. //把镜头从箭中移除,放到一个固定的容器里,免得随着动物抖动
  115. if (!container) {
  116. container = new GameObject("CameraContainer");
  117. container.transform.position = this.arrowCamera.arrow.transform.position;
  118. container.transform.rotation = this.arrowCamera.arrow.transform.rotation;
  119. this.arrowCamera.transform.SetParent(container.transform);
  120. //检测有没有树遮挡
  121. Quaternion rot = container.transform.rotation;
  122. Quaternion back = rot * Quaternion.AngleAxis(180, Vector3.up); //绕本地坐标轴旋转
  123. for (int i = -36; i <= 36; i += 3) {
  124. float angle = i;
  125. Quaternion axis = back * Quaternion.AngleAxis(angle, Vector3.up);
  126. Vector3 direction = axis * Vector3.forward;
  127. RaycastHit[] raycastHits = Physics.RaycastAll(container.transform.position, direction, 6f);
  128. foreach (var raycastHit in raycastHits) {
  129. Transform tf = raycastHit.transform;
  130. if (tf && tf.name.StartsWith("TreeCollider")) {
  131. cameraFinalPosition = cameraFinalPosition_whenBlockByTree;
  132. hasBlockByTree = true;
  133. Arrow arrow = this.arrowCamera.arrow;
  134. blockByTreeLookAtPoint = arrow.Head().position - arrow.transform.forward * 0.5f;
  135. break;
  136. }
  137. }
  138. }
  139. }
  140. //记录需要同步的消息
  141. if (!this.arrowCamera.isArrowSync) {
  142. if (this.arrowCamera.arrow.outputSyncData != null) {
  143. this.arrowCamera.arrow.outputSyncData.SetArrowCameraTemplate3(this.hasBlockByTree, this.quicklyNextShoot);
  144. }
  145. }
  146. }
  147. public void beforHitWhenSync(bool hasBlockByTree) {
  148. this.hasBlockByTree = hasBlockByTree;
  149. container = new GameObject("CameraContainer");
  150. container.transform.position = this.arrowCamera.arrowSync.transform.position;
  151. container.transform.rotation = Quaternion.LookRotation(this.arrowCamera.arrowSync.transform.forward, Vector3.up);
  152. this.arrowCamera.transform.SetParent(container.transform);
  153. this.arrowCamera.transform.localRotation = Quaternion.identity;
  154. if (hasBlockByTree) {
  155. cameraFinalPosition = cameraFinalPosition_whenBlockByTree;
  156. ArrowSync arrowSync = this.arrowCamera.arrowSync;
  157. blockByTreeLookAtPoint = arrowSync.Head().position - arrowSync.transform.forward * 0.5f;
  158. }
  159. }
  160. public bool quicklyNextShoot = ChallengeGameMode.IsChallengeWolf();
  161. public override void SendMsg(int id, object msg) {
  162. if (id == 0) {
  163. quicklyNextShoot = true;
  164. }
  165. //记录需要同步的消息
  166. if (!this.arrowCamera.isArrowSync) {
  167. if (this.arrowCamera.arrow.outputSyncData != null) {
  168. this.arrowCamera.arrow.outputSyncData.SetArrowCameraTemplate3(this.hasBlockByTree, this.quicklyNextShoot);
  169. }
  170. }
  171. }
  172. }
  173. /* 模板2:从侧面看箭飞 */
  174. class ArrowCameraTemplate2 : ArrowCameraTemplate
  175. {
  176. public ArrowCameraTemplate2(ArrowCamera arrowCamera) : base(arrowCamera) {
  177. this.arrowCamera.transform.parent = null;
  178. arrowCamera.transform.localPosition = new Vector3(8.33f, 2.45f, 6.4f);
  179. arrowCamera.transform.localEulerAngles = new Vector3(0, -42, 0);
  180. }
  181. bool isHit = false;
  182. public override void Update() {
  183. if (arrowCamera.isArrowSync) {
  184. if (!isHit) {
  185. isHit = arrowCamera.arrowSync.isHit;
  186. if (isHit) {
  187. this.arrowCamera.transform.SetParent(this.arrowCamera.arrowSync.transform);
  188. arrowCamera.transform.localPosition = new Vector3(-0.3f, 0.2f, -1.3f);
  189. arrowCamera.transform.LookAt(this.arrowCamera.arrowSync.Head());
  190. Sequence seq = DOTween.Sequence();
  191. seq.PrependInterval(2.2f);
  192. seq.AppendCallback(delegate() {
  193. GameObject.Destroy(this.arrowCamera.gameObject);
  194. });
  195. return;
  196. }
  197. }
  198. if (!arrowCamera.arrowSync) {
  199. GameObject.Destroy(this.arrowCamera.gameObject);
  200. }
  201. } else {
  202. if (!isHit) {
  203. isHit = arrowCamera.arrow.isHit;
  204. if (isHit) {
  205. this.arrowCamera.transform.SetParent(this.arrowCamera.arrow.transform);
  206. arrowCamera.transform.localPosition = new Vector3(-0.3f, 0.2f, -1.3f);
  207. arrowCamera.transform.LookAt(this.arrowCamera.arrow.Head());
  208. Sequence seq = DOTween.Sequence();
  209. seq.PrependInterval(2.2f);
  210. seq.AppendCallback(delegate() {
  211. this.arrowCamera.arrow.nextShoot();
  212. GameObject.Destroy(this.arrowCamera.gameObject);
  213. });
  214. return;
  215. }
  216. }
  217. if (!arrowCamera.arrow) {
  218. GameObject.Destroy(this.arrowCamera.gameObject);
  219. }
  220. }
  221. }
  222. }
  223. /* 模板1:跟着箭飞 */
  224. class ArrowCameraTemplate1 : ArrowCameraTemplate
  225. {
  226. public ArrowCameraTemplate1(ArrowCamera arrowCamera) : base(arrowCamera) {
  227. if (!arrowCamera.isArrowSync) {
  228. this.arrowCamera.arrow.activeEffectCyclone(true);
  229. } else {
  230. this.arrowCamera.arrowSync.activeEffectCyclone(true);
  231. }
  232. }
  233. private bool cameraMoveFinish = false;
  234. Vector3 cameraToRunPosition = new Vector3(0.4f, 0.3f, -1 - Mathf.Clamp(Arrow.speed / 20 * 6, 0, 6));
  235. Vector3 cameraFinalPosition = new Vector3(-0.3f, 0.2f, -1.3f);
  236. public override void Update() {
  237. if (cameraMoveFinish) {
  238. return;
  239. }
  240. Transform cameraT = this.arrowCamera.transform;
  241. Vector3 cameraPosition = cameraT.localPosition;
  242. if ((!this.arrowCamera.isArrowSync && this.arrowCamera.arrow.isHit) ||
  243. (this.arrowCamera.isArrowSync && this.arrowCamera.arrowSync.isHit)) {
  244. cameraPosition = Vector3.Lerp(cameraPosition, cameraFinalPosition, Time.deltaTime * 8);
  245. float d = Vector3.Distance(cameraPosition, cameraFinalPosition);
  246. if (d < 0.001f) {
  247. cameraMoveFinish = true;
  248. Sequence seq = DOTween.Sequence();
  249. seq.AppendInterval(2.2f);
  250. seq.AppendCallback(delegate() {
  251. if (!arrowCamera.isArrowSync) {
  252. this.arrowCamera.arrow.nextShoot();
  253. }
  254. GameObject.Destroy(this.arrowCamera.gameObject);
  255. });
  256. }
  257. } else {
  258. cameraPosition = Vector3.Lerp(cameraPosition, cameraToRunPosition, Time.deltaTime * 6);
  259. }
  260. if (this.arrowCamera.isArrowSync) {
  261. cameraT.LookAt(this.arrowCamera.arrowSync.Head());
  262. } else {
  263. cameraT.LookAt(this.arrowCamera.arrow.Head());
  264. }
  265. cameraT.localPosition = cameraPosition;
  266. }
  267. }
  268. /// <summary>
  269. /// 箭相机模板:靶锁模式
  270. /// 相机会跟随箭头运动,并在靠近目标时减速停下,支持缓动曲线控制
  271. /// </summary>
  272. class ArrowCameraTemplate_targetLock : ArrowCameraTemplate
  273. {
  274. //当前操作状态,是否删除
  275. private bool isDeleting = false;
  276. public bool IsDeleting => isDeleting;
  277. private bool cameraStopped = false; // 标记相机是否已停止(到达目标附近后停止更新位置)
  278. private Transform target; // 被锁定的目标(通常是靶心)
  279. private bool isFollowTarget = true;
  280. /// <summary>
  281. /// 相机相对箭的偏移量
  282. /// x = 左右偏移(负数表示往左,正数往右)
  283. /// y = 高度偏移(正数表示高于箭,固定高度时候,和目标y值叠加)
  284. /// z = 前后偏移(负数表示在箭的后面,正数表示在箭的前面)
  285. /// </summary>
  286. private Vector3 offsetFromArrow = new Vector3(0, 0.8f, -2.2f);
  287. private Vector3 targetCenterPos;//目标的中心点位置,靶子的射击靶中点
  288. //private float stopDistanceToTarget = 4f; // 相机与目标之间的最小停止距离(XZ 平面)
  289. private float totalDist = 0f; // 初始相机与目标之间的水平总距离(用于计算进度)
  290. private Vector3 lastArrowPos; // 记录箭的上一次位置
  291. private float arrowSpeed = 0;
  292. public float maxVerticalOffset = -2f; // 相机最多比箭低 2
  293. public ArrowCameraTemplate_targetLock(ArrowCamera arrowCamera, Transform target,bool followTarget = true) : base(arrowCamera)
  294. {
  295. //在 ArmBow.ins 调试参数
  296. offsetFromArrow = ArmBow.ins.offsetFromArrow;
  297. //stopDistanceToTarget = ArmBow.ins.stopDistanceToTarget;
  298. //靶子的位置是 y = 1.3 中心点高度
  299. targetCenterPos = new Vector3(target.position.x, 1.3f, target.position.z);
  300. this.target = target;
  301. this.isFollowTarget = followTarget;
  302. Transform cameraT = arrowCamera.transform;
  303. Transform arrowT = arrowCamera.isArrowSync ? arrowCamera.arrowSync.transform : arrowCamera.arrow.transform;
  304. this.lastArrowPos = arrowT.position;
  305. // 相机初始位置(箭位置 + 偏移,锁定 Y 高度)
  306. Vector3 offsetPos = arrowT.position + arrowT.TransformDirection(offsetFromArrow);
  307. offsetPos.y = target.position.y + offsetFromArrow.y;
  308. offsetPos.x = target.position.x;
  309. cameraT.position = offsetPos;
  310. // 记录初始总路程(XZ 平面,不考虑高度差)
  311. totalDist = GetXZDistance(offsetPos, targetCenterPos);
  312. arrowSpeed = arrowCamera.isArrowSync ? arrowCamera.arrowSync.mySyncSpeed: arrowCamera.arrow.mySpeed;
  313. Debug.Log("当前弓箭速度:" + arrowSpeed);
  314. }
  315. public override void Update()
  316. {
  317. if (target == null) return;
  318. //弓箭删除
  319. if ((arrowCamera.isArrowSync && arrowCamera.arrowSync == null) || (!arrowCamera.isArrowSync && arrowCamera.arrow == null))return;
  320. Transform cameraT = arrowCamera.transform;
  321. Transform arrowT = arrowCamera.isArrowSync ? arrowCamera.arrowSync.transform : arrowCamera.arrow.transform;
  322. if (!cameraStopped)
  323. {
  324. // 计算当前相机与靶子中心的水平距离
  325. float distToTarget = GetXZDistance(cameraT.position, targetCenterPos);
  326. if (this.isFollowTarget)
  327. {
  328. //if (distToTarget <= stopDistanceToTarget)
  329. //{
  330. // // 到达目标附近 -> 停止跟随
  331. // cameraStopped = true;
  332. // cameraT.SetParent(null);
  333. // Debug.Log("distToTarget:" + distToTarget + ",stopDistanceToTarget:"+ stopDistanceToTarget);
  334. // Debug.DrawLine(cameraT.position, targetCenterPos, Color.blue, 2f);
  335. //}
  336. //else
  337. //{
  338. //// progress = 相机已接近目标的进度(0 = 起点,1 = 停止点)
  339. //float progress = Mathf.Clamp01(
  340. // 1f - (distToTarget - stopDistanceToTarget) / (totalDist - stopDistanceToTarget)
  341. //);
  342. //}
  343. // progress = 相机已接近目标的进度(0 = 起点,1 = 停止点)
  344. float progress = Mathf.Clamp01(
  345. 1f - (distToTarget) / (totalDist)
  346. );
  347. // 用缓动曲线计算归一化进度(决定移动节奏)
  348. float easedT = ArmBow.ins.customCurveCamera.Evaluate(progress);
  349. // -----------------------
  350. // 插值偏移量 (从 offsetFromArrow -> offsetFromArrowEnd)
  351. // -----------------------
  352. Vector3 lerpedOffset = Vector3.Lerp(offsetFromArrow, ArmBow.ins.offsetFromArrowEnd, easedT);
  353. // 箭位置 + 偏移(相机保持与箭的相对位置,并锁定到目标的高度)
  354. Vector3 offsetPos = arrowT.position + arrowT.TransformDirection(lerpedOffset);
  355. // -----------------------
  356. // 修改:保持 x 对齐靶子
  357. // -----------------------
  358. offsetPos.x = target.position.x;
  359. // y 固定为靶子高度
  360. offsetPos.y = target.position.y + offsetFromArrow.y;
  361. cameraT.position = offsetPos;
  362. }
  363. else {
  364. //如果没射中目标靶,不用判断目标距离,
  365. //需要判断一下射出目标高度,比如现在是固定y和目标靶的,如何飞高之后,相机不再参考目标靶高度,跟随箭飞出去,
  366. //也给一个能看到的最低水平距离,比如箭飞出相机的距离是10,那就是需要相机和箭保持5的相对高度
  367. // ----------------------------
  368. // 自由跟随模式(非目标锁定,下方视角 + 阶段性切换)
  369. // ----------------------------
  370. Vector3 offsetPos = arrowT.position + arrowT.TransformDirection(offsetFromArrow);
  371. // 相机的目标高度,初始参考靶子,后续逐渐切换为箭下方固定距离
  372. float targetY = target.position.y + offsetFromArrow.y;
  373. // 箭当前位置的理想“下方位置”
  374. float arrowFollowY = arrowT.position.y + maxVerticalOffset;
  375. // 如果相机位置比箭下方限制还高,就参考靶子高度;
  376. // 一旦箭飞到比 maxVerticalOffset 更高的地方,相机高度固定在箭下方 maxVerticalOffset。
  377. if (targetY > arrowFollowY)
  378. offsetPos.y = targetY;
  379. else
  380. offsetPos.y = arrowFollowY;
  381. // 保持相机与箭的最小水平距离,避免重叠
  382. float minXZDistance = 4f;
  383. Vector3 flatCamPos = new Vector3(offsetPos.x, 0, offsetPos.z);
  384. Vector3 flatArrowPos = new Vector3(arrowT.position.x, 0, arrowT.position.z);
  385. float currentXZDist = Vector3.Distance(flatCamPos, flatArrowPos);
  386. if (currentXZDist < minXZDistance)
  387. {
  388. // 推开到最小水平距离
  389. Vector3 dir = (flatCamPos - flatArrowPos).normalized;
  390. flatCamPos = flatArrowPos + dir * minXZDistance;
  391. offsetPos.x = flatCamPos.x;
  392. offsetPos.z = flatCamPos.z;
  393. }
  394. // 更新相机位置
  395. cameraT.position = offsetPos;
  396. // 相机始终朝向箭头方向(可选,保证画面观感)
  397. //cameraT.LookAt(arrowT);
  398. }
  399. }
  400. // 命中处理(箭命中目标后,1.5 秒后销毁相机对象)
  401. if ((!arrowCamera.isArrowSync && arrowCamera.arrow.isHit) ||
  402. (arrowCamera.isArrowSync && arrowCamera.arrowSync.isHit))
  403. {
  404. if (isDeleting) return;
  405. isDeleting = true;
  406. cameraStopped = true;
  407. Sequence seq = DOTween.Sequence();
  408. seq.AppendInterval(1.5f);
  409. seq.AppendCallback(() =>
  410. {
  411. if (!arrowCamera.isArrowSync)
  412. arrowCamera.arrow.nextShoot(); // 下一发准备
  413. GameObject.Destroy(arrowCamera.gameObject); // 销毁相机
  414. });
  415. }
  416. }
  417. /// <summary>
  418. /// 计算两个点在 XZ 平面的水平距离(忽略 Y 高度差)
  419. /// </summary>
  420. private float GetXZDistance(Vector3 a, Vector3 b)
  421. {
  422. float dx = a.x - b.x;
  423. float dz = a.z - b.z;
  424. return Mathf.Sqrt(dx * dx + dz * dz);
  425. }
  426. }
  427. public class ArrowCameraTemplate {
  428. public ArrowCamera arrowCamera;
  429. public ArrowCameraTemplate(ArrowCamera arrowCamera)
  430. {
  431. this.arrowCamera = arrowCamera;
  432. }
  433. public virtual void Update() {}
  434. public virtual void beforeHit() {}
  435. public virtual void SendMsg(int id, object msg) {}
  436. }