【木棉花】学习笔记--分布式数字华容道(下) 原创 精华

木棉花沈泳鑫
发布于 2021-10-6 13:17
浏览
8收藏

前言

我又来了。最近比较忙,导致这一篇文章拖了好久还没写,只能趁着国庆放假抽空写一写了。继前两篇文章的铺垫,我们这一篇文章就来实现分布式数字华容道的双人模式。前面的请参考学习笔记–分布式数字华容道(中)
那为了大家更好地熟练掌握鸿蒙手机应用开发,为了供大家更方便的学习鸿蒙手机的应用开发,我会将所有的笔记都整理到Awesome-HarmonyOS_木棉花,更多详细学习资料请查看Awesome-HarmonyOS_木棉花。也请关注我们这个项目,我们会持续更新的,会帮大家把资料都整理好更便于大家的学习。
那现在就先开始我们今天的学习吧。
【木棉花】学习笔记--分布式数字华容道(下)-鸿蒙开发者社区


正文

那我们在双人模式中呢,怎么进行同步就是一个最重要的问题了。那我们这个游戏呢,就运用分布式数据库来进行我们两台设备数据的同步了。
那要实现出一个分布式数据库的话,我们就需要先定义一些变量。

    private static final HiLogLabel TAG = new HiLogLabel(HiLog.LOG_APP,0x12345,"signal");
    private static int difficulty = 3;
    private DirectionalLayout layout;
    private static int length;
    private static final int interval = 5;
    private static final int left = 32;
    private static final int top = 300;
    private static final int margin = 15;
    private static int length1;
    private static final int left1 = 32;
    private static final int top1 = 1350;
    private static final int margin1 = 10;
    private static int row_a0;
    private static int column_a0;
    private static int row_b0;
    private static int column_b0;
    private float startX;
    private float startY;
    private static Timer timer;
    private static Timer updatetimer;
    private static Timer restarttimer;
    private static Timer backtimer;
    private static Text timetext ;
    private static Text maxtext;
    private static Text wintext;
    private static int hour;
    private static int min;
    private static int sec;
    private static int msec;
    private static int restart = 0;
    private static int back = 0;
    private static int maxhour = 23;
    private static int maxmin = 59;
    private static int maxsec = 59;
    private static int maxmsec = 99;
    private static int[][] grids_a;
    private static int[][] grids_b;
    private static final String STROE_ID = "data";
    private static String randomstr = "";
    private static String winner = "";
    private static KvManager kvManager;
    private static String strhour = "";
    private static String strmin = "";
    private static String strsec = "";
    private static String strmsec = "";
    private static String text = "暂停";
    private SingleKvStore singleKvStore;
    private static Button button_moveback;
    private static Boolean isLocal;

接下来就是写一个分布式数据库

    private KvManager createManger() {//辅助类
        KvManager manager = null;
        try{
            KvManagerConfig config = new KvManagerConfig(this);
            manager = KvManagerFactory.getInstance().createKvManager(config);
        } catch (KvStoreException exception) {
            HiLog.info(TAG,"ERROR");
        }
        return manager;
    }

    private SingleKvStore createDb(KvManager kvManager) {//数据库
        SingleKvStore kvStore = null;
        try{
            Options options = new Options();
            options.setCreateIfMissing(true).setEncrypt(false).setKvStoreType(KvStoreType.SINGLE_VERSION);
            kvStore = kvManager.getKvStore(options,STROE_ID);
        } catch (KvStoreException exception) {
            HiLog.info(TAG,"ERROR");
        }
        return kvStore;
    }
    private void subscribeDb(SingleKvStore singleKvStore) {//订阅
        class KvStoreObserveClient implements KvStoreObserver {
            @Override
            public void onChange(ChangeNotification notification) {
                
            }
        }
        KvStoreObserver kvStoreObserverClient = new KvStoreObserveClient();
        singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_ALL,kvStoreObserverClient);
    }

    private void initDbManager() {
        kvManager = createManger();
        singleKvStore = createDb(kvManager);
        subscribeDb(singleKvStore);
    }

再写两个写入数据库的函数还有读取的函数。

    private int queryContact_int(String key) {
        try {
            return singleKvStore.getInt(key);
        } catch (KvStoreException exception) {
            HiLog.info(TAG,"int error" + exception.getMessage());
            return -1;
        }
    }
    private String queryContact_String(String key) {
        try {
            return singleKvStore.getString(key);
        } catch (KvStoreException exception) {
            HiLog.info(TAG,"String error" + exception.getMessage());
            return null;
        }
    }

    private void writeData_String(String key , String value) {
        if (key == null || key.isEmpty() || value == null || value.isEmpty())
            return;
        singleKvStore.putString(key,value);
    }
    private void writeData_int(String key,int value) {
        if (key == null || key.isEmpty()) {
            return;
        }
        singleKvStore.putInt(key,value);
    }

那接下里我们就写一个在数据库中读取记录的函数,当开始游戏的时候,会读取该阶数中的最快的记录。

    public void getrecord() {
        if (queryContact_int("hour" + difficulty) != -1)
            maxhour = queryContact_int("hour" + difficulty);
        else
            maxhour = 23;
        if (queryContact_int("min" + difficulty) != -1)
            maxmin = queryContact_int("min" + difficulty);
        else
            maxmin = 59;
        if (queryContact_int("sec" + difficulty) != -1)
            maxsec = queryContact_int("sec" + difficulty);
        else
            maxsec = 59;
        if (queryContact_int("msec" + difficulty) != -1)
            maxmsec = queryContact_int("msec" + difficulty);
        else
            maxmsec = 99;
    }

接下来我们就开始初始化我们的数组了,然后把一台设备初始化完了的数组写到数据库中,让另外一台设备来读取数据库中的数据,再用来初始化该设备的数组。

    public void createGrids() {
        randomstr = "";
        if (isLocal) {
            int random;
            int i = 0;
            while(i < difficulty * difficulty * 5) {
                random = (int)Math.floor(Math.random() *4 );
                randomstr += random;

                int temp_row = row_a0;
                int tem_column = column_a0;

                if(random == 0){
                    changeGrids(row_a0 - 1, column_a0);
                }else if(random == 1){
                    changeGrids(row_a0 + 1, column_a0);
                }else if(random == 2){
                    changeGrids(row_a0, column_a0 - 1);
                }else if(random == 3){
                    changeGrids(row_a0, column_a0 + 1);
                }
                if(temp_row != row_a0 || tem_column != column_a0){
                    i++;
                }
            }
            HiLog.info(TAG,randomstr);
            writeData_String("randomstr",randomstr);
        } else {
            while (true) {
                if (queryContact_String("randomstr") != null && !queryContact_String("randomstr").isEmpty()) {
                    break;
                }
            }
            randomstr = queryContact_String("randomstr");
            HiLog.info(TAG,"randomstr: "+randomstr);
            int random;
            int i = 0;
            while(i < randomstr.length()) {
                random = randomstr.charAt(i) - '0';
                if(random == 0){
                    changeGrids(row_a0 - 1, column_a0);
                }else if(random == 1){
                    changeGrids(row_a0 + 1, column_a0);
                }else if(random == 2){
                    changeGrids(row_a0, column_a0 - 1);
                }else if(random == 3){
                    changeGrids(row_a0, column_a0 + 1);
                }
                i++;
            }
            singleKvStore.putString("randomstr","");
        }
        for (int row = 0;row < difficulty; row++) {
            for (int column = 0;column <difficulty; column++) {
                grids_b[row][column] = grids_a[row][column];
            }
        }
        row_b0 = row_a0;
        column_b0 = column_a0;
        HiLog.info(TAG,"row_a0: "+ row_a0 + " column_a0: " + column_a0);
    }

那接下去就是把数组中的数字给画出来了。

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

                Color backcolor = new Color(Color.rgb(151,75,49));
                paint.setColor(backcolor);
                RectFloat rect = new RectFloat(left - margin, top - margin, length * difficulty + interval * (difficulty - 1) + left + margin, length * difficulty + interval * (difficulty - 1) + top + margin);
                canvas.drawRect(rect, paint);

                for (int row = 0; row < difficulty; row++) {
                    for (int column = 0; column < difficulty; column++) {
                        Color backgroundcolor = new Color(Color.rgb(229,188,132));
                        paint.setColor(backgroundcolor);
                        RectFloat rectFloat = new RectFloat(left + column * (length + interval), top + row * (length + interval), left + length + column * (length + interval), top + length + row * (length + interval));
                        canvas.drawRect(rectFloat, paint);

                        Color numbercolor = new Color(Color.rgb(140,85,47));
                        paint.setColor(numbercolor);
                        paint.setTextSize(length / 2);
                        if(grids_a[row][column] != 0){
                            if(grids_a[row][column] < 10){
                                canvas.drawText(paint,Integer.toString(grids_a[row][column]),left + column * (length + interval) + length / 12 * 5,top + row * (length + interval) + length / 3 * 2);
                            }else{
                                canvas.drawText(paint,Integer.toString(grids_a[row][column]),left + column * (length + interval) + length / 12 * 3,top + row * (length + interval) + length / 3 * 2);
                            }
                        }
                    }
                }

                paint.setColor(backcolor);
                length1 = 600 / difficulty - interval;
                RectFloat rect1= new RectFloat(left1 - margin1, top1 - margin1, length1 * difficulty + interval * (difficulty - 1) + left1 + margin1, length1 * difficulty + interval * (difficulty - 1) + top1 + margin1);
                canvas.drawRect(rect1, paint);
                for (int row = 0; row < difficulty; row++) {
                    for (int column = 0; column < difficulty; column++) {
                        Color backgroundcolor1 = new Color(Color.rgb(229,188,132));
                        paint.setColor(backgroundcolor1);
                        RectFloat rectFloat = new RectFloat(left1 + column * (length1 + interval), top1 + row * (length1 + interval), left1 + length1 + column * (length1 + interval), top1 + length1 + row * (length1 + interval));
                        canvas.drawRect(rectFloat, paint);

                        Color numbercolor1 = new Color(Color.rgb(140,85,47));
                        paint.setColor(numbercolor1);
                        paint.setTextSize(length1 / 2);
                        if(grids_b[row][column] != 0){
                            if(grids_b[row][column] < 10){
                                canvas.drawText(paint,Integer.toString(grids_b[row][column]),left1 + column * (length1 + interval) + length1 / 12 * 5,top1 + row * (length1 + interval) + length1 / 3 * 2);
                            }else{
                                canvas.drawText(paint,Integer.toString(grids_b[row][column]),left1 + column * (length1 + interval) + length1 / 12 * 3,top1 + row * (length1 + interval) + length1 / 3 * 2);
                            }
                        }
                    }
                }
            }
        };

        layout.addDrawTask(task);
        setUIContent(layout);
    }

接下来就是把我们的时间还有按钮这些给画出来了。

    public void draw() {
        maxtext = new Text(this);
        setMaxtext();
        maxtext.setTextSize(80);
        maxtext.setMarginTop(40);
        maxtext.setMarginLeft(140);
        layout.addComponent(maxtext);

        timetext = new Text(this);
        timetext.setText("time: 00:00:00:00");
        timetext.setTextSize(80);
        timetext.setMarginTop(10);
        timetext.setMarginLeft(230);
        layout.addComponent(timetext);

        ShapeElement background = new ShapeElement();
        background.setRgbColor(new RgbColor(138,70,50));
        background.setCornerRadius(100);

        Button button_again = new Button(this);
        button_again.setText("重新开始");
        button_again.setTextAlignment(TextAlignment.CENTER);
        button_again.setTextColor(Color.WHITE);
        button_again.setTextSize(100);
        button_again.setMarginTop(1150);
        button_again.setMarginLeft(730);
        button_again.setPadding(30, 0, 30, 0);
        button_again.setBackground(background);
        button_again.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                getrecord();
                writeData_int("restart",1);
            }
        });
        layout.addComponent(button_again);

        Button button_back = new Button(this);
        button_back.setText("返回");
        button_back.setTextAlignment(TextAlignment.CENTER);
        button_back.setTextColor(Color.WHITE);
        button_back.setTextSize(100);
        button_back.setMarginLeft(730);
        button_back.setMarginTop(90);
        button_back.setPadding(30, 0, 30, 0);
        button_back.setBackground(background);
        button_back.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {

            }
        });
        layout.addComponent(button_back);

        setUIContent(layout);
    }

【木棉花】学习笔记--分布式数字华容道(下)-鸿蒙开发者社区
这就是我们的效果图,你们也可以运行查看一下是否完全一样。保证一样了之后才继续跟着一步一步慢慢走。
我们这已经算是完成了最开始同步的问题了,这算是比较重要的一个问题了,那我们接下来就要继续先完成我们的游戏功能,那在完成游戏功能之前,我们需要先写一个判断游戏是否成功的函数。

    public boolean gamesuccess() {
        int[][] Grids = new int[difficulty][difficulty];
        for (int row = 0; row < difficulty; row++){
            for (int column = 0; column < difficulty; column++){
                Grids[row][column] = difficulty * row + column + 1;
            }
        }
        Grids[difficulty - 1][difficulty - 1] = 0;

        for (int row = 0; row < difficulty; row++){
            for (int column = 0; column < difficulty; column++){
                if(grids_a[row][column] != Grids[row][column]){
                    return false;
                }
            }
        }

        return true;
    }

那接下来再写一个判断当前时间是否比最短时间要快的函数,并且写一个将当前时间写到数据库的函数。

    public boolean compare() {
        int nowtime = hour * 36000 + min * 6000 + sec * 100 + msec;
        int maxtime = maxhour * 36000 + maxmin * 6000 + maxsec * 100 + maxmsec;

        return nowtime > maxtime;
    }

    public void Write() {
        if (!compare()) {
            writeData_int("hour" + difficulty, hour);
            writeData_int("min" + difficulty, min);
            writeData_int("sec" + difficulty,sec);
            writeData_int("msec" + difficulty, msec);
        }
    }

那现在我们就可以来完成的我们的游戏功能了,并且我们要实现出在这一台设备上玩游戏,要将数据写入数据库,让另外一台设备来读取数据,实现数据的同步。

    public void swipeGrids(){
        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:
                        startX = point.getX();
                        startY = point.getY();

                        String str_row = String.valueOf(Math.floor((startY - top - 80) / (length + interval)));
                        String str2_column = String.valueOf(Math.floor((startX - left) / (length + interval)));
                        if (!gamesuccess()) {
                            writeData_int("row" + isLocal ,str_row.charAt(0)-'0');
                            writeData_int("column" + isLocal ,str2_column.charAt(0)-'0');
                            changeGrids(str_row.charAt(0)-'0', str2_column.charAt(0)-'0');
                            drawGrids();
                            if(gamesuccess()){
                                getrecord();
                                setMaxtext();
                                HiLog.info(TAG,Integer.toString(maxmsec));
                                timer.cancel();
                                updatetimer.cancel();
                                if (!compare()) {
                                    maxhour = hour;
                                    maxmin = min;
                                    maxsec = sec;
                                    maxmsec = msec;
                                    Write();
                                    setMaxtext();
                                    setUIContent(layout);
                                }
                            }
                        }
                        break;
                }
                return true;
            }
        });
    }

那数据同步了之后,我们是还没将数据写呈现出来。那我们现在就需要将数组b中的内容给画出来了,那我们就来写另外一个时间线程,来实现这个功能。

    public void update() {
        updatetimer = new Timer();
        updatetimer.schedule(new TimerTask() {
            @Override
            public void run() {
                getUITaskDispatcher().asyncDispatch(() -> {
                    drawGrids();
                });
            }
        },0,500);
    }

那我们还需要在订阅函数中去添加一个数据同步的操作。

                if (queryContact_int("row" + !isLocal)!=-1 && queryContact_int("column" + !isLocal)!=-1)
                    changeGrids_b(queryContact_int("row"+ !isLocal),queryContact_int("column"+ !isLocal));

那我们接下来就去写一个函数让我们的时间开始动起来。

    public void runing(){
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                getUITaskDispatcher().asyncDispatch(()->{
                    msec++;
                    if (msec >= 100){
                        sec++;
                        msec = msec % 100;
                        if (sec >= 60) {
                            min++;
                            sec = sec % 60;
                            if (min >= 60) {
                                hour++;
                                min = min % 60;
                            }
                        }
                    }
                    String strhour = "";
                    String strmin = "";
                    String strsec = "";
                    String strmsec = "";
                    if (msec < 10) {
                        strmsec = "0" + Integer.toString(msec);
                    } else if (msec >= 10) {
                        strmsec = Integer.toString(msec);
                    }
                    if (sec < 10){
                        strsec = "0" + Integer.toString(sec);
                    } else if (sec >= 10) {
                        strsec = Integer.toString(sec);
                    }
                    if (min < 10){
                        strmin = "0" + Integer.toString(min);
                    } else if (min >= 10) {
                        strmin = Integer.toString(min);
                    }
                    if (hour < 10){
                        strhour = "0" + Integer.toString(hour);
                    } else if (hour >= 10) {
                        strhour = Integer.toString(hour);
                    }
                    timetext.setText("time: "+ strhour +":"+strmin+":"+strsec+":"+strmsec);
                });
            }
        },0,10);
    }

同样的也是需要在订阅函数中实现数据的同步

                if (queryContact_int("hour"+difficulty) != -1)
                    maxhour = queryContact_int("hour"+difficulty);
                if(queryContact_int("min"+difficulty)!=-1)
                    maxmin = queryContact_int("min"+difficulty);
                if(queryContact_int("sec"+difficulty)!=-1)
                    maxsec = queryContact_int("sec"+difficulty);
                if (queryContact_int("msec"+difficulty)!=-1)
                    maxmsec = queryContact_int("msec"+difficulty);

那这样子我们就已经是完成了游戏功能了,接下去就来做返回按钮的点击事件吧。

                timer.cancel();
                updatetimer.cancel();
                terminate();

那接下来我们就来写重新开始的点击事件了。那为了两台设备能同时重新开始,我就在点击重新开始的时候往数据库里面写一个信号,然后还是用一个时间线程来检测这个信号,检测到了,就重新开始游戏。

    public void restarting() {
        restarttimer = new Timer();
        restarttimer.schedule(new TimerTask() {
            @Override
            public void run() {
                getUITaskDispatcher().asyncDispatch(() ->{
                    if (restart == 1){
                        timer.cancel();
                        updatetimer.cancel();
                        restart = 0;
                        initialize();
                        runing();
                        update();
                    }
                });
            }
        },0,25);
    }

那到这里我们的游戏也就完成了。我们也可以利用这些分布式能力来创建更多双人小游戏了。


结语

源码我会放到附件中的,有需求的可以自行下载自行学习,大家有什么看不懂的地方可以私信问我或者对照源码进行学习。

更多资料请关注我们的项目 :Awesome-Harmony_木棉花

本项目会长期更新 ,希望继续关注,我们也会加油做得更好的。明年3月,深大校园内的木棉花会盛开,那时,鸿蒙也会变的更好,愿这花开,有你我的一份。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
MyKlotski.rar 9.04M 66次下载
已于2021-10-6 13:17:37修改
8
收藏 8
回复
举报
5条回复
按时间正序
/
按时间倒序
微生香岚
微生香岚

感谢楼主分享

回复
2021-10-7 12:02:34
甜甜爱开发
甜甜爱开发

楼主666

1
回复
2021-10-9 09:31:09
longlong899
longlong899

学习了,谢谢分享!

回复
2021-10-9 09:43:34
木棉花沈泳鑫
木棉花沈泳鑫 回复了 longlong899
学习了,谢谢分享!

请继续支持

回复
2021-10-9 20:36:04
qq616d5eb5475a5
qq616d5eb5475a5

沈总牛逼

回复
2021-10-18 19:47:22
回复
    相关推荐