從零開發HarmonyOS(鴻蒙)手機小遊戲——數字華容道

前言

2月16號HarmonyOS2.0手機開發者Beta版已經發布了,做爲「1+8+N」戰略的重要入口和生態核心,怎麼能少得了手機應用開發呢,今天將由深鴻會深大學習小組(Zzt_01-23)從零基礎開發第一個HarmonyOS手機小遊戲——數字華容道(界面略醜陋,大佬別噴),此前已經在運動手錶上成功開發了:HarmonyOS運動手錶遊戲合併HarmonyOS手錶遊戲——數字華容道,一樣是深鴻會深大小組學習完HarmonyOS後自行開發的一個鴻蒙demo,詳細講述了數字華容道在鴻蒙手機上開發思路。深鴻會深大學習小組是一羣熱衷於學習鴻蒙相關知識和開發鴻蒙相關應用的開發者們,咱們的學習項目爲:荔園Harmony,同時也歡迎與各位感興趣的讀者一塊兒學習HarmonyOS開發,相互交流、共同進步。java

概述

本個demo將從零基礎開始完成鴻蒙小遊戲APP在手機上的編譯在項目中咱們所使用到的軟件爲DevEco Studio,下載地址爲:DevEco Studio下載DevEco Studio安裝教程,在項目中咱們要實現的內容爲數字華容道APP的開發。android

  1. 打開引用首先爲數字華容道的初始界面,點擊開始遊戲即會切換到數字華容道的遊戲界面。
    在這裏插入圖片描述
  2. 進入數字華容道的遊戲界面顯示4*4的方陣,方陣中分佈有隨意打亂的1至15的數字和一個空白方格,方陣下方顯示一個「從新開始」的按鈕和一個「返回」按鈕,點擊「從新開始」按鈕即會從新生成隨意打亂的1至15的數字和一個空白方格的方陣,點擊「返回」按鈕即會切換到數字華容道的初始界面,最下方有四個指示不一樣方向箭頭的按鈕,點擊任一按鈕或向上、下、左、右任一方向滑動,空白方格周圍對應位置的方格便會隨之向對應的方向移動一格。
    在這裏插入圖片描述
  3. 通過若干次滑動或點擊後,當全部的數字按順序排列後,則會彈出遊戲成功的界面,再滑動或點擊也不會有任何變化。
    在這裏插入圖片描述

正文

建立項目

DevEco Studio下載安裝成功後,打開DevEco Studio,點擊左上角的File,點擊New,再選擇New Project,選擇Phone選項,選擇默認的模板(java版),而後選擇保存路徑,將文件命名爲MyPhoneApplication(文件名不能出現中文或者特殊字符,不然將沒法成功建立項目文件),最後點擊Finish。
在這裏插入圖片描述
在這裏插入圖片描述git

實現初始界面佈局

首先,咱們要先實現數字華容道的初始界面,點擊開始遊戲即會切換到另外一個空白的界面。
在這裏插入圖片描述json

  1. 先在entry>src>main>config.json文件中最下方"launchType": 「standard"的後面添加如下代碼,而且將上方的「label」:「MyPhoneApplication」修改爲"label」: 「數字華容道」,這樣就實現去掉應用上方的標題欄和將應用名稱改成數字華容道了
    config.json最下面部分代碼:
"orientation": "unspecified",
        "name": "com.example.myphoneapplication.MainAbility",
        "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": ""
            }
          ]
        }
  1. 先將咱們事先準備好的圖片複製粘貼到entry>src>main>resources>base>media文件夾中(ctrl+c、ctrl+v複製粘貼),而且命名爲game,點擊OK
    在這裏插入圖片描述
    在entry>src>main>resources>base>layout>ability_main.xml中添加布局,先將事先存在的Text組件刪去,添加Image圖片組件,引入咱們剛纔複製粘貼的圖片,再添加一個Button按鈕組件,加入惟一標識符id並配置好其餘相應的屬性
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_parent" ohos:width="match_parent" ohos:orientation="vertical">

    <Image ohos:height="match_parent" ohos:width="match_parent" ohos:image_src="$media:game" ohos:layout_alignment="center" />

    <Button ohos:id="$+id:button_game" ohos:height="150" ohos:width="515" ohos:text_alignment="center" ohos:top_margin="-810" ohos:left_margin="250" />

</DirectionalLayout>
  1. 在entry>src>main>java>com.example.myphoneapplication>slice中右鍵選擇New>Java Class增長一個空白的類以用來後面編寫數字華容道的遊戲界面,而且命名爲SecondAbilitySlice在這裏插入圖片描述
    將entry>src>main>java>com.example.myphoneapplication>slice>SecondAbilitySlice中的代碼修改爲以下:
package com.example.myphoneapplication.slice;

import com.example.myphoneapplication.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;

public class SecondAbilitySlice extends AbilitySlice { 

    public void onStart(Intent intent) { 
        super.onStart(intent);
        
    }

    @Override
    public void onActive() { 
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) { 
        super.onForeground(intent);
    }
}
  1. 在entry>src>main>java>com.example.myphoneapplication>slice>MainAbilitySlice中的onStart函數中添加一個按鈕指向咱們(2)中添加的按鈕,按鈕添加一個響應點擊事件的函數,用parsent函數跳轉到SecondAbilitySlice
package com.example.myphoneapplication.slice;

import com.example.myphoneapplication.ResourceTable;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;

public class MainAbilitySlice extends ohos.aafwk.ability.AbilitySlice { 
    @Override
    public void onStart(Intent intent) { 
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);

        Button button = (Button) findComponentById(ResourceTable.Id_button_game);
        button.setClickedListener(new Component.ClickedListener() { 
            @Override
            public void onClick(Component component) { 
                present(new SecondAbilitySlice(),new Intent());
            }
        });

    }

    @Override
    public void onActive() { 
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) { 
        super.onForeground(intent);
    }
}

至此,這一部分就完成了。canvas

實現數字的隨機打亂

而後咱們要在數字華容道的遊戲界面生成隨意打亂的1至15的數字和一個空白方格的方陣。
在這裏插入圖片描述
在entry>src>main>java>com.example.myphoneapplication>slice>SecondAbilitySlice編寫代碼
先定義個一個位置佈局layout和一個二維數組grids,建立函數initializeinitialize()分別對其初始化,在onStart函數中調用函數initializeinitialize()數組

private float starX, starY, distanceX, distanceY;
    private DirectionalLayout layout;
    private int[][] grids;

    public void onStart(Intent intent) { 
        super.onStart(intent);

        initialize();
    }

    public void initialize(){ 
        layout = new DirectionalLayout(this);
        grids = new int[][]{ { 1, 2, 3, 4}, { 5, 6, 7, 8,}, { 9, 10, 11, 12}, { 13, 14, 15, 0}};
    }

而後定義函數drawGrids(int[][] grids)用於繪製4*4方陣和其二維數組對應的數字app

public void drawGrids(int[][] grids){ 
        layout.setLayoutConfig((new ComponentContainer.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT,ComponentContainer.LayoutConfig.MATCH_PARENT)));

        Component.DrawTask task=new Component.DrawTask() { 
            public void onDraw(Component component, Canvas canvas) { 
                Paint mPaint = new Paint();

                mPaint.setColor(Color.GRAY);
                RectFloat rect=new RectFloat(2,230,1078,1306);
                canvas.drawRect(rect,mPaint);

                for(int row = 0; row < 4; row++){ 
                    for(int column = 0; column < 4; column++){ 
                        mPaint.setColor(Color.CYAN);
                        RectFloat rectFloat=new RectFloat(22+column*262,250+row*262,272+column*262,500+row*262);
                        canvas.drawRect(rectFloat,mPaint);

                        mPaint.setColor(Color.YELLOW);
                        mPaint.setTextSize(125);
                        if(grids[row][column]!=0){ 
                            if(grids[row][column]<10){ 
                                canvas.drawText(mPaint, String.valueOf(grids[row][column]),105+column*262,425+row*262);
                            }
                            else{ 
                                canvas.drawText(mPaint, String.valueOf(grids[row][column]),65+column*262,425+row*262);
                            }
                        }
                    }
                }
            }
        };
        layout.addDrawTask(task);
        setUIContent(layout);
    }

再定義函數changeGrids(int[][] grids,int direction),每次接收一個方向,2表示上移,-1表示左移,1表示右移,-2表示下移,找出空白方格所在位置對應的二維數組下標,對應的方格和空白方格對應的二維數組的數值對調dom

public void changeGrids(int[][] grids,int direction){ 
        int row_0 = 3;
        int column_0 = 3;
        int temp;
        for(int row = 0; row < 4; row++) { 
            for (int column = 0; column < 4; column++) { 
                if(grids[row][column] == 0){ 
                    row_0 = row;
                    column_0 = column;
                }
            }
        }
        if(direction == -1 && (column_0 + 1) <= 3){ 
            temp = grids[row_0][column_0 + 1];
            grids[row_0][column_0 + 1] = grids[row_0][column_0];
            grids[row_0][column_0] = temp;
        }else if (direction == 1 && (column_0 - 1) >= 0) { 
            temp = grids[row_0][column_0 - 1];
            grids[row_0][column_0 - 1] = grids[row_0][column_0];
            grids[row_0][column_0] = temp;
        } else if (direction == 2 && (row_0 + 1) <= 3) { 
            temp = grids[row_0 + 1][column_0];
            grids[row_0 + 1][column_0] = grids[row_0][column_0];
            grids[row_0][column_0] = temp;
        } else if (direction == -2 && (row_0 - 1) >= 0) { 
            temp = grids[row_0 - 1][column_0];
            grids[row_0 - 1][column_0] = grids[row_0][column_0];
            grids[row_0][column_0] = temp;
        }
    }

定義函數createGrids(int[][] grids)用於隨機生成一個表示方向的數字,循環調用函數changeGrids(grids,direction)用於隨機打亂二維數組對應的數字ide

public void createGrids(int[][] grids){ 
        int[] array = { -1,-2,1,2};

        for(int i = 0; i < 100; i++){ 
            int random = (int)Math.floor(Math.random()*4);
            int direction = array[random];
            changeGrids(grids,direction);
        }
    }
最後在initialize()函數中調用createGrids(grids)函數和drawGrids(grids)函數

public void initialize(){ 
        layout = new DirectionalLayout(this);
        grids = new int[][]{ { 1, 2, 3, 4}, { 5, 6, 7, 8,}, { 9, 10, 11, 12}, { 13, 14, 15, 0}};
        createGrids(grids);
        drawGrids(grids);
    }

至此,這一部分完成了。函數

實現滑動或點擊調換數字

添加「從新開始」和「返回」按鈕,在最下方添加四個指示不一樣方向箭頭的按鈕,點擊任一按鈕或向上、下、左、右任一方向滑動,空白方格周圍對應位置的方格便會隨之向對應的方向移動一格。
在這裏插入圖片描述
在entry>src>main>java>com.example.myphoneapplication>slice>SecondAbilitySlice編寫代碼
先定義一個函數drawButton()用於繪製全部的按鈕,四個指示不一樣方向箭頭的按鈕分別添加四個響應點擊事件的函數,分別調用對應的changeGrids(grids,direction)函數實現空白方格周圍對應位置的方格便會隨之向對應的方向移動一格,並調用drawGrids(grids)函數用於繪製新的方陣

public void drawButton(){ 

        Button button=new Button(this);
        button.setText("從新開始");
        button.setTextSize(100);
        button.setTextAlignment(TextAlignment.CENTER);
        button.setTextColor(Color.WHITE);
        button.setMarginTop(1400);
        button.setMarginLeft(80);
        button.setPadding(20,20,20,20);
        ShapeElement background = new ShapeElement();
        background.setRgbColor(new RgbColor(174, 158, 143));
        background.setCornerRadius(100);
        button.setBackground(background);
        layout.addComponent(button);

        Button button0=new Button(this);
        button0.setText("返回");
        button0.setTextSize(100);
        button0.setTextAlignment(TextAlignment.CENTER);
        button0.setTextColor(Color.WHITE);
        button0.setMarginTop(-170);
        button0.setMarginLeft(680);
        button0.setPadding(20,20,20,20);
        button0.setBackground(background);
        layout.addComponent(button0);


        ShapeElement background0 = new ShapeElement();
        background0.setRgbColor(new RgbColor(174, 158, 143));
        background0.setCornerRadius(100);

        Button button1=new Button(this);
        button1.setText("↑");
        button1.setTextAlignment(TextAlignment.CENTER);
        button1.setTextColor(Color.WHITE);
        button1.setTextSize(100);
        button1.setMarginLeft(500);
        button1.setMarginTop(70);
        button1.setPadding(10,0,10,0);
        button1.setBackground(background0);
        button1.setClickedListener(new Component.ClickedListener() { 
            @Override
            public void onClick(Component component) { 
                 changeGrids(grids,2);
            }
        });
        layout.addComponent(button1);

        Button button2=new Button(this);
        button2.setText("←");
        button2.setTextAlignment(TextAlignment.CENTER);
        button2.setTextColor(Color.WHITE);
        button2.setTextSize(100);
        button2.setMarginTop(10);
        button2.setMarginLeft(400);
        button2.setPadding(10,0,10,0);
        button2.setBackground(background0);
        button2.setClickedListener(new Component.ClickedListener() { 
            @Override
            public void onClick(Component component) { 
                changeGrids(grids,-1);
            }
        });
        layout.addComponent(button2);

        Button button3=new Button(this);
        button3.setText("→");
        button3.setTextAlignment(TextAlignment.CENTER);
        button3.setTextColor(Color.WHITE);
        button3.setTextSize(100);
        button3.setMarginLeft(600);
        button3.setMarginTop(-130);
        button3.setPadding(10,0,10,0);
        button3.setBackground(background0);
        button3.setClickedListener(new Component.ClickedListener() { 
            @Override
            public void onClick(Component component) { 
                 changeGrids(grids,1);
            }
        });
        layout.addComponent(button3);

        Button button4=new Button(this);
        button4.setText("↓");
        button4.setTextAlignment(TextAlignment.CENTER);
        button4.setTextColor(Color.WHITE);
        button4.setTextSize(100);
        button4.setMarginLeft(500);
        button4.setMarginTop(10);
        button4.setPadding(10,0,10,0);
        button4.setBackground(background0);
        button4.setClickedListener(new Component.ClickedListener() { 
            @Override
            public void onClick(Component component) { 
                changeGrids(grids,-2);
            }
        });
        layout.addComponent(button4);

        drawGrids(grids);
    }

而後添加一個函數slideGrids()爲佈局layout添加一個滑動事件,並獲取滑動開始與結束的座標,並計算出大體的滑動方向,分別調用對應的changeGrids(grids,direction)函數實現空白方格周圍對應位置的方格便會隨之向對應的方向移動一格,並調用drawGrids(grids)函數用於繪製新的方陣,並在開頭添加相應的變量

private float starX, starY, distanceX, distanceY;

public void slideGrids(){ 
        layout.setTouchEventListener(new Component.TouchEventListener() { 
            @Override
            public boolean onTouchEvent(Component component, TouchEvent touchEvent) { 
                MmiPoint point = touchEvent.getPointerScreenPosition(0);

                switch (touchEvent.getAction()) { 
                    case TouchEvent.PRIMARY_POINT_DOWN:
                        starX = point.getX();
                        starY = point.getY();
                        break;
                    case TouchEvent.PRIMARY_POINT_UP:
                        distanceX = point.getX() - starX;
                        distanceY = point.getY() - starY;
                        break;
                }
                if (gameover() == false){ 
                    if (Math.abs(distanceX) > Math.abs(distanceY)) { 
                        if (distanceX > 200) { 
                            changeGrids(grids,1);
                        } else if (distanceX < -200) { 
                            changeGrids(grids,-1);

                        }
                    } else if (Math.abs(distanceX) < Math.abs(distanceY)){ 
                        if (distanceY > 200) { 
                            changeGrids(grids,-2);
                        } else if (distanceY < -200) { 
                            changeGrids(grids,2);
                        }
                    }
                }
                drawGrids(grids);

                return false;
            }
        });
    }

最後在initialize()函數中調用slideGrids()函數和drawButton()函數

public void initialize(){ 
    layout = new DirectionalLayout(this);
    grids = new int[][]{ { 1, 2, 3, 4}, { 5, 6, 7, 8,}, { 9, 10, 11, 12}, { 13, 14, 15, 0}};
    createGrids(grids);
    slideGrids();
    drawButton();
    drawGrids(grids);
}

至此,這一部分完成了

實現遊戲成功界面

點擊「從新開始」按鈕即會從新生成隨意打亂的1至15的數字和一個空白方格的方陣,點擊「返回」按鈕即會切換到數字華容道的初始界面,通過若干次滑動或點擊後,當全部的數字按順序排列後,則會彈出遊戲成功的界面,再滑動或點擊也不會有任何變化。在這裏插入圖片描述
在entry>src>main>java>com.example.myphoneapplication>slice>SecondAbilitySlice編寫代碼
首先定義一個函數drawText()用於繪製遊戲成功字樣

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);
    }

而後定義一個函數gameover()用於判斷二維數組的數字是否按順序排列,當二維數組的數字按順序排列時返回true,不然返回false

public boolean gameover() { 
        int[][] gameoverGrids = { { 1, 2, 3, 4}, { 5, 6, 7, 8,}, { 9, 10, 11, 12}, { 13, 14, 15, 0}};
        for (int row = 0; row < 4; row++) { 
            for (int column = 0; column < 4; column++) { 
                if (grids[row][column] != gameoverGrids[row][column]) { 
                    return false;
                }
            }
        }

        return true;
    }

再在drawButton()函數中從新開始按鈕中添加一個響應點擊事件的函數,用於調用函數initialize()實現從新生成隨意打亂的1至15的數字和一個空白方格的方陣,返回按鈕中添加一個響應點擊事件的函數,用parsen函數返回數字華容道的初始界面,四個指示不一樣方向箭頭的按鈕的響應點擊事件的函數中增長一個判斷,當函數gameover()返回爲false時才調用各自的changeGrids(grids,direction)函數,最後增長一個判斷,當函數gameover()返回爲true時調用函數drawText()

public void drawButton(){ //部分代碼沒有貼出,可自行下載源代碼查看

        button.setClickedListener(new Component.ClickedListener() { 
            @Override
            public void onClick(Component component) { 
                initialize();
            }
        });

        button0.setClickedListener(new Component.ClickedListener() { 
            @Override
            public void onClick(Component component) { 
                present(new SecondAbilitySlice(),new Intent());
            }
        });
    
        button1.setClickedListener(new Component.ClickedListener() { 
            @Override
            public void onClick(Component component) { 
                if (gameover() == false){ 
                    changeGrids(grids,2);
                }
            }
        });
        
        button2.setClickedListener(new Component.ClickedListener() { 
            @Override
            public void onClick(Component component) { 
                if (gameover() == false){ 
                    changeGrids(grids,-1);
                }
            }
        });
        
        button3.setClickedListener(new Component.ClickedListener() { 
            @Override
            public void onClick(Component component) { 
                if (gameover() == false){ 
                    changeGrids(grids,1);
                }
            }
        });

        button4.setClickedListener(new Component.ClickedListener() { 
            @Override
            public void onClick(Component component) { 
                if (gameover() == false){ 
                    changeGrids(grids,-2);
                }
            }
        });

        if(gameover()){ 
            drawText();
        }
    }

在函數slideGrids()函數中增長一個判斷,當函數gameover()返回爲false時才調用changeGrids(grids,direction)函數,最後增長一個判斷,當函數gameover()返回爲true時調用函數drawText()

public void slideGrids(){ //部分代碼沒有貼出,可自行下載源代碼查看
                if (gameover() == false){ 
                    //{...}
                }
                if(gameover()){ 
                    drawText();
                }
    }

至此,整個demo所有完成了。

源代碼

源代碼下載連接:源代碼下載

結語

以上就是數字華容道小遊戲在手機的主要編寫思路以及代碼,源碼將放在附件中,歡迎你們下載,感興趣的讀者能夠自行跟着編寫學習,相信大家也可以完成的。更多深鴻會深大小組學習項目能夠查看荔園Harmony,若是有遇到什麼問題,或者查找出其中的錯誤之處,或者可以優化代碼和界面,也歡迎各位在評論區留言討論,讓咱們一塊兒學習進步!

相關文章
相關標籤/搜索