App下載

如何用鴻蒙開(kāi)發(fā)1024專注小游戲?

狼哥Army 2021-10-20 10:27:39 瀏覽數(shù) (4100)
反饋
原文: https://harmonyos.51cto.com/posts/8834#zao

一、前言

1024 一起專注游戲是在屏幕上畫上 N x N 個(gè)方格(如 4x4 共 16 個(gè)),格子內(nèi)任意填寫上從1開(kāi)始順序生成的數(shù)字(如 1 ~ 16 共 16 個(gè)數(shù)字)。游戲時(shí),要求玩家用手指按從小到大(如 1 ~ 16 )的順序依次指出其位置,按完所有數(shù)字后,顯示所用的時(shí)間(秒)。所用時(shí)間越短,注意力水平越高。能夠培養(yǎng)注意力集中、分配、控制能力;拓展視幅;加快視頻;提高視覺(jué)的穩(wěn)定性、辨別人、定向搜索能力。此游戲?yàn)樽詈?jiǎn)單,最有效也是最科學(xué)的注意力訓(xùn)練方法。尋找目標(biāo)數(shù)字時(shí),注意力是需要極度集中的,把這短暫的高強(qiáng)度的集中精力過(guò)程反復(fù)練習(xí),大腦的集中注意力功能就會(huì)不斷的加固,提高。注意水平越來(lái)越高。

同時(shí),1024 一起專注游戲使用了鴻蒙分布式協(xié)同技術(shù),在訓(xùn)練小孩子專注力和耐力時(shí),大人也可以一起陪伴訓(xùn)練,只要兩臺(tái)鴻蒙系統(tǒng)手機(jī)或一臺(tái)手機(jī)一臺(tái)平板,大人,小孩就可以同時(shí)一起在玩一個(gè)游戲,比如大人在其中一臺(tái)手機(jī)上按了一部份小數(shù)字,然后點(diǎn)擊分布式協(xié)同圖標(biāo),拉起另一臺(tái)手機(jī)的 1024 一起專注游戲,小孩可以接著按大人沒(méi)有按完的數(shù)字,最終顯示出所用的時(shí)間。

二、 實(shí)現(xiàn)效果

  • 開(kāi)發(fā)工具環(huán)境下視頻:https://www.bilibili.com/video/BV1B34y1m7M5?spm_id_from=333.999.0.0
  • 手機(jī) + 手機(jī)環(huán)境下視頻:https://www.bilibili.com/video/BV1kh411b7QM?spm_id_from=333.999.0.0
  • 手機(jī) + 平板環(huán)境下視頻:https://www.bilibili.com/video/BV1ov411M7sq?spm_id_from=333.999.0.0

鴻蒙開(kāi)發(fā)1024專注小游戲效果

鴻蒙1024小游戲效果

三、創(chuàng)建工程

在這當(dāng)作你已經(jīng)安裝好最新版本 DevEco-Studio 開(kāi)發(fā)工具, 點(diǎn)擊 File -> New -> New Project… 彈出 Create HarmonyOS Project 窗口, 這里我選擇空白 Java 模板創(chuàng)建, 上一個(gè)視頻播放實(shí)例是用 JS 寫的界面,這個(gè)游戲界面就用 Java 來(lái)寫,還是 JS 寫界面快,調(diào)試也快些。

鴻蒙游戲開(kāi)發(fā)創(chuàng)建工程

四、主界面開(kāi)發(fā)

在展示源代碼之前,先介紹一下使用到了 JAVA 哪些組件: DirectionalLayout, TableLayout, DependentLayout, Button, Image, Text, ListContainer, CommonDialog,通過(guò)查看 Java UI 參考文檔,就可以做出你喜歡的應(yīng)用了。

先介紹公共類 Java 代碼,有了這些公共類,以后做類似功能的應(yīng)用,可以直接復(fù)制公共類文件可以使用:

LogUtil 日志打印類:

public class LogUtil {
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD000F00, "1024Game");
    private static final String LOG_FORMAT = "%{public}s: %{public}s";


    private LogUtil() {


    }
    public static void debug(String className, String msg) {
        HiLog.debug(LABEL_LOG, LOG_FORMAT, className, msg);
    }
    public static void info(String className, String msg) {
        HiLog.info(LABEL_LOG, LOG_FORMAT, className, msg);
    }
    public static void info(Class<?> classType, final String format, Object... args) {
        String buffMsg = String.format(Locale.ROOT, format, args);
        HiLog.info(LABEL_LOG, LOG_FORMAT, classType == null ? "null" : classType.getSimpleName(), buffMsg);
    }
    public static void error(String tag, String msg) {
        HiLog.error(LABEL_LOG, LOG_FORMAT, tag, msg);
    }
}

SelectDeviceDialog設(shè)備選擇對(duì)話框:

public class SelectDeviceDialog {
    private static final int DIALOG_WIDTH = 840;
    private static final int DIALOG_HEIGHT = 900;
    private CommonDialog commonDialog;


    public SelectDeviceDialog(Context context, List<DeviceInfo> devices, SelectResultListener listener) {
        initView(context, devices, listener);
    }
    private void initView(Context context, List<DeviceInfo> devices, SelectResultListener listener) {
        // 創(chuàng)建一個(gè)公共對(duì)話框
        commonDialog = new CommonDialog(context);
        // 設(shè)置對(duì)齊方式居中
        commonDialog.setAlignment(LayoutAlignment.CENTER);
        // 設(shè)置對(duì)話框尺寸
        commonDialog.setSize(DIALOG_WIDTH, DIALOG_HEIGHT);
        // 設(shè)置對(duì)話框自動(dòng)關(guān)閉
        commonDialog.setAutoClosable(true);
        // 加載XML布局文件
        Component dialogLayout =
                LayoutScatter.getInstance(context).parse(ResourceTable.Layout_dialog_select_device, null, false);
        // 設(shè)置對(duì)話框內(nèi)容
        commonDialog.setContentCustomComponent(dialogLayout);
        // 查找到列表容器
        if (dialogLayout.findComponentById(ResourceTable.Id_list_devices) instanceof ListContainer) {
            // 獲取列表容器對(duì)象
            ListContainer devicesListContainer =
                    (ListContainer) dialogLayout.findComponentById(ResourceTable.Id_list_devices);
            // 設(shè)備列表適配器
            DevicesListAdapter devicesListAdapter = new DevicesListAdapter(devices, context);
            // 設(shè)置設(shè)備列表容器項(xiàng)提供者
            devicesListContainer.setItemProvider(devicesListAdapter);
            // 設(shè)置設(shè)備列表項(xiàng)單擊事件
            devicesListContainer.setItemClickedListener((listContainer, component, position, id) -> {
                // 回調(diào)選擇的設(shè)備信息
                listener.callBack(devices.get(position));
                // 關(guān)閉對(duì)話框
                commonDialog.hide();
            });
        }
        dialogLayout.findComponentById(ResourceTable.Id_cancel).setClickedListener(component -> {
            // 關(guān)閉對(duì)話框
            commonDialog.hide();
        });
    }
    // 顯示對(duì)話框
    public void show() {
        commonDialog.show();
    }
    /**
     * 內(nèi)部接口, 選擇設(shè)備后回調(diào)事件
     */
    public interface SelectResultListener {
        void callBack(DeviceInfo deviceInfo);
    }
}

DevicesListAdapter設(shè)備列表適配器:

public class DevicesListAdapter extends BaseItemProvider {
    // 開(kāi)始下標(biāo)從0開(kāi)始
    private static final int SUBSTRING_START = 0;
    // 結(jié)束下標(biāo)為4
    private static final int SUBSTRING_END = 4;
    // 設(shè)備信息列表
    private List<DeviceInfo> deviceInfoList;
    // 當(dāng)前上下文
    private Context context;


    // 帶參構(gòu)造方法
    public DevicesListAdapter(List<DeviceInfo> deviceInfoList, Context context) {
        this.deviceInfoList = deviceInfoList;
        this.context = context;
    }
    @Override
    public int getCount() {
        return deviceInfoList == null ? 0 : deviceInfoList.size();
    }
    @Override
    public Object getItem(int i) {
        return Optional.of(deviceInfoList.get(i));
    }
    @Override
    public long getItemId(int i) {
        return i;
    }
    @Override
    public Component getComponent(int i, Component component, ComponentContainer componentContainer) {
        // 定義設(shè)備視圖內(nèi)部類
        ViewHolder viewHolder = null;
        // 定義組件
        Component mComponent = component;
        // 組件為空時(shí)
        if (mComponent == null) {
            // 查找設(shè)備列表項(xiàng)布局XML
            mComponent = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_item_device_list, null, false);
            // 初始化設(shè)備視圖類
            viewHolder = new ViewHolder();
            // 判斷組件布局里是否包含設(shè)備名稱文本組件
            if (mComponent.findComponentById(ResourceTable.Id_device_name) instanceof Text) {
                // 獲取設(shè)備列表項(xiàng)布局XML的設(shè)備名稱文件組件,并賦值給內(nèi)部類設(shè)備視圖設(shè)備名稱屬性緩存
                viewHolder.devicesName = (Text) mComponent.findComponentById(ResourceTable.Id_device_name);
            }
            // 判斷組件布局里是否包含設(shè)備Id文本組件
            if (mComponent.findComponentById(ResourceTable.Id_device_id) instanceof Text) {
                // 獲取設(shè)備列表項(xiàng)布局XML的設(shè)備Id文件組件,并賦值給內(nèi)部類設(shè)備視圖設(shè)備Id屬性緩存
                viewHolder.devicesId = (Text) mComponent.findComponentById(ResourceTable.Id_device_id);
            }
            mComponent.setTag(viewHolder);
        } else {
            // 如果組件不為空, 并且標(biāo)簽包含內(nèi)部類設(shè)備視圖
            if (mComponent.getTag() instanceof ViewHolder) {
                // 從組件標(biāo)簽獲取出設(shè)備視圖
                viewHolder = (ViewHolder) mComponent.getTag();
            }
        }
        // 設(shè)備視圖不為空時(shí)
        if (viewHolder != null) {
            // 設(shè)置設(shè)備名稱內(nèi)容
            viewHolder.devicesName.setText(deviceInfoList.get(i).getDeviceName());
            String deviceId = deviceInfoList.get(i).getDeviceId();
            deviceId = deviceId.substring(SUBSTRING_START, SUBSTRING_END) + "******"
                    + deviceId.substring(deviceId.length() - SUBSTRING_END);
            // 設(shè)置設(shè)備名稱Id
            viewHolder.devicesId.setText(deviceId);
        }
        return mComponent;
    }
    /**
     * 內(nèi)部類, 設(shè)備視圖
     */
    private static class ViewHolder {
        // 設(shè)備名稱
        private Text devicesName;
        // 設(shè)備Id
        private Text devicesId;
    }
}

MainAbilitySlice主界面功能講解 :

主界面主要功能就是用表格布局生成 3x3, 4x4, 5x5, 6x6, 7x7, 8x8, 9x9 七個(gè)按鈕,點(diǎn)擊后跳轉(zhuǎn)游戲界面,初始化相應(yīng)的數(shù)字按鈕,用到了 Slice 之間跳轉(zhuǎn)傳參數(shù), 源碼都有詳細(xì)注釋,有興趣小伙伴可以到 gitee 查看源碼。

PlayAbilitySlice游戲界面功能講解:

游戲界面主要功能也是用表格布局生成相應(yīng)主界面?zhèn)鬟^(guò)來(lái)的參數(shù)按鈕,數(shù)字顯示順序隨機(jī), 分布式協(xié)同拉起 GameServiceAbility 游戲服務(wù),并且在點(diǎn)擊每個(gè)數(shù)字按鈕時(shí),通過(guò)訂閱 Event,把當(dāng)前點(diǎn)到哪個(gè)數(shù)字,相關(guān)變量都接收到,然后更新相應(yīng)的數(shù)據(jù), 源碼都有詳細(xì)注釋,有興趣小伙伴可以到 gitee 查看源碼。

GameServiceAbility游戲服務(wù)講解:

游戲服務(wù)主要功能是如果請(qǐng)求是 Ability 的,接收到參數(shù)后,再流轉(zhuǎn)到其它界面?zhèn)鲄?;如果是其它?qǐng)求,接收到參數(shù)后,通過(guò)公共事件發(fā)布出去,讓訂閱了此事件的 Ability 更新數(shù)據(jù),源碼都有詳細(xì)注釋,有興趣小伙伴可以到 gitee 查看源碼。

講解到此了,不要忘記了 config.json 文件的權(quán)限配置哦,在 module 下添加

"reqPermissions": [
      {
        "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
      },
      {
        "name": "ohos.permission.DISTRIBUTED_DATASYNC"
      },
      {
        "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"
      },
      {
        "name": "ohos.permission.READ_USER_STORAGE"
      },
      {
        "name": "ohos.permission.WRITE_USER_STORAGE"
      },
      {
        "name": "ohos.permission.GET_BUNDLE_INFO"
      }
    ]

同時(shí),在游戲界面入口也是要提供動(dòng)態(tài)授權(quán):

private static void grantPermission(Context context) {
   LogUtil.info(TAG, "grantPermission");
   if (context.verifySelfPermission(DISTRIBUTED_DATASYNC) != IBundleManager.PERMISSION_GRANTED) {
       if (context.canRequestPermission(DISTRIBUTED_DATASYNC)) {
           context.requestPermissionsFromUser(new String[] {DISTRIBUTED_DATASYNC}, PERMISSION_CODE);
       }
   }
}

五、總結(jié)

有興趣的小伙伴可以下載源碼查看, 項(xiàng)目代碼基本都有注釋了,游戲規(guī)則很簡(jiǎn)單,就是在界面按順序點(diǎn)擊數(shù)字,時(shí)間越短,說(shuō)明注意力越集中。 源碼同步到 gitee 碼云。

源碼在這: https://gitee.com/army16/qin-hong-jun-board

1 人點(diǎn)贊