为了更好地熟练掌握鸿蒙手机应用开发,深鸿会深大学习小组将带来一款经典的鸿蒙手机小游戏——俄罗斯方块。
本篇文章作者自研了俄罗斯方块的算法,详细讲述了俄罗斯方块在鸿蒙手机上的开发思路,内含详细注释。
本个 demo 将从零基础开始完成鸿蒙小游戏 APP 在手机上的编译在项目中我们所使用到的软件为 DevEco Studio。
https://developer.harmonyos.com/cn/develop/deveco-studio#downloadhttps://developer.harmonyos.com/cn/docs/documentation/doc-guides/software_install-0000001053582415
在项目中我们要实现的内容为俄罗斯方块 APP 的开发。



01
创建项目
DevEco Studio 下载安装成功后,打开 DevEco Studio,点击左上角的 File,点击 New,再选择 New Project,选择 Phone 选项,选择默认的模板(java 版)。


02
准备工作
在 entry>src>main>config.json 文件中最下方"launchType": "standard"的后面添加以下代码,这样就可以实现去掉应用上方的标签栏了。
并且将上方的“label”:“MyPhoneGame2”修改成"label": "俄罗斯方块",这样就可以实现将应用名称修改为俄罗斯方块了。
"icon": "$media:icon", "description": "$string:mainability_description", "label": "俄罗斯方块", "type": "page", "launchType": "standard", "metaData": { "customizeData": [ { "name": "hwc-theme", "value": "androidhwext:style/Theme.Emui.Light.NoTitleBar", "extra": "" } ] }
03
绘制基础组件

在 entry>src>main>java>com.example.myphoneapplication>slice>MainAbilitySlice 编写代码。
先定义方格的边长 length 为常量 100,方格的间距 interval 为常量 2,再定义一个位置布局 layout 和一个表示方格颜色的二维数组 grids。
创建函数 initializeinitialize() 分别对其初始化,布局 layout 初始化为线性布局 DirectionalLayout,二维数组 grids 全部赋值为 0。
public class MainAbilitySlice extends AbilitySlice { private DirectionalLayout layout; private static final int length=100; private static final int interval=2; private int[][] grids; public void onStart(Intent intent) { super.onStart(intent); initialize(); } public void initialize(){ layout = new DirectionalLayout(this); grids = new int[15][10]; for(int row = 0; row < 15; row++) for(int column = 0; column < 10; column++) grids[row][column] = 0; }
public void drawGrids(){ layout.setLayoutConfig((new ComponentContainer.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT,ComponentContainer.LayoutConfig.MATCH_PARENT))); Component.DrawTask task=new Component.DrawTask() { @Override public void onDraw(Component component, Canvas canvas) { Paint paint = new Paint(); paint.setColor(Color.BLACK); RectFloat rect=new RectFloat(30-20,250-20,length*10+interval*9+30+20,length*15+interval*14+250+20); canvas.drawRect(rect,paint); for(int row = 0; row < 15; row++){//0表示灰色,1代表红色,2代表绿色,3代表蓝绿色,4代表品红色,5代表蓝色,6代表白色,7代表黄色 for(int column = 0; column < 10; column++){ if(grids[row][column] == 0) paint.setColor(Color.GRAY); else if(grids[row][column] == 1) paint.setColor(Color.RED); else if(grids[row][column] == 2) paint.setColor(Color.GREEN); else if(grids[row][column] == 3) paint.setColor(Color.CYAN); else if(grids[row][column] == 4) paint.setColor(Color.MAGENTA); else if(grids[row][column] == 5) paint.setColor(Color.BLUE); else if(grids[row][column] == 6) paint.setColor(Color.WHITE); else if(grids[row][column] == 7) paint.setColor(Color.YELLOW); RectFloat rectFloat=new RectFloat(30+column*(length+interval),250+row*(length+interval),30+length+column*(length+interval),250+length+row*(length+interval)); canvas.drawRect(rectFloat,paint); } } } }; layout.addDrawTask(task); setUIContent(layout); }
public void drawButton(){ ShapeElement background = new ShapeElement(); background.setRgbColor(new RgbColor(174, 158, 143)); background.setCornerRadius(100); Button button1 = new Button(this); button1.setText("←"); button1.setTextAlignment(TextAlignment.CENTER); button1.setTextColor(Color.WHITE); button1.setTextSize(100); button1.setMarginTop(1800); button1.setMarginLeft(160); button1.setPadding(10,0,10,0); button1.setBackground(background); button1.setClickedListener(new Component.ClickedListener() { @Override public void onClick(Component component) { leftShift(); } }); layout.addComponent(button1); Button button2 = new Button(this); button2.setText("变"); button2.setTextAlignment(TextAlignment.CENTER); button2.setTextColor(Color.WHITE); button2.setTextSize(100); button2.setMarginLeft(480); button2.setMarginTop(-130); button2.setPadding(10,0,10,0); button2.setBackground(background); button2.setClickedListener(new Component.ClickedListener() { @Override public void onClick(Component component) { changGrids(); } }); layout.addComponent(button2); Button button3 = new Button(this); button3.setText("→"); button3.setTextAlignment(TextAlignment.CENTER); button3.setTextColor(Color.WHITE); button3.setTextSize(100); button3.setMarginLeft(780); button3.setMarginTop(-130); button3.setPadding(10,0,10,0); button3.setBackground(background); button3.setClickedListener(new Component.ClickedListener() { @Override public void onClick(Component component) { rightShift(); } }); layout.addComponent(button3); Button button = new Button(this); button.setText("重新开始"); button.setTextSize(100); button.setTextAlignment(TextAlignment.CENTER); button.setTextColor(Color.WHITE); button.setMarginTop(5); button.setMarginLeft(310); button.setPadding(10,10,10,10); button.setBackground(background); button.setClickedListener(new Component.ClickedListener() { @Override public void onClick(Component component) { initialize(); } }); layout.addComponent(button); }
public void initialize(){//部分代码没有贴出,欢迎自行下载附件查看源代码 drawButton(); drawGrids(); }
04
随机产生方块

首先说明一下本人研究出来表示不同方块的算法:用一个常量二维数组去存储不同颜色的不同形状的方块所在的位置。
如:{{0,3},{0,4},{1,4},{1,5}} 中的 {0,3} 就表示该方块的第一个方格在 grids[0][3] 的位置,{0,4} 就表示该方块的第二个方格在 grids[0][4] 的位置,以此类推,这样连起来就可以得到一种颜色的一种形状的方块了。
然后先定义各种表示方块的常量二维数组,定义方块所占方格的数量 grids_number 为常量 4。
二维数组 NowGrids 表示当前方块的形状,row_number 表示方块的总行数。
private static final int[][] RedGrids1={{0,3},{0,4},{1,4},{1,5}}; private static final int[][] RedGrids2={{0,5},{1,5},{1,4},{2,4}}; private static final int[][] GreenGrids1={{0,5},{0,4},{1,4},{1,3}}; private static final int[][] GreenGrids2={{0,4},{1,4},{1,5},{2,5}}; private static final int[][] CyanGrids1={{0,4},{1,4},{2,4},{3,4}}; private static final int[][] CyanGrids2={{0,3},{0,4},{0,5},{0,6}}; private static final int[][] MagentaGrids1={{0,4},{1,3},{1,4},{1,5}}; private static final int[][] MagentaGrids2={{0,4},{1,4},{1,5},{2,4}}; private static final int[][] MagentaGrids3={{0,3},{0,4},{0,5},{1,4}}; private static final int[][] MagentaGrids4={{0,5},{1,5},{1,4},{2,5}}; private static final int[][] BlueGrids1={{0,3},{1,3},{1,4},{1,5}}; private static final int[][] BlueGrids2={{0,5},{0,4},{1,4},{2,4}}; private static final int[][] BlueGrids3={{0,3},{0,4},{0,5},{1,5}}; private static final int[][] BlueGrids4={{0,5},{1,5},{2,5},{2,4}}; private static final int[][] WhiteGrids1={{0,5},{1,5},{1,4},{1,3}}; private static final int[][] WhiteGrids2={{0,4},{1,4},{2,4},{2,5}}; private static final int[][] WhiteGrids3={{0,5},{0,4},{0,3},{1,3}}; private static final int[][] WhiteGrids4={{0,4},{0,5},{1,5},{2,5}}; private static final int[][] YellowGrids={{0,4},{0,5},{1,5},{1,4}}; private static final int grids_number=4; private int[][] NowGrids; private int row_number; private int column_number; private int Grids; private int column_start;
public void createRedGrids1(){ NowGrids=RedGrids1; row_number=2; column_number=3; Grids=1; column_start=3; } public void createRedGrids2(){ NowGrids=RedGrids2; row_number=3; column_number=2; Grids=1; column_start=4; } public void createGreenGrids1(){ NowGrids=GreenGrids1; row_number=2; column_number=3; Grids=2; column_start=3; } public void createGreenGrids2(){ NowGrids=GreenGrids2; row_number=3; column_number=2; Grids=2; column_start=4; } public void createCyanGrids1(){ NowGrids=CyanGrids1; row_number=4; column_number=1; Grids=3; column_start=4; } public void createCyanGrids2(){ NowGrids=CyanGrids2; row_number=1; column_number=4; Grids=3; column_start=3; } public void createMagentaGrids1(){ NowGrids=MagentaGrids1; row_number=2; column_number=3; Grids=4; column_start=3; } public void createMagentaGrids2(){ NowGrids=MagentaGrids2; row_number=3; column_number=2; Grids=4; column_start=4; } public void createMagentaGrids3(){ NowGrids=MagentaGrids3; row_number=2; column_number=3; Grids=4; column_start=3; } public void createMagentaGrids4(){ NowGrids=MagentaGrids4; row_number=3; column_number=2; Grids=4; column_start=4; } public void createBlueGrids1(){ NowGrids=BlueGrids1; row_number=2; column_number=3; Grids=5; column_start=3; } public void createBlueGrids2(){ NowGrids=BlueGrids2; row_number=3; column_number=2; Grids=5; column_start=4; } public void createBlueGrids3(){ NowGrids=BlueGrids3; row_number=2; column_number=3; Grids=5; column_start=3; } public void createBlueGrids4(){ NowGrids=BlueGrids4; row_number=3; column_number=2; Grids=5; column_start=4; } public void createWhiteGrids1(){ NowGrids=WhiteGrids1; row_number=2; column_number=3; Grids=6; column_start=3; } public void createWhiteGrids2(){ NowGrids=WhiteGrids2; row_number=3; column_number=2; Grids=6; column_start=4; } public void createWhiteGrids3(){ NowGrids=WhiteGrids3; row_number=2; column_number=3; Grids=6; column_start=3; } public void createWhiteGrids4(){ NowGrids=WhiteGrids4; row_number=3; column_number=2; Grids=6; column_start=4; } public void createYellowGrids(){ NowGrids=YellowGrids; row_number=2; column_number=2; Grids=7; column_start=4; }
public void createGrids(){ double random = Math.random(); if(random >= 0 && random < 0.2){ if(random >= 0 && random < 0.1) createRedGrids1(); else createRedGrids2(); } else if(random >= 0.2 && random < 0.4){ if(random >= 0.2 && random < 0.3) createGreenGrids1(); else createGreenGrids2(); } else if(random >= 0.4 && random < 0.45){ if(random >= 0.4 &&random < 0.43) createCyanGrids1(); else createCyanGrids2(); } else if(random >= 0.45 && random < 0.6){ if(random >= 0.45 && random < 0.48) createMagentaGrids1(); else if(random >= 0.48 && random < 0.52) createMagentaGrids2(); else if(random >= 0.52 && random < 0.56) createMagentaGrids3(); else createMagentaGrids4(); } else if(random >= 0.6 && random < 0.75){ if(random >= 0.6 && random < 0.63) createBlueGrids1(); else if(random >= 0.63 && random < 0.67) createBlueGrids2(); else if(random >= 0.67 && random < 0.71) createBlueGrids3(); else createBlueGrids4(); } else if(random >= 0.75 && random < 0.9){ if(random >= 0.75 && random < 0.78) createWhiteGrids1(); else if(random >=0.78 && random < 0.82) createWhiteGrids2(); else if(random >=0.82 && random < 0.86) createWhiteGrids3(); else createWhiteGrids4(); } else { createYellowGrids(); } for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0]][NowGrids[row][1]] = Grids; } }
public void initialize(){//部分代码没有贴出,欢迎自行下载附件查看源代码 createGrids(); drawButton(); drawGrids(); }
05
方块自动下落

首先定义一个时间变量 timer,再定义当前下落的行数 Nowrow,当前左右移动的列数 Nowcolumn。
private int Nowrow; private int Nowcolumn; private Timer timer; public void createGrids(){//部分代码没有贴出,欢迎自行下载附件查看源代码 Nowcolumn = 0; Nowrow = 0; }
public boolean down(){ boolean k; if(Nowrow + row_number == 15){ return false; } for(int row = 0; row < grids_number; row++){ k = true; for(int i = 0; i < grids_number; i++){ if(NowGrids[row][0] + 1 == NowGrids[i][0] && NowGrids[row][1] == NowGrids[i][1]){ k = false; } } if(k){ if(grids[NowGrids[row][0] + Nowrow + 1][NowGrids[row][1] + Nowcolumn] != 0) return false; } } return true; }
再创建函数 run(),初始化 timer,增加时间事件,判断当方块能继续下移时则清除当前方块,Nowrow 加 1。
public void run(){ timer=new Timer(); timer.schedule(new TimerTask() { @Override public void run() { getUITaskDispatcher().asyncDispatch(()->{ if(down()){ for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] = 0; } Nowrow++; for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] = Grids; } } else{ createGrids(); } drawGrids(); }); } },0,750); }
public void onStart(Intent intent) {//部分代码没有贴出,欢迎自行下载附件查看源代码 initialize(); run(); }
06
方块左右移动

public boolean left(){ boolean k; if(Nowcolumn + column_start == 0){ return false; } for(int column = 0; column < grids_number; column++){ k = true; for(int j = 0; j < grids_number; j++){ if(NowGrids[column][0] == NowGrids[j][0] && NowGrids[column][1] - 1 == NowGrids[j][1]){ k = false; } } if(k){ if(grids[NowGrids[column][0] + Nowrow][NowGrids[column][1] + Nowcolumn - 1] != 0) return false; } } return true; }
public void leftShift(){ if(left()){ for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] = 0; } Nowcolumn--; for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] = Grids; } } drawGrids(); }
public void rightShift(){ if(right()){ for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] = 0; } Nowcolumn++; for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] = Grids; } } drawGrids(); } public boolean right(){ boolean k; if(Nowcolumn + column_number + column_start==10){ return false; } for(int column = 0; column < grids_number; column++){ k = true; for(int j = 0; j < grids_number; j++){ if(NowGrids[column][0] == NowGrids[j][0] && NowGrids[column][1] + 1 == NowGrids[j][1]){ k = false; } } if(k){ if(grids[NowGrids[column][0] + Nowrow][NowGrids[column][1] + Nowcolumn + 1] != 0) return false; } } return true; }
public void drawButton(){//绘制按钮 button1.setClickedListener(new Component.ClickedListener() { @Override public void onClick(Component component) { leftShift(); } }); button3.setClickedListener(new Component.ClickedListener() { @Override public void onClick(Component component) { rightShift(); } }); }
07
改变方块形状

public void changRedGrids(){ if(NowGrids==RedGrids1){ createRedGrids2(); } else if(NowGrids==RedGrids2){ createRedGrids1(); } } public void changeGreenGrids(){ if(NowGrids==GreenGrids1){ createGreenGrids2(); } else if(NowGrids==GreenGrids2){ createGreenGrids1(); } } public void changeCyanGrids(){ if(NowGrids==CyanGrids1){ createCyanGrids2(); } else if(NowGrids==CyanGrids2){ createCyanGrids1(); } } public void changeMagentaGrids(){ if(NowGrids==MagentaGrids1){ createMagentaGrids2(); } else if(NowGrids==MagentaGrids2){ createMagentaGrids3(); } else if(NowGrids==MagentaGrids3){ createMagentaGrids4(); } else if(NowGrids==MagentaGrids4){ createMagentaGrids1(); } } public void changeBlueGrids(){ if(NowGrids==BlueGrids1){ createBlueGrids2(); } else if(NowGrids==BlueGrids2){ createBlueGrids3(); } else if(NowGrids==BlueGrids3){ createBlueGrids4(); } else if(NowGrids==BlueGrids4){ createBlueGrids1(); } } public void changeWhiteGrids(){ if(NowGrids==WhiteGrids1){ createWhiteGrids2(); } else if(NowGrids==WhiteGrids2){ createWhiteGrids3(); } else if(NowGrids==WhiteGrids3){ createWhiteGrids4(); } else if(NowGrids==WhiteGrids4){ createWhiteGrids1(); } }
public void changGrids(){ for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] = 0; } if(column_number == 2 && Nowcolumn + column_start == 0){ Nowcolumn++; } if(Grids==1){ changRedGrids(); } else if(Grids==2){ changeGreenGrids(); } else if(Grids==3){ changeCyanGrids(); } else if(Grids==4){ changeMagentaGrids(); } else if(Grids==5){ changeBlueGrids(); } else if(Grids==6){ changeWhiteGrids(); } for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] = Grids; } drawGrids(); }
public void drawButton(){//绘制按钮 button2.setClickedListener(new Component.ClickedListener() { @Override public void onClick(Component component) { changGrids(); } }); }
08
方块消除

public void eliminateGrids() { boolean k; for (int row = 14; row >= 0; row--) { k = true; for (int column = 0; column < 10; column++) { if (grids[row][column] == 0) k = false; } if (k) { for (int i = row - 1; i >= 0; i--) { for (int j = 0; j < 10; j++) { grids[i + 1][j] = grids[i][j]; } } for (int n = 0; n < 10; n++) { grids[0][n] = 0; } } } drawGrids(); }
public void createGrids(){//部分代码没有贴出,欢迎自行下载附件查看源代码 Nowcolumn = 0; Nowrow = 0; eliminateGrids(); double random = Math.random(); }
09
游戏结束与重新开始

public void drawText(){ Text text=new Text(this); text.setText("游戏结束"); text.setTextSize(100); text.setTextColor(Color.BLUE); text.setTextAlignment(TextAlignment.CENTER); text.setMarginsTopAndBottom(-2000,0); text.setMarginsLeftAndRight(350,0); layout.addComponent(text); setUIContent(layout); }
public boolean gameover(){ for(int row = 0; row < grids_number; row++){ if(grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] != 0){ return true; } } return false; }
public void createGrids(){//部分代码没有贴出,欢迎自行下载附件查看源代码 if(gameover() == false){ for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] = Grids; } } else{ timer.cancel(); drawText(); } }
public void drawButton(){//部分代码没有贴出,欢迎自行下载附件查看源代码 button.setClickedListener(new Component.ClickedListener() { @Override public void onClick(Component component) { initialize(); run(); } }); }
到此,大功告成啦!
10
结语
以上就是俄罗斯方块小游戏在手机的主要编写思路以及代码,源码将放在附件中,内含详细注释,欢迎大家点击“阅读原文”下载查看学习。

专注开源技术,共建鸿蒙生态

点“阅读原文”下载源码






