HarmonyOS鸿蒙Next中Laya游戏如何打包为鸿蒙应用?请提供详细操作步骤

HarmonyOS鸿蒙Next中Laya游戏如何打包为鸿蒙应用?请提供详细操作步骤 Laya游戏如何打包为鸿蒙应用?请提供详细操作步骤

3 回复

以LayaAir IDE的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();
@Entry
@Component
struct Index {
  xcomponentController: XComponentController = new XComponentController();
  // EditBox
  @State editBoxArray: TextInputInfo[] = [];
  private editBoxIndexMap: Map<number, TextInputInfo> = new Map;
  // WebView
  @State webViewArray: WebViewInfo[] = [];
  private webViewIndexMap: Map<number, number> = new Map;
  // videoPlayer
  @State videoPlayerInfoArray: VideoPlayerInfo[] = [];
  private videoPlayerIndexMap: Map<number, VideoPlayerInfo> = new Map;

  // videoPlayer
  @State layaHttpClientArray: LayaHttpClient[] = [];
  private layaHttpClientIndexMap: Map<number, LayaHttpClient> = new Map;

  private pro = new process.ProcessManager();
  private m_nBackPressTime = 0;
  // textInputDialog
  showMessage: TextInputDialogEntity = new TextInputDialogEntity('');
  dialogController: CustomDialogController = new CustomDialogController({
    builder: TextInputDialog({
      showMessage: this.showMessage
    }),
    autoCancel: true,
    alignment: DialogAlignment.Bottom,
    customStyle: true,
  })
  // PanGesture
  private 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%')
  }
}

六、引用材料:

https://www.layaair.com/#/doc

更多关于HarmonyOS鸿蒙Next中Laya游戏如何打包为鸿蒙应用?请提供详细操作步骤的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,使用Laya打包鸿蒙应用,需先安装DevEco Studio 4.0及以上版本。在LayaAir IDE中完成游戏开发后,导出项目。在DevEco Studio中创建鸿蒙应用项目,将Laya导出的资源文件复制到项目的entry/src/main/resources目录下。修改entry/src/main/module.json5配置文件,确保Ability和页面路径正确。最后,在DevEco Studio中点击Build进行编译打包,生成HAP文件。

在HarmonyOS Next中,将Laya游戏打包为鸿蒙应用,主要涉及将Laya引擎生成的Web项目(通常为HTML5格式)转换为HarmonyOS应用。以下是核心操作步骤:

  1. 环境准备

    • 安装最新版本的DevEco Studio(需支持HarmonyOS Next)。
    • 确保Node.js已安装。
    • 准备好LayaAir IDE或通过命令行工具完成的Laya游戏项目,并已发布为Web(如“发行”或“构建”生成index.html及相关JS、资源文件的目录)。
  2. 创建HarmonyOS应用工程

    • 在DevEco Studio中创建一个新的“Empty Ability”应用项目,选择API Version为Next版本。
    • 记录或修改项目的包名(Bundle Name),用于后续配置。
  3. 集成Web引擎与加载游戏资源

    • HarmonyOS Next应用通过WebView组件加载网页内容。在工程的entry/src/main/ets/pages/Index.ets(或您创建的页面)中,使用Web组件(@ohos.web.webview提供)来承载游戏页面。
    • 基本示例代码:
      import web_webview from '@ohos.web.webview';
      import { BusinessError } from '@ohos.base';
      
      @Entry
      @Component
      struct Index {
        controller: web_webview.WebviewController = new web_webview.WebviewController();
      
        build() {
          Column() {
            // 加载本地或远程游戏页面
            Web({ src: $rawfile('index.html'), controller: this.controller })
              .width('100%')
              .height('100%')
          }
          .width('100%')
          .height('100%')
        }
      }
      
  4. 放置游戏资源并配置路径

    • 将Laya发布生成的整个Web目录(包含index.html、JS、图片等文件)复制到HarmonyOS工程的entry/src/main/resources/rawfile/目录下。这是应用访问本地网页资源的默认目录。
    • 确保Web组件的src属性指向正确的文件路径,如$rawfile('index.html')
  5. 配置网络权限与WebView能力

    • 如果游戏需要访问网络(如加载远程资源或进行通信),需在entry/src/main/module.json5文件中配置ohos.permission.INTERNET网络权限。
    • 同时,在module.json5"abilities"字段中为承载页面的Ability添加"skills",确保其支持"action.system.home""entity.system.home",以便正常启动。若游戏需要更高级的WebView特性(如JavaScript交互),需在"abilities"中配置"metadata"以启用增强的网络能力。
  6. 处理可能的适配问题

    • 全屏适配:在index.html中,确保<meta name="viewport">标签设置适当,或通过CSS使游戏画布自适应屏幕。
    • 路径引用:检查Laya游戏中所有资源引用路径是否相对于index.html正确,确保在rawfile目录下能正常加载。
    • JavaScript交互:若游戏需要与HarmonyOS原生侧通信,可使用WebViewJavaScriptProxy注册对象到窗口,供游戏内JavaScript调用。
  7. 编译与构建

    • 在DevEco Studio中,点击“Build” > “Build HAP(s)”进行编译。
    • 生成的HAP文件即为HarmonyOS应用安装包,可通过模拟器或真机进行安装测试。

关键点总结:本质是将Laya游戏作为本地网页资源嵌入HarmonyOS的Web组件中运行。重点在于正确放置资源文件、配置Web组件加载路径、处理权限与屏幕适配。对于复杂的游戏,可能需进一步优化WebView性能或处理原生交互。

回到顶部