0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

HarmonyOS云开发:舒尔特方格游戏

OpenHarmony技术社区 来源:OpenHarmony技术社区 2023-06-19 15:05 次阅读

舒尔特方格游戏-关系型数据库版本,请移步到上一篇帖子查看,本篇帖子内容还是舒尔特方格游戏,只是换了后宫_云数据库。

此项目是用最新版 DevEco Studio 3.1 Release 并创建端云一体开发,由于目前此版本不支持直接调用云数据库,不过可以通过云函数调用云数据库。

也就是在服务卡片业务逻辑里通过调用云函数来完成游戏数据保存到云数据库,开发工具支持本地函数调用测试,大大方便了开发。

此贴重点讲解云函数和云数据库开发,本地和远端调用,从而进一步学习 Serverless 知识。

舒尔特方格游戏效果图如下:92a74280-0da6-11ee-962d-dac502259ad0.gif  

知识点

为丰富 HarmonyOS 对云端开发的支持、实现 HarmonyOS 生态端云联动,DevEco Studio 推出了云开发功能,开发者在创建工程时选择云开发模板。

即可在 DevEco Studio 内同时完成 HarmonyOS 应用/服务的端侧与云侧开发,体验端云一体化协同开发。

相比于传统开发模式,云开发模式具备成本低、效率高、门槛低等优势,具体区别见下表。92c0bf30-0da6-11ee-962d-dac502259ad0.png

①开发流程

HarmonyOS 应用端云一体化开发流程如下图所示:

92ca6ecc-0da6-11ee-962d-dac502259ad0.jpg

②创建端云一体化开发工程

新建原子化服务工程:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/agc-harmonyos-clouddev-createproject-0000001443369760-V3#section15198822192610
工程初始化配置:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/agc-harmonyos-clouddev-createproject-0000001443369760-V3#section1938317533494
端云一体化开发工程介绍:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/agc-harmonyos-clouddev-createproject-0000001443369760-V3#section20250910164411

③开发云工程

开发云函数:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/agc-harmonyos-clouddev-cloudfunctions-0000001493089797-V3
开发云数据库:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/agc-harmonyos-clouddev-clouddb-0000001443049860-V3

④部署云工程

部署云工程:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/agc-harmonyos-clouddev-deploy-0000001493209765-V3

小结:了解这些端云一体化开发知识点后,下面围绕舒尔特方格游戏,在云数据库里设计卡片表结构和成绩表结构,然后再编写相关云函数,并在本地测试通过后,再测试远程,最后在元服务业务逻辑调用云函数。

云数据库开发讲解

①objecttype 创建

展开 CloudProgram→clouddb→objecttype 右击 objecttype 目录,创建→Cloud DB Object Type 输入 Object Type Name 为 t_form,点击确认。

修改内容如下:
{
"fields":[
{
"isNeedEncrypt":false,
"fieldName":"formId",
"notNull":true,
"belongPrimaryKey":true,
"fieldType":"String"
},
{
"isNeedEncrypt":false,
"fieldName":"formName",
"notNull":true,
"defaultValue":"",
"belongPrimaryKey":false,
"fieldType":"String"
},
{
"isNeedEncrypt":false,
"fieldName":"dimension",
"notNull":true,
"defaultValue":"0",
"belongPrimaryKey":false,
"fieldType":"Integer"
}
],
"indexes":[
{
"indexName":"formId",
"indexList":[{"fieldName":"formId","sortType":"ASC"}]
}
],
"objectTypeName":"t_form",
"permissions":[...]
}

展开 CloudProgram→clouddb→objecttype 右击 objecttype 目录,创建→Cloud DB Object Type 输入 Object Type Name 为 t_score,点击确认。

修改内容如下:
{
"fields":[
{
"isNeedEncrypt":false,
"fieldName":"formId",
"notNull":true,
"belongPrimaryKey":true,
"fieldType":"String"
},
{
"isNeedEncrypt":false,
"fieldName":"matrixNum",
"notNull":true,
"defaultValue":"",
"belongPrimaryKey":false,
"fieldType":"String"
},
{
"isNeedEncrypt":false,
"fieldName":"bestScore",
"notNull":true,
"defaultValue":"0",
"belongPrimaryKey":false,
"fieldType":"Double"
}
],
"indexes":[
{
"indexName":"formId",
"indexList":[{"fieldName":"formId","sortType":"ASC"}]
}
],
"objectTypeName":"t_score",
"permissions":[...]
}

②dataentry 创建

展开 CloudProgram→clouddb→dataentry 右击 dataentry 目录,创建→Cloud DB Data Entry 这里先选择上面创建的 Object Type 为 t_form,再输入 Data Entry Name 为 form_data,点击确认。

修改内容如下:

{
"cloudDBZoneName":"widgetCard",
"objectTypeName":"t_form",
"objects":[
{
"formId":"x000001",
"formName":"卡片1",
"dimension":2
}
]
}
展开 CloudProgram→clouddb→dataentry 右击 dataentry 目录,创建→Cloud DB Data Entry 这里先选择上面创建的 Object Type为t_score,再输入 Data Entry Name 为 score_data,点击确认。

修改内容如下:

{
"cloudDBZoneName":"widgetCard",
"objectTypeName":"t_score",
"objects":[
{
"formId":"x000001",
"matrixNum":"3x3",
"bestScore":2.234
}
]
}
小结:其实 dataentry 文件可以不创建,这里对两个表都初始化了一条数据,是方便下面的调用使用,云数据库就是定义好表结构、权限配置就可以,数据的添加、修改、删除、查询都可以通过云函数来完成。

云函数开发讲解

①卡片云函数创建

展开 CloudProgram→cloudfunctions 右击 cloudfunctions 目录,创建→Cloud Function 输入 Cloud Function Name 为 form-func,点击确认。

卡片云函数里包含了增删改查操作,所以在 form-func 下,创建不同的文件夹来区分,目录结构如下:

92d57470-0da6-11ee-962d-dac502259ad0.png首先说一下与云数据库交互文件,t_form.js 对应的是云数据库实体类,如各属性的 get 和 set 方法,之前 FA 模式下的 DevEco Studio 端云一体化开发,支持直接调用云数据库。现在 Stage 模式下的 DevEco Studio 端云一体化开发,还不支持直接调用云数据库,通过云函数来调用。所以这里的云数据库实体类,除了属性的 get 和 set 方法外,还要手工添加一些方法。

如卡片实例体类:

classt_form{
getFieldTypeMap(){
letfieldTypeMap=newMap();
fieldTypeMap.set('formId','String');
fieldTypeMap.set('formName','String');
fieldTypeMap.set('dimension','Integer');
returnfieldTypeMap;
}

getClassName(){
return't_form';
}

getPrimaryKeyList(){
letprimaryKeyList=[];
primaryKeyList.push('formId');
returnprimaryKeyList;
}

getIndexList(){
letindexList=[];
returnindexList;
}

getEncryptedFieldList(){
letencryptedFieldList=[];
returnencryptedFieldList;
}

//setandget
setFormId(formId){this.formId=formId;}
getFormId(){returnthis.formId;}
setFormName(formName){this.formName=formName;}
getFormName(){returnthis.formName;}
setDimension(dimension){this.dimension=dimension;}
getDimension(){returnthis.dimension;}
}

module.exports={t_form}

CloudDBZoneWrapper 操作云数据库,这里主要列举构造函数和增加方法内容:

import*asclouddbfrom'@agconnect/database-server';
import{t_formasFormBean}from'./models/t_form';
import*asagconnectfrom'@agconnect/common-server';

constZONE_NAME="widgetCard";

exportclassCloudDBZoneWrapper{
logger;
cloudDbZone;

constructor(credential,logger){
this.logger=logger;
try{
//初始化AGCClient
letagcClient;
try{
agcClient=agconnect.AGCClient.getInstance();
}catch{
agconnect.AGCClient.initialize(credential);
agcClient=agconnect.AGCClient.getInstance();
}
//初始化AGConnectCloudDB实例
letcloudDbInstance;
try{
cloudDbInstance=clouddb.AGConnectCloudDB.getInstance(agcClient);
}catch{
clouddb.AGConnectCloudDB.initialize(agcClient);
cloudDbInstance=clouddb.AGConnectCloudDB.getInstance(agcClient);
}
//创建CloudDBZoneConfig配置对象,并设置云侧CloudDBzone名称,打开CloudDBzone实例
constcloudDBZoneConfig=newclouddb.CloudDBZoneConfig(ZONE_NAME);
this.cloudDbZone=cloudDbInstance.openCloudDBZone(cloudDBZoneConfig);
}catch(err){
logger.error("xx[form-func]CloudDBZoneWrapperinitCloudDBZoneWrappererror:"+err);
}
}

asyncinsert(addForm){
if(!this.cloudDbZone){
this.logger.error("xx[form-func]CloudDBZoneWrapper->insertCloudDBClientisnull,tryre-initializeit");
}

try{
letres=awaitthis.cloudDbZone.executeUpsert(addForm);
this.logger.info("xx[form-func]CloudDBZoneWrapper->insertInsert"+res+"recordssuccess");
}catch(error){
this.logger.error("xx[form-func]CloudDBZoneWrapper->insertexecuteInsertaddressRecordsfailed"+error);
}
}
}

新增卡片函数 form-insert,关键代码如下:

import{CloudDBZoneWrapper}from'../clouddb/CloudDBZoneWrapper.js';
import*asUtilsfrom'../utils/Utils.js';

exportconstmyHandler=asyncfunction(event,context,callback,logger){
constcredential=Utils.getCredential(context,logger);
try{
constcloudDBZoneWrapper=newCloudDBZoneWrapper(credential,logger);
letformObj=cloudDBZoneWrapper.getForm(event);
awaitcloudDBZoneWrapper.insert(formObj);

callback({
ret:{code:0,desc:"SUCCESS"},
});
}catch(err){
logger.error("xx[form-func]insertfuncerror:"+err.message+"stack:"+err.stack);
callback({
ret:{code:-1,desc:"ERROR"},
});
}
};

卡片云函数主入口,关键代码如下:

letmyHandler=asyncfunction(event,context,callback,logger){
letoperation;
letparams;

logger.info("xxenterformfuncwithoperation"+event.operation);
operation=event.body?JSON.parse(event.body).operation:event.operation;
params=event.body?JSON.parse(event.body).params:event.params;

switch(operation){
case"query":
query.myHandler(params,context,callback,logger);
break;
case"queryById":
queryById.myHandler(params,context,callback,logger);
break;
case"insert":
insert.myHandler(params,context,callback,logger);
break;
case"update":
update.myHandler(params,context,callback,logger);
break;
case"delete":
deleteByObj.myHandler(params,context,callback,logger);
break;
default:
callback({
ret:{code:-1,desc:"nosuchfunction"},
});
}

};
module.exports.myHandler=myHandler;

②成绩云函数创建

展开 CloudProgram→cloudfunctions 右击 cloudfunctions 目录,创建→Cloud Function 输入 Cloud Function Name 为 score-func,点击确认,。成绩云函数里包含了增删改查操作,所以在 score-func 下,创建不同的文件夹来区分。

目录结构如下:

92e34546-0da6-11ee-962d-dac502259ad0.png成绩表云数据库操作与卡片操作一样,这里就不在重复了,可以参考一下上面卡片操作方法就可以。

云函数本地与远程调试

①Run 模式启动调试

右击“cloudfunctions”目录,选择“Run Cloud Functions”。查看“Run”面板。如果出现“Cloud Functions loaded successfully”,表示所有函数已成功加载到本地运行的 HTTP Server 中,并生成对应的 POST URL。在菜单栏选择“Tools > CloudDev > Cloud Functions Requestor”,使用 Cloud Functions Requestor 触发函数调用。在弹出的“Cloud Functions Requestor”面板,填写触发事件参数点击“Save”,可保存当前触发事件。

②Debug 模式启动调试

右击“cloudfunctions”目录,选择“Run Cloud Functions”。查看 Console 面板。如果出现“Cloud Functions loaded successfully”,表示函数成功加载到本地运行的 HTTP Server 中,并生成对应的 POST URL。如需设置断点调试,在函数代码中选定要设置断点的有效代码行,在行号后单击鼠标左键设置断点,设置断点后,调试能够在断点处中断,并高亮显示该行。在菜单栏选择“Tools > CloudDev > Cloud Functions Requestor”,使用 Cloud Functions Requestor 触发函数调用。在弹出的“Cloud Functions Requestor”面板,填写触发事件参数。点击“Save”,可保存当前触发事件。

③自定义 Run/Debug 配置

在菜单栏选择“Run > Edit Configurations”。在“Run/Debug Configurations”窗口,点击+,选择“Cloud Functions”,新增一个 Run/Debug 配置。

自定义 Run/Debug 配置,完成后点击“OK”。

· Name:Run/Debug配置的名称,如“functions-custom1”。
· Server Port:HTTP服务端监听端口。默认为“18090”,自定义端口号建议大于1024。勾选“Autoincrement”表示如当前端口被占用则端口号自动加“1”。
· Environment variables:函数运行的环境变量,为key-value形式。
点击“Edit environment variables”按钮,在“Environment Variables”弹窗中点击“+”添加一个环境变量,然后点击“OK”。添加成功后,您便可以将变量配置信息传入到函数执行环境中,用于函数运行时读取。
选择刚刚自定义的 Run/Debug 配置,分别点击 Run 或 Debug。后续调试步骤与默认配置下的调试步骤一致,请分别参见Run 模式启动调试Debug 模式启动调试

④测试

实现云函数调用云数据库,需要您先部署云工程,云端才会有相关数据及环境变量。同时,云函数为访问云数据库使用了“PROJECT_CREDENTIAL”环境变量,部署函数到 AGC 云端时,云端会自动配置好“PROJECT_CREDENTIAL”以运行环境变量。但在本地调试函数时,需要您手动将“PROJECT_CREDENTIAL”环境变量添加到 Run/Debug 配置中。

否则,函数调试代码执行会因获取不到“PROJECT_CREDENTIAL”环境变量而中断。

92ec2d1e-0da6-11ee-962d-dac502259ad0.png

从 AGC 获取的“PROJECT_CREDENTIAL”环境变量添加到调试配置中。您也可以添加您需要的其他环境变量。

92fcb45e-0da6-11ee-962d-dac502259ad0.png

9306e578-0da6-11ee-962d-dac502259ad0.png

添加完环境变量后,启动函数,再点击 Trigger,就可以看到成功返回数据了。

9312f3fe-0da6-11ee-962d-dac502259ad0.png

代码讲解

①云函数调用公共类

DatabaseUtils.ets 云函数操作类部分代码如下:

exportclassDatabaseUtils{

asynccallWithParams(context,trigger,operation,params){
awaitgetAGConnect(context);
letbody={
"operation":operation,
"params":params
}

try{
letfunctionCallable=agconnect.function().wrap(trigger);
letfunctionResult=awaitfunctionCallable.call(body);
returnfunctionResult.getValue();
//returnfunctionResult.getValue().result;
}
catch(err){
return{
"ret":{"code":-1,"desc":"ERROR"}
}
}
}

asyncinvoke(context:any,trigger?:string,operation?:string,params?:object){
console.info(CommonConstants.DATABASE_TAG,'xxinvokeparams:'+JSON.stringify(params))
returnawaitthis.callWithParams(context,trigger,operation,params);
}

/**
*插入卡片数据。
*
*@param{Form}Form表单实体。
*@param{DataRdb.RdbStore}RDB存储RDB数据库。
*@return返回操作信息。
*/
asyncinsertForm(context:any,form:Form){
letres=awaitthis.invoke(context,Triggers.FormFunc,RequestType.Insert,form);
console.info(CommonConstants.DATABASE_TAG,'xxinsertFormresult:'+JSON.stringify(res));
}
......
}

②卡片 Ability 调用公共类

EntryFormAbility.ets 卡片生命周期代码如下:

onAddForm(want){
//获取卡片ID:ohos.extra.param.key.form_identity
letformId:string=want.parameters[CommonConstants.FORM_PARAM_IDENTITY_KEY]asstring;
//获取卡片名称:ohos.extra.param.key.form_name
letformName:string=want.parameters[CommonConstants.FORM_PARAM_NAME_KEY]asstring;
//获取卡片规格:ohos.extra.param.key.form_dimension
letdimensionFlag:number=want.parameters[CommonConstants.FORM_PARAM_DIMENSION_KEY]asnumber;

//卡片信息
letform:Form=newForm();
form.formId=formId;
form.formName=formName;
form.dimension=dimensionFlag;

//保存卡片信息到数据库
DatabaseUtils.insertForm(this.context,form);
//获取最优成绩
getScoreById(this.context,dimensionFlag,formId);

//每五分钟刷新一次
formProvider.setFormNextRefreshTime(formId,CommonConstants.FORM_NEXT_REFRESH_TIME,(error,data)=>{
if(error){
console.error(CommonConstants.ENTRY_FORM_ABILITY_TAG,'xx onAddForm 更新卡片失败:'+JSON.stringify(error))
}else{
console.info(CommonConstants.ENTRY_FORM_ABILITY_TAG,'xxonAddForm更新卡片成功')
}
});

//返回初始化卡片数据
letformData:FormData=newFormData();
formData.formId=formId;
formData.bestScore=0;
formData.matrixNum='1x1';
formData.totalBestScore=0;
returnformBindingData.createFormBindingData(formData);
}

③主界面调用公共类

@Entry
@Component
structIndex{
@StatescoreDataList:Array=[]

aboutToAppear(){
//请求通知栏权限
this.requestNotification();
//更新卡片信息
DatabaseUtils.updateForms(getContext(this));
//获取成绩历史记录
this.getScoreListData()
}
onPageShow(){
//更新卡片信息
DatabaseUtils.updateForms(getContext(this));
//获取成绩历史记录
this.getScoreListData()
}
//获取成绩历史数据
getScoreListData(){
DatabaseUtils.getScoreListData(getContext(this))
.then((res)=>{
this.scoreDataList=res;
//发送通知
NotificationUtils.sendNotifications(this.scoreDataList[0].totalBestScore);
}).catch((error)=>{
console.error(CommonConstants.MAIN_PAGE_TAG,'xxaboutToAppearoronPageShowgetScoreListDataerror'+JSON.stringify(error));
});
}

build(){...}
}

总结

通过把之前小游戏元服务-关系型数据库修改为使用 Serverless 云函数、云数据库,学习到不少知识,开始时不懂得怎么使用云函数调用云数据库,一边参考官方商城模板,一边测试,到使用到这个小游戏上,。总结这个项目用到以下知识点:
  • 使用 notification 发布通知。
  • 使用端云一体化开发、开发云函数、开发云数据库。
  • 使用 FormExtensionAbility 创建、更新、删除元服务卡片。

备注:资源文件是我在学习云函数调用云数据库写的一个简单实例,有云函数调用云数据库需求的小伙伴可以下载下来参考一下。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据库
    +关注

    关注

    7

    文章

    3591

    浏览量

    63369
  • 模板
    +关注

    关注

    0

    文章

    106

    浏览量

    20478
  • HarmonyOS
    +关注

    关注

    79

    文章

    1827

    浏览量

    29261

原文标题:HarmonyOS云开发:舒尔特方格游戏

文章出处:【微信号:gh_834c4b3d87fe,微信公众号:OpenHarmony技术社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    HarmonyOS应用兼容稳定性测试

    ,使用华为开发者帐号进行登录。 点击界面上的“从这里开始”按钮,进入到控制台。 选择或者创建一个项目,如果新创建一个项目,注意选择 HarmonyOS。 进入项目空间,在左侧导航栏中选择 测试服务
    发表于 12-25 10:56

    HarmonyOS应用性能与功耗测试

    ,进入到控制台。 选择或者创建一个项目,如果新创建一个项目,注意选择 HarmonyOS。 进入项目空间,在左侧导航栏中选择 测试服务 > HarmonyOS 测试 ,进入
    发表于 12-26 16:39

    36小时开发-办公之便签打印机

    办公之便签打印机》为例,告诉你他们是如何在短时间内完成参赛项目的原型,大家为什么都钟爱于机智,选择机智完成智能硬件产品开发。这两位好基友就是本次大赛的《办公之便签打印机》选手,是
    发表于 12-27 15:03

    STM32+机智WebSocket实现物联网游戏

    游戏显示与STM32显示同步(实际会有一点网络延迟)。1、实现设备  1)SmarKit - ESP STM32核心板  2)机智平台,注册一个开发者账号  3)0.96寸的OLED显示屏(四针
    发表于 07-16 14:30

    从零开发HarmonyOS(鸿蒙)运动手表小游戏——数字华容道

    HarmonyOS(鸿蒙)运动手表第二个小游戏app——数字华容道前言概述正文创建项目实现开始界面的布局实现数字的随机打乱和方格的移动实现计时器、重新开始和游戏成功心得体会结语源代码前
    发表于 11-24 08:57

    HarmonyOS 开发小视频展示(五)

    基于HarmonyOS鸿蒙—北向HAP应用开发之2048小游戏简介:基于HarmonyOS鸿蒙——北向HAP应用开发之2048小
    发表于 03-05 16:15

    HarmonyOS应用开发对角数字游戏练习

    一、效果与说明这是一个数字游戏项目,输入1~9数字到方格中完成横竖对角线相加之和都为15。如下图 二、部分代码展示jsexport default { data: {result: '将1~9数字
    发表于 06-07 14:36

    HarmonyOS与OpenHarmony应用开发差异

    DevEco Studio是HarmonyOS的配套的开发IDE,因为HarmonyOS是基于OpenHarmony开发的,因此,使用DevEco Studio(配套
    发表于 10-22 10:35

    直播预告丨Hello HarmonyOS进阶课程第三课——游戏开发实践

    HarmonyOS进阶系列应用篇第三课《游戏开发实践》直播如约而至。本节课,华为HDE徐礼文老师将带领大家探讨HarmonyOS游戏场景,
    发表于 05-16 11:45

    【学习打卡】基于ArkUI框架的方格游戏

    项目开发介绍方格游戏有主界面和游戏界面两个页
    发表于 07-04 00:26

    每日推荐 | 基于RK2206的电子秤,OpenHarmony操作系统的智能停车场

    】基于ArkUI框架的方格游戏推荐理由:
    发表于 07-05 10:17

    HarmonyOS应用端一体化开发主要流程

    图示 *附件:HarmonyOS应用端一体化开发主要流程.docx
    发表于 05-19 14:27

    HarmonyOS如何玩 3d 游戏

    总有网友期待 HarmonyOS 什么时候能出 3d 游戏,今天就教你写一个能玩的 3d 游戏
    的头像 发表于 01-04 14:23 1926次阅读

    行空板Python入门教程第七课:舒尔特方格游戏

    。舒尔特方格训练法,是全世界范围内最简单,最有效也是最科学的注意力训练方法之一。让我们一起用DFRobot行空板设计一个舒尔特方格游戏来训练一下自己的注意力吧!
    的头像 发表于 06-02 15:45 1670次阅读
    行空板Python入门教程第七课:舒尔特<b class='flag-5'>方格</b>小<b class='flag-5'>游戏</b>

    HarmonyOS携手库洛游戏推动《战双帕弥什》鸿蒙原生应用开发

    4 月 22 日,华为宣布库洛游戏的《战双帕弥什》正式启动鸿蒙原生应用开发项目,与 HarmonyOS NEXT 鸿蒙星河版的方舟引擎展开深度合作,旨在提升游戏的运行流畅度,为玩家提供
    的头像 发表于 04-22 15:21 93次阅读