【木棉花】#打卡不停更#HarmonyOS小游戏项目——数独Sudoku(5) 原创 精华

木棉花_小蓝
发布于 2022-10-14 10:16
浏览
1收藏

前言

     Hello,各位好久不见!

     非常抱歉,这段时间作者在忙于其他事情,所以一直没有在社区更文。

     时间过得很快,HarmonyOS与OpenHarmony也发展地很快,目前DevEco Studio的鸿蒙SDK也已经更新到API8的版本了。对于鸿蒙操作系统的蒸蒸日上,我感到非常喜悦。

     最近的一段时间,我重新阅读了放在IDE中的数独游戏项目的代码,发现自己曾经写的代码其实挺烂的。事实上,我是在接触了鸿蒙的前端后,才开始尝试去学习Java语言并利用Java创建UI的。所以在写这个项目时,我的Java仍处于比较菜的水平,权限修饰符用的很乱,并且有些代码虽然能运行,但也写的不简洁或者不合理,所以近期我对这个项目的代码进行了许多优化。实际上,作为一个学习者,任何人在编程的初期都不可避免地会写一些烂代码,以及踩一些坑。我们进步的过程,不仅是学习一些未接触过的新知识,更重要的是在之前所写过的代码中不断优化,并在这个过程中不断提炼新的计算机思维。

    另外,因为DevEco studo的SDK8版本已经将Java移除了,这意味着在IDE中用Java代码编译的程序只能运行在SDK7以前的华为机型,所以笔者在更新完关于数独项目的文章后,就暂时不继续分享有关Java的代码了,而是先尝试去学习与分享关于JavaScrip与ets的内容。

     

    上期的内容回顾——>>​https://ost.51cto.com/posts/15252​

正文

 在本期的分享中,笔者将继续完善项目的相关功能,并制作判定游戏是否通关的功能。


定义两个重要的数组

打开GameAbilitySlice,并在合适的位置定义两个二维数组——grids_win和grids_input。注意,这两个数组是作为全局变量定义的,他们不能定义在生命周期函数或其他函数内。

   //定义两个二维数组
    int[][] grids_win=new int[6][];
    int[][] grids_input=new int[6][];

    ......

   @Override
    protected void onStart(Intent intent) {
        super.onStart(intent);
   .......

在这两个新定义的数组变量,grids_win代表储存了数独答案的二维数组,而grids_input代表用户在游戏交互过程中将要改变的数组。这两个数组在项目中主要用于判定游戏的胜利与否,而判断逻辑是这样的:如果grid_input中每行每列的元素都等于grids_win中每行每列的元素,这就意味着用户在数独网格填入的数字与答案一致,此时游戏胜利;否则,游戏未成功。

在定义grids_win与grids_input前,我们在之前的文章中已经定义了grid_c0,而grid_c0用于生成网格区域中的数独题目的。接下来,我们再定义grid_c0数组所对应的答案数组——grid_v0(注意,grid_c0与grid_v0均是定义在onstart函数内的局部变量):

...
@Override
    protected void onStart(Intent intent) {
        super.onStart(intent);

        grid_c0[0]=new int[]{0,0,0,0,1,0};
        ...
       
       //定义grid_v0     
        int[][] grid_v0=new int[6][];
        grid_v0[0]=new int[]{4,3,5,6,1,2};
        grid_v0[1]=new int[]{1,5,2,4,6,3};
        grid_v0[2]=new int[]{6,2,3,1,4,5};
        grid_v0[3]=new int[]{5,4,1,2,3,6};
        grid_v0[4]=new int[]{2,6,4,3,5,1};
        grid_v0[5]=new int[]{3,1,6,5,2,4};

然后,我们再进行如下赋值操作(笔者也惊讶地发现,Java的数组赋值竟然可以如此轻松快捷qwq):

        grids_input=grid_c0;
        grids_win=grid_v0;

这样,grids_input和grids_win就被输入对应的数据了。

或许读者会问,为什么不直接把grid_c0和grid_v0分别作为用户交互的数组答案数组来使用呢?原因是,grid_c0与grid_v0只是分别作为这个游戏的某一个一个关卡的题目与答案,在后文,笔者会导入更多的题目与答案,所以我们需要另外定义grid_input与grid_win作为两个全局变量,当关卡不同时,这两个变量也就分别负责储存不同的题目与答案,以及参与判断游戏是否胜利的逻辑判断过程。



创建三个功能按钮

我们先找到之前创建的6个圆形button对象(即用于在网格区域输入数字的Button组件),并在这6个圆形button对象的监听器内各加入一行指令(即下面代码中“//”前的部分):

//按键区域
        Button button_input1=new Button(this);
        button_input1.setText("1");
        button_input1.setTextColor(Color.BLACK);
        button_input1.setTextSize(75);
        button_input1.setBackground(element2);
        button_input1.setComponentSize(150, 150);
        button_input1.setPosition(70,1600);
        button_input1.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                button_temp.setTextColor(Color.BLACK);
                button_temp.setText("1");
                grids_input[j1][k1]=1;  //为网格区域对应的题目矩阵的相应位置赋值1

            }
        });
        layout1.addComponent(button_input1);


        Button button_input2=new Button(this);
        button_input2.setText("2");
        button_input2.setTextColor(Color.BLACK);
        button_input2.setTextSize(75);
        button_input2.setBackground(element2);
        button_input2.setComponentSize(150, 150);
        button_input2.setPosition(70+160*1,1600);
        button_input2.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                button_temp.setTextColor(Color.BLACK);
                button_temp.setText("2");
                grids_input[j1][k1]=2;//为网格区域对应的题目矩阵的相应位置赋值2

            }
        });
        layout1.addComponent(button_input2);

        Button button_input3=new Button(this);
        button_input3.setText("3");
        button_input3.setTextColor(Color.BLACK);
        button_input3.setTextSize(75);
        button_input3.setBackground(element2);
        button_input3.setComponentSize(150, 150);
        button_input3.setPosition(70+160*2,1600);
        button_input3.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                button_temp.setTextColor(Color.BLACK);
                button_temp.setText("3");
                grids_input[j1][k1]=3;//为网格区域对应的题目矩阵的相应位置赋值3
            }
        });
        layout1.addComponent(button_input3);

        Button button_input4=new Button(this);
        button_input4.setText("4");
        button_input4.setTextColor(Color.BLACK);
        button_input4.setTextSize(75);
        button_input4.setBackground(element2);
        button_input4.setComponentSize(150, 150);
        button_input4.setPosition(70+160*3,1600);
        button_input4.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                button_temp.setText("4");
                grids_input[j1][k1]=4;//为网格区域对应的题目矩阵的相应位置赋值4
            }
        });
        layout1.addComponent(button_input4);

        Button button_input5=new Button(this);
        button_input5.setText("5");
        button_input5.setTextColor(Color.BLACK);
        button_input5.setTextSize(75);
        button_input5.setBackground(element2);
        button_input5.setComponentSize(150, 150);
        button_input5.setPosition(70+160*4,1600);
        button_input5.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                button_temp.setText("5");
                grids_input[j1][k1]=5;//为网格区域对应的题目矩阵的相应位置赋值5
            }
        });
        layout1.addComponent(button_input5);

        Button button_input6=new Button(this);
        button_input6.setText("6");
        button_input6.setTextColor(Color.BLACK);
        button_input6.setTextSize(75);
        button_input6.setBackground(element2);
        button_input6.setComponentSize(150, 150);
        button_input6.setPosition(70+160*5,1600);
        button_input6.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                button_temp.setText("6");
                grids_input[j1][k1]=6;//为网格区域对应的题目矩阵的相应位置赋值6
            }
        });
        layout1.addComponent(button_input6);

        setUIContent(layout1);

    }

指令中的变量j1和变量k1,就是笔者在上期内容中利用Text组件转换所得的的每个网格的位置信息(详细原理详见上期内容)。这样以后,用户每次在某个区域上输入数字(1到6的整数)时,数组grids_input的对应存储位置也会被输入相应的数字元素。


完成上述操作后,我们开始另外创建三个新的按钮,每个按钮都负责不同的功能(如图箭头所指部分):

【木棉花】#打卡不停更#HarmonyOS小游戏项目——数独Sudoku(5)-鸿蒙开发者社区

首先,我们为将要创建的Button对象定义基本的背景元素:

//背景元素
        ...
          
        ShapeElement element4=new ShapeElement();
        element4.setRgbColor(new RgbColor(0,125,225));  //设置Rgb颜色
        element4.setCornerRadius(50);  //设置圆角

然后通过代码布局创建这三个按钮(需要写入的Button对象的方法都差不多,只是一些参数不同):

  //按键区—操作

        Button button_clean=new Button(this);//“清除”按钮

       //基本UI属性方法
        button_clean.setText("清除");
        button_clean.setTextColor(Color.WHITE);
        button_clean.setTextSize(75);
        button_clean.setBackground(element4);
        button_clean.setComponentSize(280, 150);
        button_clean.setPosition(50,1900);

       //设置点击监听器
        button_clean.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                button_temp.setText("");
                grids_input[j1][k1]=0;
            }
        });

    //设置状态改变监听器,让按钮拥有动态效果
        button_clean.setComponentStateChangedListener(new Component.ComponentStateChangedListener() {
            @Override
            public void onComponentStateChanged(Component component, int i) {
                if (ComponentState.isStateMatched(ComponentState.COMPONENT_STATE_PRESSED,i)){
                    button_clean.setComponentSize(290, 160);  //组件处于被点击态时尺寸放大
                }else {
                    button_clean.setComponentSize(280, 150);  //组件不处于被点击态时尺寸恢复初始值
                }
            }
        });
      //将组件放入自定义布局
        layout1.addComponent(button_clean);



        Button button_replay=new Button(this);//"重新开始"按钮
       //基本UI属性方法
        button_replay.setText("重新开始");
        button_replay.setTextColor(Color.WHITE);
        button_replay.setTextSize(75);
        button_replay.setBackground(element4);
        button_replay.setComponentSize(370, 150);
        button_replay.setPosition(50+307,1900);

//设置点击监听器
        button_replay.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                Intent intent_replay=new Intent();
                present(new GameAbilitySlice(),intent_replay);
            }
        });

    //设置状态改变监听器,让按钮拥有动态效果
        button_replay.setComponentStateChangedListener(new Component.ComponentStateChangedListener() {
            @Override
            public void onComponentStateChanged(Component component, int i) {
                if (ComponentState.isStateMatched(ComponentState.COMPONENT_STATE_PRESSED,i)){
                    button_replay.setComponentSize(380,160);
                }else {
                    button_replay.setComponentSize(370, 150);
                }
            }
        });
      //将组件放入自定义布局
        layout1.addComponent(button_replay);


        Button button_pr=new Button(this);//“提交“按钮
       //基本UI属性方法
        button_pr.setText("提交");
        button_pr.setTextColor(Color.WHITE);
        button_pr.setTextSize(75);
        button_pr.setBackground(element4);
        button_pr.setComponentSize(280, 150);
        button_pr.setPosition(50+700,1900);
//设置点击监听器
        button_pr.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {

            }
        });

    //设置状态改变监听器,让按钮拥有动态效果
        button_pr.setComponentStateChangedListener(new Component.ComponentStateChangedListener() {
            @Override
            public void onComponentStateChanged(Component component, int i) {
                if (ComponentState.isStateMatched(ComponentState.COMPONENT_STATE_PRESSED,i)){
                    button_pr.setComponentSize(290, 160);
                }else {
                    button_pr.setComponentSize(280, 150);
                }
            }
        });
      //将组件放入自定义布局
        layout1.addComponent(button_pr);

这样之后,我们就新创建了三个按钮。其中,第一个”清除“按钮用于在网格区域的获焦网格上输入空字符串,这意味着我们实现了清除白色网格内已输入数字的功能。在清除白色网格内的数字的同时,此按钮还将grid_input(即在游戏过程会被用户修改参数的矩阵)对应位置的元素赋值为0,确保grid_input内的对应元素可以在清除功能被执行时更新(否则游之后的游戏判断功能会出bug)。

第二个”重新开始“按钮用于让应用从当前的GameAbilitySlice跳转至另一个新生成的GameAbilitySlice,能够实现页面刷新的功能。由于在之前的文章中,我们在onBackground回调中加入了销毁指令,所以系统由旧的GameAbilitySlice导航至新的GameAbilitySlice时,旧的GameAbilitySlice由于进入background态后会启用onBackground回调,进而被销毁。假如之前我们没有添加销毁指令,那么用户每点击一次”重新开始“的按钮,AbilitySlice的实例栈就会堆放一个GameAbilitySlice,这是非常浪费系统内存资源的。

第三个按钮是”提交“键,我们已经写入了基本的方法,但他的监听器内是未添加任何代码的。接下来我们就在这个button对象的监听器内加入一些指令,以实现判断游戏是否成功的功能。


制作判定游戏成功的函数

首先,我们在合适的位置定义一个布尔型函数:

 import ......


public class GameAbilitySlice extends AbilitySlice {

    ......
    
private boolean Gamesuccess(){
        int a,b;
        for(a=0;a<6;a++){
            for (b=0;b<6;b++){

                if (grids_win[a][b]!=grids_input[a][b]){
                    return false;
                }
            }
        }

        return true;
    }
    
    ......
    
    @Override
    protected void onStart(Intent intent) {
    ......

这个函数是返回布尔值的函数,其封装的代码也非常简单,就是让grid_win矩阵与grid_input矩阵的每个位置的元素进行比较,如果经过循环判断为全部相等,那么这两个矩阵就完全相等,即满足了游戏胜利的条件。值得一提的是,由于此函数封装的代码包含的两个变量——grids_win与grids_input是全局变量,所以笔者并没有将执行不同功能的代码完全模块化,这意味着如果这两个全局变量在某些指令中被修改,Gamesuccess()是有几率受到影响的,这是项目中不足的地方。


接着,我们通过代码布局初始化两个弹窗(弹窗必须定义在 button_pr之前):

        //对话框
        CommonDialog Dialog_win=new CommonDialog(getContext());
        Dialog_win.setSize(800,400);
        Dialog_win.setTitleText(" 游戏解答成功!");
        Dialog_win.setButton(IDialog.BUTTON1,"返回主菜单",(iDialog, i) -> Dialog_win.destroy());


        CommonDialog Dialog_fail=new CommonDialog(getContext());
        Dialog_fail.setSize(800,400);
        Dialog_fail.setTitleText(" 啊哦~没有通过哦");
        Dialog_fail.setContentText("                  游戏未成功解答");
        Dialog_fail.setButton(IDialog.BUTTON1,"继续游戏",(iDialog, i) -> Dialog_fail.destroy());

最后,我们在“提交”按钮的监听器内加入指令:

        Button button_pr=new Button(this);
  	    ......
        button_pr.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                   //加入指令
                   if (Gamesuccess()){
                   Dialog_win.show();//成功
               }else {
                      //加入指令
                   Dialog_fail.setContentText("           游戏未完成或答案不正确" );
                   Dialog_fail.show();//失败
               }
              
              
              
            }
        });
         ......
        layout1.addComponent(button_pr);

可以看到,如果Gamesuccess()函数返回的值是true,那么系统就会调用Dialog_win的show()方法,将Dialog_win展示在屏幕前以提示游戏成功;如果返回的值是false,系统则调用 Dialog_fail的show()方法,将Dialog_fail展示在屏幕前以提示游戏未成功。这样一来,游戏成功与否的判断逻辑便得以成功搭建。



经过上述的所有操作后,我们可以打开API为6的模拟机试玩,效果图如下:


【木棉花】#打卡不停更#HarmonyOS小游戏项目——数独Sudoku(5)-鸿蒙开发者社区

【木棉花】#打卡不停更#HarmonyOS小游戏项目——数独Sudoku(5)-鸿蒙开发者社区


因为SDK6版本的P40模拟机在近期被停用了,所以我只能用平板模拟机调试程序(/(ㄒoㄒ)/~~),图片看起来可能有点怪怪的。

结语

本期的内容就先分享到这里,更多关于数独小游戏项目精彩的内容我将在下期继续为大家揭晓。

【木棉花】#打卡不停更#HarmonyOS小游戏项目——数独Sudoku(5)-鸿蒙开发者社区



©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
已于2022-10-14 18:08:20修改
2
收藏 1
回复
举报
2条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

不断的审视过去的自己,也是提升自己的好方法,向楼主学习

回复
2022-10-14 10:23:13
一緑向北
一緑向北

Java和ets都要会一点,开发更方便

回复
2022-10-14 14:54:20
回复
    相关推荐