鸿蒙上做个“俄罗斯方块”小游戏

鸿蒙上做个“俄罗斯方块”小游戏

首页休闲益智单机俄罗斯方块更新时间:2024-04-16

小时候有个游戏叫俄罗斯方块,大人小孩都喜欢玩,我们就一起看看如何能用 OpenHarmony 学习做个 Tetris。

效果如下图:

开发

①HAP 应用建立

此前文章我们介绍了简单的 Hap 应用的开发以及基础控件的介绍,这里我们就不赘述 Hap 项目的建立过程。

以下就是基础的 Hap 的 page 文件:index.ets。

build() { Row() { Column() { Canvas(this.context) .width('100%') .height('100%') .onClick((ev: ClickEvent) => { console.info("click!!") this.doClick() }) .onReady(() =>{ this.context.imageSmoothingEnabled = false this.randomType() this.drawall() }) } .width('100%') } .height('100%') .backgroundColor("#cccccc") }

build 是基础页面的构造函数,用于界面的元素构造,其他的页面的生命周期函数如下:

declare class CustomComponent { /** * Customize the pop-up content constructor. * @since 7 */ build(): void; /** * aboutToAppear Method * @since 7 */ aboutToAppear?(): void; /** * aboutToDisappear Method * @since 7 */ aboutToDisappear?(): void; /** * onPageShow Method * @since 7 */ onPageShow?(): void; /** * onPageHide Method * @since 7 */ onPageHide?(): void; /** * onBackPress Method * @since 7 */ onBackPress?(): void; }

②Canvas 介绍

canvas 是画布组件用于自定义绘制图形,具体的 API 页面如下:

https://developer.harmonyos.com/cn/docs/documentation/doc-references/ts-components-canvas-canvas-0000001333641081

页面显示前会调用 aboutToAppear() 函数,此函数为页面生命周期函数。

canvas 组件初始化完毕后会调用 onReady() 函数,函数内部实现小游戏的初始页面的绘制

初始化页面数据:

drawall() { this.drawBox() this.drawSideBlock() this.drawBoxBlock() this.drawScore() }

因为都是画布画的,所以布局有点麻烦,需要画几个部分:

绘制大框:

drawBox() { this.context.lineWidth = 4 this.context.beginPath() this.context.lineCap = 'butt' this.context.moveTo(0, 100) this.context.lineTo(270, 100) this.context.moveTo(270, 100) this.context.lineTo(270, 690) this.context.moveTo(0, 690) this.context.lineTo(270, 690) }

绘制提示方块:

drawSideBlock() { this.context.fillStyle = 'rgb(250,0,0)' let bs = this.blockSize let coords = this.blockShapBasic[this.blockType] let x = this.sideStartX coords[0][0]*this.blockSize let y = this.sideStartY coords[0][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) x = this.sideStartX coords[1][0]*this.blockSize y = this.sideStartY coords[1][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) x = this.sideStartX coords[2][0]*this.blockSize y = this.sideStartY coords[2][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) x = this.sideStartX coords[3][0]*this.blockSize y = this.sideStartY coords[3][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) this.context.stroke() }绘制运动方块:

drawBoxBlock() { this.setDirection() this.context.fillStyle = 'rgb(250,0,0)' let bs = this.blockSize let coords = this.curBlockShap let starty = this.slotStartY this.step * this.blockSize let x = this.slotStartX coords[0][0]*this.blockSize let y = starty coords[0][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) x = this.slotStartX coords[1][0]*this.blockSize y = starty coords[1][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) x = this.slotStartX coords[2][0]*this.blockSize y = starty coords[2][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) x = this.slotStartX coords[3][0]*this.blockSize y = starty coords[3][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) this.context.stroke() this.slotBottomY = y }

绘制得分区域:

drawScore() { this.context.fillStyle = 'rgb(0,0,0)' this.context.font = '80px sans-serif' this.context.fillText("Score:" this.score.toString(), 20, 740) }

③游戏逻辑

简单的小游戏主体游戏逻辑为:等待开始,开始。

结束流程图如下:

graph LR timer开始 --> 方块下落 timer开始 --> click[点击] click[点击] --> 方块变形 方块下落 --> |落到底| 能消除 --> 计分 --> 堆积 方块下落 --> |落到底| 不能消除 --> 堆积 堆积 --> |堆积到顶| 满了 --> 游戏结束 堆积 --> |堆积到顶| 未满 --> 方块下落

doClick() { this.direction = 1 }完整逻辑:

@Entry @Component struct Index { @State message: string = 'Hello World' private settings: RenderingContextSettings = new RenderingContextSettings(true); private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings); private blockType: number = 0 private blockSize: number = 30 private blockShapBasic = [ [[0,0],[0,1],[0,2],[0,3]], [[0,0],[0,1],[0,2],[1,2]], [[0,0],[0,1],[1,1],[0,2]], [[0,0],[0,1],[1,1],[1,2]], [[0,0],[0,1],[1,0],[1,1]], ] private blockShap = [ [[0,0],[0,1],[0,2],[0,3]], [[0,0],[0,1],[0,2],[1,2]], [[0,0],[0,1],[1,1],[0,2]], [[0,0],[0,1],[1,1],[1,2]], [[0,0],[0,1],[1,0],[1,1]], ] private curBlockShap = [] private sideStartX = 300; private sideStartY = 150; private slotStartX = 120; private slotStartY = 150; private slotBottomY = 150;; private score = 0; private step = 0; private direction = 0; aboutToDisappear() { } aboutToAppear() { this.sleep(1000) } async sleep(ms: number) { return new Promise((r) => { setInterval(() => { console.log(this.message) this.drawStep() }, ms) }) } doClick() { this.direction = 1 } drawBox() { this.context.lineWidth = 4 this.context.beginPath() this.context.lineCap = 'butt' this.context.moveTo(0, 100) this.context.lineTo(270, 100) this.context.moveTo(270, 100) this.context.lineTo(270, 690) this.context.moveTo(0, 690) this.context.lineTo(270, 690) } setDirection() { this.curBlockShap = this.blockShap[this.blockType] if (this.direction > 0) { for (let i=0;i<4;i ) { let x = this.curBlockShap[i][0] this.curBlockShap[i][0] = this.curBlockShap[i][1] this.curBlockShap[i][1] = x } this.direction = 0 } } drawSideBlock() { this.context.fillStyle = 'rgb(250,0,0)' let bs = this.blockSize let coords = this.blockShapBasic[this.blockType] let x = this.sideStartX coords[0][0]*this.blockSize let y = this.sideStartY coords[0][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) x = this.sideStartX coords[1][0]*this.blockSize y = this.sideStartY coords[1][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) x = this.sideStartX coords[2][0]*this.blockSize y = this.sideStartY coords[2][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) x = this.sideStartX coords[3][0]*this.blockSize y = this.sideStartY coords[3][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) this.context.stroke() } drawBoxBlock() { this.setDirection() this.context.fillStyle = 'rgb(250,0,0)' let bs = this.blockSize let coords = this.curBlockShap let starty = this.slotStartY this.step * this.blockSize let x = this.slotStartX coords[0][0]*this.blockSize let y = starty coords[0][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) x = this.slotStartX coords[1][0]*this.blockSize y = starty coords[1][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) x = this.slotStartX coords[2][0]*this.blockSize y = starty coords[2][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) x = this.slotStartX coords[3][0]*this.blockSize y = starty coords[3][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("x,y" x.toString() ":" y.toString()) this.context.stroke() this.slotBottomY = y } drawScore() { this.context.fillStyle = 'rgb(0,0,0)' this.context.font = '80px sans-serif' this.context.fillText("Score:" this.score.toString(), 20, 740) } randomType() { this.blockType = Math.floor(Math.random()*5) console.info("blocktype:" this.blockType.toString()) } drawStep() { this.context.clearRect(0,0,this.context.width,this.context.height) this.step = 1 this.drawBox() this.drawSideBlock() this.drawBoxBlock() this.drawScore() if (this.slotBottomY >= 660) { this.step = 0 this.randomType() } } drawall() { this.drawBox() this.drawSideBlock() this.drawBoxBlock() this.drawScore() } build() { Row() { Column() { Canvas(this.context) .width('100%') .height('100%') .onClick((ev: ClickEvent) => { console.info("click!!") this.doClick() }) .onReady(() =>{ this.context.imageSmoothingEnabled = false this.randomType() this.drawall() }) } .width('100%') } .height('100%') .backgroundColor("#cccccc") } }

遗留问题:

下面我们将继续完善页面,增加了左右按键和之前方块显示,方块消除。

①按键增加

之前我们布局一直是只有个 Canvas 控件,现在我们需要设置高度后增加一个 Row 的布局,并增加两个 Button 控件,以下就是基础的 Hap 的 page 文件:index.ets。

build() { Column() { Column() { Canvas(this.context) .width('100%') .height('100%') .onClick((ev: ClickEvent) => { console.info("click!!") this.doClick() }) .onTouch((ev) => { console.info("touch:" ev.type.toString()) console.info("touch x:" ev.touches[0].screenX.toString()) console.info("touch y:" ev.touches[0].screenY.toString()) }) .onReady(() =>{ this.context.imageSmoothingEnabled = false this.randomType() this.drawall() }) } .height('92%') .width('100%') Row() { Button() { //按钮控件 Text('左') .fontSize(20) .fontWeight(FontWeight.Bold) }.type(ButtonType.Capsule) .width('20%') .height('6%') .backgroundColor('#0D9FFB') .onClick(() => { //点击事件 if (this.left > 0) { this.moveAction -= 1 } }) Button() { //按钮控件 Text('右') .fontSize(20) .fontWeight(FontWeight.Bold) }.type(ButtonType.Capsule) .width('20%') .height('6%') .backgroundColor('#0D9FFB') .onClick(() => { //点击事件 if (this.rightX < 240) { this.moveAction = 1 } }) } } .width('100%') .height('100%') .backgroundColor("#cccccc") }②游戏完善的说明

之前我们的游戏只有布局,方块显示和变形,在完善后我们增加了积累方块的显示,消除,计分,游戏结束判断等。

积累方块显示:

drawBlockmap() { let bs = this.blockSize this.context.fillStyle = 'rgb(250,0,0)' for (let i=0;i<23;i ) { for (let j=0;j<9;j ) { //是否有方块 if (this.blockmap[i][j] == 1) { let y = i * this.blockSize let x = j * this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("drawBlockmap:" x.toString() ":" y.toString()) } } this.context.stroke() console.info("drawBlockmap:" this.storeBlock.length.toString()) } }

因为都是画布画的,为了重绘已经存在的方块,我们应用二维数组 blockmap 表示,值为 1 则有方块,数组索引则表示绘制坐标位置。

判断是否到底还是到顶:

checkBlockmap() { if (this.storeBlock.length == 0) { if (this.slotBottomY >= 660) { return 1 } } else { let coords = this.curBlockShap let startx = this.slotStartX this.moveAction * this.blockSize let starty = this.slotStartY this.step * this.blockSize for (let i=0;i<4;i ) { let x = startx coords[i][0]*this.blockSize let y = starty coords[i][1]*this.blockSize this.blockSize for (let k=0;k<22;k ) { for (let l=0;l<9;l ) { if (this.blockmap[k][l] == 1) { let blocky = k * this.blockSize let blockx = l * this.blockSize //判断是否到底 if ((x == blockx && y == blocky) || y > 660) { //判断是否到顶 if (y == 210) { this.context.drawImage( this.gameoverImg,this.startX,this.startY,300,90) //到顶回2 return 2 } //到底回1 return 1 } } } } } } return 0 }

先判断是否到底,到底的同时判断是否到顶,如果到顶了就是满了,如果只是到底则表明游戏可以继续。

到底积累方块:

stackBlock() { let block = [] let coords = this.curBlockShap let startx = this.slotStartX this.moveAction * this.blockSize let starty = this.slotStartY this.step * this.blockSize for (let i=0;i<4;i ) { let x = startx coords[i][0]*this.blockSize let y = starty coords[i][1]*this.blockSize console.info("stackBlock x:" x.toString() "y:" y.toString()) let indexX = x/this.blockSize let indexY = y/this.blockSize this.blockmap[indexY][indexX] = 1 console.info("stackBlock:" indexX ":" indexY) block.push([x,y]) } this.storeBlock.push(block) console.info("stackBlock:" this.storeBlock.length.toString()) }

如果到底了,就用坐标计算出 索引,然后在 blockmap 里标识为 1,说明此处有方块,这样方便后面的绘制的显示。

清除方块:

cleanBlockmap() { //检查是否一行满了 let needMove = 0 for (let i=22;i>=0;i--) { let linecnt = 0 for (let j = 8;j >= 0; j--) { //是否有方块 if (this.blockmap[i][j] == 1) { linecnt = 1 } } if (linecnt == 9) { //此行都是方块,消除,计分 for (let j = 8;j >= 0; j--) { this.blockmap[i][j] = 0 } needMove = 1 this.score = 1 } else if (needMove > 0) { for (let j = 8;j >= 0; j--) { this.blockmap[i needMove][j] = this.blockmap[i][j] this.blockmap[i][j] = 0 } } } }

二维数组的好处就是方便每行计数清除,然后从底向上再逐层替换。

绘制每一步:

drawStep() { this.context.clearRect(0,0,this.context.width,this.context.height) this.step = 1 this.drawBox() this.drawBlockmap() this.cleanBlockmap() this.drawSideBlock() this.drawBoxBlock() this.drawScore() let stepType = this.checkBlockmap() if ( stepType == 1) { this.stackBlock() this.step = 0 this.randomType() } else if (stepType == 2) { this.state= 2 this.context.drawImage( this.gameoverImg,this.startX,this.startY,300,90) } }

绘制每一步其实就是重绘界面,包括如果游戏结束显示 game over。

③游戏逻辑

简单的小游戏主体游戏逻辑为:等待开始,开始,结束流程图如下:

graph LR timer开始 --> 方块下落 timer开始 --> click[点击] click[点击] --> 方块变形 方块下落 --> |落到底| 能消除 --> 计分 --> 堆积 方块下落 --> |落到底| 不能消除 --> 堆积 堆积 --> |堆积到顶| 满了 --> 游戏结束 堆积 --> |堆积到顶| 未满 --> 方块下落

doClick() { if (this.state == 0) { this.direction = 1 } else if (this.state == 2) { this.state = 0 this.score = 0 this.storeBlock = [] this.initMap() } }

游戏结束后需要重新初始化内部数据。

④完整逻辑

@Entry @Component struct Index { @State message: string = 'Hello World' private settings: RenderingContextSettings = new RenderingContextSettings(true); private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings); private gameoverImg:ImageBitmap = new ImageBitmap("common/images/gameover.png") private blockType: number = 0 private blockSize: number = 30 private blockShapBasic = [ [[0,0],[0,1],[0,2],[0,3]], [[0,0],[0,1],[0,2],[1,2]], [[0,0],[0,1],[1,1],[0,2]], [[0,0],[0,1],[1,1],[1,2]], [[0,0],[0,1],[1,0],[1,1]], ] private blockShap = [ [[0,0],[0,1],[0,2],[0,3]], [[0,0],[0,1],[0,2],[1,2]], [[0,0],[0,1],[1,1],[0,2]], [[0,0],[0,1],[1,1],[1,2]], [[0,0],[0,1],[1,0],[1,1]], ] private blockmap = []; private curBlockShap = [] private storeBlock = [] private sideStartX = 300; private sideStartY = 150; private startX = 20 private startY = 300 private slotStartX = 120; private slotStartY = 150; private slotBottomY = 150; private xleft = 0; private rightX = 0; private score = 0; private step = 0; private direction = 0; private moveAction = 0; private state = 0; aboutToDisappear() { } aboutToAppear() { this.initMap() this.sleep(1000) } initMap() { for (let i=0;i<23;i ) { let item = [] for (let j=0;j<9;j ) { item.push(0) } this.blockmap.push(item) } } async sleep(ms: number) { return new Promise((r) => { setInterval(() => { // console.log(this.message) if (this.state == 0) { this.drawStep() } }, ms) }) } doClick() { if (this.state == 0) { this.direction = 1 } else if (this.state == 2) { this.state = 0 this.score = 0 this.storeBlock = [] this.initMap() } } drawBox() { this.context.lineWidth = 4 this.context.beginPath() this.context.lineCap = 'butt' this.context.moveTo(0, 100) this.context.lineTo(270, 100) this.context.moveTo(270, 100) this.context.lineTo(270, 690) this.context.moveTo(0, 690) this.context.lineTo(270, 690) } setDirection() { this.curBlockShap = this.blockShap[this.blockType] if (this.direction > 0) { for (let i=0;i<4;i ) { let x = this.curBlockShap[i][0] this.curBlockShap[i][0] = this.curBlockShap[i][1] this.curBlockShap[i][1] = x } this.direction = 0 } } drawSideBlock() { this.context.fillStyle = 'rgb(250,0,0)' let bs = this.blockSize let coords = this.blockShapBasic[this.blockType] for (let i=0;i<4;i ) { let x = this.sideStartX coords[i][0]*this.blockSize let y = this.sideStartY coords[i][1]*this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) // console.info("x,y" x.toString() ":" y.toString()) } this.context.stroke() } drawBoxBlock() { let min = 690 let max = 0 this.setDirection() this.context.fillStyle = 'rgb(250,0,0)' let bs = this.blockSize let coords = this.curBlockShap let startx = this.slotStartX this.moveAction * this.blockSize let starty = this.slotStartY this.step * this.blockSize for (let i=0;i<4;i ) { let x = startx coords[i][0]*this.blockSize let y = starty coords[i][1]*this.blockSize min = min > x ? x:min max = max < x ? x:max this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) // console.info("x,y" x.toString() ":" y.toString()) this.slotBottomY = y this.xleft = min this.rightX = max } this.context.stroke() // console.info("min,max" min.toString() ":" max.toString()) } stackBlock() { let block = [] let coords = this.curBlockShap let startx = this.slotStartX this.moveAction * this.blockSize let starty = this.slotStartY this.step * this.blockSize for (let i=0;i<4;i ) { let x = startx coords[i][0]*this.blockSize let y = starty coords[i][1]*this.blockSize console.info("stackBlock x:" x.toString() "y:" y.toString()) let indexX = x/this.blockSize let indexY = y/this.blockSize this.blockmap[indexY][indexX] = 1 console.info("stackBlock:" indexX ":" indexY) block.push([x,y]) } this.storeBlock.push(block) console.info("stackBlock:" this.storeBlock.length.toString()) } checkBlockmap() { if (this.storeBlock.length == 0) { if (this.slotBottomY >= 660) { return 1 } } else { let coords = this.curBlockShap let startx = this.slotStartX this.moveAction * this.blockSize let starty = this.slotStartY this.step * this.blockSize for (let i=0;i<4;i ) { let x = startx coords[i][0]*this.blockSize let y = starty coords[i][1]*this.blockSize this.blockSize for (let k=0;k<22;k ) { for (let l=0;l<9;l ) { if (this.blockmap[k][l] == 1) { let blocky = k * this.blockSize let blockx = l * this.blockSize //判断是否到底 if ((x == blockx && y == blocky) || y > 660) { //判断是否到顶 if (y == 210) { this.context.drawImage( this.gameoverImg,this.startX,this.startY,300,90) //到顶回2 return 2 } //到底回1 return 1 } } } } } } return 0 } cleanBlockmap() { //检查是否一行满了 let needMove = 0 for (let i=22;i>=0;i--) { let linecnt = 0 for (let j = 8;j >= 0; j--) { //是否有方块 if (this.blockmap[i][j] == 1) { linecnt = 1 } } if (linecnt == 9) { //此行都是方块,消除,计分 for (let j = 8;j >= 0; j--) { this.blockmap[i][j] = 0 } needMove = 1 this.score = 1 } else if (needMove > 0) { for (let j = 8;j >= 0; j--) { this.blockmap[i needMove][j] = this.blockmap[i][j] this.blockmap[i][j] = 0 } } } } drawBlockmap() { let bs = this.blockSize this.context.fillStyle = 'rgb(250,0,0)' for (let i=0;i<23;i ) { for (let j=0;j<9;j ) { //是否有方块 if (this.blockmap[i][j] == 1) { let y = i * this.blockSize let x = j * this.blockSize this.context.fillRect(x, y, bs, bs) this.context.rect(x, y, bs, bs) console.info("drawBlockmap:" x.toString() ":" y.toString()) } } this.context.stroke() console.info("drawBlockmap:" this.storeBlock.length.toString()) } } drawScore() { this.context.fillStyle = 'rgb(0,0,0)' this.context.font = '80px sans-serif' this.context.fillText("Score:" this.score.toString(), 20, 140) } randomType() { this.blockType = Math.floor(Math.random()*5) console.info("blocktype:" this.blockType.toString()) } drawStep() { this.context.clearRect(0,0,this.context.width,this.context.height) this.step = 1 this.drawBox() this.drawBlockmap() this.cleanBlockmap() this.drawSideBlock() this.drawBoxBlock() this.drawScore() let stepType = this.checkBlockmap() if ( stepType == 1) { this.stackBlock() this.step = 0 this.randomType() } else if (stepType == 2) { this.state= 2 this.context.drawImage( this.gameoverImg,this.startX,this.startY,300,90) } } drawall() { this.drawBox() this.drawSideBlock() this.drawBoxBlock() this.drawScore() } build() { Column() { Column() { Canvas(this.context) .width('100%') .height('100%') .onClick((ev: ClickEvent) => { console.info("click!!") this.doClick() }) .onTouch((ev) => { console.info("touch:" ev.type.toString()) console.info("touch x:" ev.touches[0].screenX.toString()) console.info("touch y:" ev.touches[0].screenY.toString()) }) .onReady(() =>{ this.context.imageSmoothingEnabled = false this.randomType() this.drawall() }) } .height('92%') .width('100%') Row() { Button() { //按钮控件 Text('左') .fontSize(20) .fontWeight(FontWeight.Bold) }.type(ButtonType.Capsule) .width('20%') .height('6%') .backgroundColor('#0D9FFB') .onClick(() => { //点击事件 if (this.xleft > 0) { this.moveAction -= 1 } }) Button() { //按钮控件 Text('右') .fontSize(20) .fontWeight(FontWeight.Bold) }.type(ButtonType.Capsule) .width('20%') .height('6%') .backgroundColor('#0D9FFB') .onClick(() => { //点击事件 if (this.rightX < 240) { this.moveAction = 1 } }) } } .width('100%') .height('100%') .backgroundColor("#cccccc") } }

遗留问题:

⑤获取源码

等游戏完整发布,会有两个版本,单机和联机版本。

总结

本文主要介绍了小游戏的开发,画布功能的使用,游戏逻辑,分布式。

作者:王石

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved