在虚拟现实(VR)游戏中,控制器是玩家与游戏世界互动的关键设备。控制器可以是手柄、手套、头戴式显示器(HMD)等,它们通过输入设备将玩家的动作和指令传递给游戏引擎。Monado引擎中的控制器开发旨在提供一个灵活、高效且易于扩展的框架,使开发者能够轻松地支持多种控制器设备,并实现丰富的交互功能。
Monado控制器的架构分为以下几个主要部分:
输入设备管理:负责检测和管理连接到系统的各种控制器设备。
输入事件处理:将控制器的原始输入数据转换为游戏引擎可以理解和处理的事件。
控制器配置:允许开发者和玩家对控制器进行自定义配置,以适应不同的游戏需求。
交互系统:根据输入事件触发相应的游戏逻辑和动画效果。
在Monado引擎中,检测和识别控制器是输入设备管理的第一步。引擎需要能够自动识别连接到系统的控制器类型,并初始化相应的驱动程序。这可以通过以下步骤实现:
枚举设备:扫描系统中的所有输入设备。
识别设备类型:根据设备的唯一标识符(如USB VID和PID)识别控制器类型。
初始化驱动程序:加载并初始化相应的控制器驱动程序。
在C++中,可以使用系统API来枚举连接的输入设备。以下是一个简单的示例,展示如何在Windows系统中枚举连接的USB设备:
#include
#include
#include
#include
// 枚举USB设备
void enumerateUSBDevices() {
HDEVINFO hDevInfo;
SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
SP_DEVINFO_DATA deviceInfoData;
DWORD requiredSize;
GUID guid = GUID_DEVINTERFACE_USB_DEVICE;
hDevInfo = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE) {
printf("SetupDiGetClassDevs failed with error: %d\n", GetLastError());
return;
}
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (DWORD i = 0; SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &guid, i, &deviceInterfaceData); i++) {
SetupDiGetDeviceInterfaceDetail(hDevInfo, &deviceInterfaceData, NULL, 0, &requiredSize, NULL);
PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requiredSize);
deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &deviceInterfaceData, deviceInterfaceDetailData, requiredSize, NULL, &deviceInfoData)) {
printf("Device Path: %s\n", deviceInterfaceDetailData->DevicePath);
} else {
printf("SetupDiGetDeviceInterfaceDetail failed with error: %d\n", GetLastError());
}
free(deviceInterfaceDetailData);
}
SetupDiDestroyDeviceInfoList(hDevInfo);
}
识别设备类型通常需要获取设备的唯一标识符,如USB的VID和PID。以下是一个示例,展示如何在Windows系统中获取这些信息:
#include
#include
#include
#include
#include
// 获取设备的VID和PID
void getDeviceVIDPID(const char* devicePath) {
HDEVINFO hDevInfo;
SP_DEVINFO_DATA deviceInfoData;
hDevInfo = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE) {
printf("SetupDiGetClassDevs failed with error: %d\n", GetLastError());
return;
}
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &deviceInfoData); i++) {
DWORD requiredSize = 0;
SetupDiGetDeviceInterfaceDetail(hDevInfo, (PSP_DEVICE_INTERFACE_DATA)devicePath, NULL, 0, &requiredSize, NULL);
PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requiredSize);
deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (SetupDiGetDeviceInterfaceDetail(hDevInfo, (PSP_DEVICE_INTERFACE_DATA)devicePath, deviceInterfaceDetailData, requiredSize, NULL, &deviceInfoData)) {
char hardwareID[256];
CM_Get_Device_IDA(deviceInfoData.DevInst, (PCHAR)hardwareID, sizeof(hardwareID), 0);
// 解析硬件ID以获取VID和PID
char* vidStr = strstr(hardwareID, "VID_");
char* pidStr = strstr(hardwareID, "PID_");
if (vidStr && pidStr) {
vidStr += 4; // 跳过"VID_"
pidStr += 4; // 跳过"PID_"
printf("VID: %s, PID: %s\n", vidStr, pidStr);
}
} else {
printf("SetupDiGetDeviceInterfaceDetail failed with error: %d\n", GetLastError());
}
free(deviceInterfaceDetailData);
}
SetupDiDestroyDeviceInfoList(hDevInfo);
}
初始化驱动程序是将识别到的控制器设备与Monado引擎进行连接的关键步骤。驱动程序负责与硬件进行通信,获取输入数据并传递给引擎。以下是一个简单的示例,展示如何初始化一个控制器驱动程序:
#include
class ControllerDriver {
public:
ControllerDriver(const char* vid, const char* pid) {
// 假设这里有一些初始化代码
std::cout << "Initializing controller driver for VID: " << vid << " PID: " << pid << std::endl;
}
// 获取输入数据
bool getInputData(float& x, float& y) {
// 假设这里有一些获取输入数据的代码
x = 0.5f;
y = 0.5f;
return true;
}
};
int main() {
enumerateUSBDevices();
getDeviceVIDPID("DevicePathHere"); // 替换为实际的设备路径
ControllerDriver driver("1234", "5678"); // 替换为实际的VID和PID
float x, y;
if (driver.getInputData(x, y)) {
std::cout << "Controller input: X: " << x << " Y: " << y << std::endl;
}
return 0;
}
输入事件可以分为以下几类:
按钮事件:控制器上的按钮被按下或释放。
轴事件:控制器上的轴(如摇杆)移动。
触摸事件:控制器上的触摸板被触摸。
手势事件:控制器上的手势识别。
按钮事件是最常见的输入事件之一。处理按钮事件通常需要注册回调函数,当按钮状态变化时调用这些函数。以下是一个简单的示例,展示如何在Monado引擎中处理按钮事件:
#include
#include
class Controller {
public:
// 注册按钮事件回调
void registerButtonCallback(int button, std::function<void(bool)> callback) {
buttonCallbacks[button] = callback;
}
// 模拟按钮事件
void simulateButtonEvent(int button, bool isPressed) {
if (buttonCallbacks.find(button) != buttonCallbacks.end()) {
buttonCallbacks[button](isPressed);
}
}
private:
std::map<int, std::function<void(bool)>> buttonCallbacks;
};
void onButtonA(bool isPressed) {
std::cout << "Button A " << (isPressed ? "pressed" : "released") << std::endl;
}
int main() {
Controller controller;
controller.registerButtonCallback(1, onButtonA); // 假设按钮A的ID为1
// 模拟按钮A被按下的事件
controller.simulateButtonEvent(1, true);
controller.simulateButtonEvent(1, false);
return 0;
}
轴事件通常用于处理控制器上的摇杆或类似设备的移动。处理轴事件也需要注册回调函数,当轴状态变化时调用这些函数。以下是一个简单的示例,展示如何在Monado引擎中处理轴事件:
#include
#include
class Controller {
public:
// 注册轴事件回调
void registerAxisCallback(int axis, std::function<void(float)> callback) {
axisCallbacks[axis] = callback;
}
// 模拟轴事件
void simulateAxisEvent(int axis, float value) {
if (axisCallbacks.find(axis) != axisCallbacks.end()) {
axisCallbacks[axis](value);
}
}
private:
std::map<int, std::function<void(float)>> axisCallbacks;
};
void onAxisX(float value) {
std::cout << "Axis X moved to " << value << std::endl;
}
int main() {
Controller controller;
controller.registerAxisCallback(0, onAxisX); // 假设X轴的ID为0
// 模拟X轴移动到0.5
controller.simulateAxisEvent(0, 0.5f);
return 0;
}
触摸事件通常用于处理控制器上的触摸板或触摸屏。处理触摸事件同样需要注册回调函数。以下是一个简单的示例,展示如何在Monado引擎中处理触摸事件:
#include
#include
class Controller {
public:
// 注册触摸事件回调
void registerTouchCallback(std::function<void(int, int)> callback) {
touchCallback = callback;
}
// 模拟触摸事件
void simulateTouchEvent(int x, int y) {
if (touchCallback) {
touchCallback(x, y);
}
}
private:
std::function<void(int, int)> touchCallback;
};
void onTouch(int x, int y) {
std::cout << "Touched at: (" << x << ", " << y << ")" << std::endl;
}
int main() {
Controller controller;
controller.registerTouchCallback(onTouch);
// 模拟触摸事件
controller.simulateTouchEvent(100, 200);
return 0;
}
手势事件通常用于处理控制器上的复杂手势识别。处理手势事件需要注册回调函数,并在识别到特定手势时调用这些函数。以下是一个简单的示例,展示如何在Monado引擎中处理手势事件:
#include
#include
class Controller {
public:
// 注册手势事件回调
void registerGestureCallback(std::function<void(const std::string& gesture)> callback) {
gestureCallback = callback;
}
// 模拟手势事件
void simulateGestureEvent(const std::string& gesture) {
if (gestureCallback) {
gestureCallback(gesture);
}
}
private:
std::function<void(const std::string& gesture)> gestureCallback;
};
void onGesture(const std::string& gesture) {
std::cout << "Gesture recognized: " << gesture << std::endl;
}
int main() {
Controller controller;
controller.registerGestureCallback(onGesture);
// 模拟手势事件
controller.simulateGestureEvent("swipe_left");
return 0;
}
Monado引擎中的控制器配置通常使用JSON格式的配置文件。配置文件中包含控制器的名称、类型、按钮映射、轴映射等信息。以下是一个示例配置文件:
{
"name": "Monado VR Controller",
"type": "VR",
"buttons": {
"A": 1,
"B": 2,
"X": 3,
"Y": 4
},
"axes": {
"LeftStickX": 0,
"LeftStickY": 1,
"RightStickX": 2,
"RightStickY": 3
}
}
读取和解析配置文件是控制器配置的关键步骤。可以使用第三方库如nlohmann/json来解析JSON配置文件。以下是一个示例,展示如何读取和解析配置文件:
#include
#include
#include
using json = nlohmann::json;
class ControllerConfig {
public:
void loadConfig(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Failed to open config file: " << filename << std::endl;
return;
}
json data;
file >> data;
name = data["name"];
type = data["type"];
for (auto& [buttonName, buttonID] : data["buttons"].items()) {
buttonMap[buttonName] = buttonID;
}
for (auto& [axisName, axisID] : data["axes"].items()) {
axisMap[axisName] = axisID;
}
}
std::string getName() const { return name; }
std::string getType() const { return type; }
int getButtonID(const std::string& buttonName) const {
if (buttonMap.find(buttonName) != buttonMap.end()) {
return buttonMap.at(buttonName);
}
return -1;
}
int getAxisID(const std::string& axisName) const {
if (axisMap.find(axisName) != axisMap.end()) {
return axisMap.at(axisName);
}
return -1;
}
private:
std::string name;
std::string type;
std::map<std::string, int> buttonMap;
std::map<std::string, int> axisMap;
};
int main() {
ControllerConfig config;
config.loadConfig("controller_config.json");
std::cout << "Controller Name: " << config.getName() << std::endl;
std::cout << "Controller Type: " << config.getType() << std::endl;
std::cout << "Button A ID: " << config.getButtonID("A") << std::endl;
std::cout << "Axis LeftStickX ID: " << config.getAxisID("LeftStickX") << std::endl;
return 0;
}
Monado引擎中的交互系统通常基于事件驱动的模型。当控制器触发输入事件时,交互系统会根据配置文件中的映射信息,将这些事件转换为游戏逻辑中的操作。以下是一个简单的示例,展示如何实现一个事件驱动的交互系统:
#include
#include
#include
#include
using json = nlohmann::json;
class ControllerConfig {
public:
void loadConfig(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Failed to open config file: " << filename << std::endl;
return;
}
json data;
file >> data;
name = data["name"];
type = data["type"];
for (auto& [buttonName, buttonID] : data["buttons"].items()) {
buttonMap[buttonName] = buttonID;
}
for (auto& [axisName, axisID] : data["axes"].items()) {
axisMap[axisName] = axisID;
}
}
std::string getName() const { return name; }
std::string getType() const { return type; }
int getButtonID(const std::string& buttonName) const {
if (buttonMap.find(buttonName) != buttonMap.end()) {
return buttonMap.at(buttonName);
}
return -1;
}
int getAxisID(const std::string& axisName) const {
if (axisMap.find(axisName) != axisMap.end()) {
return axisMap.at(axisName);
}
return -1;
}
private:
std::string name;
std::string type;
std::map<std::string, int> buttonMap;
std::map<std::string, int> axisMap;
};
class InteractionSystem {
public:
void setControllerConfig(const ControllerConfig& config) {
controllerConfig = config;
}
void handleButtonEvent(int button, bool isPressed) {
for (auto& [action, buttonID] : buttonActions) {
if (buttonID == button) {
buttonCallbacks[buttonID](isPressed);
}
}
}
void handleAxisEvent(int axis, float value) {
for (auto& [action, axisID] : axisActions) {
if (axisID == axis) {
axisCallbacks[axisID](value);
}
}
}
void registerButtonAction(const std::string& actionName, std::function<void(bool)> action) {
int buttonID = controllerConfig.getButtonID(actionName);
if (buttonID != -1) {
buttonActions[actionName] = buttonID;
buttonCallbacks[buttonID] = action;
}
}
void registerAxisAction(const std::string& axisName, std::function<void(float)> action) {
int axisID = controllerConfig.getAxisID(axisName);
if (axisID != -1) {
axisActions[axisName] = axisID;
axisCallbacks[axisID] = action;
}
}
private:
ControllerConfig controllerConfig;
std::map<std::string, int> buttonActions;
std::map<std::string, int> axisActions;
std::map<int, std::function<void(bool)>> buttonCallbacks;
std::map<int, std::function<void(float)>> axisCallbacks;
};
void onJump(bool isPressed) {
std::cout << "Jump button " << (isPressed ? "pressed" : "released") << std::endl;
}
void onMove(float value) {
std::cout << "Move axis value: " << value << std::endl;
}
int main() {
ControllerConfig config;
config.loadConfig("controller_config.json");
std::cout << "Controller Name: " << config.getName() << std::endl;
std::cout << "Controller Type: " << config.getType() << std::endl;
std::cout << "Button A ID: " << config.getButtonID("A") << std::endl;
std::cout << "Axis LeftStickX ID: " << config.getAxisID("LeftStickX") << std::endl;
InteractionSystem interactionSystem;
interactionSystem.setControllerConfig(config);
// 注册按钮和轴的动作
interactionSystem.registerButtonAction("A", onJump);
interactionSystem.registerAxisAction("LeftStickX", onMove);
// 模拟按钮A被按下的事件
interactionSystem.handleButtonEvent(1, true);
interactionSystem.handleButtonEvent(1, false);
// 模拟左摇杆X轴移动到0.5
interactionSystem.handleAxisEvent(0, 0.5f);
return 0;
}
Monado引擎的交互系统设计为高度可扩展。开发者可以通过添加新的事件类型和回调函数来支持更多的控制器功能。例如,可以添加支持手势事件和触摸事件的处理逻辑。
手势事件的处理可以通过类似的回调机制来实现。以下是一个示例,展示如何在交互系统中扩展手势事件的处理:
#include
#include
#include
#include
using json = nlohmann::json;
class ControllerConfig {
public:
void loadConfig(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Failed to open config file: " << filename << std::endl;
return;
}
json data;
file >> data;
name = data["name"];
type = data["type"];
for (auto& [buttonName, buttonID] : data["buttons"].items()) {
buttonMap[buttonName] = buttonID;
}
for (auto& [axisName, axisID] : data["axes"].items()) {
axisMap[axisName] = axisID;
}
for (auto& [gestureName, gestureID] : data["gestures"].items()) {
gestureMap[gestureName] = gestureID;
}
}
std::string getName() const { return name; }
std::string getType() const { return type; }
int getButtonID(const std::string& buttonName) const {
if (buttonMap.find(buttonName) != buttonMap.end()) {
return buttonMap.at(buttonName);
}
return -1;
}
int getAxisID(const std::string& axisName) const {
if (axisMap.find(axisName) != axisMap.end()) {
return axisMap.at(axisName);
}
return -1;
}
int getGestureID(const std::string& gestureName) const {
if (gestureMap.find(gestureName) != gestureMap.end()) {
return gestureMap.at(gestureName);
}
return -1;
}
private:
std::string name;
std::string type;
std::map<std::string, int> buttonMap;
std::map<std::string, int> axisMap;
std::map<std::string, int> gestureMap;
};
class InteractionSystem {
public:
void setControllerConfig(const ControllerConfig& config) {
controllerConfig = config;
}
void handleButtonEvent(int button, bool isPressed) {
for (auto& [action, buttonID] : buttonActions) {
if (buttonID == button) {
buttonCallbacks[buttonID](isPressed);
}
}
}
void handleAxisEvent(int axis, float value) {
for (auto& [action, axisID] : axisActions) {
if (axisID == axis) {
axisCallbacks[axisID](value);
}
}
}
void handleGestureEvent(int gesture, const std::string& gestureName) {
for (auto& [action, gestureID] : gestureActions) {
if (gestureID == gesture) {
gestureCallbacks[gestureID](gestureName);
}
}
}
void registerButtonAction(const std::string& actionName, std::function<void(bool)> action) {
int buttonID = controllerConfig.getButtonID(actionName);
if (buttonID != -1) {
buttonActions[actionName] = buttonID;
buttonCallbacks[buttonID] = action;
}
}
void registerAxisAction(const std::string& axisName, std::function<void(float)> action) {
int axisID = controllerConfig.getAxisID(axisName);
if (axisID != -1) {
axisActions[axisName] = axisID;
axisCallbacks[axisID] = action;
}
}
void registerGestureAction(const std::string& gestureName, std::function<void(const std::string&)> action) {
int gestureID = controllerConfig.getGestureID(gestureName);
if (gestureID != -1) {
gestureActions[gestureName] = gestureID;
gestureCallbacks[gestureID] = action;
}
}
private:
ControllerConfig controllerConfig;
std::map<std::string, int> buttonActions;
std::map<std::string, int> axisActions;
std::map<std::string, int> gestureActions;
std::map<int, std::function<void(bool)>> buttonCallbacks;
std::map<int, std::function<void(float)>> axisCallbacks;
std::map<int, std::function<void(const std::string&)>> gestureCallbacks;
};
void onJump(bool isPressed) {
std::cout << "Jump button " << (isPressed ? "pressed" : "released") << std::endl;
}
void onMove(float value) {
std::cout << "Move axis value: " << value << std::endl;
}
void onSwipe(const std::string& gesture) {
std::cout << "Gesture recognized: " << gesture << std::endl;
}
int main() {
ControllerConfig config;
config.loadConfig("controller_config.json");
std::cout << "Controller Name: " << config.getName() << std::endl;
std::cout << "Controller Type: " << config.getType() << std::endl;
std::cout << "Button A ID: " << config.getButtonID("A") << std::endl;
std::cout << "Axis LeftStickX ID: " << config.getAxisID("LeftStickX") << std::endl;
std::cout << "Gesture Swipe ID: " << config.getGestureID("swipe_left") << std::endl;
InteractionSystem interactionSystem;
interactionSystem.setControllerConfig(config);
// 注册按钮、轴和手势的动作
interactionSystem.registerButtonAction("A", onJump);
interactionSystem.registerAxisAction("LeftStickX", onMove);
interactionSystem.registerGestureAction("swipe_left", onSwipe);
// 模拟按钮A被按下的事件
interactionSystem.handleButtonEvent(1, true);
interactionSystem.handleButtonEvent(1, false);
// 模拟左摇杆X轴移动到0.5
interactionSystem.handleAxisEvent(0, 0.5f);
// 模拟手势事件
interactionSystem.handleGestureEvent(config.getGestureID("swipe_left"), "swipe_left");
return 0;
}
触摸事件的处理也可以通过类似的回调机制来实现。以下是一个示例,展示如何在交互系统中扩展触摸事件的处理:
#include
#include
#include
#include
using json = nlohmann::json;
class ControllerConfig {
public:
void loadConfig(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Failed to open config file: " << filename << std::endl;
return;
}
json data;
file >> data;
name = data["name"];
type = data["type"];
for (auto& [buttonName, buttonID] : data["buttons"].items()) {
buttonMap[buttonName] = buttonID;
}
for (auto& [axisName, axisID] : data["axes"].items()) {
axisMap[axisName] = axisID;
}
for (auto& [touchName, touchID] : data["touches"].items()) {
touchMap[touchName] = touchID;
}
for (auto& [gestureName, gestureID] : data["gestures"].items()) {
gestureMap[gestureName] = gestureID;
}
}
std::string getName() const { return name; }
std::string getType() const { return type; }
int getButtonID(const std::string& buttonName) const {
if (buttonMap.find(buttonName) != buttonMap.end()) {
return buttonMap.at(buttonName);
}
return -1;
}
int getAxisID(const std::string& axisName) const {
if (axisMap.find(axisName) != axisMap.end()) {
return axisMap.at(axisName);
}
return -1;
}
int getTouchID(const std::string& touchName) const {
if (touchMap.find(touchName) != touchMap.end()) {
return touchMap.at(touchName);
}
return -1;
}
int getGestureID(const std::string& gestureName) const {
if (gestureMap.find(gestureName) != gestureMap.end()) {
return gestureMap.at(gestureName);
}
return -1;
}
private:
std::string name;
std::string type;
std::map<std::string, int> buttonMap;
std::map<std::string, int> axisMap;
std::map<std::string, int> touchMap;
std::map<std::string, int> gestureMap;
};
class InteractionSystem {
public:
void setControllerConfig(const ControllerConfig& config) {
controllerConfig = config;
}
void handleButtonEvent(int button, bool isPressed) {
for (auto& [action, buttonID] : buttonActions) {
if (buttonID == button) {
buttonCallbacks[buttonID](isPressed);
}
}
}
void handleAxisEvent(int axis, float value) {
for (auto& [action, axisID] : axisActions) {
if (axisID == axis) {
axisCallbacks[axisID](value);
}
}
}
void handleTouchEvent(int touch, int x, int y) {
for (auto& [action, touchID] : touchActions) {
if (touchID == touch) {
touchCallbacks[touchID](x, y);
}
}
}
void handleGestureEvent(int gesture, const std::string& gestureName) {
for (auto& [action, gestureID] : gestureActions) {
if (gestureID == gesture) {
gestureCallbacks[gestureID](gestureName);
}
}
}
void registerButtonAction(const std::string& actionName, std::function<void(bool)> action) {
int buttonID = controllerConfig.getButtonID(actionName);
if (buttonID != -1) {
buttonActions[actionName] = buttonID;
buttonCallbacks[buttonID] = action;
}
}
void registerAxisAction(const std::string& axisName, std::function<void(float)> action) {
int axisID = controllerConfig.getAxisID(axisName);
if (axisID != -1) {
axisActions[axisName] = axisID;
axisCallbacks[axisID] = action;
}
}
void registerTouchAction(const std::string& touchName, std::function<void(int, int)> action) {
int touchID = controllerConfig.getTouchID(touchName);
if (touchID != -1) {
touchActions[touchName] = touchID;
touchCallbacks[touchID] = action;
}
}
void registerGestureAction(const std::string& gestureName, std::function<void(const std::string&)> action) {
int gestureID = controllerConfig.getGestureID(gestureName);
if (gestureID != -1) {
gestureActions[gestureName] = gestureID;
gestureCallbacks[gestureID] = action;
}
}
private:
ControllerConfig controllerConfig;
std::map<std::string, int> buttonActions;
std::map<std::string, int> axisActions;
std::map<std::string, int> touchActions;
std::map<std::string, int> gestureActions;
std::map<int, std::function<void(bool)>> buttonCallbacks;
std::map<int, std::function<void(float)>> axisCallbacks;
std::map<int, std::function<void(int, int)>> touchCallbacks;
std::map<int, std::function<void(const std::string&)>> gestureCallbacks;
};
void onJump(bool isPressed) {
std::cout << "Jump button " << (isPressed ? "pressed" : "released") << std::endl;
}
void onMove(float value) {
std::cout << "Move axis value: " << value << std::endl;
}
void onTouch(int x, int y) {
std::cout << "Touched at: (" << x << ", " << y << ")" << std::endl;
}
void onSwipe(const std::string& gesture) {
std::cout << "Gesture recognized: " << gesture << std::endl;
}
int main() {
ControllerConfig config;
config.loadConfig("controller_config.json");
std::cout << "Controller Name: " << config.getName() << std::endl;
std::cout << "Controller Type: " << config.getType() << std::endl;
std::cout << "Button A ID: " << config.getButtonID("A") << std::endl;
std::cout << "Axis LeftStickX ID: " << config.getAxisID("LeftStickX") << std::endl;
std::cout << "Touch ID: " << config.getTouchID("touch_pad") << std::endl;
std::cout << "Gesture Swipe ID: " << config.getGestureID("swipe_left") << std::endl;
InteractionSystem interactionSystem;
interactionSystem.setControllerConfig(config);
// 注册按钮、轴、触摸和手势的动作
interactionSystem.registerButtonAction("A", onJump);
interactionSystem.registerAxisAction("LeftStickX", onMove);
interactionSystem.registerTouchAction("touch_pad", onTouch);
interactionSystem.registerGestureAction("swipe_left", onSwipe);
// 模拟按钮A被按下的事件
interactionSystem.handleButtonEvent(1, true);
interactionSystem.handleButtonEvent(1, false);
// 模拟左摇杆X轴移动到0.5
interactionSystem.handleAxisEvent(0, 0.5f);
// 模拟触摸事件
interactionSystem.handleTouchEvent(config.getTouchID("touch_pad"), 100, 200);
// 模拟手势事件
interactionSystem.handleGestureEvent(config.getGestureID("swipe_left"), "swipe_left");
return ## 5. 交互系统
### 5.1 事件驱动的交互系统
Monado引擎中的交互系统通常基于事件驱动的模型。当控制器触发输入事件时,交互系统会根据配置文件中的映射信息,将这些事件转换为游戏逻辑中的操作。以下是一个简单的示例,展示如何实现一个事件驱动的交互系统:
```cpp
#include
#include
#include
#include
using json = nlohmann::json;
class ControllerConfig {
public:
void loadConfig(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Failed to open config file: " << filename << std::endl;
return;
}
json data;
file >> data;
name = data["name"];
type = data["type"];
for (auto& [buttonName, buttonID] : data["buttons"].items()) {
buttonMap[buttonName] = buttonID;
}
for (auto& [axisName, axisID] : data["axes"].items()) {
axisMap[axisName] = axisID;
}
for (auto& [touchName, touchID] : data["touches"].items()) {
touchMap[touchName] = touchID;
}
for (auto& [gestureName, gestureID] : data["gestures"].items()) {
gestureMap[gestureName] = gestureID;
}
}
std::string getName() const { return name; }
std::string getType() const { return type; }
int getButtonID(const std::string& buttonName) const {
if (buttonMap.find(buttonName) != buttonMap.end()) {
return buttonMap.at(buttonName);
}
return -1;
}
int getAxisID(const std::string& axisName) const {
if (axisMap.find(axisName) != axisMap.end()) {
return axisMap.at(axisName);
}
return -1;
}
int getTouchID(const std::string& touchName) const {
if (touchMap.find(touchName) != touchMap.end()) {
return touchMap.at(touchName);
}
return -1;
}
int getGestureID(const std::string& gestureName) const {
if (gestureMap.find(gestureName) != gestureMap.end()) {
return gestureMap.at(gestureName);
}
return -1;
}
private:
std::string name;
std::string type;
std::map<std::string, int> buttonMap;
std::map<std::string, int> axisMap;
std::map<std::string, int> touchMap;
std::map<std::string, int> gestureMap;
};
class InteractionSystem {
public:
void setControllerConfig(const ControllerConfig& config) {
controllerConfig = config;
}
void handleButtonEvent(int button, bool isPressed) {
for (auto& [action, buttonID] : buttonActions) {
if (buttonID == button) {
buttonCallbacks[buttonID](isPressed);
}
}
}
void handleAxisEvent(int axis, float value) {
for (auto& [action, axisID] : axisActions) {
if (axisID == axis) {
axisCallbacks[axisID](value);
}
}
}
void handleTouchEvent(int touch, int x, int y) {
for (auto& [action, touchID] : touchActions) {
if (touchID == touch) {
touchCallbacks[touchID](x, y);
}
}
}
void handleGestureEvent(int gesture, const std::string& gestureName) {
for (auto& [action, gestureID] : gestureActions) {
if (gestureID == gesture) {
gestureCallbacks[gestureID](gestureName);
}
}
}
void registerButtonAction(const std::string& actionName, std::function<void(bool)> action) {
int buttonID = controllerConfig.getButtonID(actionName);
if (buttonID != -1) {
buttonActions[actionName] = buttonID;
buttonCallbacks[buttonID] = action;
}
}
void registerAxisAction(const std::string& axisName, std::function<void(float)> action) {
int axisID = controllerConfig.getAxisID(axisName);
if (axisID != -1) {
axisActions[axisName] = axisID;
axisCallbacks[axisID] = action;
}
}
void registerTouchAction(const std::string& touchName, std::function<void(int, int)> action) {
int touchID = controllerConfig.getTouchID(touchName);
if (touchID != -1) {
touchActions[touchName] = touchID;
touchCallbacks[touchID] = action;
}
}
void registerGestureAction(const std::string& gestureName, std::function<void(const std::string&)> action) {
int gestureID = controllerConfig.getGestureID(gestureName);
if (gestureID != -1) {
gestureActions[gestureName] = gestureID;
gestureCallbacks[gestureID] = action;
}
}
private:
ControllerConfig controllerConfig;
std::map<std::string, int> buttonActions;
std::map<std::string, int> axisActions;
std::map<std::string, int> touchActions;
std::map<std::string, int> gestureActions;
std::map<int, std::function<void(bool)>> buttonCallbacks;
std::map<int, std::function<void(float)>> axisCallbacks;
std::map<int, std::function<void(int, int)>> touchCallbacks;
std::map<int, std::function<void(const std::string&)>> gestureCallbacks;
};
void onJump(bool isPressed) {
std::cout << "Jump button " << (isPressed ? "pressed" : "released") << std::endl;
}
void onMove(float value) {
std::cout << "Move axis value: " << value << std::endl;
}
void onTouch(int x, int y) {
std::cout << "Touched at: (" << x << ", " << y << ")" << std::endl;
}
void onSwipe(const std::string& gesture) {
std::cout << "Gesture recognized: " << gesture << std::endl;
}
int main() {
ControllerConfig config;
config.loadConfig("controller_config.json");
std::cout << "Controller Name: " << config.getName() << std::endl;
std::cout << "Controller Type: " << config.getType() << std::endl;
std::cout << "Button A ID: " << config.getButtonID("A") << std::endl;
std::cout << "Axis LeftStickX ID: " << config.getAxisID("LeftStickX") << std::endl;
std::cout << "Touch ID: " << config.getTouchID("touch_pad") << std::endl;
std::cout << "Gesture Swipe ID: " << config.getGestureID("swipe_left") << std::endl;
InteractionSystem interactionSystem;
interactionSystem.setControllerConfig(config);
// 注册按钮、轴、触摸和手势的动作
interactionSystem.registerButtonAction("A", onJump);
interactionSystem.registerAxisAction("LeftStickX", onMove);
interactionSystem.registerTouchAction("touch_pad", onTouch);
interactionSystem.registerGestureAction("swipe_left", onSwipe);
// 模拟按钮A被按下的事件
interactionSystem.handleButtonEvent(1, true);
interactionSystem.handleButtonEvent(1, false);
// 模拟左摇杆X轴移动到0.5
interactionSystem.handleAxisEvent(0, 0.5f);
// 模拟触摸事件
interactionSystem.handleTouchEvent(config.getTouchID("touch_pad"), 100, 200);
// 模拟手势事件
interactionSystem.handleGestureEvent(config.getGestureID("swipe_left"), "swipe_left");
return 0;
}
Monado引擎的交互系统设计为高度可扩展。开发者可以通过添加新的事件类型和回调函数来支持更多的控制器功能。例如,可以添加支持手势事件和触摸事件的处理逻辑。
手势事件的处理可以通过类似的回调机制来实现。以下是一个示例,展示如何在交互系统中扩展手势事件的处理:
#include
#include
#include
#include
using json = nlohmann::json;
class ControllerConfig {
public:
void loadConfig(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Failed to open config file: " << filename << std::endl;
return;
}
json data;
file >> data;
name = data["name"];
type = data["type"];
for (auto& [buttonName, buttonID] : data["buttons"].items()) {
buttonMap[buttonName] = buttonID;
}
for (auto& [axisName, axisID] : data["axes"].items()) {
axisMap[axisName] = axisID;
}
for (auto& [gestureName, gestureID] : data["gestures"].items()) {
gestureMap[gestureName] = gestureID;
}
}
std::string getName() const { return name; }
std::string getType() const { return type; }
int getButtonID(const std::string& buttonName) const {
if (buttonMap.find(buttonName) != buttonMap.end()) {
return buttonMap.at(buttonName);
}
return -1;
}
int getAxisID(const std::string& axisName) const {
if (axisMap.find(axisName) != axisMap.end()) {
return axisMap.at(axisName);
}
return -1;
}
int getGestureID(const std::string& gestureName) const {
if (gestureMap.find(gestureName) != gestureMap.end()) {
return gestureMap.at(gestureName);
}
return -1;
}
private:
std::string name;
std::string type;
std::map<std::string, int> buttonMap;
std::map<std::string, int> axisMap;
std::map<std::string, int> gestureMap;
};
class InteractionSystem {
public:
void setControllerConfig(const ControllerConfig& config) {
controllerConfig = config;
}
void handleButtonEvent(int button, bool isPressed) {
for (auto& [action, buttonID] : buttonActions) {
if (buttonID == button) {
buttonCallbacks[buttonID](isPressed);
}
}
}
void handleAxisEvent(int axis, float value) {
for (auto& [action, axisID] : axisActions) {
if (axisID == axis) {
axisCallbacks[axisID](value);
}
}
}
void handleGestureEvent(int gesture, const std::string& gestureName) {
for (auto& [action, gestureID] : gestureActions) {
if (gestureID == gesture) {
gestureCallbacks[gestureID](gestureName);
}
}
}
void registerButtonAction(const std::string& actionName, std::function<void(bool)> action) {
int buttonID = controllerConfig.getButtonID(actionName);
if (buttonID != -1) {
buttonActions[actionName] = buttonID;
buttonCallbacks[buttonID] = action;
}
}
void registerAxisAction(const std::string& axisName, std::function<void(float)> action) {
int axisID = controllerConfig.getAxisID(axisName);
if (axisID != -1) {
axisActions[axisName] = axisID;
axisCallbacks[axisID] = action;
}
}
void registerGestureAction(const std::string& gestureName, std::function<void(const std::string&)> action) {
int gestureID = controllerConfig.getGestureID(gestureName);
if (gestureID != -1) {
gestureActions[gestureName] = gestureID;
gestureCallbacks[gestureID] = action;
}
}
private:
ControllerConfig controllerConfig;
std::map<std::string, int> buttonActions;
std::map<std::string, int> axisActions;
std::map<std::string, int> gestureActions;
std::map<int, std::function<void(bool)>> buttonCallbacks;
std::map<int, std::function<void(float)>> axisCallbacks;
std::map<int, std::function<void(const std::string&)>> gestureCallbacks;
};
void onJump(bool isPressed) {
std::cout << "Jump button " << (isPressed ? "pressed" : "released") << std::endl;
}
void onMove(float value) {
std::cout << "Move axis value: " << value << std::endl;
}
void onSwipe(const std::string& gesture) {
std::cout << "Gesture recognized: " << gesture << std::endl;
}
int main() {
ControllerConfig config;
config.loadConfig("controller_config.json");
std::cout << "Controller Name: " << config.getName() << std::endl;
std::cout << "Controller Type: " << config.getType() << std::endl;
std::cout << "Button A ID: " << config.getButtonID("A") << std::endl;
std::cout << "Axis LeftStickX ID: " << config.getAxisID("LeftStickX") << std::endl;
std::cout << "Gesture Swipe ID: " << config.getGestureID("swipe_left") << std::endl;
InteractionSystem interactionSystem;
interactionSystem.setControllerConfig(config);
// 注册按钮、轴和手势的动作
interactionSystem.registerButtonAction("A", onJump);
interactionSystem.registerAxisAction("LeftStickX", onMove);
interactionSystem.registerGestureAction("swipe_left", onSwipe);
// 模拟按钮A被按下的事件
interactionSystem.handleButtonEvent(1, true);
interactionSystem.handleButtonEvent(1, false);
// 模拟左摇杆X轴移动到0.5
interactionSystem.handleAxisEvent(0, 0.5f);
// 模拟手势事件
interactionSystem.handleGestureEvent(config.getGestureID("swipe_left"), "swipe_left");
return 0;
}
触摸事件的处理也可以通过类似的回调机制来实现。以下是一个示例,展示如何在交互系统中扩展触摸事件的处理:
#include
#include
#include
#include
using json = nlohmann::json;
class ControllerConfig {
public:
void loadConfig(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Failed to open config file: " << filename << std::endl;
return;
}
json data;
file >> data;
name = data["name"];
type = data["type"];
for (auto& [buttonName, buttonID] : data["buttons"].items()) {
buttonMap[buttonName] = buttonID;
}
for (auto& [axisName, axisID] : data["axes"].items()) {
axisMap[axisName] = axisID;
}
for (auto& [touchName, touchID] : data["touches"].items()) {
touchMap[touchName] = touchID;
}
for (auto& [gestureName, gestureID] : data["gestures"].items()) {
gestureMap[gestureName] = gestureID;
}
}
std::string getName() const { return name; }
std::string getType() const { return type; }
int getButtonID(const std::string& buttonName) const {
if (buttonMap.find(buttonName) != buttonMap.end()) {
return buttonMap.at(buttonName);
}
return -1;
}
int getAxisID(const std::string& axisName) const {
if (axisMap.find(axisName) != axisMap.end()) {
return axisMap.at(axisName);
}
return -1;
}
int getTouchID(const std::string& touchName) const {
if (touchMap.find(touchName) != touchMap.end()) {
return touchMap.at(touchName);
}
return -1;
}
int getGestureID(const std::string& gestureName) const {
if (gestureMap.find(gestureName) != gestureMap.end()) {
return gestureMap.at(gestureName);
}
return -1;
}
private:
std::string name;
std::string type;
std::map<std::string, int> buttonMap;
std::map<std::string, int> axisMap;
std::map<std::string, int> touchMap;
std::map<std::string, int> gestureMap;
};
class InteractionSystem {
public:
void setControllerConfig(const ControllerConfig& config) {
controllerConfig = config;
}
void handleButtonEvent(int button, bool isPressed) {
for (auto& [action, buttonID] : buttonActions) {
if (buttonID == button) {
buttonCallbacks[buttonID](isPressed);
}
}
}
void handleAxisEvent(int axis, float value) {
for (auto& [action, axisID] : axisActions) {
if (axisID == axis) {
axisCallbacks[axisID](value);
}
}
}
void handleTouchEvent(int touch, int x, int y) {
for (auto& [action, touchID] : touchActions) {
if (touchID == touch) {
touchCallbacks[touchID](x, y);
}
}
}
void handleGestureEvent(int gesture, const std::string& gestureName) {
for (auto& [action, gestureID] : gestureActions) {
if (gestureID == gesture) {
gestureCallbacks[gestureID](gestureName);
}
}
}
void registerButtonAction(const std::string& actionName, std::function<void(bool)> action) {
int buttonID = controllerConfig.getButtonID(actionName);
if (buttonID != -1) {
buttonActions[actionName] = buttonID;
buttonCallbacks[buttonID] = action;
}
}
void registerAxisAction(const std::string& axisName, std::function<void(float)> action) {
int axisID = controllerConfig.getAxisID(axisName);
if (axisID