人体学接口设备(HID)开发指南
本章将详细介绍游戏引擎中人体学接口设备(Human Interface Device, HID)的理论与实现,提供C和C++的实例代码,帮助开发者构建高效、响应迅速的输入系统。
8.1 各种人体学接口设备
人体学接口设备是用户与游戏交互的关键媒介,包括但不限于以下几类:
8.1.1 标准输入设备
cpp
// 常见标准输入设备枚举
enum class StandardInputDevice {
Keyboard,
Mouse,
Gamepad,
Joystick,
TouchScreen,
MotionController
};
8.1.2 专业游戏设备
cpp
// 专业游戏输入设备
struct SpecializedGameDevice {
std::string deviceName;
std::string manufacturer;
uint32_t numButtons;
uint32_t numAxes;
bool hasForceFeadback;
bool hasMotionSensing;
// 设备功能检测
bool supportsFeature(GameDeviceFeature feature) const {
// 实现检测逻辑
return (supportedFeatures & static_cast<uint32_t>(feature)) != 0;
}
private:
uint32_t supportedFeatures;
};
8.1.3 VR/AR接口设备
cpp
// VR控制器数据结构
struct VRControllerState {
Vector3 position; // 3D空间位置
Quaternion orientation; // 旋转方向
bool triggerPressed; // 扳机按钮
float triggerValue; // 扳机模拟值(0.0-1.0)
bool touchpadPressed; // 触摸板按下
Vector2 touchpadPosition; // 触摸位置
// 手柄震动控制
void applyHapticFeedback(float intensity, float durationMs) {
// 实现力反馈逻辑
}
};
8.2 人体学接口设备的接口技术
8.2.1 USB设备接口
cpp
#include <libusb-1.0/libusb.h>
class USBDeviceInterface {
private:
libusb_context* context;
libusb_device_handle* deviceHandle;
public:
USBDeviceInterface() : context(nullptr), deviceHandle(nullptr) {}
~USBDeviceInterface() {
close();
}
bool initialize() {
int result = libusb_init(&context);
return (result == 0);
}
bool openDevice(uint16_t vendorId, uint16_t productId) {
deviceHandle = libusb_open_device_with_vid_pid(context, vendorId, productId);
return (deviceHandle != nullptr);
}
bool claimInterface(int interfaceNumber) {
if (deviceHandle) {
return (libusb_claim_interface(deviceHandle, interfaceNumber) == 0);
}
return false;
}
int readData(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) {
int transferred = 0;
if (deviceHandle) {
libusb_bulk_transfer(deviceHandle, endpoint, data, length, &transferred, timeout);
}
return transferred;
}
void close() {
if (deviceHandle) {
libusb_close(deviceHandle);
deviceHandle = nullptr;
}
if (context) {
libusb_exit(context);
context = nullptr;
}
}
};
8.2.2 蓝牙设备接口
cpp
#ifdef _WIN32
#include <windows.h>
#include <bthsdpdef.h>
#include <bluetoothapis.h>
#endif
class BluetoothDeviceInterface {
private:
// 平台特定的蓝牙句柄
void* deviceHandle;
bool connected;
public:
BluetoothDeviceInterface() : deviceHandle(nullptr), connected(false) {}
bool scanDevices(std::vector<BluetoothDeviceInfo>& devices) {
// 实现蓝牙设备扫描
#ifdef _WIN32
// Windows蓝牙设备扫描代码
BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams = {0};
searchParams.dwSize = sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS);
searchParams.fReturnAuthenticated = TRUE;
searchParams.fReturnConnected = TRUE;
searchParams.fReturnRemembered = TRUE;
searchParams.fIssueInquiry = TRUE;
searchParams.cTimeoutMultiplier = 4;
BLUETOOTH_DEVICE_INFO deviceInfo = {0};
deviceInfo.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);
HBLUETOOTH_DEVICE_FIND hFind = BluetoothFindFirstDevice(&searchParams, &deviceInfo);
if (hFind) {
do {
BluetoothDeviceInfo info;
info.address = deviceInfo.Address;
info.name = deviceInfo.szName;
devices.push_back(info);
} while (BluetoothFindNextDevice(hFind, &deviceInfo));
BluetoothFindDeviceClose(hFind);
return true;
}
#endif
return false;
}
bool connect(const BluetoothDeviceInfo& device) {
// 实现蓝牙设备连接
return false; // 占位,需要实际实现
}
bool sendData(const uint8_t* data, size_t length) {
if (!connected) return false;
// 实现数据发送
return true; // 占位,需要实际实现
}
bool receiveData(uint8_t* buffer, size_t maxLength, size_t& bytesReceived) {
if (!connected) return false;
// 实现数据接收
return true; // 占位,需要实际实现
}
};
8.2.3 无线接口技术
cpp
class WirelessDeviceManager {
private:
std::unordered_map<uint32_t, WirelessDevice*> connectedDevices;
std::thread pollingThread;
std::atomic<bool> isRunning;
public:
WirelessDeviceManager() : isRunning(false) {}
~WirelessDeviceManager() {
stopPolling();
for (auto& pair : connectedDevices) {
delete pair.second;
}
}
void startPolling() {
if (!isRunning) {
isRunning = true;
pollingThread = std::thread(&WirelessDeviceManager::pollingFunction, this);
}
}
void stopPolling() {
if (isRunning) {
isRunning = false;
if (pollingThread.joinable()) {
pollingThread.join();
}
}
}
private:
void pollingFunction() {
while (isRunning) {
for (auto& pair : connectedDevices) {
pair.second->poll();
}
std::this_thread::sleep_for(std::chrono::milliseconds(16)); // ~60Hz
}
}
};
8.3 输入类型
8.3.1 数字输入
cpp
// 表示二元状态的数字输入(按下/释放)
class DigitalInput {
private:
bool currentState;
bool previousState;
public:
DigitalInput() : currentState(false), previousState(false) {}
void update(bool newState) {
previousState = currentState;
currentState = newState;
}
// 当前是否按下
bool isPressed() const {
return currentState;
}
// 是否刚按下(当前帧按下,上一帧未按下)
bool isJustPressed() const {
return currentState && !previousState;
}
// 是否刚释放(当前帧未按下,上一帧按下)
bool isJustReleased() const {
return !currentState && previousState;
}
};
8.3.2 模拟输入
cpp
// 表示连续值的模拟输入(如摇杆、触发器)
class AnalogInput {
private:
float currentValue;
float previousValue;
float deadZone;
public:
AnalogInput(float deadZoneValue = 0.1f)
: currentValue(0.0f), previousValue(0.0f), deadZone(deadZoneValue) {}
void update(float newValue) {
previousValue = currentValue;
currentValue = newValue;
}
// 获取当前值(应用死区)
float getValue() const {
if (std::abs(currentValue) < deadZone) {
return 0.0f;
}
// 重新映射值以考虑死区
float sign = (currentValue > 0.0f) ? 1.0f : -1.0f;
return sign * (std::abs(currentValue) - deadZone) / (1.0f - deadZone);
}
// 获取原始值(不应用死区)
float getRawValue() const {
return currentValue;
}
// 获取变化量
float getDelta() const {
return currentValue - previousValue;
}
// 设置死区
void setDeadZone(float value) {
deadZone = std::max(0.0f, std::min(value, 1.0f));
}
};
8.3.3 触摸输入
cpp
// 表示触摸输入事件
struct TouchEvent {
enum class Type {
Begin, // 触摸开始
Move, // 触摸移动
End, // 触摸结束
Cancel // 触摸取消
};
uint32_t touchId; // 触摸点ID
Type type; // 触摸事件类型
float x, y; // 触摸坐标
float pressure; // 压力值(如果支持)
float size; // 触摸面积(如果支持)
uint64_t timestamp; // 时间戳
};
class TouchInputManager {
private:
std::unordered_map<uint32_t, TouchEvent> activeTouches;
std::vector<TouchEvent> touchEvents;
public:
// 处理新的触摸事件
void processTouchEvent(const TouchEvent& event) {
touchEvents.push_back(event);
if (event.type == TouchEvent::Type::Begin) {
activeTouches[event.touchId] = event;
} else if (event.type == TouchEvent::Type::Move) {
if (activeTouches.find(event.touchId) != activeTouches.end()) {
activeTouches[event.touchId] = event;
}
} else if (event.type == TouchEvent::Type::End ||
event.type == TouchEvent::Type::Cancel) {
activeTouches.erase(event.touchId);
}
}
// 获取所有活跃的触摸点
const std::unordered_map<uint32_t, TouchEvent>& getActiveTouches() const {
return activeTouches;
}
// 获取并清除当前帧的触摸事件
std::vector<TouchEvent> getAndClearEvents() {
std::vector<TouchEvent> result = std::move(touchEvents);
touchEvents.clear();
return result;
}
};
8.3.4 手势输入
cpp
// 表示手势类型
enum class GestureType {
Tap,
DoubleTap,
LongPress,
Swipe,
Pinch,
Rotate,
Pan
};
// 手势事件数据
struct GestureEvent {
GestureType type;
Vector2 position; // 手势位置
Vector2 delta; // 位置变化量(用于平移、滑动)
float scale; // 缩放比例(用于捏合)
float rotation; // 旋转角度(用于旋转)
uint64_t timestamp; // 时间戳
// 特定于轻触的数据
int tapCount; // 轻触次数
};
class GestureRecognizer {
private:
std::vector<TouchEvent> touchHistory;
std::vector<GestureEvent> recognizedGestures;
// 手势识别参数
float tapMaxDistance; // 轻触最大距离
float tapMaxDuration; // 轻触最大持续时间
float doubleTapMaxDelay; // 双击最大间隔
float longPressMinDuration; // 长按最小持续时间
float swipeMinDistance; // 滑动最小距离
float swipeMaxTime; // 滑动最大时间
public:
GestureRecognizer() {
// 设置默认参数
tapMaxDistance = 20.0f;
tapMaxDuration = 300.0f; // 毫秒
doubleTapMaxDelay = 300.0f; // 毫秒
longPressMinDuration = 500.0f; // 毫秒
swipeMinDistance = 50.0f;
swipeMaxTime = 300.0f; // 毫秒
}
// 更新触摸历史并尝试识别手势
void update(const std::vector<TouchEvent>& newTouches) {
// 添加新的触摸事件到历史
touchHistory.insert(touchHistory.end(), newTouches.begin(), newTouches.end());
// 识别手势
recognizeTaps();
recognizeSwipes();
recognizePinchAndRotate();
// 清理过旧的触摸历史
cleanupTouchHistory();
}
// 获取并清除识别的手势
std::vector<GestureEvent> getAndClearGestures() {
std::vector<GestureEvent> result = std::move(recognizedGestures);
recognizedGestures.clear();
return result;
}
private:
// 实现各种手势识别算法
void recognizeTaps() {
// 轻触和双击识别逻辑
}
void recognizeSwipes() {
// 滑动识别逻辑
}
void recognizePinchAndRotate() {
// 捏合和旋转识别逻辑
}
void cleanupTouchHistory() {
// 移除过旧的触摸事件
uint64_t currentTime = getCurrentTimestamp();
auto it = touchHistory.begin();
while (it != touchHistory.end()) {
if (currentTime - it->timestamp > 1000) { // 删除1秒前的事件
it = touchHistory.erase(it);
} else {
++it;
}
}
}
uint64_t getCurrentTimestamp() {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
}
};
8.4 输出类型
8.4.1 力反馈
cpp
// 力反馈效果类型
enum class ForceFeedbackType {
Constant, // 持续力
Ramp, // 渐变力
Periodic, // 周期性力(如正弦波)
Condition, // 条件力(如弹簧)
Custom // 自定义效果
};
// 力反馈效果基类
class ForceFeedbackEffect {
protected:
ForceFeedbackType type;
uint32_t duration; // 持续时间(毫秒)
float strength; // 力度(0.0-1.0)
public:
ForceFeedbackEffect(ForceFeedbackType effectType, uint32_t effectDuration, float effectStrength)
: type(effectType), duration(effectDuration), strength(effectStrength) {}
virtual ~ForceFeedbackEffect() {}
ForceFeedbackType getType() const { return type; }
uint32_t getDuration() const { return duration; }
float getStrength() const { return strength; }
// 获取特定时间点的力反馈强度
virtual float getIntensityAtTime(uint32_t timeMs) const = 0;
};
// 持续力反馈效果
class ConstantForceEffect : public ForceFeedbackEffect {
public:
ConstantForceEffect(uint32_t duration, float strength)
: ForceFeedbackEffect(ForceFeedbackType::Constant, duration, strength) {}
float getIntensityAtTime(uint32_t timeMs) const override {
if (timeMs > duration) return 0.0f;
return strength;
}
};
// 震动效果
class VibrationEffect : public ForceFeedbackEffect {
private:
float frequency; // 震动频率(Hz)
public:
VibrationEffect(uint32_t duration, float strength, float freq)
: ForceFeedbackEffect(ForceFeedbackType::Periodic, duration, strength),
frequency(freq) {}
float getIntensityAtTime(uint32_t timeMs) const override {
if (timeMs > duration) return 0.0f;
// 生成震动波形(正弦波)
float phase = (timeMs / 1000.0f) * frequency * 2.0f * 3.14159f;
return strength * std::abs(std::sin(phase));
}
};
// 力反馈控制器
class ForceFeedbackController {
private:
std::vector<std::unique_ptr<ForceFeedbackEffect>> activeEffects;
bool deviceSupported;
public:
ForceFeedbackController() : deviceSupported(false) {}
bool initialize() {
// 检测设备是否支持力反馈
return checkDeviceSupport();
}
// 添加效果并开始播放
bool playEffect(std::unique_ptr<ForceFeedbackEffect> effect) {
if (!deviceSupported) return false;
activeEffects.push_back(std::move(effect));
return true;
}
// 停止所有效果
void stopAllEffects() {
activeEffects.clear();
applyZeroForce();
}
// 更新力反馈状态
void update() {
if (!deviceSupported || activeEffects.empty()) return;
uint32_t currentTime = getCurrentTimeMs();
float totalIntensity = 0.0f;
// 计算所有活跃效果的合成强度
for (auto it = activeEffects.begin(); it != activeEffects.end();) {
ForceFeedbackEffect* effect = it->get();
uint32_t effectTime = currentTime % (effect->getDuration() + 1);
float intensity = effect->getIntensityAtTime(effectTime);
totalIntensity += intensity;
// 移除已完成的效果
if (effectTime >= effect->getDuration()) {
it = activeEffects.erase(it);
} else {
++it;
}
}
// 限制强度范围
totalIntensity = std::max(0.0f, std::min(totalIntensity, 1.0f));
// 应用到设备
applyForce(totalIntensity);
}
private:
bool checkDeviceSupport() {
// 实现设备支持检查
return false; // 占位,需要实际实现
}
void applyForce(float intensity) {
// 实现向设备发送力反馈命令
}
void applyZeroForce() {
applyForce(0.0f);
}
uint32_t getCurrentTimeMs() {
return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count());
}
};
8.4.2 视觉反馈
cpp
// 控制器LED效果
class ControllerLEDEffect {
public:
enum class Pattern {
Solid,
Pulse,
Blink,
Fade,
Rainbow
};
ControllerLEDEffect(Pattern p, RGBColor c, uint32_t dur = 0)
: pattern(p), color(c), duration(dur), startTime(getCurrentTimeMs()) {}
RGBColor getColorAtTime(uint32_t timeMs) const {
uint32_t elapsedTime = timeMs - startTime;
switch (pattern) {
case Pattern::Solid:
return color;
case Pattern::Pulse: {
float phase = (elapsedTime % 1000) / 1000.0f;
float intensity = std::sin(phase * 3.14159f);
return color * intensity;
}
case Pattern::Blink: {
uint32_t period = 500; // 500ms on, 500ms off
return ((elapsedTime % (period * 2)) < period) ? color : RGBColor(0, 0, 0);
}
case Pattern::Fade: {
if (duration > 0) {
float t = std::min(1.0f, elapsedTime / static_cast<float>(duration));
return color * (1.0f - t);
}
return color;
}
case Pattern::Rainbow: {
float hue = (elapsedTime % 5000) / 5000.0f;
return RGBColor::fromHSV(hue, 1.0f, 1.0f);
}
default:
return color;
}
}
bool isFinished(uint32_t currentTime) const {
if (duration == 0) return false; // 无限持续
return (currentTime - startTime) >= duration;
}
private:
Pattern pattern;
RGBColor color;
uint32_t duration; // 持续时间(毫秒),0表示无限
uint32_t startTime; // 开始时间
uint32_t getCurrentTimeMs() const {
return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count());
}
};
8.4.3 音频反馈
cpp
// 控制器扬声器接口
class ControllerAudioInterface {
private:
bool audioSupported;
float volume;
public:
ControllerAudioInterface() : audioSupported(false), volume(1.0f) {}
bool initialize() {
// 检测控制器是否支持音频输出
return checkAudioSupport();
}
// 播放预定义的音效
bool playSound(AudioSoundEffect effect, float volumeScale = 1.0f) {
if (!audioSupported) return false;
// 实现音效播放
return true;
}
// 从内存缓冲区播放自定义音频
bool playAudioBuffer(const uint8_t* buffer, size_t bufferSize,
int sampleRate, int channels, float volumeScale = 1.0f) {
if (!audioSupported) return false;
// 实现音频缓冲区播放
return true;
}
// 设置主音量
void setVolume(float newVolume) {
volume = std::max(0.0f, std::min(newVolume, 1.0f));
}
private:
bool checkAudioSupport() {
// 实现控制器音频支持检查
return false; // 占位,需要实际实现
}
};
8.5 游戏引擎的人体学接口设备系统
8.5.1 输入管理器设计
cpp
// 定义输入类型和动作
enum class InputActionType {
Digital, // 数字按钮(按下/释放)
Analog, // 模拟输入(范围值)
Vector2D, // 2D向量输入(如摇杆)
Vector3D, // 3D向量输入(如陀螺仪)
Gesture // 手势输入
};
// 输入设备类型
enum class InputDeviceType {
Keyboard,
Mouse,
Gamepad,
TouchScreen,
MotionController,
VRController,
Custom
};
// 输入动作映射
struct InputActionMapping {
std::string actionName;
InputActionType actionType;
InputDeviceType deviceType;
int deviceId;
int inputId; // 键码、按钮ID等
float scale; // 输入缩放
bool inverted; // 是否反转
InputActionMapping(const std::string& name, InputActionType type,
InputDeviceType device, int devId, int inId,
float inputScale = 1.0f, bool inv = false)
: actionName(name), actionType(type), deviceType(device),
deviceId(devId), inputId(inId), scale(inputScale), inverted(inv) {}
};
// 输入管理器核心类
class InputManager {
private:
std::unordered_map<std::string, std::vector<InputActionMapping>> actionMappings;
std::unordered_map<InputDeviceType, std::vector<InputDevice*>> devices;
// 当前输入状态
std::unordered_map<std::string, DigitalInput> digitalInputs;
std::unordered_map<std::string, AnalogInput> analogInputs;
std::unordered_map<std::string, Vector2D> vector2DInputs;
std::unordered_map<std::string, Vector3D> vector3DInputs;
// 手势识别器
GestureRecognizer gestureRecognizer;
public:
InputManager() {}
~InputManager() {
// 清理设备
for (auto& devicePair : devices) {
for (auto* device : devicePair.second) {
delete device;
}
}
}
// 初始化输入系统
bool initialize() {
// 初始化各种输入设备
initializeKeyboard();
initializeMouse();
initializeGamepads();
initializeTouchScreen();
return true;
}
// 添加输入映射
void addActionMapping(const InputActionMapping& mapping) {
actionMappings[mapping.actionName].push_back(mapping);
}
// 移除输入映射
void removeActionMapping(const std::string& actionName) {
actionMappings.erase(actionName);
}
// 检查数字输入状态
bool isActionPressed(const std::string& actionName) const {
auto it = digitalInputs.find(actionName);
if (it != digitalInputs.end()) {
return it->second.isPressed();
}
return false;
}
// 检查数字输入是否刚被按下
bool isActionJustPressed(const std::string& actionName) const {
auto it = digitalInputs.find(actionName);
if (it != digitalInputs.end()) {
return it->second.isJustPressed();
}
return false;
}
// 获取模拟输入值
float getAnalogActionValue(const std::string& actionName) const {
auto it = analogInputs.find(actionName);
if (it != analogInputs.end()) {
return it->second.getValue();
}
return 0.0f;
}
// 获取2D向量输入
Vector2D getVector2DAction(const std::string& actionName) const {
auto it = vector2DInputs.find(actionName);
if (it != vector2DInputs.end()) {
return it->second;
}
return Vector2D();
}
// 更新所有输入设备状态
void update() {
// 更新所有设备
for (auto& devicePair : devices) {
for (auto* device : devicePair.second) {
device->update();
}
}
// 处理触摸输入和手势
std::vector<TouchEvent> touchEvents = getTouchEvents();
gestureRecognizer.update(touchEvents);
// 应用输入映射
updateActionMappings();
}
private:
// 初始化各种设备
void initializeKeyboard() {
// 实现键盘初始化
}
void initializeMouse() {
// 实现鼠标初始化
}
void initializeGamepads() {
// 实现游戏手柄初始化
}
void initializeTouchScreen() {
// 实现触摸屏初始化
}
// 获取当前帧的触摸事件
std::vector<TouchEvent> getTouchEvents() {
// 实现从触摸设备获取事件
return std::vector<TouchEvent>();
}
// 更新动作映射
void updateActionMappings() {
// 处理每个映射
for (const auto& mappingPair : actionMappings) {
const std::string& actionName = mappingPair.first;
const auto& mappings = mappingPair.second;
for (const auto& mapping : mappings) {
// 根据映射类型处理输入
switch (mapping.actionType) {
case InputActionType::Digital:
processDigitalMapping(actionName, mapping);
break;
case InputActionType::Analog:
processAnalogMapping(actionName, mapping);
break;
case InputActionType::Vector2D:
processVector2DMapping(actionName, mapping);
break;
// 其他类型处理...
}
}
}
}
// 处理数字输入映射
void processDigitalMapping(const std::string& actionName, const InputActionMapping& mapping) {
bool isPressed = false;
// 根据设备类型获取输入状态
if (mapping.deviceType == InputDeviceType::Keyboard) {
isPressed = isKeyPressed(mapping.inputId);
} else if (mapping.deviceType == InputDeviceType::Gamepad) {
isPressed = isGamepadButtonPressed(mapping.deviceId, mapping.inputId);
}
// 更新数字输入状态
if (digitalInputs.find(actionName) == digitalInputs.end()) {
digitalInputs[actionName] = DigitalInput();
}
// 应用当前输入状态
digitalInputs[actionName].update(isPressed);
}
// 处理模拟输入映射
void processAnalogMapping(const std::string& actionName, const InputActionMapping& mapping) {
float value = 0.0f;
// 根据设备类型获取输入值
if (mapping.deviceType == InputDeviceType::Gamepad) {
value = getGamepadAnalogValue(mapping.deviceId, mapping.inputId);
}
// 应用缩放和反转
value = value * mapping.scale * (mapping.inverted ? -1.0f : 1.0f);
// 更新模拟输入状态
if (analogInputs.find(actionName) == analogInputs.end()) {
analogInputs[actionName] = AnalogInput();
}
// 应用当前输入值
analogInputs[actionName].update(value);
}
// 处理2D向量输入映射
void processVector2DMapping(const std::string& actionName, const InputActionMapping& mapping) {
Vector2D value;
// 根据设备类型获取输入向量
if (mapping.deviceType == InputDeviceType::Gamepad) {
value = getGamepadStickValue(mapping.deviceId, mapping.inputId);
}
// 应用缩放
value.x *= mapping.scale * (mapping.inverted ? -1.0f : 1.0f);
value.y *= mapping.scale;
// 更新向量输入状态
vector2DInputs[actionName] = value;
}
// 设备输入状态检查函数(这些需要具体实现)
bool isKeyPressed(int keyCode) {
// 实现键盘按键检查
return false;
}
bool isGamepadButtonPressed(int gamepadId, int buttonId) {
// 实现游戏手柄按钮检查
return false;
}
float getGamepadAnalogValue(int gamepadId, int analogId) {
// 实现游戏手柄模拟输入检查
return 0.0f;
}
Vector2D getGamepadStickValue(int gamepadId, int stickId) {
// 实现游戏手柄摇杆值获取
return Vector2D();
}
};
8.5.2 输入映射系统
cpp
// 输入映射配置
class InputMappingConfig {
private:
std::string configName;
std::unordered_map<std::string, std::vector<InputActionMapping>> mappings;
public:
InputMappingConfig(const std::string& name) : configName(name) {}
void addMapping(const InputActionMapping& mapping) {
mappings[mapping.actionName].push_back(mapping);
}
void removeMapping(const std::string& actionName) {
mappings.erase(actionName);
}
// 获取特定动作的所有映射
const std::vector<InputActionMapping>& getMappingsForAction(const std::string& actionName) const {
static const std::vector<InputActionMapping> emptyVector;
auto it = mappings.find(actionName);
if (it != mappings.end()) {
return it->second;
}
return emptyVector;
}
// 保存映射到文件
bool saveToFile(const std::string& filename) const {
// 实现保存逻辑
return false; // 占位,需要实际实现
}
// 从文件加载映射
bool loadFromFile(const std::string& filename) {
// 实现加载逻辑
return false; // 占位,需要实际实现
}
};
8.5.3 输入事件系统
cpp
// 输入事件类型
enum class InputEventType {
ButtonPressed,
ButtonReleased,
AnalogChanged,
AxisChanged,
GestureDetected,
DeviceConnected,
DeviceDisconnected
};
// 输入事件基类
struct InputEvent {
InputEventType type;
uint64_t timestamp;
InputEvent(InputEventType t) : type(t), timestamp(getCurrentTimestamp()) {}
virtual ~InputEvent() {}
static uint64_t getCurrentTimestamp() {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
}
};
// 按钮事件
struct ButtonEvent : public InputEvent {
InputDeviceType deviceType;
int deviceId;
int buttonId;
bool pressed;
ButtonEvent(InputDeviceType device, int devId, int btn, bool p)
: InputEvent(p ? InputEventType::ButtonPressed : InputEventType::ButtonReleased),
deviceType(device), deviceId(devId), buttonId(btn), pressed(p) {}
};
// 模拟输入事件
struct AnalogEvent : public InputEvent {
InputDeviceType deviceType;
int deviceId;
int analogId;
float value;
float previousValue;
AnalogEvent(InputDeviceType device, int devId, int analog, float val, float prevVal)
: InputEvent(InputEventType::AnalogChanged),
deviceType(device), deviceId(devId), analogId(analog),
value(val), previousValue(prevVal) {}
};
// 输入事件分发器
class InputEventDispatcher {
private:
using EventCallback = std::function<void(const InputEvent&)>;
std::unordered_map<InputEventType, std::vector<EventCallback>> eventHandlers;
std::queue<std::unique_ptr<InputEvent>> eventQueue;
std::mutex queueMutex;
public:
// 注册事件处理器
void registerHandler(InputEventType type, EventCallback handler) {
eventHandlers[type].push_back(handler);
}
// 生成事件并添加到队列
template<typename T, typename... Args>
void queueEvent(Args&&... args) {
std::lock_guard<std::mutex> lock(queueMutex);
eventQueue.push(std::make_unique<T>(std::forward<Args>(args)...));
}
// 处理所有排队的事件
void processEvents() {
std::queue<std::unique_ptr<InputEvent>> currentEvents;
{
std::lock_guard<std::mutex> lock(queueMutex);
std::swap(currentEvents, eventQueue);
}
while (!currentEvents.empty()) {
const InputEvent& event = *currentEvents.front();
auto it = eventHandlers.find(event.type);
if (it != eventHandlers.end()) {
for (const auto& handler : it->second) {
handler(event);
}
}
currentEvents.pop();
}
}
};
8.6 人体学接口设备使用实践
8.6.1 实现一个完整的输入系统
以下是一个将上述所有组件整合的简化版输入系统示例:
cpp
#include <iostream>
#include <vector>
#include <unordered_map>
#include <string>
#include <functional>
#include <memory>
#include <chrono>
#include <thread>
#include <mutex>
#include <queue>
#include <algorithm>
#include <cmath>
// 基本数学结构
struct Vector2D {
float x, y;
Vector2D() : x(0.0f), y(0.0f) {}
Vector2D(float _x, float _y) : x(_x), y(_y) {}
float length() const {
return std::sqrt(x*x + y*y);
}
Vector2D normalized() const {
float len = length();
if (len > 0.0001f) {
return Vector2D(x / len, y / len);
}
return Vector2D();
}
Vector2D operator*(float scalar) const {
return Vector2D(x * scalar, y * scalar);
}
};
struct Vector3D {
float x, y, z;
Vector3D() : x(0.0f), y(0.0f), z(0.0f) {}
Vector3D(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
};
// 简单输入系统实现
class GameInputSystem {
public:
GameInputSystem() {
// 注册默认控制方案
setupDefaultMappings();
}
void initialize() {
std::cout << "Initializing game input system...\n";
// 实际实现中会初始化各种设备
}
void update() {
// 实际实现中会轮询设备和处理输入
// 这里我们模拟一些输入状态
// 更新输入状态
updateInputStates();
// 处理输入事件
eventDispatcher.processEvents();
}
// 检查动作是否被触发
bool isActionPressed(const std::string& actionName) const {
auto it = digitalInputs.find(actionName);
if (it != digitalInputs.end()) {
return it->second.isPressed();
}
return false;
}
bool isActionJustPressed(const std::string& actionName) const {
auto it = digitalInputs.find(actionName);
if (it != digitalInputs.end()) {
return it->second.isJustPressed();
}
return false;
}
// 获取模拟值
float getAnalogValue(const std::string& actionName) const {
auto it = analogInputs.find(actionName);
if (it != analogInputs.end()) {
return it->second.getValue();
}
return 0.0f;
}
// 获取向量值
Vector2D getVector2D(const std::string& actionName) const {
auto it = vector2DInputs.find(actionName);
if (it != vector2DInputs.end()) {
return it->second;
}
return Vector2D();
}
// 注册输入事件处理器
void registerEventHandler(InputEventType type, std::function<void(const InputEvent&)> handler) {
eventDispatcher.registerHandler(type, handler);
}
// 触发振动效果
void triggerVibration(float intensity, uint32_t durationMs) {
std::cout << "Triggering vibration: " << intensity << " for " << durationMs << "ms\n";
// 在实际实现中,会向控制器发送振动命令
if (forceFeedbackController) {
auto effect = std::make_unique<VibrationEffect>(durationMs, intensity, 100.0f);
forceFeedbackController->playEffect(std::move(effect));
}
}
private:
// 数字输入状态
std::unordered_map<std::string, DigitalInput> digitalInputs;
// 模拟输入状态
std::unordered_map<std::string, AnalogInput> analogInputs;
// 向量输入状态
std::unordered_map<std::string, Vector2D> vector2DInputs;
// 输入映射
std::unordered_map<std::string, std::vector<InputActionMapping>> actionMappings;
// 事件分发器
InputEventDispatcher eventDispatcher;
// 力反馈控制器
std::unique_ptr<ForceFeedbackController> forceFeedbackController;
// 设置默认输入映射
void setupDefaultMappings() {
// 移动映射
addActionMapping(InputActionMapping("MoveForward", InputActionType::Digital,
InputDeviceType::Keyboard, 0, 'W'));
addActionMapping(InputActionMapping("MoveBackward", InputActionType::Digital,
InputDeviceType::Keyboard, 0, 'S'));
addActionMapping(InputActionMapping("MoveLeft", InputActionType::Digital,
InputDeviceType::Keyboard, 0, 'A'));
addActionMapping(InputActionMapping("MoveRight", InputActionType::Digital,
InputDeviceType::Keyboard, 0, 'D'));
// 跳跃映射
addActionMapping(InputActionMapping("Jump", InputActionType::Digital,
InputDeviceType::Keyboard, 0, ' '));
// 手柄移动映射
addActionMapping(InputActionMapping("Move", InputActionType::Vector2D,
InputDeviceType::Gamepad, 0, 0));
// 手柄跳跃映射
addActionMapping(InputActionMapping("Jump", InputActionType::Digital,
InputDeviceType::Gamepad, 0, 0));
}
// 添加动作映射
void addActionMapping(const InputActionMapping& mapping) {
actionMappings[mapping.actionName].push_back(mapping);
}
// 更新输入状态(在实际实现中会从设备读取)
void updateInputStates() {
// 这里是简化的模拟,实际实现会从设备获取数据
// 模拟键盘输入
simulateKeyboardInput();
// 模拟手柄输入
simulateGamepadInput();
// 更新动作映射
for (const auto& mappingPair : actionMappings) {
const std::string& actionName = mappingPair.first;
// 处理每种输入类型
processDigitalMappings(actionName, mappingPair.second);
processAnalogMappings(actionName, mappingPair.second);
processVector2DMappings(actionName, mappingPair.second);
}
}
// 模拟键盘输入(实际中会从系统获取)
void simulateKeyboardInput() {
// 这里只是简单模拟
static bool wPressed = false;
static bool aPressed = false;
static bool sPressed = false;
static bool dPressed = false;
static bool spacePressed = false;
// 模拟按键变化
bool newWPressed = (rand() % 100) < 30;
bool newAPressed = (rand() % 100) < 30;
bool newSPressed = (rand() % 100) < 30;
bool newDPressed = (rand() % 100) < 30;
bool newSpacePressed = (rand() % 100) < 10;
// 生成按键事件
if (newWPressed != wPressed) {
eventDispatcher.queueEvent<ButtonEvent>(InputDeviceType::Keyboard, 0, 'W', newWPressed);
wPressed = newWPressed;
}
if (newAPressed != aPressed) {
eventDispatcher.queueEvent<ButtonEvent>(InputDeviceType::Keyboard, 0, 'A', newAPressed);
aPressed = newAPressed;
}
if (newSPressed != sPressed) {
eventDispatcher.queueEvent<ButtonEvent>(InputDeviceType::Keyboard, 0, 'S', newSPressed);
sPressed = newSPressed;
}
if (newDPressed != dPressed) {
eventDispatcher.queueEvent<ButtonEvent>(InputDeviceType::Keyboard, 0, 'D', newDPressed);
dPressed = newDPressed;
}
if (newSpacePressed != spacePressed) {
eventDispatcher.queueEvent<ButtonEvent>(InputDeviceType::Keyboard, 0, ' ', newSpacePressed);
spacePressed = newSpacePressed;
}
}
// 模拟手柄输入
void simulateGamepadInput() {
// 模拟手柄摇杆
static Vector2D lastStickPos(0.0f, 0.0f);
// 随机生成摇杆位置
float x = (((rand() % 200) - 100) / 100.0f);
float y = (((rand() % 200) - 100) / 100.0f);
// 应用死区
Vector2D stickPos(x, y);
float length = stickPos.length();
if (length < 0.25f) {
stickPos = Vector2D(0.0f, 0.0f);
} else {
// 正规化
if (length > 1.0f) {
stickPos = stickPos.normalized();
}
}
// 如果摇杆位置变化显著,生成事件
if (std::abs(stickPos.x - lastStickPos.x) > 0.1f ||
std::abs(stickPos.y - lastStickPos.y) > 0.1f) {
// 在实际实现中会生成适当的轴事件
lastStickPos = stickPos;
}
}
// 处理数字输入映射
void processDigitalMappings(const std::string& actionName,
const std::vector<InputActionMapping>& mappings) {
bool isAnyPressed = false;
for (const auto& mapping : mappings) {
if (mapping.actionType != InputActionType::Digital) continue;
bool pressed = false;
// 根据设备类型检查按键状态
if (mapping.deviceType == InputDeviceType::Keyboard) {
// 在实际实现中会检查实际键盘状态
pressed = (rand() % 100) < 30; // 模拟30%概率按下
} else if (mapping.deviceType == InputDeviceType::Gamepad) {
// 在实际实现中会检查实际游戏手柄状态
pressed = (rand() % 100) < 20; // 模拟20%概率按下
}
if (pressed) {
isAnyPressed = true;
break;
}
}
// 如果动作不存在,创建它
if (digitalInputs.find(actionName) == digitalInputs.end()) {
digitalInputs[actionName] = DigitalInput();
}
// 更新动作状态
digitalInputs[actionName].update(isAnyPressed);
}
// 处理模拟输入映射
void processAnalogMappings(const std::string& actionName,
const std::vector<InputActionMapping>& mappings) {
float value = 0.0f;
bool found = false;
for (const auto& mapping : mappings) {
if (mapping.actionType != InputActionType::Analog) continue;
// 根据设备类型获取模拟值
if (mapping.deviceType == InputDeviceType::Gamepad) {
// 在实际实现中会获取实际模拟值
value = ((rand() % 200) - 100) / 100.0f; // 模拟-1到1的值
found = true;
break;
}
}
if (found) {
// 如果动作不存在,创建它
if (analogInputs.find(actionName) == analogInputs.end()) {
analogInputs[actionName] = AnalogInput();
}
// 更新动作状态
analogInputs[actionName].update(value);
}
}
// 处理2D向量映射
void processVector2DMappings(const std::string& actionName,
const std::vector<InputActionMapping>& mappings) {
Vector2D value(0.0f, 0.0f);
bool found = false;
for (const auto& mapping : mappings) {
if (mapping.actionType != InputActionType::Vector2D) continue;
// 根据设备类型获取向量值
if (mapping.deviceType == InputDeviceType::Gamepad) {
// 在实际实现中会获取实际摇杆位置
float x = ((rand() % 200) - 100) / 100.0f;
float y = ((rand() % 200) - 100) / 100.0f;
// 应用死区
if (std::sqrt(x*x + y*y) < 0.25f) {
x = y = 0.0f;
}
value = Vector2D(x, y);
found = true;
break;
}
}
if (found) {
// 更新向量值
vector2DInputs[actionName] = value;
}
}
};
// 使用示例
int main() {
GameInputSystem inputSystem;
inputSystem.initialize();
// 注册按钮事件处理器
inputSystem.registerEventHandler(InputEventType::ButtonPressed,
[](const InputEvent& event) {
const ButtonEvent& buttonEvent = static_cast<const ButtonEvent&>(event);
std::cout << "Button pressed: " << buttonEvent.buttonId << "\n";
});
// 主循环模拟
for (int i = 0; i < 100; ++i) {
inputSystem.update();
// 检查输入动作
if (inputSystem.isActionJustPressed("Jump")) {
std::cout << "Player jumped!\n";
// 触发跳跃振动反馈
inputSystem.triggerVibration(0.7f, 100);
}
if (inputSystem.isActionPressed("MoveForward")) {
std::cout << "Moving forward\n";
}
// 获取移动向量
Vector2D moveVector = inputSystem.getVector2D("Move");
if (moveVector.length() > 0.1f) {
std::cout << "Moving with gamepad: x=" << moveVector.x << ", y=" << moveVector.y << "\n";
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return 0;
}
8.6.2 示例:实现VR控制器接口
cpp
// VR控制器接口实现
class VRControllerInterface {
private:
struct ControllerState {
bool connected;
Vector3D position;
Quaternion orientation;
std::unordered_map<int, bool> buttonStates;
std::unordered_map<int, float> analogStates;
Vector2D touchpadPosition;
bool touchpadTouched;
};
ControllerState leftController;
ControllerState rightController;
ForceFeedbackController feedbackController;
public:
VRControllerInterface() {
leftController.connected = false;
rightController.connected = false;
}
bool initialize() {
// 初始化VR系统接口(依赖于具体的VR SDK)
std::cout << "Initializing VR controller interface...\n";
// 模拟连接成功
leftController.connected = true;
rightController.connected = true;
// 初始化力反馈
feedbackController.initialize();
return true;
}
void update() {
// 在实际实现中,会从VR SDK获取最新的控制器状态
// 这里只是模拟更新
if (leftController.connected) {
// 更新位置和方向
updateControllerSimulation(leftController);
}
if (rightController.connected) {
// 更新位置和方向
updateControllerSimulation(rightController);
}
}
// 检查按钮状态
bool isButtonPressed(bool isLeft, int buttonId) const {
const ControllerState& controller = isLeft ? leftController : rightController;
if (!controller.connected) return false;
auto it = controller.buttonStates.find(buttonId);
return (it != controller.buttonStates.end()) && it->second;
}
// 获取触摸板位置
Vector2D getTouchpadPosition(bool isLeft) const {
const ControllerState& controller = isLeft ? leftController : rightController;
if (!controller.connected || !controller.touchpadTouched) {
return Vector2D(0.0f, 0.0f);
}
return controller.touchpadPosition;
}
// 获取控制器位置
Vector3D getPosition(bool isLeft) const {
const ControllerState& controller = isLeft ? leftController : rightController;
return controller.position;
}
// 获取控制器方向
Quaternion getOrientation(bool isLeft) const {
const ControllerState& controller = isLeft ? leftController : rightController;
return controller.orientation;
}
// 触发控制器震动
void triggerHapticPulse(bool isLeft, float intensity, uint32_t durationMs) {
if ((isLeft && !leftController.connected) || (!isLeft && !rightController.connected)) {
return;
}
std::cout << "Triggering haptic pulse on "
<< (isLeft ? "left" : "right")
<< " controller: " << intensity << " for " << durationMs << "ms\n";
// 实际实现会调用VR SDK的力反馈函数
auto effect = std::make_unique<VibrationEffect>(durationMs, intensity, 100.0f);
feedbackController.playEffect(std::move(effect));
}
private:
// 模拟控制器运动
void updateControllerSimulation(ControllerState& controller) {
// 在实际实现中,会从VR SDK获取实际位置和方向
// 这里只是简单模拟
// 模拟位置小幅度变动
controller.position.x += (rand() % 100 - 50) * 0.001f;
controller.position.y += (rand() % 100 - 50) * 0.001f;
controller.position.z += (rand() % 100 - 50) * 0.001f;
// 模拟按钮状态
for (int i = 0; i < 4; ++i) {
bool newState = (rand() % 100) < 5; // 5%概率改变按钮状态
if (newState != controller.buttonStates[i]) {
controller.buttonStates[i] = newState;
std::cout << "VR controller button " << i << " "
<< (newState ? "pressed" : "released") << "\n";
}
}
// 模拟触摸板
bool newTouched = (rand() % 100) < 30; // 30%概率触摸
controller.touchpadTouched = newTouched;
if (newTouched) {
controller.touchpadPosition.x = ((rand() % 200) - 100) / 100.0f;
controller.touchpadPosition.y = ((rand() % 200) - 100) / 100.0f;
}
}
};
// 使用示例
void vrControllerDemo() {
VRControllerInterface vrInterface;
if (!vrInterface.initialize()) {
std::cout << "Failed to initialize VR controllers\n";
return;
}
// 模拟VR交互循环
for (int i = 0; i < 100; ++i) {
vrInterface.update();
// 检查按钮输入
if (vrInterface.isButtonPressed(true, 0)) { // 左控制器触发键
std::cout << "Left trigger pressed!\n";
// 触发左控制器震动
vrInterface.triggerHapticPulse(true, 0.5f, 50);
}
if (vrInterface.isButtonPressed(false, 0)) { // 右控制器触发键
std::cout << "Right trigger pressed!\n";
// 触发右控制器震动
vrInterface.triggerHapticPulse(false, 0.5f, 50);
}
// 获取控制器位置
Vector3D leftPos = vrInterface.getPosition(true);
Vector3D rightPos = vrInterface.getPosition(false);
// 计算控制器之间的距离
float dx = leftPos.x - rightPos.x;
float dy = leftPos.y - rightPos.y;
float dz = leftPos.z - rightPos.z;
float distance = std::sqrt(dx*dx + dy*dy + dz*dz);
// 检测特殊手势
if (distance < 0.1f) {
std::cout << "Controllers brought close together - special action!\n";
// 触发双控制器震动
vrInterface.triggerHapticPulse(true, 1.0f, 200);
vrInterface.triggerHapticPulse(false, 1.0f, 200);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
通过以上代码示例,您可以了解如何在游戏引擎中设计和实现人体学接口设备系统。这些示例涵盖了HID设备的接口技术、输入类型处理、输出反馈系统以及整合为完整的输入管理系统的方法。在实际开发中,需要针对特定平台和设备进行进一步的适配和优化。