鸿蒙HarmonyOS项目实战开发:传炸弹小游戏

简介

demo基于OpenHarmony系统使用ETS语言进行编写,在邀请用户进行设备认证后,用户根据操作提示完成相应操作,然后通过分布式流转实现随机传递炸弹的效果。

show

目录

完整的项目结构目录如下

├─entry\src\main
│     │  config.json // 应用配置
│     ├─ets
│     │  └─MainAbility
│     │      │  app.ets //ets应用程序主入口
│     │      └─pages
│     │              CommonLog.ets // 日志类
│     │              game.ets // 游戏首页
│     │              RemoteDeviceManager.ets // 设备管理类
│     └─resources // 静态资源目录
│         ├─base
│         │  ├─element
│         │  ├─graphic
│         │  ├─layout
│         │  ├─media // 存放媒体资源
│         │  └─profile
│         └─rawfile

开发步骤

1. 新建OpenHarmony ETS项目

在DevEco Studio中点击File -> New Project ->[Standard]Empty Ability->Next,Language 选择ETS语言,最后点击Finish即创建成功。 

image-20211124092813545

2. 编写游戏页面

image-20211124093106260

效果图如上可以分为两部分

2.1 顶部状态提示栏

1)首先在@entry组件入口build()中使用Stack作为容器,达到图片和文字堆叠的效果;

2)接着依次写入ImageColumn包裹的两个Text组件;

  Stack() {
     Image($r("app.media.title")).objectFit(ImageFit.Contain).height(120)
     Column() {
        Text(this.duration.toString() + 'ms').fontColor(Color.White)
        Text(this.touchText).fontColor(Color.White)
     }
  }
2.2 中间游戏炸弹九宫格区域

1)使用Grid网格容器来编写九宫格区域;

2)在GridItemStack容器依次添加方块背景图片和炸弹图片;

3)在visibility属性中用bombIndex变量值来决定炸弹显示的位置;

4)通过onClick点击事件和GestureGroup组合手势加入单击、双击和长按的监听事件;

 Stack() {
     Image($r("app.media.background")).objectFit(ImageFit.Contain)
     Grid() {
         ForEach(this.grid, (item) => {
             GridItem() {
                 Stack() {
                     Image($r("app.media.squares")).objectFit(ImageFit.Contain)
                     Image($r("app.media.bomb"))
                         .width('50%')
                         .objectFit(ImageFit.Contain)
                         .visibility(this.bombIndex == item ? Visibility.Visible : Visibility.Hidden)
                         // 炸弹点击事件
                         .onClick((event) => {
                             // 单击
                             this.judgeGame(RuleType.click)
                         })
                         .gesture(
                         GestureGroup(GestureMode.Exclusive,
                         LongPressGesture({ repeat: false })
                             .onAction((event: GestureEvent) => {
                                 // 长按
                                 this.judgeGame(RuleType.longPress)
                             }),
                         TapGesture({ count: 2 })
                             .onAction(() => {
                                 // 双击
                                 this.judgeGame(RuleType.doubleClick)
                             })
                         )
                 }
             }.forceRebuild(false)
         }, item => item)
     }
     .columnsTemplate('1fr 1fr 1fr')
     .rowsTemplate('1fr 1fr 1fr')
     .columnsGap(10)
     .rowsGap(10)
     .width('90%')
     .height('75%')
 }.width('80%').height('70%')
3. 添加弹窗
3.1 创建规则游戏弹窗

1)通过 @CustomDialog 装饰器来创建自定义弹窗,使用方式可参考 自定义弹窗

2)规则弹窗效果如下,弹窗组成由两个Text和两个Image竖向排列组成,所以我们可以在build()下使用Column容器来包裹,组件代码如下;

微信图片_20211124151049

   @CustomDialog
   struct RuleDialog {
      controller: CustomDialogController
      confirm: () => void
      invite: () => void
      @Consume deviceList: RemoteDevice[]
   
      build() {
         Column() {
            Text('游戏规则').fontSize(30).margin(20)
            Text('炸弹会随机出现在9个方块内,需要在规定时间内完成指定操作(点击、双击或长按),即可将炸弹传递给下一个人,小心炸弹可是会越来越快的喔!')
               .fontSize(24).margin({ bottom: 10 })
            Image($r("app.media.btn_start")).objectFit(ImageFit.Contain).height(80).margin(10)
               .onClick(() => {
                  console.info(TAG + 'Click start game')
                  if (checkTrustedDevice(this.remoteDeviceModel)) {
                     this.controller.close()
                     this.confirm()
                  }
               })
            Image($r("app.media.btn_Invite")).objectFit(ImageFit.Contain).height(80).margin(10)
               .onClick(() => {
                  this.invite()
               })
         }.width('90%')
         .margin(20)
         .backgroundColor(Color.White)
      }
   }

3)在@entry创建CustomDialogController对象并传入弹窗所需参数,后面可通过该对象open()和close()方法进行打开和关闭弹窗;

@Provide deviceList: RemoteDevice[] = []
private ruleDialog: CustomDialogController = new CustomDialogController({
   builder: RuleDialog({
      invite: () => this.InvitePlayer(),
      confirm: () => this.startGame(),
      deviceList: this.deviceList
   }),
   autoCancel: false
})
3.2 创建游戏失败弹窗,并添加动画效果

合成 4

1)编写弹窗布局:将游戏失败文本、炸弹图片和再来一局按钮图片放置于Column容器中;

2)用变量来控制动画起始和结束的位置:用Flex容器包裹炸弹图片,并用@State装饰变量toggle,通过变量来动态修改Flex的direction属性;

@State toggle: boolean = true
private controller: CustomDialogController
@Consume deviceList: RemoteDevice[]
private confirm: () => void
private interval = null

build() {
   Column() {
      Text('游戏失败').fontSize(30).margin(20)
      Flex({
         direction: this.toggle ? FlexDirection.Column : FlexDirection.ColumnReverse,
         alignItems: ItemAlign.Center
      })
      {
         Image($r("app.media.bomb")).objectFit(ImageFit.Contain).height(80)
      }.height(200)

      Image($r("app.media.btn_restart")).objectFit(ImageFit.Contain).height(120).margin(10)
         .onClick(() => {
               this.controller.close()
               this.confirm()
         })
   }
   .width('80%')
   .margin(50)
   .backgroundColor(Color.White)
}

3)设置动画效果:使用 animateTo 显式动画接口炸弹位置切换时添加动画,并且设置定时器定时执行动画;

aboutToAppear() {
   this.setBombAnimate()
}

setBombAnimate() {
   let fun = () => {
      this.toggle = !this.toggle;
   }
   this.interval = setInterval(() => {
      animateTo({ duration: 1500, curve: Curve.Sharp }, fun)
   }, 1600)
}
4. 添加分布式流转

分布式流转需要在同一网络下通过 DeviceManager组件 进行设备间发现和认证,获取到可信设备的deviceId调用 featureAbility.startAbility ,即可把应用程序流转到另一设备。

原本分布式流转应用流程如下:

1)创建DeviceManager实例;

2)调用实例的startDeviceDiscovery(),开始设备发现未信任设备;

3)设置设备状态监听on('deviceStateChange',callback),监听设备上下线状态;

4)设置设备状态监听on('deviceFound',callback),监听设备发现;

5)传入未信任设备参数,调用实例authenticateDevice方法,对设备进行PIN码认证;

6)若是已信任设备,可通过实例的getTrustedDeviceListSync()方法来获取设备信息;

7)将设备信息中的deviceId传入featureAbility.startAbility方法,实现流转;

8)流转接收方可通过featureAbility.getWant()获取到发送方携带的数据;

9)注销设备发现监听off('deviceFound');

10)注销设备状态监听off('deviceStateChange');

项目中将上面设备管理封装至RemoteDeviceManager,通过RemoteDeviceManager的四个方法来动态维护deviceList设备信息列表。

RemoteDeviceManager

项目实现分布式流转只需如下流程:

4.1 创建RemoteDeviceManager实例

1)导入RemoteDeviceManager

import {RemoteDeviceManager} from './RemoteDeviceManager'

2)声明@Provide装饰的设备列表变量deviceList,和创建RemoteDeviceManager实例。

@Provide deviceList: RemoteDevice[] = []
private remoteDm: RemoteDeviceManager = new RemoteDeviceManager(this.deviceList)
4.2 刷新设备列表

在生命周期aboutToAppear中,调用刷新设备列表和开始发现设备。

aboutToAppear定义:函数在创建自定义组件的新实例后,在执行其build函数之前执行。

aboutToAppear() {
    this.remoteDm.refreshRemoteDeviceList() // 刷新设备列表
    this.remoteDm.startDeviceDiscovery() // 开始发现设备
}
4.3 设备认证
invitePlayer(remoteDevice:RemoteDevice) {
    if (remoteDevice.status == RemoteDeviceStatus.ONLINE) {
        prompt.showToast({ message: "Already invited!" })
        return
    }
    this.remoteDm.authDevice(remoteDevice).then(() => {
        prompt.showToast({ message: "Invite success! deviceName=" + remoteDevice.deviceName })
    }).catch(() => {
        prompt.showToast({ message: "Invite fail!" })
    })
}
4.4 跨设备流转

从deviceList中获取设备列表在线的设备Id,通过featureAbility.startAbility进行流转。

async startAbilityRandom() {
    let deviceId = this.getRandomDeviceId() // 随机获取设备id
    CommonLog.info('featureAbility.startAbility deviceId=' + deviceId);
    let bundleName = await getBundleName()
    let wantValue = {
        bundleName: bundleName,
        abilityName: 'com.sample.bombgame.MainAbility',
        deviceId: deviceId,
        parameters: {
            ongoing: true,
            transferNumber: this.transferNumber + 1
        }
    };
    featureAbility.startAbility({
        want: wantValue
    }).then((data) => {
        CommonLog.info(' featureAbility.startAbility finished, ' + JSON.stringify(data));
        featureAbility.terminateSelf((error) => {
            CommonLog.info('terminateSelf finished, error=' + error);
        });
    });
}
4.5 注销监听

在声明周期aboutToDisappear进行注销监听。

aboutToDisappear定义:函数在自定义组件析构消耗之前执行。

aboutToDisappear() {
    this.remoteDm.stopDeviceDiscovery() // 注销监听
}
5. 编写游戏逻辑
5.1 开始游戏
startGame() {
    CommonLog.info('startGame');
    this.randomTouchRule() // 随机游戏点击规则
    this.setRandomBomb() // 随机生成炸弹位置
    this.stopCountDown() // 停止倒计时
    if (this.transferNumber < 10) {
        this.duration = 3000 - this.transferNumber * 100
    } else {
        this.duration = 2000
    }
    const interval: number = 500
    // 开始倒计时
    this.timer = setInterval(() => {
        if (this.duration <= interval) {
            this.duration = 0
            clearInterval(this.timer)
            this.timer = null
            this.gameFail()
        } else {
            this.duration -= interval
        }
    }, interval)
}
5.2 判断输赢

编写判断逻辑,用于不同的点击事件中调用。

/**
 * 判断游戏输赢
 * @param operation 点击类型
 */
judgeGame(operation:RuleType) {
   this.stopCountDown()
   if (operation != this.ruleText) {
      this.gameFail()
   } else {
      prompt.showToast({ message: "finish" })
      this.bombIndex = -1
      this.startAbilityRandom()
   }
}
5.3 游戏失败

游戏失败,弹出游戏失败弹框。

gameFail() {
    prompt.showToast({
        message: 'Game Fail'
    })
    CommonLog.info('gameFail');
    this.gameFailDialog.open()
}

最后,有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(Harmony NEXT)资料用来跟着学习是非常有必要的。 

为了能够帮助大家快速掌握鸿蒙(Harmony NEXT)应用开发技术知识。在此给大家分享一下我结合鸿蒙最新资料整理出来的鸿蒙南北向开发学习路线以及整理的最新版鸿蒙学习文档资料。

这份鸿蒙(Harmony NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点。

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

如果你是一名有经验的资深Android移动开发、Java开发、前端开发、对鸿蒙感兴趣以及转行人员,可以直接领取这份资料

 获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

鸿蒙(Harmony NEXT)最新学习路线

  •  HarmonOS基础技能

  • HarmonOS就业必备技能 
  •  HarmonOS多媒体技术

  • 鸿蒙NaPi组件进阶

  • HarmonOS高级技能

  • 初识HarmonOS内核 
  • 实战就业级设备开发

 有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

《鸿蒙 (OpenHarmony)开发入门教学视频》

《鸿蒙生态应用开发V2.0白皮书》

图片

《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

图片

 《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

图片

 《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ……

图片

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

图片

 获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个使用.NET编写的简单炸弹人小游戏的示例代码: ```csharp using System; namespace BombermanGame { class Program { static int playerX = 0; static int playerY = 0; static bool gameOver = false; static void Main(string[] args) { ConsoleKeyInfo keyInfo; Console.WriteLine("Welcome to Bomberman!"); while (!gameOver) { DrawGame(); keyInfo = Console.ReadKey(true); // 根据玩家输入移动炸弹人 switch (keyInfo.Key) { case ConsoleKey.LeftArrow: MovePlayer(-1, 0); break; case ConsoleKey.RightArrow: MovePlayer(1, 0); break; case ConsoleKey.UpArrow: MovePlayer(0, -1); break; case ConsoleKey.DownArrow: MovePlayer(0, 1); break; case ConsoleKey.Escape: gameOver = true; break; } } Console.WriteLine("Game Over. Press any key to exit..."); Console.ReadKey(); } static void DrawGame() { Console.Clear(); // 绘制游戏地图 string[] map = { "#######", "# #", "# #", "# P #", "# #", "#######" }; for (int y = 0; y < map.Length; y++) { for (int x = 0; x < map[y].Length; x++) { if (x == playerX && y == playerY) { Console.Write("P"); // 绘制炸弹人 } else { Console.Write(map[y][x]); // 绘制地图元素 } } Console.WriteLine(); } } static void MovePlayer(int deltaX, int deltaY) { int newPlayerX = playerX + deltaX; int newPlayerY = playerY + deltaY; // 检查新位置是否在游戏地图内 if (newPlayerX >= 0 && newPlayerX < 7 && newPlayerY >= 0 && newPlayerY < 6) { // 检查新位置是否是空地 if (GetMapElement(newPlayerX, newPlayerY) == ' ') { playerX = newPlayerX; playerY = newPlayerY; } } } static char GetMapElement(int x, int y) { string[] map = { "#######", "# #", "# #", "# P #", "# #", "#######" }; return map[y][x]; } } } ``` 这个示例使用控制台来绘制游戏地图,并通过方向键让玩家移动炸弹人(P)。地图由一个字符串数组表示,每个字符代表一个地图元素。玩家通过移动来躲避障碍物(#)并探索游戏世界。 请注意,这只是一个简单的示例,你可以根据自己的需求和创意来扩展和改进游戏。你可以添加敌人、炸弹、道具等元素,以及更复杂的游戏规则和逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值