鸿蒙分布式游戏手柄系统设计与实现 原创

进修的泡芙
发布于 2025-6-18 22:15
浏览
0收藏

鸿蒙分布式游戏手柄系统设计与实现

一、系统架构设计

基于HarmonyOS的分布式能力和传感器技术,我们设计了一个将手机变为游戏手柄的系统,可以将手机的运动传感器和触摸输入转换为控制指令,跨设备控制其他设备上的游戏。

!https://example.com/distributed-gamepad-arch.png

系统包含三个核心模块:
传感器控制模块 - 使用@ohos.sensor获取手机传感器数据

分布式通信模块 - 通过@ohos.distributedDeviceManager建立设备间连接

游戏指令转换模块 - 将传感器和触摸输入转换为标准游戏指令

二、核心代码实现
分布式手柄服务(ArkTS)

// GamepadService.ets
import sensor from ‘@ohos.sensor’;
import deviceManager from ‘@ohos.distributedDeviceManager’;
import distributedData from ‘@ohos.distributedData’;

const GAMEPAD_SYNC_CHANNEL = ‘gamepad_control’;

class GamepadService {
private static instance: GamepadService = null;
private deviceManager: deviceManager.DeviceManager;
private dataManager: distributedData.DataManager;
private targetDeviceId: string = ‘’;
private listeners: GamepadListener[] = [];

// 手柄状态
@State buttonA: boolean = false;
@State buttonB: boolean = false;
@State buttonX: boolean = false;
@State buttonY: boolean = false;
@State axisX: number = 0; // -1.0到1.0
@State axisY: number = 0; // -1.0到1.0

private constructor() {
this.initDeviceManager();
this.initDataManager();
this.initSensors();
public static getInstance(): GamepadService {

if (!GamepadService.instance) {
  GamepadService.instance = new GamepadService();

return GamepadService.instance;

private initDeviceManager() {

deviceManager.createDeviceManager('com.example.gamepad', (err, manager) => {
  if (err) {
    console.error('创建DeviceManager失败:', JSON.stringify(err));
    return;

this.deviceManager = manager;

  this.discoverDevices();
});

private initDataManager() {

this.dataManager = distributedData.createDataManager({
  bundleName: 'com.example.gamepad',
  area: distributedData.Area.GLOBAL
});

this.dataManager.registerDataListener(GAMEPAD_SYNC_CHANNEL, (data) => {
  this.handleSyncData(data);
});

private initSensors() {

// 加速度传感器
sensor.on(sensor.SensorId.ACCELEROMETER, (data) => {
  this.handleAccelerometer(data);
});

// 陀螺仪
sensor.on(sensor.SensorId.GYROSCOPE, (data) => {
  this.handleGyroscope(data);
});

private discoverDevices() {

this.deviceManager.startDeviceDiscovery(['gameConsole']);

this.deviceManager.on('deviceFound', (data) => {
  this.listeners.forEach(listener => {
    listener.onDeviceDiscovered(data.device);
  });
});

public connectToDevice(deviceId: string): boolean {

this.targetDeviceId = deviceId;
return true;

public disconnect(): void {

this.targetDeviceId = '';

public setButtonState(button: ‘A’ ‘B’ ‘X’
‘Y’, pressed: boolean): void {

switch (button) {
  case 'A': this.buttonA = pressed; break;
  case 'B': this.buttonB = pressed; break;
  case 'X': this.buttonX = pressed; break;
  case 'Y': this.buttonY = pressed; break;

this.syncGamepadState();

public setAxis(x: number, y: number): void {

this.axisX = Math.max(-1.0, Math.min(1.0, x));
this.axisY = Math.max(-1.0, Math.min(1.0, y));
this.syncGamepadState();

private handleAccelerometer(data: sensor.AccelerometerResponse) {

// 简单处理:将手机倾斜转换为方向轴
const sensitivity = 0.1;
const x = -data.x * sensitivity;
const y = data.y * sensitivity;
this.setAxis(x, y);

private handleGyroscope(data: sensor.GyroscopeResponse) {

// 可用于更精确的控制或特殊动作检测

private syncGamepadState(): void {

if (!this.targetDeviceId) return;

const state: GamepadState = {
  buttonA: this.buttonA,
  buttonB: this.buttonB,
  buttonX: this.buttonX,
  buttonY: this.buttonY,
  axisX: this.axisX,
  axisY: this.axisY,
  timestamp: Date.now()
};

this.dataManager.sendData(this.targetDeviceId, GAMEPAD_SYNC_CHANNEL, state);

private handleSyncData(data: any): void {

if (data?.type === 'gamepadFeedback') {
  this.listeners.forEach(listener => {
    listener.onFeedback(data.feedback);
  });

}

public addListener(listener: GamepadListener): void {
if (!this.listeners.includes(listener)) {
this.listeners.push(listener);
}

public removeListener(listener: GamepadListener): void {
this.listeners = this.listeners.filter(l => l !== listener);
}

interface GamepadState {
buttonA: boolean;
buttonB: boolean;
buttonX: boolean;
buttonY: boolean;
axisX: number;
axisY: number;
timestamp: number;
interface GamepadFeedback {

vibration?: number; // 震动强度 0-1
ledColor?: string; // LED颜色
interface GamepadListener {

onDeviceDiscovered(device: deviceManager.DeviceInfo): void;
onFeedback(feedback: GamepadFeedback): void;
export const gamepadService = GamepadService.getInstance();

手柄控制界面(ArkUI)

// GamepadController.ets
import { gamepadService } from ‘./GamepadService’;

@Entry
@Component
struct GamepadController {
@State discoveredDevices: deviceManager.DeviceInfo[] = [];
@State connectedDevice: deviceManager.DeviceInfo | null = null;
@State showControls: boolean = false;
@State feedbackColor: string = ‘#FFFFFF’;

private gamepadListener: GamepadListener = {
onDeviceDiscovered: (device) => {
this.discoveredDevices = […this.discoveredDevices, device];
},
onFeedback: (feedback) => {
if (feedback.ledColor) {
this.feedbackColor = feedback.ledColor;
}

};

aboutToAppear() {
gamepadService.addListener(this.gamepadListener);
aboutToDisappear() {

gamepadService.removeListener(this.gamepadListener);

build() {

Column() {
  if (!this.connectedDevice) {
    this.buildDeviceList()

else {

    this.buildGamepadControls()

}

.width('100%')
.height('100%')
.backgroundColor(this.feedbackColor)

@Builder

buildDeviceList() {
Column() {
Text(‘可连接的设备’)
.fontSize(20)
.margin({ bottom: 20 })

  if (this.discoveredDevices.length === 0) {
    Text('正在搜索设备...')
      .fontSize(16)

else {

    List() {
      ForEach(this.discoveredDevices, (device) => {
        ListItem() {
          Column() {
            Text(device.deviceName)
              .fontSize(18)
            Text(device.deviceId)
              .fontSize(12)
              .margin({ top: 4 })

.width(‘100%’)

          .padding(10)
          .onClick(() => {
            this.connectToDevice(device);
          })

})

.layoutWeight(1)

}

.padding(20)

@Builder

buildGamepadControls() {
Column() {
// 设备信息
Row() {
Text(this.connectedDevice?.deviceName || ‘已连接设备’)
.fontSize(18)
.layoutWeight(1)

    Button('断开')
      .onClick(() => {
        this.disconnect();
      })

.margin({ bottom: 20 })

  // 方向控制区
  Stack() {
    // 背景
    Circle()
      .width(200)
      .height(200)
      .fill('#EEEEEE')
    
    // 摇杆
    Circle()
      .width(60)
      .height(60)
      .fill('#333333')
      .position({
        x: 100 + 70 * gamepadService.axisX - 30,
        y: 100 + 70 * gamepadService.axisY - 30
      })

.width(200)

  .height(200)
  .margin({ bottom: 30 })
  .gesture(
    GestureGroup(GestureMode.Exclusive,
      PanGesture({ distance: 5 })
        .onActionUpdate((event: GestureEvent) => {
          const centerX = 100;
          const centerY = 100;
          const maxDistance = 70;
          
          let dx = event.offsetX - centerX;
          let dy = event.offsetY - centerY;
          const distance = Math.sqrt(dx  dx + dy  dy);
          
          if (distance > maxDistance) {
            dx = dx * maxDistance / distance;
            dy = dy * maxDistance / distance;

gamepadService.setAxis(dx / maxDistance, dy / maxDistance);

        })
        .onActionEnd(() => {
          gamepadService.setAxis(0, 0);
        })
    )
  )
  
  // 按钮区
  Grid() {
    GridItem() {
      Button('A')
        .fontSize(24)
        .backgroundColor('#FF0000')
        .onTouch((event: TouchEvent) => {
          if (event.type === TouchType.Down) {
            gamepadService.setButtonState('A', true);

else if (event.type === TouchType.Up) {

            gamepadService.setButtonState('A', false);

})

GridItem() {

      Button('B')
        .fontSize(24)
        .backgroundColor('#00FF00')
        .onTouch((event: TouchEvent) => {
          if (event.type === TouchType.Down) {
            gamepadService.setButtonState('B', true);

else if (event.type === TouchType.Up) {

            gamepadService.setButtonState('B', false);

})

GridItem() {

      Button('X')
        .fontSize(24)
        .backgroundColor('#0000FF')
        .onTouch((event: TouchEvent) => {
          if (event.type === TouchType.Down) {
            gamepadService.setButtonState('X', true);

else if (event.type === TouchType.Up) {

            gamepadService.setButtonState('X', false);

})

GridItem() {

      Button('Y')
        .fontSize(24)
        .backgroundColor('#FFFF00')
        .onTouch((event: TouchEvent) => {
          if (event.type === TouchType.Down) {
            gamepadService.setButtonState('Y', true);

else if (event.type === TouchType.Up) {

            gamepadService.setButtonState('Y', false);

})

}

  .columnsTemplate('1fr 1fr')
  .rowsTemplate('1fr 1fr')
  .columnsGap(20)
  .rowsGap(20)
  .width('80%')

.padding(20)

private connectToDevice(device: deviceManager.DeviceInfo): void {

const success = gamepadService.connectToDevice(device.deviceId);
if (success) {
  this.connectedDevice = device;

}

private disconnect(): void {
gamepadService.disconnect();
this.connectedDevice = null;
}

游戏主机端接收器(ArkTS)

// GameConsoleReceiver.ets
import distributedData from ‘@ohos.distributedData’;

class GameConsoleReceiver {
private static instance: GameConsoleReceiver = null;
private dataManager: distributedData.DataManager;
private listeners: ConsoleListener[] = [];

private constructor() {
this.initDataManager();
public static getInstance(): GameConsoleReceiver {

if (!GameConsoleReceiver.instance) {
  GameConsoleReceiver.instance = new GameConsoleReceiver();

return GameConsoleReceiver.instance;

private initDataManager() {

this.dataManager = distributedData.createDataManager({
  bundleName: 'com.example.gameconsole',
  area: distributedData.Area.GLOBAL
});

this.dataManager.registerDataListener(GAMEPAD_SYNC_CHANNEL, (data) => {
  this.handleGamepadInput(data);
});

private handleGamepadInput(state: GamepadState): void {

this.listeners.forEach(listener => {
  listener.onGamepadInput(state);
});

public sendFeedback(deviceId: string, feedback: GamepadFeedback): void {

this.dataManager.sendData(deviceId, GAMEPAD_SYNC_CHANNEL, {
  type: 'gamepadFeedback',
  feedback: feedback
});

public addListener(listener: ConsoleListener): void {

if (!this.listeners.includes(listener)) {
  this.listeners.push(listener);

}

public removeListener(listener: ConsoleListener): void {
this.listeners = this.listeners.filter(l => l !== listener);
}

interface ConsoleListener {
onGamepadInput(state: GamepadState): void;
export const consoleReceiver = GameConsoleReceiver.getInstance();

三、项目配置
权限配置

// module.json5
“module”: {

"requestPermissions": [

“name”: “ohos.permission.DISTRIBUTED_DATASYNC”,

    "reason": "跨设备同步游戏控制指令"
  },

“name”: “ohos.permission.ACCELEROMETER”,

    "reason": "获取加速度传感器数据"
  },

“name”: “ohos.permission.GYROSCOPE”,

    "reason": "获取陀螺仪传感器数据"
  },

“name”: “ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE”,

    "reason": "发现和管理分布式设备"

],

"abilities": [

“name”: “MainAbility”,

    "type": "page",
    "visible": true

],

"distributedNotification": {
  "scenarios": [

“name”: “gamepad_control”,

      "value": "game_controller"

]

}

资源文件

<!-- resources/base/element/string.json -->
“string”: [

“name”: “app_name”,

  "value": "分布式游戏手柄"
},

“name”: “available_devices”,

  "value": "可连接的设备"
},

“name”: “searching_devices”,

  "value": "正在搜索设备..."
},

“name”: “connected_device”,

  "value": "已连接设备"
},

“name”: “disconnect_button”,

  "value": "断开"

]

四、功能扩展
触觉反馈实现

// 在GamepadController中添加触觉反馈
import vibrator from ‘@ohos.vibrator’;

class GamepadController {
// …其他代码

private playHapticFeedback(intensity: number, duration: number): void {
const vibrationPattern = {
intensity: intensity, // 0-100
duration: duration // 毫秒
};

vibrator.vibrate(vibrationPattern, (err) => {
  if (err) {
    console.error('触发震动失败:', JSON.stringify(err));

});

// 在反馈接收处调用

private handleFeedback(feedback: GamepadFeedback): void {
if (feedback.vibration) {
this.playHapticFeedback(feedback.vibration * 100, 200);
// …其他反馈处理

}

自定义控制布局

// 在GamepadController中添加布局配置
@State controlLayout: ‘default’ | ‘alternate’ = ‘default’;

@Builder
buildGamepadControls() {
if (this.controlLayout === ‘default’) {
this.buildDefaultLayout();
else {

this.buildAlternateLayout();

// 布局切换按钮

Button(‘切换布局’)
.onClick(() => {
this.controlLayout = this.controlLayout === ‘default’ ? ‘alternate’ : ‘default’;
})
.margin({ top: 20 })
@Builder

buildAlternateLayout() {
// 实现另一种按钮布局
// …

多手柄支持

// 在GameConsoleReceiver中修改
class GameConsoleReceiver {
private connectedGamepads: Map<string, GamepadState> = new Map();

private handleGamepadInput(state: GamepadState, deviceId: string): void {
this.connectedGamepads.set(deviceId, state);

this.listeners.forEach(listener => {
  listener.onGamepadInput(deviceId, state);
});

public getConnectedGamepads(): Map<string, GamepadState> {

return new Map(this.connectedGamepads);

}

// 游戏逻辑中可以区分不同手柄
consoleReceiver.addListener({
onGamepadInput: (deviceId, state) => {
// 根据deviceId区分不同玩家
if (deviceId === player1DeviceId) {
// 玩家1逻辑
else {

  // 玩家2逻辑

}

});

五、总结

通过这个分布式游戏手柄系统的实现,我们学习了:
使用HarmonyOS传感器获取手机运动数据

通过分布式能力建立设备间通信

将传感器和触摸输入转换为游戏控制指令

实现双向反馈机制(视觉和触觉)

构建完整的控制界面

系统特点:
低延迟的控制指令传输

利用手机传感器实现丰富的控制方式

可扩展的多手柄支持

双向反馈增强游戏体验

这个系统可以进一步扩展为功能更完善的游戏控制平台,如:
支持更多自定义控制布局

添加按键映射配置

实现控制指令录制和回放

支持云端配置同步

添加游戏内快捷指令功能

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报
回复
    相关推荐