GameNode.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. import { _decorator, geometry, Node, Camera, screen, EventTouch, instantiate, v3, tween, MeshRenderer, Vec3, find, input, Input, Tween, PhysicsSystem, RigidBody, Prefab, math, BoxCollider, Collider, director, Director } from 'cc';
  2. import { TileItem } from './TileItem';
  3. import { Main } from './Main';
  4. import { ResUtil } from '../core/utils/ResUtil';
  5. import { ComponentEx } from '../core/component/ComponentEx';
  6. import { audioMgr } from '../core/manager/AudioManager';
  7. import BusyLoadingManager, { BUSY_TYPE } from '../core/manager/BusyLoadingManager';
  8. import Data from '../core/manager/Data';
  9. import WindowManager from '../core/manager/WindowManager';
  10. import MsgHints from '../core/utils/MsgHints';
  11. import Utils from '../core/utils/Utils';
  12. import { DrawStarLayer } from '../gameui/DrawStarLayer';
  13. import { eventEmitter } from '../core/event/EventEmitter';
  14. import platformSystem from '../platform/platformSystem';
  15. import { GameConst, ITEM_TYPE } from '../core/common/GameConst';
  16. import { levelsData } from '../user/LevelsData';
  17. import i18n from '../core/i18n/runtime-scripts/LanguageData';
  18. import { PoolManager } from '../core/manager/PoolManager';
  19. const { Ray } = geometry;
  20. const { ccclass, property, executeInEditMode } = _decorator;
  21. @ccclass('GameNode')
  22. export class GameNode extends ComponentEx {
  23. public lock: boolean = false;
  24. public collectpos: Vec3[] = [];
  25. public collectTiles: Node[] = [];
  26. public allTiles: Node[] = [];
  27. public optionTiles: Node[] = [];
  28. public start() {
  29. setTimeout(() => {
  30. this.initWall();
  31. this.collectpos = [];
  32. this.GetNode("collectbox").children.map(a => {
  33. this.collectpos.push(a.worldPosition);
  34. a.active = false;
  35. })
  36. }, 100);
  37. eventEmitter.register(this, GameConst.CLEAR_ALL_BOX, () => {
  38. console.log("清空盒子")
  39. this.lock=false
  40. setTimeout(() => {
  41. for (let i = 0; i < this.collectTiles.length; ++i) {
  42. this.pushBask(this.collectTiles[i].name);
  43. }
  44. this.collectTiles.map(a => this.put(a));
  45. this.collectTiles = [];
  46. }, 500);
  47. })
  48. //提示
  49. eventEmitter.register(this, GameConst.USE_ITEM_HINT,this.prompt.bind(this))
  50. }
  51. onEnable() {
  52. input.on(Input.EventType.TOUCH_START, this.onTouchStart, this);
  53. input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this);
  54. input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
  55. }
  56. onDisable() {
  57. input.off(Input.EventType.TOUCH_START, this.onTouchStart, this);
  58. input.off(Input.EventType.TOUCH_END, this.onTouchEnd, this);
  59. input.off(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
  60. }
  61. moveToRightPos() {
  62. this.collectTiles.map((a, i) => {
  63. let tileItem = a.getComponent(TileItem)
  64. Tween.stopAllByTarget(a);
  65. tileItem.auto_rotation = true;
  66. // this.parabolicMovementWithScale(a,this.collectpos[i].clone(),10,0.15,a.scale.clone(),v3(1, 1, 1),
  67. // (n: Node) => {
  68. tween(a).to(0.25, { worldPosition: this.collectpos[i].clone(), worldScale: new Vec3(1, 1, 1) }).call(() => {
  69. if (tileItem.removed) {
  70. Utils.remove(this.optionTiles,a);
  71. tween(a).to(0.2, { worldPosition: tileItem.removedPos }, { easing: 'backIn' }).call(async () => {
  72. //最中央的播放移除特效
  73. if(tileItem.playRmovedEff) {
  74. //增加星星
  75. let pos = this.GetNode("Main Camera").getComponent(Camera).convertToUINode(a.worldPosition, find("Canvas"));
  76. let node_ani = await ResUtil.playSkAni("spine/effect_hecheng", "effect", find("Canvas"), pos);
  77. audioMgr.playOneShot(GameConst.audios.starCollect);
  78. platformSystem.platform.vibrateShort();
  79. Utils.flyAnim(ITEM_TYPE.Star, node_ani, Main.I._GameUI.starNode, 1, 0, (b) => {
  80. if (b) {
  81. PoolManager.putNode(node_ani);
  82. Main.I._GameUI.star++;
  83. }
  84. });
  85. }
  86. this.put(a);
  87. this.moveToRightPos();
  88. this.checkResult()
  89. }).start()
  90. }
  91. }).start();
  92. // }
  93. // );
  94. })
  95. }
  96. chooseTile(tile: Node) {
  97. let chooseName = tile.name;
  98. this.optionTiles.push(tile);
  99. let tileItem:TileItem = tile.getComponent(TileItem);
  100. tileItem.destoryCollider();
  101. for (var i = 0; i < this.allTiles.length; ++i) {
  102. if (this.allTiles[i] == tile) {
  103. this.allTiles.splice(i, 1);
  104. break;
  105. }
  106. }
  107. //计算目标位置
  108. let targetPos: Vec3;
  109. let bInsert = false;
  110. for (var i = this.collectTiles.length - 1; i >= 0; --i) {
  111. if (this.collectTiles[i].name == chooseName) {
  112. targetPos = this.collectpos[i + 1]?.clone() || this.collectpos[this.collectpos.length - 1].clone();
  113. this.collectTiles.splice(i + 1, 0, tile);
  114. bInsert = true;
  115. break;
  116. }
  117. }
  118. if (!bInsert) {
  119. targetPos = this.collectpos[this.collectTiles.length]?.clone() || this.collectpos[0].clone();
  120. this.collectTiles.push(tile);
  121. }
  122. this.moveToRightPos()
  123. //动画完成后执行原有逻辑
  124. let tmp = this.collectTiles.filter(a =>a.name == chooseName);
  125. if(tmp.length == 3) {
  126. for (var i = this.collectTiles.length - 1; i >= 0; --i) {
  127. if (this.collectTiles[i].name == chooseName) {
  128. let delNode = this.collectTiles[i];
  129. delNode.getComponent(TileItem).removed = true;
  130. this.collectTiles.splice(i, 1);
  131. delNode.getComponent(TileItem).removedPos = tmp[1].worldPosition;
  132. if (delNode == tmp[1])
  133. delNode.getComponent(TileItem).playRmovedEff = true;
  134. }
  135. }
  136. }
  137. this.checkResult();
  138. }
  139. checkResult() {
  140. if(this.lock)return
  141. //检测胜利和失败
  142. if (this.collectTiles.length >= 7) {
  143. this.lock=true
  144. if (!WindowManager.ins.isShow("OutOfBoxLayer"))
  145. WindowManager.ins.open("OutOfBoxLayer");
  146. } else if (this.collectTiles.length == 0 && this.allTiles.length == 0) {
  147. this.lock=true
  148. WindowManager.ins.open("DrawStarLayer").then((com: DrawStarLayer) => {
  149. com.setStar(Main.I._GameUI.star);
  150. })
  151. }
  152. }
  153. /**
  154. * 点击的Collider
  155. * @param event 事件
  156. */
  157. public getClikCollider(event:EventTouch):Collider{
  158. let clikCollider:Collider = null;
  159. const p = event.getLocation();
  160. let camera = this.GetNode("Main Camera").getComponent(Camera);
  161. const r = new Ray();
  162. camera?.screenPointToRay(p.x, p.y, r);
  163. const hasHit = PhysicsSystem.instance.raycast(r, 0xffffffff, 100, true);
  164. if(hasHit){
  165. //获取所有结果并过滤掉_tag为100的碰撞体
  166. const filteredResults = PhysicsSystem.instance.raycastResults.filter(result => {
  167. return !(result.collider['_tag'] === 100);
  168. });
  169. //按距离排序
  170. filteredResults.sort((a, b) => a.distance - b.distance);
  171. if(filteredResults && filteredResults.length > 0){
  172. clikCollider = filteredResults[0].collider;
  173. }
  174. }
  175. return clikCollider;
  176. }
  177. private touchTileItem: TileItem = null;
  178. onTouchStart(event: EventTouch) {
  179. if (Main.I._GameUI.pasue) return;
  180. if (Data.user.useMagnet) return;
  181. if (this.allTiles.length == 0) return;
  182. if (this.collectTiles.length >= 7) return;
  183. let collider: Collider = this.getClikCollider(event);
  184. if (collider) {
  185. //console.log(collider.node.name, collider.getComponent(RigidBody).group);
  186. let item = collider.node.parent.getComponent(TileItem);
  187. if (item){
  188. item.enableCollider = false;
  189. let pos = item.node.getWorldPosition();
  190. pos.y += 0.3;
  191. item.node.setWorldPosition(pos);
  192. this.touchTileItem = item;
  193. }
  194. }
  195. }
  196. onTouchEnd(event: EventTouch) {
  197. if (Main.I._GameUI.pasue) return;
  198. if (Data.user.useMagnet) return;
  199. if (!this.touchTileItem) return;
  200. if (this.allTiles.length == 0) return;
  201. if (this.collectTiles.length >= 7) {
  202. return;
  203. }
  204. let collider: Collider = this.getClikCollider(event);
  205. if (collider) {
  206. let item = collider.node.parent.getComponent(TileItem);
  207. if (item && item == this.touchTileItem) {
  208. platformSystem.platform.vibrateShort();
  209. this.chooseTile(item.node);
  210. this.touchTileItem = null;
  211. audioMgr.playOneShot(GameConst.audios.tap);
  212. }else {
  213. this.touchTileItem.enableCollider = true;
  214. }
  215. }
  216. }
  217. onTouchMove(event: EventTouch) {
  218. if (Main.I._GameUI.pasue) return;
  219. if (Data.user.useMagnet) return;
  220. if (this.allTiles.length == 0) return;
  221. if (this.collectTiles.length >= 7) {
  222. return;
  223. }
  224. let collider: Collider = this.getClikCollider(event);
  225. if (collider) {
  226. let item = collider.node.parent.getComponent(TileItem);
  227. if (item) {
  228. if (this.touchTileItem && item == this.touchTileItem) {
  229. }else {
  230. if (this.touchTileItem) this.touchTileItem.enableCollider = true;
  231. item.enableCollider = false;
  232. let pos = item.node.getWorldPosition();
  233. pos.y += 0.3;
  234. item.node.setWorldPosition(pos);
  235. this.touchTileItem = item;
  236. }
  237. }else {
  238. if (this.touchTileItem) {
  239. // console.log("无目标")
  240. this.touchTileItem.enableCollider = true;
  241. this.touchTileItem = null;
  242. }
  243. }
  244. }
  245. }
  246. initWall() {
  247. if (!PhysicsSystem.instance) return;
  248. ["top","bottom","left","right"].forEach(s => {
  249. this.GetNode("box").getChildByName(s).getComponent(BoxCollider)["_tag"] = 100;
  250. });
  251. const OFFSET = 25;
  252. let camera = this.GetNode("Main Camera").getComponent(Camera);
  253. const r = new Ray();
  254. let size = screen.windowSize;
  255. //左边墙
  256. camera?.screenPointToRay(OFFSET, size.height / 2, r);
  257. if (PhysicsSystem.instance.raycastClosest(r)) {
  258. const result = PhysicsSystem.instance.raycastClosestResult;
  259. //this.GetNode("PlaneLeft").setWorldPosition(result.hitPoint);
  260. }
  261. //右边墙
  262. camera?.screenPointToRay(size.width - OFFSET, size.height / 2, r);
  263. if (PhysicsSystem.instance.raycastClosest(r)) {
  264. const result = PhysicsSystem.instance.raycastClosestResult;
  265. //this.GetNode("PlaneRight").setWorldPosition(result.hitPoint);
  266. }
  267. //上边墙
  268. camera?.screenPointToRay(size.width / 2, size.height - OFFSET * 4, r);
  269. if (PhysicsSystem.instance.raycastClosest(r)) {
  270. const result = PhysicsSystem.instance.raycastClosestResult;
  271. //this.GetNode("PlaneTop").setWorldPosition(result.hitPoint);
  272. }
  273. let minx = this.GetNode("PlaneLeft").worldPosition.x;
  274. let maxx = this.GetNode("PlaneRight").worldPosition.x;
  275. let scale = (maxx - minx) / 8.461091041564941;
  276. //this.GetNode("bottom").scale = v3(scale, scale, scale);
  277. //格子位置
  278. camera?.screenPointToRay(size.width / 2, 10, r);
  279. if (PhysicsSystem.instance.raycastClosest(r)) {
  280. const result = PhysicsSystem.instance.raycastClosestResult;
  281. //result.hitPoint
  282. let p: Vec3 = new Vec3(result.hitPoint.x,4,result.hitPoint.z);
  283. //this.GetNode("bottom").setWorldPosition(p);
  284. }
  285. //下边墙
  286. //this.GetNode("PlaneDown").setWorldPosition(this.GetNode("bottom").worldPosition.add3f(0, 0, -2.5 * scale));
  287. }
  288. async createTiles() {
  289. let Margin = 0
  290. let minx = this.GetNode("PlaneLeft").worldPosition.x + Margin;
  291. let maxx = this.GetNode("PlaneRight").worldPosition.x - Margin;
  292. let maxz = this.GetNode("PlaneDown").worldPosition.z + Margin;
  293. let minz = this.GetNode("PlaneTop").worldPosition.z - Margin;
  294. let obj = levelsData.getCurLevelInfo();
  295. //数量
  296. let tileCount = Math.floor(obj.count / 3);
  297. let names:Array<string> = levelsData.getModesNames();
  298. let tilepools = Utils.getRandomElements(names,obj.kind);
  299. tilepools = Utils.extendArray(tilepools,tileCount);
  300. Main.I._GameUI.pasue = true;
  301. BusyLoadingManager.ins.addBusy(BUSY_TYPE.RES);
  302. for (let i = 0; i < tileCount; i++) {
  303. const name:string = tilepools[i];
  304. for (let j = 0; j < 3; j++) {
  305. const globalIndex = (i * 3 + j) % 9;
  306. let lnode = this.GetNode(`l${globalIndex}`);
  307. let mNode: Node = PoolManager.getName(name) ?? await ResUtil.loadModelPrefabName(name);
  308. if(Data.user.lv == 1){
  309. mNode.setWorldPosition(lnode.getWorldPosition());
  310. }else{
  311. let p = v3(Utils.getRandomInt(-2, 2), Utils.getRandom(6, 11), Utils.getRandomInt(-2, 2));
  312. mNode.worldPosition = p;
  313. }
  314. let tile = mNode.getComponent(TileItem);
  315. if(!tile){
  316. tile = mNode.addComponent(TileItem);
  317. }
  318. let rigid = tile.addCollider();
  319. setTimeout(async ()=>{
  320. mNode.active = true;
  321. mNode.parent = this.node;
  322. this.allTiles.push(mNode);
  323. director.once(Director.EVENT_AFTER_PHYSICS, () => {
  324. rigid.applyImpulse(v3(0, 1.5, 0))
  325. });
  326. },j * 0.03 * 1000);
  327. }
  328. if (i == tileCount - 1) {
  329. BusyLoadingManager.ins.removeBusy(BUSY_TYPE.RES);
  330. Main.I._GameUI.pasue = false;
  331. }
  332. }
  333. setTimeout(() => {
  334. if (Data.user.useMagnet) {
  335. Main.I._GameUI.pasue = true;
  336. Data.user.magnet--;
  337. let p = this.GetNode("magnet").worldPosition;
  338. this.GetNode("magnet").worldPosition = v3(-10, p.y, p.z);
  339. tween(this.GetNode("magnet")).to(0.5, { worldPosition: v3(0, p.y, p.z) }).call(async () => {
  340. let arr = [];
  341. for (var i = this.allTiles.length - 1; i >= 0; --i) {
  342. let pick = false;
  343. let item = this.allTiles[i];
  344. let tmp: Node = arr[0];
  345. if(tmp){
  346. if (tmp.name == item.name && arr.length < 3) {
  347. arr.push(item);
  348. pick = true;
  349. }
  350. }else{
  351. arr.push(item);
  352. pick = true;
  353. }
  354. if(pick) {
  355. let tile: TileItem = this.allTiles[i].getComponent(TileItem);
  356. tile.destoryCollider();
  357. this.GetNode("magnet_content").addChild(tile.node);
  358. tween(tile.node).to(0.5, { worldPosition: this.GetNode(arr.length % 2 ? "m_left" : "r_left").worldPosition }).start();
  359. this.allTiles.splice(i, 1);
  360. console.log("删除", tile.name, this.allTiles.length)
  361. }
  362. }
  363. Data.user.useMagnet = false;
  364. }).delay(1.5).to(0.5, { worldPosition: v3(10, p.y, p.z) }).call(() => {
  365. this.GetNode("magnet_content").removeAllChildren();
  366. Main.I._GameUI.pasue = false;
  367. if (Data.user.useTime && Data.user.time > 0)
  368. eventEmitter.dispatch(GameConst.USE_ITEM_TIME)
  369. }).start();
  370. }else {
  371. if (Data.user.useTime && Data.user.time > 0){
  372. eventEmitter.dispatch(GameConst.USE_ITEM_TIME)}
  373. }
  374. }, 2000);
  375. }
  376. /**
  377. * 提示
  378. */
  379. restart() {
  380. this.allTiles.map(a => this.put(a));
  381. this.collectTiles.map(a => this.put(a));
  382. Main.I._GameUI.star = 0;
  383. this.allTiles = [];
  384. this.optionTiles = [];
  385. this.collectTiles = [];
  386. this.createTiles();
  387. this.lock = false;
  388. }
  389. /**移除*/
  390. public remove(){
  391. if (this.optionTiles.length === 0 || this.lock) return;
  392. // 获取最后一个收集的物品
  393. const lastItem = this.optionTiles.pop();
  394. if (!lastItem) return;
  395. Utils.remove(this.collectTiles,lastItem);
  396. const tile = lastItem.getComponent(TileItem);
  397. tile.removed = false;
  398. tile.auto_rotation = false;
  399. //播放撤销动画
  400. Tween.stopAllByTarget(lastItem);
  401. //const globalIndex = Math.floor(Math.random() * 9);
  402. let lnode = this.GetNode(`l${4}`);
  403. let p:Vec3 = lnode.position.clone();
  404. tween(tile.node)
  405. .to(0.35, {
  406. worldPosition: new Vec3(p.x,p.y + 1,p.z),
  407. worldScale: v3(1.25, 1.25, 1.25)
  408. })
  409. .call(() => {
  410. tile.addCollider();
  411. tile.enableCollider = true;
  412. this.allTiles.push(lastItem);
  413. //向上施加力 然后在向下重力效果
  414. tile.rigid.applyImpulse(v3(0, 3, 0))
  415. this.moveToRightPos(); // 重新排列收集框中的物品
  416. })
  417. .start();
  418. }
  419. /**乱序*/
  420. public mess(){
  421. if(this.allTiles.length === 0 || this.lock) return;
  422. this.allTiles.forEach((item, index) => {
  423. const tileItem = item.getComponent(TileItem);
  424. tileItem.auto_rotation = false;
  425. tileItem.enableCollider = true;
  426. Tween.stopAllByTarget(item);
  427. tween(item)
  428. .to(0.3, {
  429. worldPosition: this.GetNode("l4").worldPosition.clone().add(v3(0, 1, 0)),
  430. worldScale: v3(1.25, 1.25, 1.25)
  431. })
  432. .call(() => {
  433. const rigid = tileItem.addCollider();
  434. rigid.applyImpulse(v3(
  435. Utils.getRandom(-3, 3),
  436. Utils.getRandom(5, 8),
  437. Utils.getRandom(-3, 3)
  438. ));
  439. item.setWorldRotationFromEuler(Utils.getRandom(0, 300), Utils.getRandom(0, 300), Utils.getRandom(0, 300))
  440. })
  441. .start();
  442. });
  443. //audioManager.playOneShot(GameConst.audios.shuffle);
  444. platformSystem.platform.vibrateShort();
  445. }
  446. /**凑齐*/
  447. public prompt(){
  448. if(Data.user.hint <= 0){
  449. MsgHints.show(i18n("main.凑齐道具不足"))
  450. return;
  451. }
  452. if(this.collectTiles.length <= 0){
  453. MsgHints.show(i18n("main.格子中没有物品"))
  454. return;
  455. }
  456. let obj = {}
  457. for (var i = 0; i < this.collectTiles.length; ++i) {
  458. let name = this.collectTiles[i].name;
  459. if (!obj[name]) obj[name] = 0;
  460. obj[name]++;
  461. }
  462. let hint_succ = false;
  463. let bFind = false;
  464. let hint_name = ""
  465. //多少个物品消除一次
  466. const dispe: number = 3;
  467. for (const key in obj) {
  468. if (obj[key] == dispe - 1) {
  469. hint_name = key;
  470. bFind = true;
  471. break;
  472. }
  473. }
  474. //找到两个一样
  475. if (bFind) {
  476. console.log("找到两个一样")
  477. for (let i = 0; i < this.allTiles.length; ++i) {
  478. if (this.allTiles[i].name == hint_name) {
  479. this.chooseTile(this.allTiles[i]);
  480. break;
  481. }
  482. }
  483. hint_succ = true;
  484. }else {
  485. if (this.collectTiles.length >= 5) {
  486. MsgHints.show(i18n("main.未找到合适物品"));
  487. return
  488. }else {
  489. hint_name = this.collectTiles[0].name;
  490. let list = this.allTiles.filter(a => a.name == hint_name).slice(0, dispe - 1);
  491. list.map((a, i) => {
  492. setTimeout(() => {
  493. this.chooseTile(a);
  494. }, 350 * i);
  495. })
  496. hint_succ = true;
  497. }
  498. }
  499. if (hint_succ) {
  500. Data.user.hint--;
  501. }
  502. }
  503. //放回场景中
  504. async pushBask(name: string) {
  505. let Margin = 0;
  506. let maxz = this.GetNode("PlaneDown").worldPosition.z + Margin;
  507. let minz = this.GetNode("PlaneTop").worldPosition.z - Margin;
  508. let minx = this.GetNode("PlaneLeft").worldPosition.x + Margin;
  509. let maxx = this.GetNode("PlaneRight").worldPosition.x - Margin;
  510. let nomal_node = await ResUtil.loadModelPrefabName(name);
  511. nomal_node.name = name;
  512. let tile = nomal_node.addComponent(TileItem);
  513. let rigid = tile.addCollider();
  514. nomal_node.parent = this.node;
  515. rigid.applyImpulse(v3(0, 3, 0))
  516. let p = v3(minx + (maxx - minx) / 2, Utils.getRandom(12, 15), minz + (maxz - minz) / 2);
  517. nomal_node.setWorldRotationFromEuler(Utils.getRandom(0, 300), Utils.getRandom(0, 300), Utils.getRandom(0, 300))
  518. nomal_node.setWorldPosition(p);
  519. this.allTiles.push(nomal_node);
  520. }
  521. /**
  522. * 放入对象次
  523. * @param n 模型节点
  524. */
  525. public put(n){
  526. if(!n)return;
  527. let tile:TileItem = n.getComponent(TileItem);
  528. if(tile){
  529. n.scale = tile.oScale;
  530. tile.auto_rotation = false;
  531. tile.rigid.enabled = false;
  532. PoolManager.putNode(n);
  533. }
  534. }
  535. /**
  536. * 抛物线运动工具(带缩放动画)
  537. * @param node 要移动的节点
  538. * @param startPos 起始坐标(世界坐标)
  539. * @param endPos 目标坐标(世界坐标)
  540. * @param height 抛物线最高点高度(相对于起点)
  541. * @param duration 动画时长(秒)
  542. * @param startScale 起始缩放(默认原始大小)
  543. * @param endScale 目标缩放(默认原始大小)
  544. * @param onComplete 完成回调
  545. */
  546. public parabolicMovementWithScale(
  547. n:Node,
  548. endPos: Vec3,
  549. height: number = 2,
  550. duration: number = 1.5,
  551. startScale: Vec3 = Vec3.ONE,
  552. endScale: Vec3 = Vec3.ONE,
  553. onComplete?: (n:Node) => void
  554. ){
  555. console.log("抛物开始....");
  556. let startPos: Vec3 = n.position.clone();
  557. if(startPos.z.toFixed(2) == endPos.z.toFixed(2)) {
  558. console.log("z周线位置抛物....回调了");
  559. tween(n).to(0.25, { worldPosition: endPos.clone(), worldScale: v3(1, 1, 1) }).call(() => {
  560. onComplete?.(n);
  561. }).start();
  562. }else{
  563. //初始化节点状态
  564. n.setWorldPosition(startPos.clone());
  565. n.setScale(startScale);
  566. //计算抛物线顶点
  567. const midPoint = new Vec3(
  568. (startPos.x + endPos.x) / 2,
  569. Math.max(startPos.y, endPos.y) + height,
  570. (startPos.z + endPos.z) / 2
  571. );
  572. //创建组合动画
  573. return tween(n)
  574. .to(duration,
  575. {
  576. worldPosition: endPos,
  577. worldScale: endScale
  578. },{
  579. onUpdate: (target: Node, ratio: number) => {
  580. //1. 计算抛物线位置
  581. const t = ratio;
  582. const invT = 1 - t;
  583. const tempPos = new Vec3();
  584. // XZ轴线性插值
  585. Vec3.lerp(tempPos, startPos, endPos, t);
  586. //Y轴抛物线计算
  587. const y = invT * invT * startPos.y
  588. + 2 * invT * t * midPoint.y
  589. + t * t * endPos.y;
  590. //2. 计算当前缩放比例
  591. const currentScale = new Vec3();
  592. Vec3.lerp(currentScale, startScale, endScale, t);
  593. //更新节点状态
  594. target.worldPosition = new Vec3(tempPos.x, y, tempPos.z);
  595. target.setScale(currentScale);
  596. }
  597. }
  598. )
  599. .call(() => {//最终确保缩放精确
  600. n.setScale(endScale);
  601. console.log("不相同的位置抛物....回调了");
  602. onComplete?.(n);
  603. })
  604. .start();
  605. }
  606. }
  607. }