LevelAction.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. import { _decorator, BoxCollider2D, Button, Camera, CCFloat, CircleCollider2D, Color, Component, debug, DebugView, director, EventTouch, find, geometry, Input, input, math, Node, NodeEventType, PhysicsSystem, Quat, RenderTexture, Tween, tween, v3, Vec3, view } from 'cc';
  2. import { EventDispatcher } from '../../core_tgx/easy_ui_framework/EventDispatcher';
  3. import { GameEvent } from './Enum/GameEvent';
  4. import { LineDrawer } from './LineDrawer';
  5. import { EnemyComponent } from './Components/EnemyComponent';
  6. import { AliensGlobalInstance } from './AliensGlobalInstance';
  7. import { ScreenShotComponent } from './Components/ScreenShotComponent';
  8. import { GameUtil } from './GameUtil';
  9. import { RadarComponent } from './Components/RadarComponent';
  10. import { tgxUIMgr } from '../../core_tgx/tgx';
  11. import { UI_BattleGambit } from '../../scripts/UIDef';
  12. import { CameraSegmentation, moveDuration } from './CamerSegmentation';
  13. import { TimerMgr } from './Manager/TimerMgr';
  14. const { ccclass, property } = _decorator;
  15. //动画时长
  16. export const ANIMATION_DURATION = 0.5;
  17. @ccclass('LevelAction')
  18. export class LevelAction extends Component {
  19. @property(Camera)
  20. public camera: Camera = null!;
  21. private _renderTex: RenderTexture | null = null;
  22. private _cameraOriginalPos: Vec3 = v3();
  23. private _touchStartPos: Vec3 = v3();
  24. private _isDragging = false;
  25. private _isZooming = false;
  26. public targetNode: Node = null!;
  27. //关卡怪物总数
  28. @property({type: CCFloat,displayName:"怪物总数"})
  29. public enemyTotal: number = 0;
  30. // 添加旋转限制属性
  31. @property({type: CCFloat ,displayName:"相机X轴最小旋转角度(上下)"})
  32. public minXRotation: number = -30; // X轴最小旋转角度(上下)
  33. @property({type: CCFloat ,displayName:"相机X轴最大旋转角度(上下)"})
  34. public maxXRotation: number = 30; // X轴最大旋转角度(上下)
  35. @property({type: CCFloat ,displayName:"相机Y轴最小旋转角度(左右)"})
  36. public minYRotation: number = -50; // Y轴最小旋转角度(左右)
  37. @property({type: CCFloat ,displayName:"相机Y轴最大旋转角度(左右)"})
  38. public maxYRotation: number = 50; // Y轴最大旋转角度(左右)
  39. private _originalRotation: Vec3 = v3();
  40. @property({type: CCFloat ,displayName:"镜头缩进距离"})
  41. public indentationDistance: number = 20;
  42. //镜头拉近属性
  43. private isTweening: boolean = false;
  44. onLoad(): void {
  45. this._cameraOriginalPos = this.camera.node.position.clone();
  46. this._originalRotation = this.camera.node.eulerAngles.clone();
  47. this.registerEvent();
  48. }
  49. start() {
  50. this.initilizeUI();
  51. this.saveCameraState();
  52. EventDispatcher.instance.emit(GameEvent.EVENT_INIT_REMAIN_ENEMY,this.enemyTotal);
  53. }
  54. private initilizeUI(){
  55. const renderNode = AliensGlobalInstance.instance.renderNode;
  56. const aimTarget = AliensGlobalInstance.instance.aimTarget;
  57. const radarNode = AliensGlobalInstance.instance.radarNode;
  58. renderNode.active = false;
  59. aimTarget.active = false;
  60. radarNode.active = false;
  61. const match = tgxUIMgr.inst.isShowing(UI_BattleGambit);
  62. if (!match) {
  63. tgxUIMgr.inst.showUI(UI_BattleGambit);
  64. }
  65. }
  66. private registerEvent(){
  67. // 触摸事件监听
  68. input.on(Input.EventType.TOUCH_START, this._onTouchStart, this);
  69. input.on(Input.EventType.TOUCH_MOVE, this._onTouchMove, this);
  70. input.on(Input.EventType.TOUCH_END, this._onTouchEnd, this);
  71. input.on(Input.EventType.TOUCH_CANCEL, this._onTouchEnd, this);
  72. //事件监听
  73. EventDispatcher.instance.on(GameEvent.EVENT_CAMERA_AIM,this.onAimTarget,this);
  74. EventDispatcher.instance.on(GameEvent.EVENT_CAMERA_RESET_AIM,this.onResetAimTarget,this);
  75. EventDispatcher.instance.on(GameEvent.EVENT_CAMERA_SHOOT,this.onShoot,this);
  76. EventDispatcher.instance.on(GameEvent.EVENT_CAMERA_SCREENSHOT_RADAR_LOCK,this.onCameraToTarget,this);
  77. }
  78. private unRegisterEvent(){
  79. // 触摸事件监听
  80. input.off(Input.EventType.TOUCH_START, this._onTouchStart, this);
  81. input.off(Input.EventType.TOUCH_MOVE, this._onTouchMove, this);
  82. input.off(Input.EventType.TOUCH_END, this._onTouchEnd, this);
  83. input.off(Input.EventType.TOUCH_CANCEL, this._onTouchEnd, this);
  84. //事件监听
  85. EventDispatcher.instance.off(GameEvent.EVENT_CAMERA_AIM,this.onAimTarget,this);
  86. EventDispatcher.instance.off(GameEvent.EVENT_CAMERA_RESET_AIM,this.onResetAimTarget,this);
  87. EventDispatcher.instance.off(GameEvent.EVENT_CAMERA_SHOOT,this.onShoot,this);
  88. EventDispatcher.instance.off(GameEvent.EVENT_CAMERA_SCREENSHOT_RADAR_LOCK,this.onCameraToTarget,this);
  89. }
  90. private onAimTarget(){
  91. this.moveCameraAlongForward(-this.indentationDistance); // 负值表示拉近
  92. }
  93. private onResetAimTarget(){
  94. this.moveCameraAlongForward(this.indentationDistance); // 正值表示拉远
  95. }
  96. private async onShoot(){
  97. // 获取正确的屏幕中心坐标
  98. const screenCenter = view.getVisibleSize();
  99. const screenX = screenCenter.width * 0.5 * view.getScaleX();
  100. const screenY = screenCenter.height * 0.5 * view.getScaleY();
  101. // 从屏幕中心发射射线
  102. const ray = new geometry.Ray();
  103. this.camera.screenPointToRay(screenX, screenY, ray);
  104. // 射线检测参数
  105. const mask = 0xffffffff;
  106. const maxDistance = 1000;
  107. const queryTrigger = true;
  108. // 执行射线检测
  109. const hasHit = PhysicsSystem.instance.raycast(ray, mask, maxDistance, queryTrigger);
  110. if (hasHit) {
  111. const results = PhysicsSystem.instance.raycastResults;
  112. for (let i = 0; i < results.length; i++) {
  113. const item = results[i];
  114. const hitNode = item.collider.node;
  115. console.log(`碰撞物体${i}: ${hitNode.name} 距离: ${item.distance.toFixed(2)}`);
  116. if(hitNode.getComponent(EnemyComponent)){
  117. const levelNode = AliensGlobalInstance.instance.levels.children[0];
  118. const remain = levelNode.getChildByName('et')!.children.length;
  119. if(remain > 1){
  120. EventDispatcher.instance.emit(GameEvent.EVENT_CAMERA_SHOOT_TEXT);
  121. EventDispatcher.instance.emit(GameEvent.EVENT_CAMERA_SHOOT_ENEMY,hitNode);
  122. }else{
  123. const origin = levelNode.getChildByName('origin')!;
  124. const target = hitNode;
  125. EventDispatcher.instance.emit(GameEvent.EVENT_LAST_ENEMY_KILLED);
  126. TimerMgr.inst.pauseCountdown();
  127. AliensGlobalInstance.instance.guns.active = false;
  128. CameraSegmentation.segmentation(origin,target);
  129. this.scheduleOnce(()=>{
  130. EventDispatcher.instance.emit(GameEvent.EVENT_CAMERA_SHOOT_ENEMY,hitNode);
  131. },(moveDuration + 1) / 10);
  132. }
  133. }
  134. }
  135. }
  136. }
  137. private moveCameraAlongForward(distance: number) {
  138. if(this._isZooming) return;
  139. this._isZooming = true;
  140. const currentPos = this.camera.node.worldPosition.clone();
  141. const forward = this.camera.node.forward.negative();
  142. const targetPos = currentPos.add(forward.multiplyScalar(distance));
  143. tween(this.camera.node.worldPosition)
  144. .to(ANIMATION_DURATION, targetPos, {
  145. easing: 'smooth',
  146. onUpdate: (target: Vec3) => {
  147. this.camera.node.worldPosition = target;
  148. // 根据镜头距离动态调整旋转限制
  149. this.adjustRotationLimits();
  150. this._isZooming = false;
  151. }
  152. })
  153. .start();
  154. }
  155. //相机转向目标
  156. private async onCameraToTarget(targetNode: Node){
  157. const camera = this.camera;
  158. if (!targetNode || !camera) return;
  159. const targetPos = new Vec3();
  160. targetNode.getWorldPosition(targetPos);
  161. // 获取相机位置
  162. const cameraPos = new Vec3();
  163. camera.node.getWorldPosition(cameraPos);
  164. // 计算从相机到目标的方向向量
  165. const direction = new Vec3();
  166. Vec3.subtract(direction, targetPos, cameraPos);
  167. direction.normalize();
  168. // 计算目标欧拉角
  169. const targetYaw = math.toDegree(Math.atan2(-direction.x, -direction.z));
  170. const targetPitch = math.toDegree(Math.asin(direction.y));
  171. // 获取当前欧拉角
  172. const currentRotation = camera.node.eulerAngles.clone();
  173. // 创建一个对象用于tween
  174. const tweenObj = {
  175. pitch: currentRotation.x,
  176. yaw: currentRotation.y
  177. };
  178. this.isTweening = true;
  179. tween(tweenObj)
  180. .to(ANIMATION_DURATION, {
  181. pitch: targetPitch,
  182. yaw: targetYaw
  183. }, {
  184. easing: 'smooth',
  185. onUpdate: () => {
  186. // 更新相机旋转
  187. camera.node.setRotationFromEuler(tweenObj.pitch, tweenObj.yaw, 0);
  188. },
  189. onComplete: () => {
  190. this.isTweening = false;
  191. }
  192. })
  193. .start();
  194. }
  195. private adjustRotationLimits() {
  196. // 计算当前镜头距离比例 (0-1范围)
  197. const distanceRatio = Math.min(1, Math.max(0, (this.camera.node.position.z - this._cameraOriginalPos.z) / - this.indentationDistance));
  198. // 动态调整旋转限制范围
  199. const dynamicMinY = this.minYRotation * (1 + distanceRatio);
  200. const dynamicMaxY = this.maxYRotation * (1 + distanceRatio);
  201. // 应用调整后的限制
  202. this.minYRotation = dynamicMinY;
  203. this.maxYRotation = dynamicMaxY;
  204. }
  205. /***************************触摸事件**********************************/
  206. private _onTouchStart(event: EventTouch) {
  207. if(this._isZooming)return;
  208. const touchPos = event.getLocation();
  209. this._touchStartPos = v3(touchPos.x, touchPos.y, 0);
  210. this._isDragging = true;
  211. // 记录初始旋转角度
  212. this._originalRotation = this.camera.node.eulerAngles.clone();
  213. const radarComponent = AliensGlobalInstance.instance.renderNode.getComponent(RadarComponent)!;
  214. if(radarComponent){
  215. radarComponent.unlockPositionUpdate();
  216. }
  217. }
  218. private async _onTouchMove(event: EventTouch) {
  219. if (!this._isDragging) return;
  220. const currentPos = event.getLocation();
  221. const deltaX = currentPos.x - this._touchStartPos.x;
  222. const deltaY = currentPos.y - this._touchStartPos.y;
  223. // 仅计算旋转角度变化
  224. const newRotation = this._originalRotation.clone();
  225. newRotation.y = this._originalRotation.y - deltaX * 0.2;
  226. newRotation.x = this._originalRotation.x + deltaY * 0.2;
  227. // 添加旋转限制
  228. newRotation.x = Math.max(this.minXRotation, Math.min(this.maxXRotation, newRotation.x));
  229. newRotation.y = Math.max(this.minYRotation, Math.min(this.maxYRotation, newRotation.y));
  230. this.camera.node.setRotationFromEuler(newRotation);
  231. await this.saveCameraState();
  232. }
  233. //保存相机的位置和旋转角度
  234. private async saveCameraState() {
  235. const cameraOriginalPos = this.camera.node.worldPosition.clone();
  236. const originalRotation = this.camera.node.eulerAngles.clone();
  237. const screenShot = AliensGlobalInstance.instance.renderNode.getComponent(ScreenShotComponent)!;
  238. screenShot.saveCameraState(cameraOriginalPos,originalRotation);
  239. }
  240. private _onTouchEnd() {
  241. this._isDragging = false;
  242. const radarComponent = AliensGlobalInstance.instance.renderNode.getComponent(RadarComponent)!;
  243. if(radarComponent){
  244. radarComponent.unlockPositionUpdate();
  245. }
  246. }
  247. /***************************触摸事件end**********************************/
  248. onDestroy () {
  249. Tween.stopAllByTarget(this.node);
  250. this.unRegisterEvent();
  251. if (this._renderTex) {
  252. this._renderTex.destroy();
  253. this._renderTex = null;
  254. }
  255. }
  256. }