什么是游戏场景感知

上一篇我们介绍了用 ArkTS 实现游戏场景感知,这篇来看看用 C++ 怎么做。C++ 版本功能更强大,可以获取更详细的设备信息。

简单回顾一下:游戏场景感知让游戏可以和系统"对话"。游戏告诉系统"我现在在做什么",系统告诉游戏"设备现在状态怎么样"。这样双方就能更好地配合,既保证游戏流畅,又不让手机过热。

环境搭建

硬件要求

  • 设备类型:华为手机、平板和 PC/2in1
  • HarmonyOS 系统:HarmonyOS 5.0.2 Beta1 及以上

软件要求

  • DevEco Studio 版本:DevEco Studio 5.0.2 Beta1 及以上
  • HarmonyOS SDK 版本:HarmonyOS 5.0.2 Beta1 SDK 及以上

搭建步骤

  1. 安装 DevEco Studio:去华为开发者官网下载安装
  2. 配置开发环境:确保网络环境正常
  3. 设备调试:使用真机进行调试

项目结构

├── entry/src/main
│  ├── cpp
│  │  ├── CMakeLists.txt              // 编译配置
│  │  ├── init.cpp                    // 初始化
│  │  ├── thermallevel_callback.cpp   // 温度回调
│  │  ├── updategameinfo.cpp          // 更新场景信息
│  │  ├── query_gpuinfo.cpp           // 查询GPU信息
│  │  └── query_thermalinfo.cpp       // 查询温度信息
│  ├── ets
│  │  ├── entryability
│  │  │  └── EntryAbility.ets
│  │  └── pages
│  │     └── Index.ets
│  └── resources

整体流程概览

下面是 C++ 版本游戏场景感知的整体开发流程:

配置 CMakeLists.txt

导入头文件

初始化场景感知

注册温度变化回调

上报游戏场景信息

查询 GPU 和温度信息

是否需要取消?

取消注册回调

下面是温度变化回调的处理流程:

设备状态变化

触发回调函数

获取 GPU 信息

获取温度信息

温度是否过高?

降低游戏画质

保持当前画质

释放内存资源

第一步:配置 CMakeLists.txt

target_include_directories(entry PUBLIC ${HMOS_SDK_NATIVE}/sysroot/usr/include)
target_link_directories(entry PUBLIC ${HMOS_SDK_NATIVE}/sysroot/usr/lib/aarch64-linux-ohos)
target_link_libraries(entry PUBLIC libgame_performance.z.so)

链接 game_performance 动态库,并设置头文件和库文件的搜索路径。

第二步:导入头文件

#include <GameServiceKit/game_performance.h>

导入 Game Service Kit 的 C++ 头文件。

第三步:初始化场景感知

int32_t Init() {
    // 创建初始化参数。
    GamePerformance_InitParameters *initParameters = NULL;
    HMS_GamePerformance_CreateInitParameters(&initParameters);
    
    // 设置应用版本。
    GamePerformance_ErrorCode appVersionSetCode =
        HMS_GamePerformance_InitParameters_SetAppVersion(initParameters, "1.0");
    if (appVersionSetCode != GAME_PERFORMANCE_SUCCESS) {
        // 错误处理
    }
    
    // 设置包名。
    HMS_GamePerformance_InitParameters_SetBundleName(initParameters, "com.example.gameperformancedemoforc");
    
    // 初始化。
    GamePerformance_ErrorCode ret = HMS_GamePerformance_Init(initParameters);
    
    // 释放内存。
    HMS_GamePerformance_DestroyInitParameters(&initParameters);
    int32_t code = static_cast<int32_t>(ret);
    return code;
}

初始化步骤:

  1. 创建参数对象HMS_GamePerformance_CreateInitParameters 创建一个参数对象
  2. 设置参数:设置应用版本和包名
  3. 初始化HMS_GamePerformance_Init 执行初始化
  4. 释放内存HMS_GamePerformance_DestroyInitParameters 释放参数对象

和 ArkTS 版本相比,C++ 版本需要手动管理内存,创建和销毁都要显式调用。

第四步:注册温度变化回调

// 定义回调函数
static void onThermalLevelChanged(GamePerformance_DeviceInfo *deviceInfo, void *userData) {
    (void) userData;
    
    // 获取 GPU 信息。
    GamePerformance_GpuInfo *gpuInfo = NULL;
    HMS_GamePerformance_DeviceInfo_GetGpuInfo(deviceInfo, &gpuInfo);
    
    int32_t gpuloadLevel = -1;
    int32_t vertexLevel = -1;
    int32_t fragmentLoadLevel = -1;
    int32_t bandwidthLoadLevel = -1;
    int32_t textureLoadLevel = -1;
    int32_t currentFrequency = -1;
    
    HMS_GamePerformance_GpuInfo_GetGpuLoadLevel(gpuInfo, &gpuloadLevel);
    HMS_GamePerformance_GpuInfo_GetVertexLoadLevel(gpuInfo, &vertexLevel);
    HMS_GamePerformance_GpuInfo_GetFragmentLoadLevel(gpuInfo, &fragmentLoadLevel);
    HMS_GamePerformance_GpuInfo_GetBandwidthLoadLevel(gpuInfo, &bandwidthLoadLevel);
    HMS_GamePerformance_GpuInfo_GetTextureLoadLevel(gpuInfo, &textureLoadLevel);
    HMS_GamePerformance_GpuInfo_GetCurrentFrequency(gpuInfo, &currentFrequency);

回调函数会在设备状态变化时被调用。这里获取了 GPU 的详细信息:

  • gpuloadLevel:GPU 整体负载等级
  • vertexLevel:顶点处理负载
  • fragmentLoadLevel:片元处理负载
  • bandwidthLoadLevel:带宽负载
  • textureLoadLevel:纹理负载
  • currentFrequency:当前频率
    // 获取温度信息。
    GamePerformance_ThermalInfo *thermalInfo = NULL;
    HMS_GamePerformance_DeviceInfo_GetThermalInfo(deviceInfo, &thermalInfo);
    
    int32_t margin = INT32_MIN;
    int32_t trend = INT32_MIN;
    int32_t level = -1;
    
    HMS_GamePerformance_ThermalInfo_GetThermalMargin(thermalInfo, &margin);
    HMS_GamePerformance_ThermalInfo_GetThermalTrend(thermalInfo, &trend);
    HMS_GamePerformance_ThermalInfo_GetThermalLevel(thermalInfo, &level);
    
    // 释放内存。
    HMS_GamePerformance_DestroyGpuInfo(&gpuInfo);
    HMS_GamePerformance_DestroyThermalInfo(&thermalInfo);
}

获取温度信息:

  • margin:温度余量,表示距离温度上限还有多少
  • trend:温度趋势,表示温度在升高还是降低
  • level:温度等级
int32_t RegisterThermalLevelChangedCallback() {
    // 订阅设备信息类型。
    size_t size = 2;
    GamePerformance_DeviceInfoType *types[] = {
        new GamePerformance_DeviceInfoType(GAME_PERFORMANCE_DEVICEINFO_TYPE_GPU),
        new GamePerformance_DeviceInfoType(GAME_PERFORMANCE_DEVICEINFO_TYPE_THERMAL)};
    
    void *userData = (void *)"mydata";
    
    GamePerformance_ErrorCode ret =
        HMS_GamePerformance_RegisterThermalLevelChangedCallback(types, size, onThermalLevelChanged, userData);
    
    int32_t code = static_cast<int32_t>(ret);
    return code;
}

注册回调时,指定要订阅的设备信息类型:

  • GAME_PERFORMANCE_DEVICEINFO_TYPE_GPU:GPU 信息
  • GAME_PERFORMANCE_DEVICEINFO_TYPE_THERMAL:温度信息

第五步:取消注册温度变化回调

int32_t UnregisterThermalLevelChangedCallback() {
    GamePerformance_ErrorCode ret = HMS_GamePerformance_UnregisterThermalLevelChangedCallback(onThermalLevelChanged);
    int32_t code = static_cast<int32_t>(ret);
    return code;
}

int32_t UnregisterAllThermalLevelChangedCallbacks() {
    GamePerformance_ErrorCode ret = HMS_GamePerformance_UnregisterAllThermalLevelChangedCallbacks();
    int32_t code = static_cast<int32_t>(ret);
    return code;
}

有两种取消方式:

  • UnregisterThermalLevelChangedCallback:取消指定的回调
  • UnregisterAllThermalLevelChangedCallbacks:取消所有回调

第六步:上报游戏场景信息

int32_t UpdateSceneInfo() {
    GamePerformance_SceneInfo *sceneInfo = NULL;
    HMS_GamePerformance_CreateSceneInfo(&sceneInfo);
    
    // 设置必填字段。
    HMS_GamePerformance_SceneInfo_SetSceneID(sceneInfo, 1);
    HMS_GamePerformance_SceneInfo_SetImportanceLevel(sceneInfo, GAME_PERFORMANCE_SIL_LEVEL1);
    
    // 设置可选字段。
    HMS_GamePerformance_SceneInfo_SetDescription(sceneInfo, "this is description of scene");
    HMS_GamePerformance_SceneInfo_SetSubSceneID(sceneInfo, "20101020304");
    HMS_GamePerformance_SceneInfo_SetSubDescription(sceneInfo, "this is description of subscene");
    HMS_GamePerformance_SceneInfo_SetSceneFrequency(sceneInfo, 2);
    HMS_GamePerformance_SceneInfo_SetSceneTime(sceneInfo, 15);

创建场景信息对象,设置各种参数:

  • SceneID:场景 ID
  • ImportanceLevel:重要性等级
  • Description:场景描述
  • SubSceneID:子场景 ID
  • SceneFrequency:场景刷新频率
  • SceneTime:场景持续时间
    // 设置推荐的硬件等级。
    HMS_GamePerformance_SceneInfo_SetRecommendedCpuLevel(sceneInfo, GAME_PERFORMANCE_CPU_LEVEL_HIGH);
    HMS_GamePerformance_SceneInfo_SetRecommendedGpuLevel(sceneInfo, GAME_PERFORMANCE_GPU_LEVEL_HIGH);
    HMS_GamePerformance_SceneInfo_SetRecommendedDdrLevel(sceneInfo, GAME_PERFORMANCE_DDR_LEVEL_HIGH);

设置推荐的硬件等级,告诉系统这个场景需要什么样的硬件资源。

    // 设置渲染信息。
    HMS_GamePerformance_SceneInfo_SetKeyThread(sceneInfo, "render");
    HMS_GamePerformance_SceneInfo_SetDrawCallCount(sceneInfo, 100);
    HMS_GamePerformance_SceneInfo_SetVertexCount(sceneInfo, 100);
    HMS_GamePerformance_SceneInfo_SetTriangleCount(sceneInfo, 100);
    HMS_GamePerformance_SceneInfo_SetShaderCount(sceneInfo, 100);
    HMS_GamePerformance_SceneInfo_SetTextureCount(sceneInfo, 100);
    HMS_GamePerformance_SceneInfo_SetMeshCount(sceneInfo, 100);
    HMS_GamePerformance_SceneInfo_SetChannelCount(sceneInfo, 100);
    HMS_GamePerformance_SceneInfo_SetParticipantCount(sceneInfo, 5);
    
    // 上报场景信息。
    GamePerformance_ErrorCode ret = HMS_GamePerformance_UpdateSceneInfo(sceneInfo);
    
    // 释放内存。
    HMS_GamePerformance_DestroySceneInfo(&sceneInfo);
    int32_t code = static_cast<int32_t>(ret);
    return code;
}

设置渲染相关的详细信息,这些信息可以帮助系统更好地理解游戏的负载情况。

第七步:查询 GPU 信息

int32_t QueryGpuInfo() {
    GamePerformance_GpuInfo *gpuInfo = NULL;
    GamePerformance_ErrorCode ret = HMS_GamePerformance_QueryGpuInfo(&gpuInfo);
    
    int32_t gpuloadLevel = -1;
    int32_t bandwidth = -1;
    int32_t currentFrequency = -1;
    int32_t fragmentLoadLevel = -1;
    int32_t textureLoadLevel = -1;
    int32_t vertexLoadLevel = -1;
    
    HMS_GamePerformance_GpuInfo_GetGpuLoadLevel(gpuInfo, &gpuloadLevel);
    HMS_GamePerformance_GpuInfo_GetBandwidthLoadLevel(gpuInfo, &bandwidth);
    HMS_GamePerformance_GpuInfo_GetCurrentFrequency(gpuInfo, &currentFrequency);
    HMS_GamePerformance_GpuInfo_GetFragmentLoadLevel(gpuInfo, &fragmentLoadLevel);
    HMS_GamePerformance_GpuInfo_GetTextureLoadLevel(gpuInfo, &textureLoadLevel);
    HMS_GamePerformance_GpuInfo_GetVertexLoadLevel(gpuInfo, &vertexLoadLevel);
    
    HMS_GamePerformance_DestroyGpuInfo(&gpuInfo);
    int32_t code = static_cast<int32_t>(ret);
    return code;
}

主动查询 GPU 信息,获取当前的负载和频率状态。

第八步:查询温度信息

int32_t QueryThermalInfo() {
    GamePerformance_ThermalInfo *thermalInfo = NULL;
    GamePerformance_ThermalInfoQueryParameters *parameters = NULL;
    
    // 创建查询参数。
    HMS_GamePerformance_CreateThermalInfoQueryParameters(&parameters);
    
    // 设置是否需要预测温度趋势。
    HMS_GamePerformance_ThermalInfoQueryParameters_SetNeedsPrediction(parameters, true);
    
    // 设置目标温度等级。
    HMS_GamePerformance_ThermalInfoQueryParameters_SetTargetThermalLevel(parameters, 4);
    
    // 查询温度信息。
    GamePerformance_ErrorCode ret = HMS_GamePerformance_QueryThermalInfo(parameters, &thermalInfo);
    
    int32_t margin = INT32_MIN;
    int32_t trend = INT32_MIN;
    int32_t level = -1;
    
    HMS_GamePerformance_ThermalInfo_GetThermalLevel(thermalInfo, &level);
    HMS_GamePerformance_ThermalInfo_GetThermalMargin(thermalInfo, &margin);
    HMS_GamePerformance_ThermalInfo_GetThermalTrend(thermalInfo, &trend);
    
    // 释放内存。
    HMS_GamePerformance_DestroyThermalInfo(&thermalInfo);
    HMS_GamePerformance_DestroyThermalInfoQueryParameters(&parameters);
    int32_t code = static_cast<int32_t>(ret);
    return code;
}

查询温度信息时,可以设置预测参数:

  • SetNeedsPrediction:是否需要预测温度趋势
  • SetTargetThermalLevel:目标温度等级,用于预测

C++ 版和 ArkTS 版的区别

方面 C++ 版 ArkTS 版
信息详细程度 更详细,有 GPU 负载、顶点/片元负载等 较简单,主要是温度等级
内存管理 手动管理,需要创建和销毁对象 自动管理
开发难度 较高 较低
性能 更好 一般
使用场景 复杂游戏、性能要求高 简单游戏、快速开发

适用场景

游戏场景感知特别适合以下场景:

  • 大型 3D 游戏:需要详细的 GPU 信息来优化渲染
  • 竞技游戏:对帧率要求高,需要实时调整性能
  • 长时间游戏:需要防止手机过热
  • 跨平台游戏:需要适配不同设备的性能

注意事项

  1. 内存管理:C++ 版本需要手动管理内存,创建的对象都要销毁
  2. 回调函数:回调函数要轻量,不要做耗时操作
  3. 订阅类型:注册回调时要指定订阅的设备信息类型
  4. 错误处理:每个接口调用都要检查返回值
  5. 线程安全:回调可能在非主线程调用,要注意线程安全

总结

C++ 版本的游戏场景感知功能更强大,核心流程:

  1. 配置 CMakeLists.txt,链接 game_performance 库
  2. 初始化场景感知服务
  3. 注册温度变化回调,监听设备状态
  4. 上报游戏场景信息,让系统优化资源分配
  5. 主动查询 GPU 和温度信息
  6. 不需要时取消注册回调

如果你的游戏对性能要求很高,需要详细的设备信息来优化,C++ 版本是更好的选择。

更多推荐