【HarmonyOS 5】Laya游戏如何鸿蒙构建发布详解

article/2025/8/3 16:33:22

【HarmonyOS 5】Laya游戏如何鸿蒙构建发布详解

一、前言

LayaAir引擎是国内最强大的全平台引擎之一,当年H5小游戏火的时候,腾讯入股了腊鸭。我还在游戏公司的时候,17年曾经开发使用腊鸭的H5小游戏,很怀念当年和腊鸭同事一起解决问题的时光。

从使用TypeScript开发语言,到界面组件封装,再到全平台发布,腊鸭走过的路与鸿蒙很相似。很多设计理念也很贴近。作为基础开源,定制商业化的全平台引擎,腊鸭在H5引擎市场上的占有率相当高。

Layabox成立于2014年,旗下的开源引擎产品LayaAir发布于2016年,已拥有超百万的全球开发者,是HTML5与小游戏领域的3D龙头引擎。

除了H5 3D小游戏,腊鸭也可以通过2D引擎开发H5应用或者H5 2D小游戏。

鸿蒙作为一个崭新的操作系统,H5小游戏和H5应用的助力可以极大丰富其生态。并且腊鸭的开发和学习入门上手较为简单,也可以为小游戏和H5开发小伙伴,在鸿蒙市场,带来广阔的未来前景。

今天本文主要讲解腊鸭的背景,腊鸭开发环境安装和鸿蒙构建发布。

二、LayaAir环境安装

官方环境搭建文章,点击跳转

LayaAir分为游戏引擎和编码工具两个部分。最早当年是自研的IDE一起负责了,后来将编码环境切换为了VSCode。

所以现在我们的Laya环境首先需要安装LayaAirIDE和VSCode。

之后因为Laya开发的编程语言是TypeScript,所以需要安装Node开发环境,并且要install TSC,用于将TS代码转化为JS代码。最后Laya的代码都是js代码。

npm install -g typescript

浏览器是方便调试,可选择安装。若是Window系统,使用自带的Edge浏览也可。或者安装GoogleChrome

下图是安装完LayaAirIDE之后,选择2D示例项目创建打开后的效果:
在这里插入图片描述

三、鸿蒙构建发布

点击文件-构建发布,选择鸿蒙NEXT,在右侧基本无需修改,点击下方的构建鸿蒙NEXT即可。
在这里插入图片描述
渲染模式这里,我考虑到鸿蒙对于web是有单独的渲染进程,所以没有选择OpenGL。选择的WebGL。

在这里插入图片描述
点击构建发布后,等待进度条完成,时间较长。

在这里插入图片描述
进度条完成后,如上图所示,会出现鸿蒙项目代码,点击箭头位置就可以到源码项目处。再使用DevEco Studio进行鸿蒙项目自动签名,运行就可运行Laya小游戏。
在这里插入图片描述
上图就是Laya的2D示例项目在鸿蒙手机中运行效果的静态截图。目前自动构建的鸿蒙SDK还是API11,我尝试动手i修改为API2或者API17,项目均会报错。还是等待后续官方升级吧。目前看整体效果还是很不错。

根据构建后的鸿蒙项目,我们通过Index入口文件可以发现,官方和Laya合作,新增了很多配套的自定义Component组件,例如:LayaEditBox,LayaWebview,TextInputDialog。

import laya from 'liblaya.so'
import { ContextType } from '@ohos/libSysCapabilities'
import { TextInputInfo } from '@ohos/libSysCapabilities/src/main/ets/components/EditBox'
import { TextInputDialogEntity } from '@ohos/libSysCapabilities'
import { WebViewInfo } from '@ohos/libSysCapabilities/src/main/ets/components/webview/WebViewMsg'
import { VideoPlayerInfo } from '@ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayer'
import { WorkerMsgUtils } from '@ohos/libSysCapabilities/src/main/ets/utils/WorkerMsgUtils'
import { WorkerManager } from '../workers/WorkerManager'
import { LayaEditBox } from '../components/LayaEditBox'
import { LayaWebview } from '../components/LayaWebview'
import { LayaVideoPlayer } from '../components/LayaVideoPlayer'
import { TextInputDialog } from '../components/TextInputDialog'
import { GlobalContext, GlobalContextConstants } from "@ohos/libSysCapabilities"
import { NapiHelper } from "@ohos/libSysCapabilities/src/main/ets/napi/NapiHelper"
import { Dialog } from "@ohos/libSysCapabilities"
import deviceInfo from '@ohos.deviceInfo';
import promptAction from '@ohos.promptAction'
import process from '@ohos.process';
import { LayaHttpClient } from '@ohos/libSysCapabilities/src/main/ets/system/network/LayaHttpClient'const nativePageLifecycle: laya.CPPFunctions = laya.getContext(ContextType.JSPAGE_LIFECYCLE);
NapiHelper.registerUIFunctions();let layaWorker = WorkerManager.getInstance().getWorker();


struct Index {xcomponentController: XComponentController = new XComponentController();// EditBox editBoxArray: TextInputInfo[] = [];private editBoxIndexMap: Map<number, TextInputInfo> = new Map;// WebView webViewArray: WebViewInfo[] = [];private webViewIndexMap: Map<number, number> = new Map;// videoPlayer videoPlayerInfoArray: VideoPlayerInfo[] = [];private videoPlayerIndexMap: Map<number, VideoPlayerInfo> = new Map;// videoPlayer layaHttpClientArray: LayaHttpClient[] = [];private layaHttpClientIndexMap: Map<number, LayaHttpClient> = new Map;private pro = new process.ProcessManager();private m_nBackPressTime = 0;// textInputDialogshowMessage: TextInputDialogEntity = new TextInputDialogEntity('');dialogController: CustomDialogController = new CustomDialogController({builder: TextInputDialog({showMessage: this.showMessage}),autoCancel: true,alignment: DialogAlignment.Bottom,customStyle: true,})// PanGestureprivate panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Up | PanDirection.Down });onPageShow() {console.log('[LIFECYCLE-Page] onPageShow');GlobalContext.storeGlobalThis(GlobalContextConstants.LAYA_EDIT_BOX_ARRAY, this.editBoxArray);GlobalContext.storeGlobalThis(GlobalContextConstants.LAYA_EDIT_BOX_INDEX_MAP, this.editBoxIndexMap);GlobalContext.storeGlobalThis(GlobalContextConstants.LAYA_WORKER, layaWorker);GlobalContext.storeGlobalThis(GlobalContextConstants.LAYA_WEB_VIEW_ARRAY, this.webViewArray);GlobalContext.storeGlobalThis(GlobalContextConstants.LAYA_WEB_VIEW_INDEX_MAP, this.webViewIndexMap);GlobalContext.storeGlobalThis(GlobalContextConstants.LAYA_VIDEO_PLAYER_ARRAY, this.videoPlayerInfoArray);GlobalContext.storeGlobalThis(GlobalContextConstants.LAYA_VIDEO_PLAYER_INDEX_MAP, this.videoPlayerIndexMap);GlobalContext.storeGlobalThis(GlobalContextConstants.LAYA_DIALOG_CONTROLLER, this.dialogController);GlobalContext.storeGlobalThis(GlobalContextConstants.LAYA_SHOW_MESSAGE, this.showMessage);GlobalContext.storeGlobalThis(GlobalContextConstants.LAYA_HTTP_CLIENT_ARRAY, this.layaHttpClientArray);GlobalContext.storeGlobalThis(GlobalContextConstants.LAYA_HTTP_CLIENT_INDEX_MAP, this.layaHttpClientIndexMap);nativePageLifecycle.onPageShow();Dialog.setTitle(getContext(this).resourceManager.getStringSync($r('app.string.Dialog_Title').id));}onPageHide() {console.log('[LIFECYCLE-Page] onPageHide');nativePageLifecycle.onPageHide();}onBackPress() {console.log('[LIFECYCLE-Page] onBackPress');layaWorker.postMessage({ type: "exit" });let curtm = Date.now();let MaxDelay = 3500;if (this.m_nBackPressTime == 0 || (this.m_nBackPressTime > 0 && curtm - this.m_nBackPressTime > MaxDelay)) {this.m_nBackPressTime = Date.now();promptAction.showToast({message: $r('app.string.text_backpress_toast'),duration: 1000});} else {this.pro.exit(0);}return true;}onMouseWheel(eventType: string, scrollY: number) {// layaWorker.postMessage({ type: "onMouseWheel", eventType: eventType, scrollY: scrollY });}build() {// Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {Stack() {XComponent({id: 'xcomponentId',type: 'surface',libraryname: 'laya',controller: this.xcomponentController}).focusable(true).gesture(PanGesture(this.panOption).onActionStart(() => {this.onMouseWheel("actionStart", 0);}).onActionUpdate((event: GestureEvent) => {if (deviceInfo.deviceType === '2in1') {this.onMouseWheel("actionUpdate", event.offsetY);}}).onActionEnd(() => {this.onMouseWheel("actionEnd", 0);})).onLoad((context) => {console.log('[laya] XComponent.onLoad Callback');layaWorker.postMessage({type: "abilityContextInit",context: GlobalContext.loadGlobalThis(GlobalContextConstants.LAYA_ABILITY_CONTEXT)});layaWorker.postMessage({ type: "onXCLoad", data: "XComponent" });layaWorker.onmessage = WorkerMsgUtils.recvWorkerThreadMessage;}).onDestroy(() => {})ForEach(this.editBoxArray, (item: TextInputInfo) => {LayaEditBox({ textInputInfo: item });}, (item: TextInputInfo) => item.viewTag.toString())ForEach(this.webViewArray, (item: WebViewInfo) => {LayaWebview({ viewInfo: item })}, (item: WebViewInfo) => item.uniqueId.toString())ForEach(this.videoPlayerInfoArray, (item: VideoPlayerInfo) => {LayaVideoPlayer({ videoPlayerInfo: item })}, (item: VideoPlayerInfo) => item.viewTag.toString())}.width('100%').height('100%')}
}

http://www.hkcw.cn/article/MEgCrsLcig.shtml

相关文章

理解解释器架构:原理、组成与运行机制全解析

目录 前言1. 什么是解释器架构2. 解释器的基本组成2.1 被解释执行的程序2.2 解释器引擎2.3 解释器内部状态2.4 程序执行的当前状态2.5 存储器模型 3. 解释器的工作原理3.1 解析源代码3.2 初始化运行环境3.3 逐条执行语法结构3.4 维护程序状态3.5 内存管理与变量作用域 4. 举例&…

SOD-123和SOD-123FL封装到底有什么区别?

目录 1. 封装结构与尺寸 2. 热性能 3. 电气特性 4. 应用场景 5. 兼容性与替代性 6.价格比较 大家好&#xff0c;我们硬件工程师经常用到SOD-123和SOD-123FL的封装&#xff0c;特别是二极管&#xff0c;比如二极管1N4007便有这两种封装。那么这两种封装两者到底有什么区别…

Apache IoTDB V2.0.3 发布|新增元数据导入导出脚本适配表模型功能

Release Announcement Version 2.0.3 Apache IoTDB V2.0.3 已经发布&#xff01; V2.0.3 作为树表双模型正式版本&#xff0c;主要新增元数据导入导出脚本适配表模型、Spark 生态集成&#xff08;表模型&#xff09;、AINode 返回结果新增时间戳&#xff0c;表模型新增部分聚…

NX753NX756美光科技闪存NX784NX785

技术解读与产品特性 美光科技的NX系列闪存&#xff0c;包括NX753、NX756、NX784、NX785等型号&#xff0c;代表了当前存储技术的前沿水平。这些产品基于先进的NAND闪存技术&#xff0c;采用业界领先的3D TLC NAND技术&#xff0c;实现了高速的数据读写能力。3D TLC NAND技术通…

go语言的GMP(基础)

1.概念梳理 1.1线程 通常语义中的线程&#xff0c;指的是内核级线程&#xff0c;核心点如下&#xff1a; &#xff08;1&#xff09;是操作系统最小调度单元&#xff1b; &#xff08;2&#xff09;创建、销毁、调度交由内核完成&#xff0c;cpu 需完成用户态与内核态间的切…

Docker容器创建Redis主从集群

利用虚拟机中的三个Docker容器创建主从集群&#xff0c;容器信息&#xff1a; 容器名角色IP映射端口r1master192.168.150.1017001r2slave192.168.150.1017002r3slave192.168.150.1017003 启动多个redis实例 新建一个docker-compose文件来构建主从集群&#xff1a; 文件内容&…

RTMP播放器谁更强?深入解析SmartPlayer与VLC、PotPlayer等方案的技术差异

在视频直播、安防监控、边缘计算、AI视觉识别等场景中&#xff0c;低延迟、高稳定的RTMP播放器SDK 是关键技术模块。虽然市面上存在VLC、PotPlayer等“看起来能播”的播放器&#xff0c;但真正能适用于生产环境&#xff0c;支持工程集成、多平台、复杂网络、实时反馈的专业RTMP…

DelphiXe12创建DataSnap REST Application

服务端&#xff1a;该类型应用可直接部署EXE导云端&#xff0c;指定单页直接写好函数&#xff0c;即可导出。同时自动提供网页版的API文档&#xff0c;并可在线测试写好的接口。也可以向其他语言提供接口。 客户端&#xff1a;可生成调用服务端生成的API接口&#xff0c;无效编…

跨平台猫咪桌宠 BongoCat v0.4.0 绿色版

—————【下 载 地 址】——————— 【​本章下载一】&#xff1a;https://pan.xunlei.com/s/VORWH1a7lPhdwvon6DJgKvrNA1?pwdcw2h# 【​本章下载二】&#xff1a;https://pan.quark.cn/s/c3ac86f4e296 【百款黑科技】&#xff1a;https://ucnygalh6wle.feishu.cn/wiki/…

2023ICPC杭州题解

文章目录 M. V图(签到)J. 神秘树(交互)G. 控制贪吃蛇(最短路)D. 运算符优先级(构造)H. 甜蜜的修噶 II(概率)B. 节日装饰(bitset根号分治)F. Top Cluster(LCA动态维护树的直径) 题目链接 M. V图(签到) int cmp(int x1,int y1,int x2,int y2){int xx1*y2,yx2*y1;if(x>y)return…

图文详解Java并发面试题

文章目录 1、并发与并行2、线程安全3、线程、进程、协程4、线程间通信5、线程创建方式6、8G内存创建的线程数7、普通Java程序含有的线程8、start()、run()9、线程调度、6种状态、强制停止线程、上下文切换10、守护线程、用户线程11、 volatile 、synchronized12、sleep() 、 wa…

文档核心结构优化(程序C++...)

文档核心结构优化 一、文档核心结构优化二、C关键特性详解框架2.1 从C到C的范式迁移 三、深度代码解析模板3.1 现代C特性分层解析 四、C vs C 关键差异矩阵五、交互式文档设计策略5.1 三维学习路径5.2 代码缺陷互动区 六、现代C特性演进图七、性能优化可视化呈现&#xff08;深…

Python打卡训练营Day42

DAY 42 Grad-CAM与Hook函数 知识点回顾 回调函数lambda函数hook函数的模块钩子和张量钩子Grad-CAM的示例 作业&#xff1a;理解下今天的代码即可 import torch import torch.nn as nn import torch.nn.functional as F import torchvision import torchvision.transforms as tr…

Redis 架构设计

找实习中。。。学项目学到redis&#xff0c;又偶然看了个拓展的。是得物的Redis设计&#xff0c;拿出来分享了。 文章地址&#xff0c;https://mp.weixin.qq.com/s/dnlxCXgAxHsfyVNYTDsewA 话说&#xff0c;架构师就是干这种工作的吗&#xff1f; 插曲 才知道这么念&#xff…

振动力学:无阻尼单自由度系统

单自由度振动系统是最简单的一类振动系统,仅用一个坐标就可描述。从力学角度分析,一个实际的振动系统可由三个元件组成:惯性质量、弹性、阻尼,,它们分别描述系统的惯性、弹性、能耗机制。惯性元件是运动的实体,弹性元件提供振动回复力,阻尼元件在振动过程中消耗或吸收外界…

LangChain-结合智谱AI大模型实现自定义tools应用实例

准备: 1.可供调用的实时查询天气的接口: 百度天气接口:https://lbsyun.baidu.com/faq/api?title=webapi/weather/base(没有可以去注册用户实名认证后即可免费使用) 可以使用接口工具ApiPost调用,验证接口是否正常 2.一个csv文件,文件内容中包含各个省市区的行政编码 …

DAY 34 超大力王爱学Python

CPU性能的查看&#xff1a;看架构代际、核心数、线程数GPU性能的查看&#xff1a;看显存、看级别、看架构代际GPU训练的方法&#xff1a;数据和模型移动到GPU device上类的call方法&#xff1a;为什么定义前向传播时可以直接写作self.fc1(x) ps&#xff1a;在训练过程中可以在命…

BLIP-2

目录 摘要 Abstract BLIP-2 模型框架 预训练策略 模型优势 应用场景 实验 代码 总结 摘要 BLIP-2 是一种基于冻结的图像编码器和大型语言模型的高效视觉语言预训练模型&#xff0c;由 Salesforce 研究团队提出。它在 BLIP 的基础上进一步优化&#xff0c;通过轻量级…

通过WiFi无线连接小米手机摄像头到电脑的方法

通过WiFi无线连接小米手机摄像头到电脑的方法 以下是基于Scrcpy和DroidCam两种工具的无线连接方案&#xff0c;需提前完成开发者模式与USB调试的开启&#xff08;参考原教程步骤&#xff09;&#xff1a; 方法一&#xff1a;Scrcpy无线投屏&#xff08;无需手机端安装&#xf…

8088 单板机 NMI 中断程序示例 (脱离 DOS 环境)

求组DeepSeek给的将要进行的8088单板机NMI中断编程示例。 /* nmidemo.c - 8088 单板机 NMI 中断演示程序 */ /* 脱离 DOS 环境&#xff0c;直接运行在裸机上 */ /* 使用 Digital Mars C 编译器&#xff0c;TINY 模式编译 *//* 硬件配置假设:- 8088 CPU 4.77MHz- 8255 PPI (可…