GameNode.ts 28 KB

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