HarmonyOS分布式游戏手柄开发详解
1. 项目介绍
HarmonyOS的分布式能力可以方便地扩展虚拟终端,造就新的交互,体验新的场景。本篇codelab通过分布式能力,使手机在智慧屏附近即可迅速被虚拟成一个手柄终端,将智慧屏的交互扩展到手机,充分结合手机的多模输入和智慧屏的大屏优点,组成新的多人娱乐场景。
本应用分两部分,安装在手机上的手柄端程序和安装在大屏上的大屏端程序。
说明:
为便于演示,本篇codelab所指大屏均使用支持HarmonyOS的手机代替。
启动大屏端程序进入游戏主页面,此时会弹出手柄设备列表选择框,在选择(最多选择两个手柄设备)手柄设备点击【确定】发起连接请求;手柄端收到连接请求亦会弹出设备列表选择框,选择对应大屏设备点击【确定】,此时大屏端与手柄端连接完成。手柄端可向大屏端发送指令。
主界面:
- 手柄端点击【START】按钮,大屏端进入游戏中界面,此时会根据已连接的手柄创建对应数量的玩家飞机;敌机从屏幕顶端随机水平位置出现并向屏幕下方垂直移动;降落伞从屏幕顶端随机水平位置出现并向屏幕下方移动。
- 手柄端点击【PAUSE】按钮,游戏暂停,点击【START】按钮继续游戏。
游戏中界面:
- 手柄端可通过滑动摇杆按钮控制大屏端玩家飞机的飞行方向。
- 点击绿色按钮可以发射子弹,当子弹与敌机发生碰撞后,对应玩家飞机得分数值加100,同时摧毁子弹与敌机。
- 当玩家飞机与降落伞发生碰撞,对应屏幕左下角或右下角炸弹数量+1,同时销毁降落伞。
- 点击黄色按钮可以释放炸弹(若对应玩家机炸弹数量不为0)清空屏幕敌机,根据屏幕中被清空的敌机数量N,计算对应玩家飞机的得分N * 100。
- 当玩家飞机与敌机发生碰撞,若玩家飞机数量为2,则玩家飞机和敌机将同时被摧毁,此玩家飞机对应的手柄操作无效;若屏幕内玩家飞机均被摧毁,则游戏结束并跳转到游戏结束界面。
游戏结束界面:
- 展示玩家最终得分。
- 任一手柄端点击【PAUSE】按钮,大屏端跳转到游戏主界面;任一手柄端点击【START】按钮,大屏端跳转到游戏中界面。
效果图如下:
a) 大屏端选择手柄设备,发起连接;手柄被拉起,弹出设备选择框,选择大屏设备进行连接。
b) 游戏中,飞机移动、发射子弹、捡降落伞(获取触发大招的炸弹)。
c) 游戏结束,大屏端显示所有玩家得分,手柄端显示自己得分。
2. 环境准备
-
- 安装DevEco Studio,详情请参考DevEco Studio下载。
- 设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:
- 如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
- 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。
说明:
由于分布式跨设备启动和连接需要获取真机设备ID,故此应用效果在模拟器上无法实现。
请准备至少2台开启了开发者模式的HarmonyOS手机,满足以下要求:
所使用设备均以相同的华为账号登录。
所使用设备接入相同WiFi热点或者各设备间已完成蓝牙配对操作。
看各设备的"设置 > 超级终端"界面,确认"我的在线设备"中包含所使用的设备。
请提前申请证书。
3. 手柄端代码结构解读
在本篇codelab中我们只对核心代码进行讲解,您可以在第10章下载完整代码。手柄端工程的代码结构如下图:
- devices:设备列表适配器和设备选择对话框、权限动态申请。
- handle:手柄实体类,包含游戏暂停、重新开始以及技能是否被点击。
- proxy:定义了连接远程service(PA)实现类以及代理。
- service:远程通信,读取大屏端发过来的数据,即玩家得分。
- slice:MainAbilitySlice主界面,弹窗选择大屏设备后跳转到HandleAbilitySlice界面,HandleAbilitySlice实现了分布式设备连接和手柄界面。
- utils:CalculAngle处理摇杆滑动事件,包含计算偏移角度;Constans常量类。
- resources:存放工程使用到的资源文件,其中resources\base\layout下存放xml布局文件,resources\base\media下存放图片资源。
- config.json:应用的配置文件。建议使用SDK4,即config.json中的“target”字段和模块下的build.gradle中的“compileSdkVersion”字段修改为4。
4. 大屏端代码结构解读
本篇codelab中只对核心代码进行讲解,您可以在第10章下载完整代码。大屏端工程的代码结构如下图:
- devices:展示手柄设备列表对话框,用户选择手柄设备(最多选择两个手柄设备)进行连接。
- game:游戏中的各个角色类的定义以及游戏界面定义。
- service:远程通信,用于获取手柄端发送的数据。
- slice:游戏主界面、游戏中界面、游戏结束界面逻辑判断。
- util:工具类,Constants.java定义常量,GameUtils.java获取当前设备屏幕尺寸、将图片id转换成PixelMapHolder对象。
- GameOverAbility.java:游戏结束的Page Ability。
- MainAbility.java:游戏主界面的Page Ability,由DevEco Studio生成,不需添加逻辑。
- MyApplication.java:入口类,由DevEco Studio生成,不需添加逻辑。
- PlaneGameAbility.java:游戏中的Page Ability。
- resources:存放工程使用到的资源文件,其中resources\base\layout下存放xml布局文件,resources\base\media下存放图片资源。
- config.json:Ability声明以及权限配置。
5. 相关权限
手柄端和大屏端程序开发均需申请以下多设备协同相关的四个权限,应用权限的申请可以参考权限章节。
ohos.permission.DISTRIBUTED_DATASYNC:用于允许不同设备间的数据交换。
ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE:用于允许监听分布式组网内的设备状态变化。
ohos.permission.GET_DISTRIBUTED_DEVICE_INFO:用于允许获取分布式组网内的设备列表和设备信息。
ohos.permission.GET_BUNDLE_INFO:用于允许查询其他应用信息的权限。
说明:
其中多设备协同数据同步权限"ohos.permission.DISTRIBUTED_DATASYNC",需要按照动态申请流程向用户申请授权。
6. 分布式设备启动与连接
大屏端发起连接,进入应用,大屏端弹出手柄设备列表选择框。代码示例如下:
选择手柄设备(可多选),点击【确定】按钮,连接选择的手柄,代码示例如下:
大屏端发起连接后,手柄端被远程拉起,此时手柄端弹出设备列表选择框,选择大屏设备,点击【确定】按钮,连接选择的大屏。代码示例如下:
7. 手柄端功能
向大屏端发送指令
手柄端只是向大屏端发送指令,由大屏端实现业务逻辑。有如下指令:
- 点击绿色按钮,玩家飞机发送一个发射子弹的指令。
- 点击黄色按钮,释放大招,使屏幕上所有敌机爆炸销毁(需玩家飞机获得降落伞中炸药包,指令才可生效)。
- 点击【START】按钮,发送开始游戏的指令。
- 点击【PAUSE】按钮,发送暂停游戏的指令。
- 滑动摇杆,发送用于控制飞机飞行方向的指令。
以点击绿色按钮为例,代码示例如下:
计算偏移角度
摇杆部分,大圆是固定不动的,小圆默认停在大圆中间。手指移动时,小圆跟随手指的位置移动但不能超出大圆范围。以大圆圆心为坐标系原点,手指的坐标点与X轴形成的角度为玩家飞机的偏移角度,以此来控制飞机行进方向。关键代码示例如下:
1.计算手指所在象限
2.计算偏移角度
设置小圆坐标
当手指在大圆范围内时,小圆圆心坐标跟随手指坐标;当手指滑出大圆范围后,小圆圆心坐标在大圆圆周上,不能超出大圆范围。代码示例如下:
显示得分
手柄端接收大屏端发送的数据,更新UI组件,代码示例如下:
8. 大屏端功能
通过获取手柄端发送的数据操作大屏端:开始游戏、暂停游戏、移动飞机以及发射子弹等。大屏端可随机生成敌机、降落伞以及检测碰撞事件。
精灵类定义
1.子类公共属性定义,代码示例如下:
2.当前对象与其他对象集合的碰撞检测,如未发生碰撞返回null,否则返回碰撞对象,代码示例如下:
3.画图方法,包含画图前、画图中、画图后三个方法,代码示例如下:
4.画图前执行的方法,计算精灵类垂直方向坐标,代码示例如下:
5.画图前执行的方法,计算精灵类垂直方向坐标,代码示例如下:
6.画图之后执行的方法,销毁移出屏幕的精灵对象,代码示例如下:
7.执行销毁方法,将图片pixelMapHolder对象设置为null,将销毁状态设置为true,代码示例如下:
精灵类子类定义
1.Bomb.java降落伞类,构造方法中设置水平方向随机位置、垂直方向位置和速度,代码示例如下:
2.Bullet.java子弹类,构造函数中根据玩家飞机位置设置子弹位置以及设置速度;重写afterDraw()方法,用于销毁超出屏幕范围的子弹,代码示例如下:
3.EnemyPlane.java敌机类,构造函数中设置敌机尺寸大小、水平方向随机位置、垂直方向位置和随机速度,代码示例如下:
4.Explosion.java爆炸效果类,销毁精灵子类时展示爆炸效果,构造方法中设置爆炸位置(为被销毁对象的位置)、速度为0;重写afterDraw()方法,每8帧销毁爆炸效果,代码示例如下:
5.MyPlane.java玩家飞机类,构造方法中设置水平、垂直方向位置、速度、分数为0、炸弹数为0;重写draw()方法,在屏幕上画玩家飞机,代码示例如下:
获取手柄端数据
1.GameserviceAbility.java类中实时获取手柄端数据,调用Handle类处理数据,代码示例如下:
2.Handle.java类,根据获取的手柄端数据和GameView.java类中的status变量的值对游戏进行操作,代码示例如下:
生成玩家飞机
1.当GameView.getStatus()的值为0,且获取手柄的start参数不为0时,进入游戏界面,代码示例如下:
2.进入游戏界面后,根据获取的手柄设备id集合创建玩家飞机,代码示例如下:
3.画玩家飞机,代码示例如下:
移动玩家飞机
1.根据获取到的手柄端数据,移动玩家飞机,代码示例如下:
发射子弹
1.根据获取到的手柄端数据,创建子弹,子弹初始位置根据玩家飞机位置确定,代码示例如下:
2.在屏幕中画子弹,销毁超出屏幕范围的子弹,并从子弹集合中移除,代码示例如下:
使用炸弹
1.通过获取手柄端的设备ID获得对应的玩家飞机,若该玩家飞机炸弹数大于0,则销毁屏幕中所有敌机(敌机数为N),该玩家飞机加N * 100分,并返回分数到手柄端,代码示例如下:
随机生成敌机
1.随机生成敌机从屏幕上方任意水平位置出现,代码示例如下:
2.画敌机(每50帧生成一个敌机),销毁超出屏幕范围的敌机,并从敌机集合中移除,代码示例如下:
随机生成降落伞
1.随机生成降落伞从屏幕上方任意水平位置出现,代码示例如下:
2.画降落伞(每2000帧生成一个降落伞),销毁超出屏幕范围的降落伞,并从降落伞集合中移除,代码示例如下:
碰撞检测
1.子弹碰撞敌机后,销毁该子弹和敌机,子弹对应的玩家飞机加100分,并返回分数到手柄端,代码示例如下:
2.玩家飞机碰撞敌机后,玩家飞机和敌机被销毁,若玩家飞机数量为0,则结束游戏,代码示例如下:
3.降落伞碰撞玩家飞机后,销毁该降落伞,玩家飞机对应左/右下角炸弹数量加1,代码示例如下;
返回分数
1.创建线程并绑定事件,返回分数到手柄端,代码示例如下:
2.根据设备ID将分数返回到对应的手柄端,代码示例如下:
游戏结束
1.屏幕中所有玩家飞机被摧毁后,游戏结束(GameView.status值为3),调用Handles类并传递玩家飞机最终得分,代码示例如下:
2.Handles类跳转到游戏结束界面,并传递玩家飞机最终得分,代码示例如下:
3.游戏结束界面展示玩家飞机得分,代码示例如下:
9. 恭喜您
通过本篇codelab,您可以学到:
- 常用布局和常用组件
- 分布式设备启动与连接
- 线程间通信
- 利用canvas组件绘制图形
10. 参考
你好,gitee源码失效