ArrowCamera.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  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. }
  33. //if ((isArrowSync && arrowSync.canUseSideCamera) || (!isArrowSync && arrow.canUseSideCamera)) {
  34. // arrowCameraTemplate = new ArrowCameraTemplate2(this);
  35. // Debug.Log("跟随镜头2");
  36. //} else {
  37. // arrowCameraTemplate = new ArrowCameraTemplate1(this);
  38. // Debug.Log("跟随镜头1");
  39. //}
  40. } else if (SceneManager.GetActiveScene().name == "GameChallenge") {
  41. arrowCameraTemplate = new ArrowCameraTemplate3(this);
  42. }
  43. }
  44. void OnDestroy()
  45. {
  46. try
  47. {
  48. BowCamera.ins.SetArrowFollowing(false);
  49. }
  50. catch (System.Exception) {}
  51. }
  52. void FixedUpdate()
  53. {
  54. arrowCameraTemplate.Update();
  55. }
  56. }
  57. /* 模板3:跟着箭飞_打猎场景 */
  58. class ArrowCameraTemplate3 : ArrowCameraTemplate
  59. {
  60. public ArrowCameraTemplate3(ArrowCamera arrowCamera) : base(arrowCamera) {
  61. if (!arrowCamera.isArrowSync) {
  62. this.arrowCamera.arrow.activeEffectCyclone(true);
  63. } else {
  64. this.arrowCamera.arrowSync.activeEffectCyclone(true);
  65. }
  66. Camera camera = arrowCamera.transform.GetComponent<Camera>();
  67. if (camera) camera.fieldOfView = 25;
  68. }
  69. private bool cameraMoveFinish = false;
  70. Vector3 cameraToRunPosition = new Vector3(0.4f, 0.5f, -1 - Mathf.Clamp(Arrow.speed / 20 * 6, 0, 6));
  71. Vector3 cameraFinalPosition = new Vector3(0.4f, 0.8f, -5f);
  72. //若有树的遮挡,则使用以下视角坐标
  73. Vector3 cameraFinalPosition_whenBlockByTree = new Vector3(0.15f, 0.3f, -1.2f);
  74. bool hasBlockByTree = false;
  75. Vector3 blockByTreeLookAtPoint;
  76. public override void Update() {
  77. if (cameraMoveFinish) {
  78. return;
  79. }
  80. Transform cameraT = this.arrowCamera.transform;
  81. Vector3 cameraPosition = cameraT.localPosition;
  82. if ((!this.arrowCamera.isArrowSync && this.arrowCamera.arrow.isHit) ||
  83. (this.arrowCamera.isArrowSync && this.arrowCamera.arrowSync.isHit)) {
  84. cameraPosition = Vector3.Lerp(cameraPosition, cameraFinalPosition, Time.deltaTime * 6);
  85. cameraT.localPosition = cameraPosition;
  86. if (hasBlockByTree) {
  87. cameraT.LookAt(blockByTreeLookAtPoint);
  88. }
  89. float d = Vector3.Distance(cameraPosition, cameraFinalPosition);
  90. if (d < 0.001f) {
  91. cameraMoveFinish = true;
  92. Sequence seq = DOTween.Sequence();
  93. if (!quicklyNextShoot) seq.AppendInterval(1f);
  94. seq.AppendCallback(delegate() {
  95. if (!this.arrowCamera.isArrowSync) {
  96. this.arrowCamera.arrow.nextShoot();
  97. }
  98. GameObject.Destroy(container?container:this.arrowCamera.gameObject);
  99. });
  100. }
  101. } else {
  102. cameraPosition = Vector3.Lerp(cameraPosition, cameraToRunPosition, Time.deltaTime * 6);
  103. cameraT.localPosition = cameraPosition;
  104. if (this.arrowCamera.isArrowSync) {
  105. cameraT.LookAt(this.arrowCamera.arrowSync.Head());
  106. } else {
  107. cameraT.LookAt(this.arrowCamera.arrow.Head());
  108. }
  109. }
  110. }
  111. GameObject container = null;
  112. public override void beforeHit() {
  113. //把镜头从箭中移除,放到一个固定的容器里,免得随着动物抖动
  114. if (!container) {
  115. container = new GameObject("CameraContainer");
  116. container.transform.position = this.arrowCamera.arrow.transform.position;
  117. container.transform.rotation = this.arrowCamera.arrow.transform.rotation;
  118. this.arrowCamera.transform.SetParent(container.transform);
  119. //检测有没有树遮挡
  120. Quaternion rot = container.transform.rotation;
  121. Quaternion back = rot * Quaternion.AngleAxis(180, Vector3.up); //绕本地坐标轴旋转
  122. for (int i = -36; i <= 36; i += 3) {
  123. float angle = i;
  124. Quaternion axis = back * Quaternion.AngleAxis(angle, Vector3.up);
  125. Vector3 direction = axis * Vector3.forward;
  126. RaycastHit[] raycastHits = Physics.RaycastAll(container.transform.position, direction, 6f);
  127. foreach (var raycastHit in raycastHits) {
  128. Transform tf = raycastHit.transform;
  129. if (tf && tf.name.StartsWith("TreeCollider")) {
  130. cameraFinalPosition = cameraFinalPosition_whenBlockByTree;
  131. hasBlockByTree = true;
  132. Arrow arrow = this.arrowCamera.arrow;
  133. blockByTreeLookAtPoint = arrow.Head().position - arrow.transform.forward * 0.5f;
  134. break;
  135. }
  136. }
  137. }
  138. }
  139. //记录需要同步的消息
  140. if (!this.arrowCamera.isArrowSync) {
  141. if (this.arrowCamera.arrow.outputSyncData != null) {
  142. this.arrowCamera.arrow.outputSyncData.SetArrowCameraTemplate3(this.hasBlockByTree, this.quicklyNextShoot);
  143. }
  144. }
  145. }
  146. public void beforHitWhenSync(bool hasBlockByTree) {
  147. this.hasBlockByTree = hasBlockByTree;
  148. container = new GameObject("CameraContainer");
  149. container.transform.position = this.arrowCamera.arrowSync.transform.position;
  150. container.transform.rotation = Quaternion.LookRotation(this.arrowCamera.arrowSync.transform.forward, Vector3.up);
  151. this.arrowCamera.transform.SetParent(container.transform);
  152. this.arrowCamera.transform.localRotation = Quaternion.identity;
  153. if (hasBlockByTree) {
  154. cameraFinalPosition = cameraFinalPosition_whenBlockByTree;
  155. ArrowSync arrowSync = this.arrowCamera.arrowSync;
  156. blockByTreeLookAtPoint = arrowSync.Head().position - arrowSync.transform.forward * 0.5f;
  157. }
  158. }
  159. public bool quicklyNextShoot = ChallengeGameMode.IsChallengeWolf();
  160. public override void SendMsg(int id, object msg) {
  161. if (id == 0) {
  162. quicklyNextShoot = true;
  163. }
  164. //记录需要同步的消息
  165. if (!this.arrowCamera.isArrowSync) {
  166. if (this.arrowCamera.arrow.outputSyncData != null) {
  167. this.arrowCamera.arrow.outputSyncData.SetArrowCameraTemplate3(this.hasBlockByTree, this.quicklyNextShoot);
  168. }
  169. }
  170. }
  171. }
  172. /* 模板2:从侧面看箭飞 */
  173. class ArrowCameraTemplate2 : ArrowCameraTemplate
  174. {
  175. public ArrowCameraTemplate2(ArrowCamera arrowCamera) : base(arrowCamera) {
  176. this.arrowCamera.transform.parent = null;
  177. arrowCamera.transform.localPosition = new Vector3(8.33f, 2.45f, 6.4f);
  178. arrowCamera.transform.localEulerAngles = new Vector3(0, -42, 0);
  179. }
  180. bool isHit = false;
  181. public override void Update() {
  182. if (arrowCamera.isArrowSync) {
  183. if (!isHit) {
  184. isHit = arrowCamera.arrowSync.isHit;
  185. if (isHit) {
  186. this.arrowCamera.transform.SetParent(this.arrowCamera.arrowSync.transform);
  187. arrowCamera.transform.localPosition = new Vector3(-0.3f, 0.2f, -1.3f);
  188. arrowCamera.transform.LookAt(this.arrowCamera.arrowSync.Head());
  189. Sequence seq = DOTween.Sequence();
  190. seq.PrependInterval(2.2f);
  191. seq.AppendCallback(delegate() {
  192. GameObject.Destroy(this.arrowCamera.gameObject);
  193. });
  194. return;
  195. }
  196. }
  197. if (!arrowCamera.arrowSync) {
  198. GameObject.Destroy(this.arrowCamera.gameObject);
  199. }
  200. } else {
  201. if (!isHit) {
  202. isHit = arrowCamera.arrow.isHit;
  203. if (isHit) {
  204. this.arrowCamera.transform.SetParent(this.arrowCamera.arrow.transform);
  205. arrowCamera.transform.localPosition = new Vector3(-0.3f, 0.2f, -1.3f);
  206. arrowCamera.transform.LookAt(this.arrowCamera.arrow.Head());
  207. Sequence seq = DOTween.Sequence();
  208. seq.PrependInterval(2.2f);
  209. seq.AppendCallback(delegate() {
  210. this.arrowCamera.arrow.nextShoot();
  211. GameObject.Destroy(this.arrowCamera.gameObject);
  212. });
  213. return;
  214. }
  215. }
  216. if (!arrowCamera.arrow) {
  217. GameObject.Destroy(this.arrowCamera.gameObject);
  218. }
  219. }
  220. }
  221. }
  222. /* 模板1:跟着箭飞 */
  223. class ArrowCameraTemplate1 : ArrowCameraTemplate
  224. {
  225. public ArrowCameraTemplate1(ArrowCamera arrowCamera) : base(arrowCamera) {
  226. if (!arrowCamera.isArrowSync) {
  227. this.arrowCamera.arrow.activeEffectCyclone(true);
  228. } else {
  229. this.arrowCamera.arrowSync.activeEffectCyclone(true);
  230. }
  231. }
  232. private bool cameraMoveFinish = false;
  233. Vector3 cameraToRunPosition = new Vector3(0.4f, 0.3f, -1 - Mathf.Clamp(Arrow.speed / 20 * 6, 0, 6));
  234. Vector3 cameraFinalPosition = new Vector3(-0.3f, 0.2f, -1.3f);
  235. public override void Update() {
  236. if (cameraMoveFinish) {
  237. return;
  238. }
  239. Transform cameraT = this.arrowCamera.transform;
  240. Vector3 cameraPosition = cameraT.localPosition;
  241. if ((!this.arrowCamera.isArrowSync && this.arrowCamera.arrow.isHit) ||
  242. (this.arrowCamera.isArrowSync && this.arrowCamera.arrowSync.isHit)) {
  243. cameraPosition = Vector3.Lerp(cameraPosition, cameraFinalPosition, Time.deltaTime * 8);
  244. float d = Vector3.Distance(cameraPosition, cameraFinalPosition);
  245. if (d < 0.001f) {
  246. cameraMoveFinish = true;
  247. Sequence seq = DOTween.Sequence();
  248. seq.AppendInterval(2.2f);
  249. seq.AppendCallback(delegate() {
  250. if (!arrowCamera.isArrowSync) {
  251. this.arrowCamera.arrow.nextShoot();
  252. }
  253. GameObject.Destroy(this.arrowCamera.gameObject);
  254. });
  255. }
  256. } else {
  257. cameraPosition = Vector3.Lerp(cameraPosition, cameraToRunPosition, Time.deltaTime * 6);
  258. }
  259. if (this.arrowCamera.isArrowSync) {
  260. cameraT.LookAt(this.arrowCamera.arrowSync.Head());
  261. } else {
  262. cameraT.LookAt(this.arrowCamera.arrow.Head());
  263. }
  264. cameraT.localPosition = cameraPosition;
  265. }
  266. }
  267. /// <summary>
  268. /// 箭相机模板:靶锁模式
  269. /// 相机会跟随箭头运动,并在靠近目标时减速停下,支持缓动曲线控制
  270. /// </summary>
  271. class ArrowCameraTemplate_targetLock : ArrowCameraTemplate
  272. {
  273. //当前操作状态,是否删除
  274. private bool isDeleting = false;
  275. public bool IsDeleting => isDeleting;
  276. private bool cameraStopped = false; // 标记相机是否已停止(到达目标附近后停止更新位置)
  277. private Transform target; // 被锁定的目标(通常是靶心)
  278. /// <summary>
  279. /// 相机相对箭的偏移量
  280. /// x = 左右偏移(负数表示往左,正数往右)
  281. /// y = 高度偏移(正数表示高于箭,固定高度时候,和目标y值叠加)
  282. /// z = 前后偏移(负数表示在箭的后面,正数表示在箭的前面)
  283. /// </summary>
  284. private Vector3 offsetFromArrow = new Vector3(0, 0.8f, -2.2f);
  285. private float stopDistanceToTarget = 4f; // 相机与目标之间的最小停止距离(XZ 平面)
  286. private float totalDist = 0f; // 初始相机与目标之间的水平总距离(用于计算进度)
  287. // 调节参数
  288. private const float extraSpeedMax = 100f; // 相机在箭方向上的额外冲击速度最大值(越大越“冲”)
  289. private const float minLerpSpeed = 2f; // 插值时的最小速度(进度小的时候)
  290. private const float maxLerpSpeed = 10f; // 插值时的最大速度(进度接近 1 时)
  291. public ArrowCameraTemplate_targetLock(ArrowCamera arrowCamera, Transform target) : base(arrowCamera)
  292. {
  293. //在 ArmBow.ins 调试参数
  294. offsetFromArrow = ArmBow.ins.offsetFromArrow;
  295. stopDistanceToTarget = ArmBow.ins.stopDistanceToTarget;
  296. this.target = target;
  297. Transform cameraT = arrowCamera.transform;
  298. Transform arrowT = arrowCamera.isArrowSync ? arrowCamera.arrowSync.transform : arrowCamera.arrow.transform;
  299. // 相机初始位置(箭位置 + 偏移,锁定 Y 高度)
  300. Vector3 offsetPos = arrowT.position + arrowT.TransformDirection(offsetFromArrow);
  301. offsetPos.y = target.position.y + offsetFromArrow.y;
  302. cameraT.position = offsetPos;
  303. // 记录初始总路程(XZ 平面,不考虑高度差)
  304. totalDist = GetXZDistance(offsetPos, target.position);
  305. }
  306. public override void Update()
  307. {
  308. if (target == null) return;
  309. //弓箭删除
  310. if ((arrowCamera.isArrowSync && arrowCamera.arrowSync == null) || (!arrowCamera.isArrowSync && arrowCamera.arrow == null))return;
  311. Transform cameraT = arrowCamera.transform;
  312. Transform arrowT = arrowCamera.isArrowSync ? arrowCamera.arrowSync.transform : arrowCamera.arrow.transform;
  313. if (!cameraStopped)
  314. {
  315. // 计算当前相机与目标的水平距离
  316. float distToTarget = GetXZDistance(cameraT.position, target.position);
  317. if (distToTarget <= stopDistanceToTarget)
  318. {
  319. // 到达目标附近 -> 停止跟随
  320. cameraStopped = true;
  321. cameraT.SetParent(null);
  322. }
  323. else
  324. {
  325. // progress = 相机已接近目标的进度(0 = 起点,1 = 停止点)
  326. float progress = Mathf.Clamp01(
  327. 1f - (distToTarget - stopDistanceToTarget) / (totalDist - stopDistanceToTarget)
  328. );
  329. // 用缓动曲线计算归一化进度(决定移动节奏)
  330. float easedT = ArmBow.ins.customCurveCamera.Evaluate(progress);
  331. // 箭位置 + 偏移(相机保持与箭的相对位置,并锁定到目标的高度)
  332. Vector3 offsetPos = arrowT.position + arrowT.TransformDirection(offsetFromArrow);
  333. offsetPos.y = target.position.y + offsetFromArrow.y;
  334. // 箭的水平前进方向(忽略 y 分量)
  335. Vector3 arrowDirXZ = new Vector3(arrowT.forward.x, 0f, arrowT.forward.z).normalized;
  336. // 相机额外的位移偏移量(随 easedT 增强,越靠近目标越快)
  337. Vector3 extraOffset = arrowDirXZ * Mathf.Lerp(0f, extraSpeedMax, easedT) * Time.deltaTime;
  338. // 最终目标位置 = 箭偏移位置 + 额外速度偏移
  339. Vector3 targetPos = offsetPos + extraOffset;
  340. // 相机向目标位置平滑插值(随曲线加速)
  341. float speedFactor = Mathf.Lerp(minLerpSpeed, maxLerpSpeed, easedT);
  342. cameraT.position = Vector3.Lerp(cameraT.position, targetPos, Time.deltaTime * speedFactor);
  343. //#if UNITY_EDITOR
  344. // Debug.Log($"progress:{progress:F2}, easedT:{easedT:F2}, speed:{speedFactor:F2}, extra:{extraOffset}");
  345. //#endif
  346. }
  347. }
  348. // 命中处理(箭命中目标后,1.5 秒后销毁相机对象)
  349. if ((!arrowCamera.isArrowSync && arrowCamera.arrow.isHit) ||
  350. (arrowCamera.isArrowSync && arrowCamera.arrowSync.isHit))
  351. {
  352. if (isDeleting) return;
  353. isDeleting = true;
  354. Sequence seq = DOTween.Sequence();
  355. seq.AppendInterval(1.5f);
  356. seq.AppendCallback(() =>
  357. {
  358. if (!arrowCamera.isArrowSync)
  359. arrowCamera.arrow.nextShoot(); // 下一发准备
  360. GameObject.Destroy(arrowCamera.gameObject); // 销毁相机
  361. });
  362. }
  363. }
  364. /// <summary>
  365. /// 计算两个点在 XZ 平面的水平距离(忽略 Y 高度差)
  366. /// </summary>
  367. private float GetXZDistance(Vector3 a, Vector3 b)
  368. {
  369. float dx = a.x - b.x;
  370. float dz = a.z - b.z;
  371. return Mathf.Sqrt(dx * dx + dz * dz);
  372. }
  373. }
  374. public class ArrowCameraTemplate {
  375. public ArrowCamera arrowCamera;
  376. public ArrowCameraTemplate(ArrowCamera arrowCamera)
  377. {
  378. this.arrowCamera = arrowCamera;
  379. }
  380. public virtual void Update() {}
  381. public virtual void beforeHit() {}
  382. public virtual void SendMsg(int id, object msg) {}
  383. }