|
|
@@ -0,0 +1,921 @@
|
|
|
+import { _decorator, Component, Node, find, assetManager, JsonAsset, Prefab, UITransform, Widget, Vec3, instantiate, Sprite, randomRangeInt, EventTouch, Vec2, tween, Label, Color } from 'cc'
|
|
|
+import { oops } from 'db://oops-framework/core/Oops';
|
|
|
+import { ecs } from 'db://oops-framework/libs/ecs/ECS';
|
|
|
+import { CCComp } from 'db://oops-framework/module/common/CCComp';
|
|
|
+import { GameComponent } from 'db://oops-framework/module/common/GameComponent';
|
|
|
+const { ccclass, property } = _decorator;
|
|
|
+
|
|
|
+export enum GameStatus {
|
|
|
+ init, //初始化
|
|
|
+ running, //运行中
|
|
|
+ end, //结束
|
|
|
+ pass //通关
|
|
|
+}
|
|
|
+
|
|
|
+enum GridStatus {
|
|
|
+ notUse,
|
|
|
+ used,
|
|
|
+}
|
|
|
+
|
|
|
+interface GridDataInterface {
|
|
|
+ name: string,
|
|
|
+ status: GridStatus,
|
|
|
+ gridNode: Node | null,
|
|
|
+ row: number,
|
|
|
+ column: number,
|
|
|
+ gridColorKey: string | null,
|
|
|
+ gridType: number, //代替gridColorKey
|
|
|
+}
|
|
|
+
|
|
|
+interface GridConfigDataInterface {
|
|
|
+ row: number,
|
|
|
+ column: number,
|
|
|
+}
|
|
|
+
|
|
|
+interface BrickDataInterface {
|
|
|
+ index: number,
|
|
|
+ brickKey: string,
|
|
|
+ rotateFlag: boolean,
|
|
|
+ gridConfig: GridConfigDataInterface[],
|
|
|
+ deg: number,
|
|
|
+ brickNode: Node | null,
|
|
|
+ gridColorKey: string,
|
|
|
+ brickInitPos: Vec3 | null,
|
|
|
+ rotateNode: Node | null,
|
|
|
+ gridType: number, //gridColorKey
|
|
|
+}
|
|
|
+
|
|
|
+interface EditingDataInterface {
|
|
|
+ brickData: BrickDataInterface,
|
|
|
+ gridList: GridDataInterface[],
|
|
|
+}
|
|
|
+
|
|
|
+@ccclass('GameView')
|
|
|
+export class GameView extends GameComponent {
|
|
|
+
|
|
|
+ row = 8
|
|
|
+ column = 8
|
|
|
+ gridSize = 76.25
|
|
|
+ gridMargin = 1
|
|
|
+ brickNum = 3
|
|
|
+ yOffset = 100
|
|
|
+ aniBrickMove = 0.15 //动画时长
|
|
|
+ aniBrickRotate = 0.1 //
|
|
|
+ aniBrickEliminate = 0.2
|
|
|
+
|
|
|
+ notUseColor = new Color(255, 255, 255, 255)
|
|
|
+ usableColor = new Color(0, 255, 0, 100)
|
|
|
+ unavailableColor = new Color(255, 0, 0, 100)
|
|
|
+ rotateFaultTolerant = 10
|
|
|
+
|
|
|
+ bundleName = 'movebrick'
|
|
|
+ gameConfig = {}
|
|
|
+ gameStatus = GameStatus.init
|
|
|
+ _score = 0
|
|
|
+ editingData: EditingDataInterface = {
|
|
|
+ brickData: null!,
|
|
|
+ gridList: [],
|
|
|
+ } //修改的数据
|
|
|
+ gridList: GridDataInterface[][] = []
|
|
|
+ bricksList: BrickDataInterface[] = []
|
|
|
+ gridColorList: GridDataInterface[] = []
|
|
|
+ deltaTime: number = 1
|
|
|
+ operateFlag = false
|
|
|
+ touchStartPos = new Vec2()
|
|
|
+ adShowingFlag = false
|
|
|
+
|
|
|
+ canvasNode: Node = null!
|
|
|
+ gridsNode: Node = null! //网格层
|
|
|
+ bricksNode: Node = null! //底部方块
|
|
|
+ movingNode: Node = null! //移动层
|
|
|
+ rotateContainer: Node = null! //旋转层
|
|
|
+ scoreLabel: Label = null!
|
|
|
+ gameOverPopupNode: Node = null!
|
|
|
+
|
|
|
+ gridPrefab: Prefab = null! //空格子预制体
|
|
|
+ rotatePrefab: Prefab = null! //旋转预制体
|
|
|
+ prefabUrlMap = {} //格子预制体 //所有的格子预制体
|
|
|
+
|
|
|
+ //游戏新增的
|
|
|
+ @property([Prefab]) //所有的预制体,代替prefabUrlMap
|
|
|
+ allBrickList: any[] = [];
|
|
|
+ Lab_target: Label = null! //目标分数
|
|
|
+ brickListConfig: { bricks?: any } = {} //方块配置
|
|
|
+ @property(Prefab)
|
|
|
+ gridPb: Prefab = null! //空格子预制体
|
|
|
+ @property({ type: Prefab, displayName: "旋转标志" })
|
|
|
+ rotatePb: Prefab = null! //空格子预制体
|
|
|
+
|
|
|
+
|
|
|
+ get score() {
|
|
|
+ return this._score
|
|
|
+ }
|
|
|
+
|
|
|
+ set score(v: number) {
|
|
|
+ this._score = v
|
|
|
+ this.scoreLabel.string = `${this.score}`
|
|
|
+ }
|
|
|
+
|
|
|
+ setAdShowingFlag(v: boolean) {
|
|
|
+ this.adShowingFlag = v
|
|
|
+ // 打开激励视频时暂停背景音乐
|
|
|
+
|
|
|
+ }
|
|
|
+ onLoad() {
|
|
|
+
|
|
|
+ this.gridsNode = this.node.getChildByPath('Scene/Center/gridNode')!
|
|
|
+ this.bricksNode = this.node.getChildByPath('Scene/Bottom/bricksNode')!
|
|
|
+ this.movingNode = this.node.getChildByPath('Scene/moveNode')!
|
|
|
+ this.rotateContainer = this.node.getChildByPath('Scene/rotateNode')!
|
|
|
+ const scoreNode = this.node.getChildByPath('Scene/Top/scoreNode/lab_score')
|
|
|
+ if (!scoreNode) {
|
|
|
+ console.error('Score node not found')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const label = scoreNode.getComponent(Label)
|
|
|
+ if (!label) {
|
|
|
+ console.error('Label component not found on score node')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.scoreLabel = label
|
|
|
+ }
|
|
|
+
|
|
|
+ start() {
|
|
|
+ // 模拟延时
|
|
|
+ this.scheduleOnce(() => {
|
|
|
+ this.loadGameConfig().then(() => {
|
|
|
+ // 生成游戏网格
|
|
|
+ this.generateGrids()
|
|
|
+ this.scheduleOnce(() => {
|
|
|
+ this.initData()
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }, 1)
|
|
|
+ }
|
|
|
+
|
|
|
+ loadGameConfig() {
|
|
|
+ let json_name: string = "gui/eliminate/config/GridConfig";
|
|
|
+ return new Promise<void>((resolve, reject) => {
|
|
|
+ oops.res.load(json_name, JsonAsset, (err: Error | null, res: any) => {
|
|
|
+ if (res) {
|
|
|
+ this.brickListConfig = res.json;
|
|
|
+ resolve();
|
|
|
+ } else {
|
|
|
+ console.log("JSON数据加载失,请检查文件");
|
|
|
+ reject(err);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ setGameStauts(stauts: GameStatus) {
|
|
|
+ if (stauts === this.gameStatus) return
|
|
|
+ this.gameStatus = stauts
|
|
|
+ if (this.gameStatus === GameStatus.init) {
|
|
|
+ // this.gameOverPopupNode.active = false
|
|
|
+ }
|
|
|
+ else if (this.gameStatus === GameStatus.running) {
|
|
|
+ // this.gameOverPopupNode.active = false
|
|
|
+ }
|
|
|
+ else if (this.gameStatus === GameStatus.end) {
|
|
|
+ // this.gameOverPopupNode.active = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ initData() {
|
|
|
+ this.setGameStauts(GameStatus.init)
|
|
|
+ this.score = 0
|
|
|
+ this.gridColorList.length = 0
|
|
|
+ this.operateFlag = false
|
|
|
+ this.adShowingFlag = false
|
|
|
+ this.touchStartPos.set(Vec2.ZERO)
|
|
|
+ this.rotateContainer.destroyAllChildren()
|
|
|
+ // 初始化格子状态
|
|
|
+ for (let rowIndex = 0; rowIndex < this.row; rowIndex++) {
|
|
|
+ for (let columnIndex = 0; columnIndex < this.column; columnIndex++) {
|
|
|
+ this.gridList[rowIndex][columnIndex].status = GridStatus.notUse
|
|
|
+ this.generateGrid(this.gridList[rowIndex][columnIndex])
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 初始化方块
|
|
|
+ this.bricksNode.destroyAllChildren()
|
|
|
+ this.bricksList.length = 0
|
|
|
+ for (let i = 1; i <= this.brickNum; i++) {
|
|
|
+ this.addBrick(i)
|
|
|
+ }
|
|
|
+
|
|
|
+ this.setGameStauts(GameStatus.running)
|
|
|
+ this.operateFlag = true
|
|
|
+ }
|
|
|
+
|
|
|
+ generateGrids() {
|
|
|
+ this.gridsNode.destroyAllChildren()
|
|
|
+ const transform = this.gridsNode.getComponent(UITransform)
|
|
|
+ if (transform) {
|
|
|
+ transform.setContentSize(
|
|
|
+ this.gridSize * this.column + this.gridMargin * 2,
|
|
|
+ this.gridSize * this.row + this.gridMargin * 2,
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let rowIndex = 0; rowIndex < this.row; rowIndex++) {
|
|
|
+ this.gridList.push([])
|
|
|
+ for (let columnIndex = 0; columnIndex < this.column; columnIndex++) {
|
|
|
+ let gridData: GridDataInterface = {
|
|
|
+ name: `Grid-${rowIndex}-${columnIndex}`,
|
|
|
+ status: GridStatus.notUse,
|
|
|
+ gridNode: null,
|
|
|
+ row: rowIndex,
|
|
|
+ column: columnIndex,
|
|
|
+ gridColorKey: "null",
|
|
|
+ gridType: 0
|
|
|
+ }
|
|
|
+ this.gridList[rowIndex].push(gridData)
|
|
|
+ // 节点
|
|
|
+ gridData.gridNode = new Node()
|
|
|
+ this.gridsNode.addChild(gridData.gridNode)
|
|
|
+ gridData.gridNode.name = `Grid-${rowIndex}-${columnIndex}`
|
|
|
+ gridData.gridNode.addComponent(UITransform).setContentSize(this.gridSize, this.gridSize)
|
|
|
+ const gridWidget: Widget = gridData.gridNode.addComponent(Widget)
|
|
|
+ gridWidget.isAlignLeft = true
|
|
|
+ gridWidget.left = this.gridSize * columnIndex + this.gridMargin
|
|
|
+ gridWidget.isAlignBottom = true
|
|
|
+ gridWidget.bottom = this.gridSize * rowIndex + this.gridMargin
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ generateGrid(gridData: GridDataInterface) {
|
|
|
+ if (!gridData.gridNode) return;
|
|
|
+ gridData.gridNode.children.forEach(node => { node.destroy() })
|
|
|
+ let prefab = null
|
|
|
+ if (gridData.status === GridStatus.notUse) {
|
|
|
+ // prefab = this.gridPrefab
|
|
|
+ prefab = this.gridPb;
|
|
|
+ }
|
|
|
+ else if (gridData.status === GridStatus.used) {
|
|
|
+
|
|
|
+ //什么颜色的格子 --可以通过下标获取
|
|
|
+ // prefab = this.prefabUrlMap[this.gameConfig['gridColor'][gridData.gridColorKey]['prefabUrl']]
|
|
|
+ prefab = this.allBrickList[gridData.gridType];
|
|
|
+
|
|
|
+ }
|
|
|
+ const node = instantiate(prefab);
|
|
|
+ if (!node) return;
|
|
|
+ if (gridData.status === GridStatus.notUse) {
|
|
|
+ node.getComponent(Sprite).color = this.notUseColor
|
|
|
+ }
|
|
|
+ gridData.gridNode.addChild(node)
|
|
|
+ node.getComponent(UITransform).setContentSize(
|
|
|
+ this.gridSize - this.gridMargin * 2,
|
|
|
+ this.gridSize - this.gridMargin * 2,
|
|
|
+ )
|
|
|
+ node.setPosition(Vec3.ZERO)
|
|
|
+ }
|
|
|
+
|
|
|
+ generateBrick(brickKey: string, gridColorKey: string, gridType: number) {
|
|
|
+ const brickConfig = this.brickListConfig['bricks'][brickKey]
|
|
|
+ let rowMin = 0
|
|
|
+ let rowMax = 0
|
|
|
+ let columnMin = 0
|
|
|
+ let columnMax = 0
|
|
|
+ brickConfig['gridConfig'].forEach((gridConfigData: GridConfigDataInterface) => {
|
|
|
+ if (gridConfigData.row < rowMin) {
|
|
|
+ rowMin = gridConfigData.row
|
|
|
+ }
|
|
|
+ else if (gridConfigData.row > rowMax) {
|
|
|
+ rowMax = gridConfigData.row
|
|
|
+ }
|
|
|
+ if (gridConfigData.column < columnMin) {
|
|
|
+ columnMin = gridConfigData.column
|
|
|
+ }
|
|
|
+ else if (gridConfigData.column > columnMax) {
|
|
|
+ columnMax = gridConfigData.column
|
|
|
+ }
|
|
|
+ })
|
|
|
+ const rowNum = (rowMax - rowMin + 1)
|
|
|
+ const columnNum = (columnMax - columnMin + 1)
|
|
|
+ // 生成独立的方块节点
|
|
|
+ const brickNode = new Node()
|
|
|
+ brickNode.name = brickKey
|
|
|
+ const transformCom: UITransform = brickNode.addComponent(UITransform)
|
|
|
+ transformCom.setContentSize(
|
|
|
+ this.gridSize * columnNum,
|
|
|
+ this.gridSize * rowNum
|
|
|
+ )
|
|
|
+ transformCom.setAnchorPoint(0.5, 0.5)
|
|
|
+ // const gridPrefab = this.prefabUrlMap[this.gameConfig['gridColor'][gridColorKey]['prefabUrl']]
|
|
|
+ const gridPrefab = this.allBrickList[gridType];
|
|
|
+ //通过配置初始化
|
|
|
+ brickConfig['gridConfig'].forEach((gridConfigData: GridConfigDataInterface) => {
|
|
|
+ const gridNode = new Node()
|
|
|
+ gridNode.name = 'grid'
|
|
|
+ brickNode.addChild(gridNode)
|
|
|
+ gridNode.addComponent(UITransform).setContentSize(this.gridSize, this.gridSize)
|
|
|
+ const gridWidget: Widget = gridNode.addComponent(Widget)
|
|
|
+ gridWidget.isAlignLeft = true
|
|
|
+ gridWidget.left = this.gridSize * gridConfigData.column - this.gridSize * columnMin
|
|
|
+ gridWidget.isAlignBottom = true
|
|
|
+ gridWidget.bottom = this.gridSize * gridConfigData.row - this.gridSize * rowMin
|
|
|
+ const node = instantiate(gridPrefab)
|
|
|
+ gridNode.addChild(node)
|
|
|
+ node.getComponent(UITransform).setContentSize(
|
|
|
+ this.gridSize - this.gridMargin * 2,
|
|
|
+ this.gridSize - this.gridMargin * 2,
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ node.setPosition(Vec3.ZERO)
|
|
|
+ node.on(Node.EventType.TOUCH_START, (event: EventTouch) => {
|
|
|
+ event.preventSwallow = true;
|
|
|
+ }, this);
|
|
|
+ node.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => {
|
|
|
+ event.preventSwallow = true;
|
|
|
+ }, this);
|
|
|
+ node.on(Node.EventType.TOUCH_END, (event: EventTouch) => {
|
|
|
+ event.preventSwallow = true;
|
|
|
+ }, this);
|
|
|
+ })
|
|
|
+
|
|
|
+ return brickNode
|
|
|
+ }
|
|
|
+ addBrick(index: number) {
|
|
|
+ const bricks = this.brickListConfig.bricks || {};
|
|
|
+ // const gridColors = this.gameConfig.gridColor || {};
|
|
|
+ //获取方块的类型
|
|
|
+ const brickKey = Object.keys(bricks)[randomRangeInt(0, Object.keys(bricks).length)];
|
|
|
+ //获取方块的颜色随机下标
|
|
|
+ // const gridColorKey = Object.keys(gridColors)[randomRangeInt(0, Object.keys(gridColors).length)];
|
|
|
+
|
|
|
+ const gridColorKey = "GridColor1";
|
|
|
+ //通过Key获取具体配置
|
|
|
+ const brickConfig = bricks[brickKey];
|
|
|
+ const gridType = randomRangeInt(0, this.allBrickList.length);
|
|
|
+ const brickData: BrickDataInterface = {
|
|
|
+ index,
|
|
|
+ brickKey,
|
|
|
+ rotateFlag: brickConfig['rotateFlag'],
|
|
|
+ gridConfig: brickConfig['gridConfig'],
|
|
|
+ deg: 0,
|
|
|
+ brickNode: null,
|
|
|
+ gridColorKey,
|
|
|
+ brickInitPos: null,
|
|
|
+ rotateNode: null,
|
|
|
+ gridType: 0
|
|
|
+ }
|
|
|
+ this.bricksList.push(brickData)
|
|
|
+ // 生成方块
|
|
|
+ const brickNode = this.generateBrick(brickKey, gridColorKey, gridType)
|
|
|
+ this.bricksNode.addChild(brickNode)
|
|
|
+ brickData.brickNode = brickNode
|
|
|
+ // 方块间隔
|
|
|
+ let offset = 220
|
|
|
+ if (this.brickNum % 2 === 1) {
|
|
|
+ const middleNum = Math.floor(this.brickNum / 2) + 1
|
|
|
+ if (index < middleNum) {
|
|
|
+ offset = - offset
|
|
|
+ }
|
|
|
+ else if (index === middleNum) {
|
|
|
+ offset = 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (brickData && brickData.brickNode) {
|
|
|
+ brickData.brickNode.setPosition(offset, 0)
|
|
|
+ brickData.brickNode.scale_x = 0.6;
|
|
|
+ brickData.brickNode.scale_y = 0.6;
|
|
|
+ brickData.brickInitPos = brickData.brickNode.getWorldPosition()
|
|
|
+ }
|
|
|
+ // 添加旋转图标
|
|
|
+ if (brickData.rotateFlag) {
|
|
|
+ // brickData.rotateNode = instantiate(this.rotatePrefab)
|
|
|
+ brickData.rotateNode = instantiate(this.rotatePb);
|
|
|
+ this.rotateContainer.addChild(brickData.rotateNode)
|
|
|
+ brickData.rotateNode.setWorldPosition(brickNode.getWorldPosition())
|
|
|
+ }
|
|
|
+ // 方块添加事件
|
|
|
+ this.brickAddEvent(brickData)
|
|
|
+ }
|
|
|
+
|
|
|
+ brickAddEvent(brickData: BrickDataInterface) {
|
|
|
+ const brickNode = brickData.brickNode
|
|
|
+ if (!brickNode) return
|
|
|
+ // 触摸时
|
|
|
+ brickNode.on(Node.EventType.TOUCH_START, (event: EventTouch) => {
|
|
|
+ if (this.adShowingFlag || !this.operateFlag) return
|
|
|
+
|
|
|
+ this.editingData.brickData = brickData
|
|
|
+ this.editingData.gridList.length = 0
|
|
|
+ // 记录开始触摸的位置
|
|
|
+ this.touchStartPos.set(event.getUILocation())
|
|
|
+ // 添加到移动节点里进行移动
|
|
|
+ const pos = brickNode.getWorldPosition()
|
|
|
+ brickNode.setParent(this.movingNode)
|
|
|
+ brickNode.setWorldPosition(pos)
|
|
|
+
|
|
|
+ const index = this.bricksList.findIndex(data => data === brickData)
|
|
|
+ if (index > -1) {
|
|
|
+ this.editingData.brickData = this.bricksList.splice(index, 1)[0]
|
|
|
+ } else {
|
|
|
+ console.error("bricksList not find brickData:", brickData)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ // 触摸移动时
|
|
|
+ brickNode.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => {
|
|
|
+ if (this.adShowingFlag || !this.operateFlag) return
|
|
|
+
|
|
|
+ const movePos: Vec2 = event.getUILocation().subtract(this.touchStartPos)
|
|
|
+ // 挪动很小时,不移动方块组合
|
|
|
+ if (movePos.length() <= this.rotateFaultTolerant) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 隐藏旋转节点
|
|
|
+ if (this.editingData.brickData?.rotateFlag && this.editingData.brickData.rotateNode) {
|
|
|
+ this.editingData.brickData.rotateNode.active = false
|
|
|
+ }
|
|
|
+ // 格子颜色恢复
|
|
|
+ this.gridColorRecovery()
|
|
|
+ // 移动
|
|
|
+ brickNode.setWorldPosition(event.getUILocation().toVec3().add3f(0, this.yOffset, 0))
|
|
|
+ // 每次移动重置数据
|
|
|
+ this.editingData.gridList.length = 0
|
|
|
+ // 实时获取方块位置判断在哪个格子上
|
|
|
+ const tempGridList: GridDataInterface[] = []
|
|
|
+ brickNode.children.forEach((brickGridNode) => {
|
|
|
+ const brickGridPos = brickGridNode.getWorldPosition()
|
|
|
+ let gridData: GridDataInterface | null = null
|
|
|
+ for (let rowIndex = 0; rowIndex < this.row && gridData === null; rowIndex++) {
|
|
|
+ for (let columnIndex = 0; columnIndex < this.column && gridData === null; columnIndex++) {
|
|
|
+ const nowGridData = this.gridList[rowIndex][columnIndex]
|
|
|
+ if (!nowGridData?.gridNode) continue
|
|
|
+ const gridPos = nowGridData.gridNode.getWorldPosition()
|
|
|
+ if (Vec3.distance(gridPos, brickGridPos) <= (this.gridSize / 2 - this.gridMargin)) {
|
|
|
+ gridData = nowGridData
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (gridData === null) return
|
|
|
+ tempGridList.push(gridData)
|
|
|
+ })
|
|
|
+ // 检查整体情况
|
|
|
+ let checkFlag = false
|
|
|
+ if (
|
|
|
+ tempGridList.length === brickData.gridConfig.length &&
|
|
|
+ tempGridList.filter(d => d.status === GridStatus.notUse).length === brickData.gridConfig.length
|
|
|
+ ) {
|
|
|
+ checkFlag = true
|
|
|
+ tempGridList.forEach((gridData) => {
|
|
|
+ this.editingData.gridList.push(gridData)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ // 格子给用户提示
|
|
|
+ tempGridList.forEach((gridData) => {
|
|
|
+ if (gridData.status !== GridStatus.notUse || !gridData.gridNode) return
|
|
|
+ const sprite = gridData.gridNode.children[0]?.getComponent(Sprite)
|
|
|
+ if (sprite) {
|
|
|
+ sprite.color = checkFlag ? this.usableColor : this.unavailableColor
|
|
|
+ // 用于恢复格子
|
|
|
+ this.gridColorList.push(gridData)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ // 触摸松开后
|
|
|
+ brickNode.on(Node.EventType.TOUCH_END, this.brickTouchEnd, this)
|
|
|
+ brickNode.on(Node.EventType.TOUCH_CANCEL, this.brickTouchEnd, this)
|
|
|
+ }
|
|
|
+
|
|
|
+ brickTouchEnd(event: EventTouch) {
|
|
|
+ if (this.adShowingFlag || !this.operateFlag) return
|
|
|
+ this.operateFlag = false
|
|
|
+ // 单击旋转
|
|
|
+ if (
|
|
|
+ this.editingData.brickData.rotateFlag &&
|
|
|
+ event.getUILocation().subtract(this.touchStartPos).length() <= this.rotateFaultTolerant
|
|
|
+ ) {
|
|
|
+ const brickData = this.editingData.brickData
|
|
|
+ // 方块放回待选区
|
|
|
+ if (brickData.brickNode && brickData.brickInitPos) {
|
|
|
+ this.bricksList.push(brickData)
|
|
|
+ this.bricksNode.addChild(brickData.brickNode)
|
|
|
+ brickData.brickNode.setWorldPosition(brickData.brickInitPos)
|
|
|
+ // 旋转
|
|
|
+ this.brickGridRotate(brickData).then(() => {
|
|
|
+ this.operateFlag = true
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 方块到格子
|
|
|
+ else if (this.editingData.brickData && this.editingData.gridList.length > 0) {
|
|
|
+ // 修改格子
|
|
|
+ this.editingData.gridList.forEach((gridData) => {
|
|
|
+ gridData.status = GridStatus.used
|
|
|
+ gridData.gridColorKey = this.editingData.brickData.gridColorKey
|
|
|
+ this.generateGrid(gridData)
|
|
|
+ })
|
|
|
+ // 销毁方块
|
|
|
+ if (this.editingData.brickData.brickNode) {
|
|
|
+ this.editingData.brickData.brickNode.destroy()
|
|
|
+ }
|
|
|
+ // 销毁旋转节点
|
|
|
+ if (this.editingData.brickData.rotateFlag && this.editingData.brickData.rotateNode) {
|
|
|
+ this.editingData.brickData.rotateNode.destroy()
|
|
|
+ }
|
|
|
+ // 新增方块
|
|
|
+ this.addBrick(this.editingData.brickData.index)
|
|
|
+ this.scheduleOnce(() => {
|
|
|
+ // 格子消除
|
|
|
+ this.gridEliminate().then(() => {
|
|
|
+ // 检查方块是否还能消除格子
|
|
|
+ this.prompt(false).then((promptFlag: boolean) => {
|
|
|
+ if (!promptFlag) {
|
|
|
+ this.gameOver()
|
|
|
+ }
|
|
|
+ this.operateFlag = true
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ // 方块回到待选区
|
|
|
+ else {
|
|
|
+ const brickData = this.editingData.brickData
|
|
|
+ this.bricksList.push(brickData)
|
|
|
+ this.operateFlag = false
|
|
|
+ // 回弹动画
|
|
|
+ if (brickData.brickNode) {
|
|
|
+ const pos = this.editingData.brickData.brickInitPos
|
|
|
+ if (!pos) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ tween(brickData.brickNode)
|
|
|
+ .to(this.aniBrickMove, { worldPosition: pos })
|
|
|
+ .call(() => {
|
|
|
+ if (brickData.brickNode && brickData.brickInitPos && brickData.rotateNode) {
|
|
|
+ this.bricksNode.addChild(brickData.brickNode)
|
|
|
+ brickData.brickNode.setWorldPosition(brickData.brickInitPos)
|
|
|
+ if (brickData.rotateFlag) {
|
|
|
+ brickData.rotateNode.active = true
|
|
|
+ }
|
|
|
+ this.operateFlag = true
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .start()
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ // 格子颜色恢复
|
|
|
+ this.gridColorRecovery()
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //格子颜色恢复
|
|
|
+ gridColorRecovery() {
|
|
|
+ while (this.gridColorList.length > 0) {
|
|
|
+ const gridData = this.gridColorList.pop()
|
|
|
+ if (gridData?.gridNode && gridData.status === GridStatus.notUse) {
|
|
|
+ const sprite = gridData.gridNode.children[0]?.getComponent(Sprite)
|
|
|
+ if (sprite) {
|
|
|
+ sprite.color = this.notUseColor
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ nextGridRotate(gridConfig: GridConfigDataInterface[], deg: number) {
|
|
|
+ const newGridConfig: GridConfigDataInterface[] = []
|
|
|
+ // 顺时针旋转
|
|
|
+ let newDeg = deg - 90
|
|
|
+ gridConfig.forEach((gridConfigData) => {
|
|
|
+ // 例如(1,2) => (-2,1),可以画图分析
|
|
|
+ newGridConfig.push({
|
|
|
+ row: -gridConfigData.column,
|
|
|
+ column: gridConfigData.row
|
|
|
+ })
|
|
|
+ })
|
|
|
+ return { gridConfig: newGridConfig, deg: newDeg }
|
|
|
+ }
|
|
|
+
|
|
|
+ 'brickGridRotate'(brickData: BrickDataInterface) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ // this.audioManager.playRotate()
|
|
|
+ const next = this.nextGridRotate(brickData.gridConfig, brickData.deg)
|
|
|
+ brickData.deg = next.deg
|
|
|
+ brickData.gridConfig = next.gridConfig
|
|
|
+
|
|
|
+ if (brickData.brickNode) {
|
|
|
+ tween(brickData.brickNode)
|
|
|
+ .to(this.aniBrickRotate, { angle: next.deg })
|
|
|
+ .call(() => {
|
|
|
+ resolve(true)
|
|
|
+ }).start()
|
|
|
+ }
|
|
|
+
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ gridEliminateCheck(gridList: GridDataInterface[][]) {
|
|
|
+ const gridEliminateList: GridDataInterface[] = []
|
|
|
+ let eliminateRowNum = 0
|
|
|
+ let eliminateColumnNum = 0
|
|
|
+ // 行检查
|
|
|
+ for (let rowIndex = 0; rowIndex < this.row; rowIndex++) {
|
|
|
+ const rowData = gridList[rowIndex]
|
|
|
+ if (rowData.every(gridData => gridData.status === GridStatus.used)) {
|
|
|
+ rowData.forEach(gridData => {
|
|
|
+ if (gridEliminateList.findIndex(data => data === gridData) < 0) {
|
|
|
+ gridEliminateList.push(gridData)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ eliminateRowNum += 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 列检查
|
|
|
+ for (let columnIndex = 0; columnIndex < this.column; columnIndex++) {
|
|
|
+ if (gridList.every(rowData => rowData[columnIndex].status === GridStatus.used)) {
|
|
|
+ gridList.forEach(rowData => {
|
|
|
+ const gridData = rowData[columnIndex]
|
|
|
+ if (gridEliminateList.findIndex(data => data === gridData) < 0) {
|
|
|
+ gridEliminateList.push(gridData)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ eliminateColumnNum += 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ gridEliminateList,
|
|
|
+ eliminateRowNum,
|
|
|
+ eliminateColumnNum,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 消除
|
|
|
+ gridEliminate() {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const d = this.gridEliminateCheck(this.gridList)
|
|
|
+ const gridEliminateList = d.gridEliminateList
|
|
|
+ const eliminateRowNum = d.eliminateRowNum
|
|
|
+ const eliminateColumnNum = d.eliminateColumnNum
|
|
|
+ // 无行可消除
|
|
|
+ if (gridEliminateList.length < 1) {
|
|
|
+ resolve(false)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 消除格子
|
|
|
+ gridEliminateList.forEach((gridData) => {
|
|
|
+ if (gridData.gridNode) {
|
|
|
+ tween(gridData.gridNode.children[0])
|
|
|
+ .to(this.aniBrickEliminate, { scale: new Vec3(0.5, 0.5) })
|
|
|
+ .call(() => {
|
|
|
+ gridData.status = GridStatus.notUse
|
|
|
+ this.generateGrid(gridData)
|
|
|
+ })
|
|
|
+ .start()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ // 音效
|
|
|
+ // this.audioManager.playEliminate()
|
|
|
+ this.scheduleOnce(() => {
|
|
|
+ // 成绩
|
|
|
+ let score = 0
|
|
|
+ for (let i = 1; i <= eliminateRowNum; i++) {
|
|
|
+ score += this.column * i
|
|
|
+ }
|
|
|
+ for (let i = 1; i <= eliminateColumnNum; i++) {
|
|
|
+ score += this.row * i
|
|
|
+ }
|
|
|
+ this.score += score
|
|
|
+ resolve(true)
|
|
|
+ }, 0.2)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ gameOver() {
|
|
|
+ // this.audioManager.playGameOver()
|
|
|
+ this.setGameStauts(GameStatus.end)
|
|
|
+ }
|
|
|
+
|
|
|
+ moveIf(row: number, column: number, gridConfig: GridConfigDataInterface[]) {
|
|
|
+ let moveFlag = true
|
|
|
+ for (let i = 0; i < gridConfig.length; i++) {
|
|
|
+ const gridConfigData = gridConfig[i]
|
|
|
+ const gridI = row + gridConfigData.row
|
|
|
+ const gridJ = column + gridConfigData.column
|
|
|
+ // 边界判断
|
|
|
+ if (
|
|
|
+ gridI < 0 ||
|
|
|
+ gridI > this.row - 1 ||
|
|
|
+ gridJ < 0 ||
|
|
|
+ gridJ > this.column - 1
|
|
|
+ ) {
|
|
|
+ moveFlag = false
|
|
|
+ break
|
|
|
+ }
|
|
|
+ // 已用
|
|
|
+ else if (this.gridList[gridI][gridJ].status === GridStatus.used) {
|
|
|
+ moveFlag = false
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return moveFlag
|
|
|
+ }
|
|
|
+
|
|
|
+ copyGridList() {
|
|
|
+ const gridList: GridDataInterface[][] = []
|
|
|
+ for (let rowIndex = 0; rowIndex < this.row; rowIndex++) {
|
|
|
+ gridList.push([])
|
|
|
+ for (let columnIndex = 0; columnIndex < this.column; columnIndex++) {
|
|
|
+ const gridData = this.gridList[rowIndex][columnIndex]
|
|
|
+ gridList[rowIndex].push({
|
|
|
+ name: gridData.name,
|
|
|
+ status: gridData.status,
|
|
|
+ gridNode: null,
|
|
|
+ row: gridData.row,
|
|
|
+ column: gridData.column,
|
|
|
+ gridColorKey: gridData.gridColorKey,
|
|
|
+ gridType: gridData.gridType
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return gridList
|
|
|
+ }
|
|
|
+
|
|
|
+ prompt(tipFlag = true) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const gridPromptList: GridDataInterface[] = []
|
|
|
+ let moveFlag = false
|
|
|
+ // 找方块可消除位置
|
|
|
+ for (let rowIndex = 0; rowIndex < this.row && !moveFlag; rowIndex++) {
|
|
|
+ for (let columnIndex = 0; columnIndex < this.column && !moveFlag; columnIndex++) {
|
|
|
+ const gridData = this.gridList[rowIndex][columnIndex]
|
|
|
+ if (gridData.status !== GridStatus.notUse) continue
|
|
|
+ // 方块不旋转检测是否能放
|
|
|
+ for (let brickI = 0; brickI < this.bricksList.length && !moveFlag; brickI++) {
|
|
|
+ const brickData = this.bricksList[brickI]
|
|
|
+ if (this.moveIf(rowIndex, columnIndex, brickData.gridConfig)) {
|
|
|
+ // 复制整体网格,以方块设置网格状态
|
|
|
+ const gridList = this.copyGridList()
|
|
|
+ brickData.gridConfig.forEach((gridConfigData) => {
|
|
|
+ gridList[gridConfigData.row + rowIndex][gridConfigData.column + columnIndex].status = GridStatus.used
|
|
|
+ })
|
|
|
+ // 检查复制的整体网格是否有可消除
|
|
|
+ if (this.gridEliminateCheck(gridList).gridEliminateList.length > 0) {
|
|
|
+ moveFlag = true
|
|
|
+ brickData.gridConfig.forEach((gridConfigData) => {
|
|
|
+ gridPromptList.push(this.gridList[gridConfigData.row + rowIndex][gridConfigData.column + columnIndex])
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 方块旋转检测是否能放
|
|
|
+ for (let brickI = 0; brickI < this.bricksList.length && !moveFlag; brickI++) {
|
|
|
+ const brickData = this.bricksList[brickI]
|
|
|
+ if (!brickData.rotateFlag) continue
|
|
|
+ let gridConfig = brickData.gridConfig
|
|
|
+ let deg = brickData.deg
|
|
|
+ // 获得旋转的方块网格配置
|
|
|
+ for (let count = 1; count <= 3 && !moveFlag; count++) {
|
|
|
+ const next = this.nextGridRotate(gridConfig, deg)
|
|
|
+ if (this.moveIf(rowIndex, columnIndex, next.gridConfig)) {
|
|
|
+ // 复制整体网格,以方块设置网格状态
|
|
|
+ const gridList = this.copyGridList()
|
|
|
+ next.gridConfig.forEach((gridConfigData) => {
|
|
|
+ gridList[gridConfigData.row + rowIndex][gridConfigData.column + columnIndex].status = GridStatus.used
|
|
|
+ })
|
|
|
+ // 检查复制的整体网格是否有可消除
|
|
|
+ if (this.gridEliminateCheck(gridList).gridEliminateList.length > 0) {
|
|
|
+ moveFlag = true
|
|
|
+ next.gridConfig.forEach((gridConfigData) => {
|
|
|
+ gridPromptList.push(this.gridList[gridConfigData.row + rowIndex][gridConfigData.column + columnIndex])
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ gridConfig = next.gridConfig
|
|
|
+ deg = next.deg
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 找方块可放置位置
|
|
|
+ for (let rowIndex = 0; rowIndex < this.row && !moveFlag; rowIndex++) {
|
|
|
+ for (let columnIndex = 0; columnIndex < this.column && !moveFlag; columnIndex++) {
|
|
|
+ const gridData = this.gridList[rowIndex][columnIndex]
|
|
|
+ if (gridData.status !== GridStatus.notUse) continue
|
|
|
+ // 方块不旋转检测是否能放
|
|
|
+ for (let brickI = 0; brickI < this.bricksList.length && !moveFlag; brickI++) {
|
|
|
+ const brickData = this.bricksList[brickI]
|
|
|
+ if (this.moveIf(rowIndex, columnIndex, brickData.gridConfig)) {
|
|
|
+ moveFlag = true
|
|
|
+ brickData.gridConfig.forEach((gridConfigData) => {
|
|
|
+ gridPromptList.push(this.gridList[gridConfigData.row + rowIndex][gridConfigData.column + columnIndex])
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 方块旋转检测是否能放
|
|
|
+ for (let brickI = 0; brickI < this.bricksList.length && !moveFlag; brickI++) {
|
|
|
+ const brickData = this.bricksList[brickI]
|
|
|
+ if (!brickData.rotateFlag) continue
|
|
|
+ let gridConfig = brickData.gridConfig
|
|
|
+ let deg = brickData.deg
|
|
|
+ for (let count = 1; count <= 3 && !moveFlag; count++) {
|
|
|
+ const next = this.nextGridRotate(gridConfig, deg)
|
|
|
+ if (this.moveIf(rowIndex, columnIndex, next.gridConfig)) {
|
|
|
+ moveFlag = true
|
|
|
+ next.gridConfig.forEach((gridConfigData) => {
|
|
|
+ gridPromptList.push(this.gridList[gridConfigData.row + rowIndex][gridConfigData.column + columnIndex])
|
|
|
+ })
|
|
|
+ }
|
|
|
+ gridConfig = next.gridConfig
|
|
|
+ deg = next.deg
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (gridPromptList.length < 1) {
|
|
|
+ resolve(false)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!tipFlag) {
|
|
|
+ resolve(true)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 提示用户(网格变绿)
|
|
|
+ gridPromptList.forEach((gridData) => {
|
|
|
+ if (gridData.gridNode) {
|
|
|
+ const sprite = gridData.gridNode.children[0]?.getComponent(Sprite)
|
|
|
+ if (sprite) {
|
|
|
+ sprite.color = this.usableColor
|
|
|
+ // 用于恢复格子
|
|
|
+ this.gridColorList.push(gridData)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ resolve(true)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //重新开始
|
|
|
+ onRefreshBtnClick() {
|
|
|
+ if (
|
|
|
+ this.gameStatus !== GameStatus.running ||
|
|
|
+ !this.operateFlag ||
|
|
|
+ this.adShowingFlag
|
|
|
+ ) return
|
|
|
+
|
|
|
+ this.setAdShowingFlag(true)
|
|
|
+ this.bricksNode.destroyAllChildren()
|
|
|
+ this.rotateContainer.destroyAllChildren()
|
|
|
+ this.bricksList.length = 0
|
|
|
+ for (let i = 1; i <= this.brickNum; i++) {
|
|
|
+ this.addBrick(i)
|
|
|
+ }
|
|
|
+ // 恢复格子颜色
|
|
|
+ this.gridColorRecovery()
|
|
|
+ this.setAdShowingFlag(false)
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ //--复活
|
|
|
+ onPromptBtnClick() {
|
|
|
+ if (
|
|
|
+ this.gameStatus !== GameStatus.running ||
|
|
|
+ !this.operateFlag ||
|
|
|
+ this.adShowingFlag
|
|
|
+ ) return
|
|
|
+
|
|
|
+ // 先检查是否死局
|
|
|
+ this.prompt(false).then((flag) => {
|
|
|
+ if (!flag) {
|
|
|
+ this.gameOver()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 非死局,打开广告
|
|
|
+ this.setAdShowingFlag(true)
|
|
|
+ // SDKManager.showVideoAd().then((isEnded: boolean) => {
|
|
|
+ // if (isEnded) {
|
|
|
+ // this.gridColorRecovery()
|
|
|
+ // this.prompt()
|
|
|
+ // }
|
|
|
+ // this.setAdShowingFlag(false)
|
|
|
+ // }).catch((err) => {
|
|
|
+ // console.error('onPromptBtnClick', err)
|
|
|
+ // this.setAdShowingFlag(false)
|
|
|
+ // })
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ //再来一次
|
|
|
+ onAgainBtnClick() {
|
|
|
+ if (!this.operateFlag) return
|
|
|
+
|
|
|
+ this.initData()
|
|
|
+ }
|
|
|
+
|
|
|
+ //确认游戏
|
|
|
+ onContinueBtnClick() {
|
|
|
+ this.setGameStauts(GameStatus.running)
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|