在游戏引擎中实现一个完整的输入(Input)类涉及到多个方面的功能,包括键盘、鼠标、触摸屏等输入设备的检测和处理。下面是一个简化的输入类的实现示例,使用C++语言和一些常见的游戏引擎库(如SDL)来实现。
1. 包含必要的头文件
#include <SDL.h>
#include <unordered_map>
#include <vector>
2. 定义输入类
class Input {
public:
Input();
~Input();
bool Initialize();
void Shutdown();
void Update();
bool IsKeyDown(SDL_Keycode key) const;
bool IsKeyUp(SDL_Keycode key) const;
bool IsKeyPressed(SDL_Keycode key) const;
bool IsMouseButtonDown(int button) const;
bool IsMouseButtonUp(int button) const;
bool IsMouseButtonPressed(int button) const;
SDL_Point GetMousePosition() const;
private:
std::unordered_map<SDL_Keycode, bool> keyStates;
std::unordered_map<int, bool> mouseButtonStates;
SDL_Point mousePosition;
};
3. 实现输入类的方法
Input::Input() {
// 初始化SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
SDL_Log("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
}
}
Input::~Input() {
Shutdown();
}
bool Input::Initialize() {
// 初始化输入系统
return true;
}
void Input::Shutdown() {
// 清理输入资源
SDL_Quit();
}
void Input::Update() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
keyStates[event.key.keysym.sym] = true;
break;
case SDL_KEYUP:
keyStates[event.key.keysym.sym] = false;
break;
case SDL_MOUSEBUTTONDOWN:
mouseButtonStates[event.button.button] = true;
break;
case SDL_MOUSEBUTTONUP:
mouseButtonStates[event.button.button] = false;
break;
case SDL_MOUSEMOTION:
mousePosition.x = event.motion.x;
mousePosition.y = event.motion.y;
break;
}
}
}
bool Input::IsKeyDown(SDL_Keycode key) const {
auto it = keyStates.find(key);
if (it != keyStates.end()) {
return it->second;
}
return false;
}
bool Input::IsKeyUp(SDL_Keycode key) const {
return !IsKeyDown(key);
}
bool Input::IsKeyPressed(SDL_Keycode key) const {
// 这里可以实现按键按下的检测逻辑
// 例如,通过记录上一帧的状态来判断是否刚刚按下
return false;
}
bool Input::IsMouseButtonDown(int button) const {
auto it = mouseButtonStates.find(button);
if (it != mouseButtonStates.end()) {
return it->second;
}
return false;
}
bool Input::IsMouseButtonUp(int button) const {
return !IsMouseButtonDown(button);
}
bool Input::IsMouseButtonPressed(int button) const {
// 这里可以实现鼠标按钮按下的检测逻辑
// 例如,通过记录上一帧的状态来判断是否刚刚按下
return false;
}
SDL_Point Input::GetMousePosition() const {
return mousePosition;
}
4. 使用输入类
int main(int argc, char* argv[]) {
Input input;
if (!input.Initialize()) {
return -1;
}
bool running = true;
while (running) {
input.Update();
if (input.IsKeyDown(SDLK_ESCAPE)) {
running = false;
}
SDL_Delay(16); // 大约60 FPS
}
input.Shutdown();
return 0;
}
5. 扩展输入类功能
为了使输入类更加强大和灵活,可以考虑添加以下功能:
5.1 触摸屏输入
对于支持触摸屏的设备,可以添加触摸屏输入的检测和处理。
bool Input::IsTouchDown(int fingerId) const {
// 实现触摸屏按下的检测逻辑
return false;
}
bool Input::IsTouchUp(int fingerId) const {
// 实现触摸屏抬起的检测逻辑
return false;
}
bool Input::IsTouchPressed(int fingerId) const {
// 实现触摸屏按下的检测逻辑
return false;
}
SDL_Point Input::GetTouchPosition(int fingerId) const {
// 实现获取触摸位置的逻辑
return {0, 0};
}
5.2 自定义输入映射
允许开发者自定义输入映射,以便更好地适应不同的游戏需求。
class InputMapping {
public:
void AddMapping(SDL_Keycode key, std::function<void()> action);
void AddMouseMapping(int button, std::function<void()> action);
void ProcessInput(const Input& input);
private:
std::unordered_map<SDL_Keycode, std::function<void()>> keyMappings;
std::unordered_map<int, std::function<void()>> mouseButtonMappings;
};
void InputMapping::AddMapping(SDL_Keycode key, std::function<void()> action) {
keyMappings[key] = action;
}
void InputMapping::AddMouseMapping(int button, std::function<void()> action) {
mouseButtonMappings[button] = action;
}
void InputMapping::ProcessInput(const Input& input) {
for (const auto& pair : keyMappings) {
if (input.IsKeyPressed(pair.first)) {
pair.second();
}
}
for (const auto& pair : mouseButtonMappings) {
if (input.IsMouseButtonPressed(pair.first)) {
pair.second();
}
}
}
6. 示例:使用扩展功能的输入类
int main(int argc, char* argv[]) {
Input input;
if (!input.Initialize()) {
return -1;
}
InputMapping inputMapping;
inputMapping.AddMapping(SDLK_SPACE, []() {
std::cout << "Space key pressed!" << std::endl;
});
inputMapping.AddMouseMapping(SDL_BUTTON_LEFT, []() {
std::cout << "Left mouse button pressed!" << std::endl;
});
bool running = true;
while (running) {
input.Update();
inputMapping.ProcessInput(input);
if (input.IsKeyDown(SDLK_ESCAPE)) {
running = false;
}
SDL_Delay(16); // 大约60 FPS
}
input.Shutdown();
return 0;
}
通过这些扩展和优化,输入类可以更好地适应不同的游戏场景和需求,提供更灵活和强大的输入处理功能。在实际的游戏引擎中,输入类可能还会包含更多的功能和优化,以满足高性能和高质量输入的需求。
7. 输入事件系统
为了更好地管理输入事件,可以实现一个输入事件系统,允许其他系统(如游戏逻辑、UI等)订阅和响应输入事件。
7.1 定义输入事件
enum class InputEventType {
KeyDown,
KeyUp,
KeyPressed,
MouseButtonDown,
MouseButtonUp,
MouseButtonPressed,
MouseMotion,
// 其他事件类型
};
7.2 定义输入事件回调
using InputEventCallback = std::function<void(InputEventType, int)>;
class Input {
public:
// ... 其他成员函数 ...
void SubscribeToEvent(InputEventType eventType, InputEventCallback callback) {
eventCallbacks[eventType].push_back(callback);
}
private:
std::unordered_map<InputEventType, std::vector<InputEventCallback>> eventCallbacks;
};
7.3 触发输入事件
void Input::TriggerEvent(InputEventType eventType, int eventData) {
if (eventCallbacks.find(eventType) != eventCallbacks.end()) {
for (auto& callback : eventCallbacks[eventType]) {
callback(eventType, eventData);
}
}
}
8. 示例:使用输入事件系统的输入类
int main(int argc, char* argv[]) {
Input input;
if (!input.Initialize()) {
return -1;
}
input.SubscribeToEvent(InputEventType::KeyDown, [](InputEventType eventType, int eventData) {
std::cout << "Key down event: " << eventData << std::endl;
});
input.SubscribeToEvent(InputEventType::MouseButtonDown, [](InputEventType eventType, int eventData) {
std::cout << "Mouse button down event: " << eventData << std::endl;
});
bool running = true;
while (running) {
input.Update();
if (input.IsKeyDown(SDLK_ESCAPE)) {
running = false;
}
SDL_Delay(16); // 大约60 FPS
}
input.Shutdown();
return 0;
}
9. 输入设备管理
为了更好地管理不同的输入设备,可以实现一个输入设备管理器,负责设备的初始化、更新和清理。
9.1 定义输入设备接口
class IInputDevice {
public:
virtual ~IInputDevice() {}
virtual void Initialize() = 0;
virtual void Update() = 0;
virtual void Shutdown() = 0;
};
9.2 实现键盘设备
class KeyboardDevice : public IInputDevice {
public:
// ... 实现键盘设备的初始化、更新和清理 ...
};
9.3 实现鼠标设备
class MouseDevice : public IInputDevice {
public:
// ... 实现鼠标设备的初始化、更新和清理 ...
};
9.4 输入设备管理器
class InputDeviceManager {
public:
InputDeviceManager() {
devices.push_back(std::make_unique<KeyboardDevice>());
devices.push_back(std::make_unique<MouseDevice>());
}
void Initialize() {
for (auto& device : devices) {
device->Initialize();
}
}
void Update() {
for (auto& device : devices) {
device->Update();
}
}
void Shutdown() {
for (auto& device : devices) {
device->Shutdown();
}
}
private:
std::vector<std::unique_ptr<IInputDevice>> devices;
};
10. 示例:使用输入设备管理器
int main(int argc, char* argv[]) {
InputDeviceManager deviceManager;
deviceManager.Initialize();
bool running = true;
while (running) {
deviceManager.Update();
// 处理输入事件
if (/* 检测到退出事件 */) {
running = false;
}
SDL_Delay(16); // 大约60 FPS
}
deviceManager.Shutdown();
return 0;
}
通过这些扩展和优化,输入类可以更好地适应不同的游戏场景和需求,提供更灵活和强大的输入处理功能。在实际的游戏引擎中,输入类可能还会包含更多的功能和优化,以满足高性能和高质量输入的需求。
11. 多线程输入处理
在现代游戏引擎中,多线程处理可以显著提高性能,特别是在处理输入这种频繁更新的任务时。以下是如何为输入类添加多线程支持的示例。
11.1 线程安全的输入更新
确保输入更新操作是线程安全的,以避免多个线程同时访问和修改输入状态时出现竞态条件。
#include <mutex>
class Input {
public:
// ... 其他成员函数 ...
void Update() {
std::lock_guard<std::mutex> lock(inputMutex);
// 更新输入状态
}
private:
std::mutex inputMutex;
};
11.2 异步输入处理
异步处理输入事件可以避免阻塞主线程,提高游戏的响应速度。
#include <future>
class Input {
public:
// ... 其他成员函数 ...
std::future<void> UpdateAsync() {
return std::async(std::launch::async, [this]() {
Update();
});
}
};
12. 示例:使用多线程功能的输入类
int main(int argc, char* argv[]) {
Input input;
if (!input.Initialize()) {
return -1;
}
bool running = true;
while (running) {
auto future = input.UpdateAsync();
// 处理其他任务
future.get(); // 等待输入更新完成
if (/* 检测到退出事件 */) {
running = false;
}
SDL_Delay(16); // 大约60 FPS
}
input.Shutdown();
return 0;
}
13. 跨平台支持
为了使输入类能够在不同的操作系统和平台上运行,需要考虑跨平台支持。
13.1 抽象输入接口
定义一个抽象的输入接口,然后为每个支持的操作系统和平台实现具体的输入类。
class IInput {
public:
virtual ~IInput() {}
virtual void Initialize() = 0;
virtual void Update() = 0;
virtual void Shutdown() = 0;
// 其他公共接口
};
class WindowsInput : public IInput {
// 实现Windows平台的输入功能
};
class LinuxInput : public IInput {
// 实现Linux平台的输入功能
};
class MacInput : public IInput {
// 实现Mac平台的输入功能
};
13.2 工厂模式创建输入实例
使用工厂模式来创建特定平台的输入实例。
class InputFactory {
public:
static std::unique_ptr<IInput> CreateInput() {
#ifdef _WIN32
return std::make_unique<WindowsInput>();
#elif __linux__
return std::make_unique<LinuxInput>();
#elif __APPLE__
return std::make浮现<pot>
#else
#error "Unsupported platform"
#endif
}
};
14. 示例:跨平台输入类使用
int main(int argc, char* argv[]) {
std::unique_ptr<IInput> input = InputFactory::CreateInput();
if (!input->Initialize()) {
return -1;
}
bool running = true;
while (running) {
input->Update();
// 处理输入事件
if (/* 检测到退出事件 */) {
running = false;
}
SDL_Delay(16); // 大约60 FPS
}
input->Shutdown();
return 0;
}
通过这些扩展和优化,输入类可以更好地适应不同的游戏场景和需求,提供更灵活和强大的输入处理功能。在实际的游戏引擎中,输入类可能还会包含更多的功能和优化,以满足高性能和高质量输入的需求。
15. 输入数据记录与回放
在某些情况下,开发者可能需要记录玩家的输入数据并在之后进行回放,以进行调试或创建演示视频。
15.1 记录输入数据
class InputRecorder {
public:
void RecordInput(const Input& input) {
recordedInputs.push_back(input);
}
std::vector<Input> GetRecordedInputs() const {
return recordedInputs;
}
private:
std::vector<Input> recordedInputs;
};
15.2 回放输入数据
class InputReplayer {
public:
void SetRecordedInputs(const std::vector<Input>& inputs) {
recordedInputs = inputs;
currentIndex = 0;
}
void ReplayInput(Input& input) {
if (currentIndex < recordedInputs.size()) {
input = recordedInputs[currentIndex++];
}
}
private:
std::vector<Input> recordedInputs;
size_t currentIndex = 0;
};
16. 示例:使用输入数据记录与回放
int main(int argc, char* argv[]) {
Input input;
InputRecorder recorder;
InputReplayer replayer;
if (!input.Initialize()) {
return -1;
}
bool recording = false;
bool replaying = false;
bool running = true;
while (running) {
input.Update();
if (recording) {
recorder.RecordInput(input);
}
if (replaying) {
replayer.ReplayInput(input);
}
// 处理输入事件
if (/* 检测到退出事件 */) {
running = false;
}
SDL_Delay(16); // 大约60 FPS
}
input.Shutdown();
return 0;
}
17. 输入自定义数据
有时开发者可能需要为输入添加自定义数据,例如玩家ID、游戏状态等。
class Input {
public:
// ... 其他成员函数 ...
void SetCustomData(void* data) {
customData = data;
}
void* GetCustomData() const {
return customData;
}
private:
void* customData = nullptr;
};
18. 示例:使用输入自定义数据
int main(int argc, char* argv[]) {
Input input;
int playerID = 1;
if (!input.Initialize()) {
return -1;
}
bool running = true;
while (running) {
input.Update();
input.SetCustomData(&playerID);
// 处理输入事件
if (/* 检测到退出事件 */) {
running = false;
}
SDL_Delay(16); // 大约60 FPS
}
input.Shutdown();
return 0;
}
19. 输入性能优化
为了提高输入处理的性能,可以考虑以下优化策略:
19.1 批量处理输入事件
批量处理输入事件可以减少函数调用的开销。
class Input {
public:
// ... 其他成员函数 ...
void ProcessEvents(const std::vector<SDL_Event>& events) {
for (const auto& event : events) {
// 处理事件
}
}
};
19.2 使用输入缓存
输入缓存可以减少重复计算的开销。
class Input {
public:
// ... 其他成员函数 ...
bool WasKeyPressedLastFrame(SDL_Keycode key) const {
return keyStatesLastFrame[key];
}
private:
std::unordered_map<SDL_Keycode, bool> keyStates;
std::unordered_map<SDL_Keycode, bool> keyStatesLastFrame;
};
通过这些扩展和优化,输入类可以更好地适应不同的游戏场景和需求,提供更灵活和强大的输入处理功能。在实际的游戏引擎中,输入类可能还会包含更多的功能和优化,以满足高性能和高质量输入的需求。
20. 输入的触摸屏支持
对于移动设备和触摸屏设备,需要特别处理触摸事件。
20.1 触摸事件处理
class Input {
public:
// ... 其他成员函数 ...
bool IsTouchDown(int fingerId) const {
// 实现触摸屏按下的检测逻辑
return false;
}
bool IsTouchUp(int fingerId) const {
// 实现触摸屏抬起的检测逻辑
return false;
}
bool IsTouchPressed(int fingerId) const {
// 实现触摸屏按下的检测逻辑
return false;
}
SDL_Point GetTouchPosition(int fingerId) const {
// 实现获取触摸位置的逻辑
return {0, 0};
}
};
21. 示例:使用触摸屏支持的输入类
int main(int argc, char* argv[]) {
Input input;
if (!input.Initialize()) {
return -1;
}
bool running = true;
while (running) {
input.Update();
// 处理触摸事件
for (int i = 0; i < MAX_FINGERS; ++i) {
if (input.IsTouchDown(i)) {
SDL_Point pos = input.GetTouchPosition(i);
std::cout << "Touch down at: (" << pos.x << ", " << pos.y << ")" << std::endl;
}
}
if (/* 检测到退出事件 */) {
running = false;
}
SDL_Delay(16); // 大约60 FPS
}
input.Shutdown();
return 0;
}
22. 输入的虚拟现实支持
对于虚拟现实设备,需要特别处理头部运动和手柄输入。
22.1 虚拟现实事件处理
class Input {
public:
// ... 其他成员函数 ...
bool IsHeadsetMoved() const {
// 实现头部移动的检测逻辑
return false;
}
SDL_Point GetHeadsetPosition() const {
// 实现获取头部位置的逻辑
return {0, 0};
}
bool IsControllerButtonPressed(int controllerId, int button) const {
// 实现手柄按钮按下的检测逻辑
return false;
}
};
23. 示例:使用虚拟现实支持的输入类
int main(int argc, char* argv[]) {
Input input;
if (!input.Initialize()) {
return -1;
}
bool running = true;
while (running) {
input.Update();
// 处理虚拟现实事件
if (input.IsHeadsetMoved()) {
SDL_Point pos = input.GetHeadsetPosition();
std::cout << "Headset moved to: (" << pos.x << ", " << pos.y << ")" << std::endl;
}
for (int i = 0; i < MAX_CONTROLLERS; ++i) {
if (input.IsControllerButtonPressed(i, BUTTON_A)) {
std::cout << "Controller "<< i << " button A pressed" << std::endl;
}
}
if (/* 检测到退出事件 */) {
running = false;
}
SDL_Delay(16); // 大约60 FPS
}
input.Shault();
return 0;
}
24. 输入的远程控制支持
对于远程控制设备,需要特别处理遥控器输入。
24.1 远程控制事件处理
class Input {
public:
// ... 其他成员函数 ...
bool IsRemoteButtonPressed(int button) const {
// 实现遥控器按钮按下的检测逻辑
return false;
}
};
25. 示例:使用远程控制支持的输入类
int main(int argc, char* argv[]) {
Input input;
if (!input.Initialize()) {
return -1;
}
bool running = true;
while (running) {
input.Update();
// 处理远程控制事件
if (input.IsRemoteButtonPressed(REMOTE_BUTTON_OK)) {
std::cout << "Remote button OK pressed" << std::endl;
}
if (/* 检测到退出事件 */) {
running = false;
}
SDL_Delay(16); // 大约60 FPS
}
input.Shutdown();
return 0;
}
通过这些扩展和优化,输入类可以更好地适应不同的游戏场景和需求,提供更灵活和强大的输入处理功能。在实际的游戏引擎中,输入类可能还会包含更多的功能和优化,以满足高性能和高质量输入的需求。
26. 输入的辅助功能支持
对于辅助功能设备,如语音输入、手势识别等,需要特别处理这些输入方式。
26.1 辅助功能事件处理
class Input {
public:
// ... 其他成员函数 ...
bool IsVoiceCommandRecognized(const std::string& command) const {
// 实现语音命令识别的检测逻辑
return false;
}
bool IsGestureRecognized(const std::string& gesture) const {
// 实现手势识别的检测逻辑
return false;
}
};
27. 示例:使用辅助功能支持的输入类
int main(int argc, char* argv[]) {
Input input;
if (!input.Initialize()) {
return -1;
}
bool running = true;
while (running) {
input.Update();
// 处理辅助功能事件
if (input.IsVoiceCommandRecognized("start")) {
std::cout << "Voice command 'start' recognized" << std::endl;
}
if (input.IsGestureRecognized("swipe_left")) {
std::cout << "Gesture 'swipe_left' recognized" << std::endl;
}
if (/* 检测到退出事件 */) {
running = false;
}
SDL_Delay(16); // 大约60 FPS
}
input.Shutdown();
return 0;
}
28. 输入的多人游戏支持
对于多人游戏,需要处理多个玩家的输入。
28.1 多人游戏输入处理
class Input {
public:
// ... 其他成员函数 ...
bool IsPlayerKeyPressed(int playerId, SDL_Keycode key) const {
// 实现多个玩家按键按下的检测逻辑
return false;
}
bool IsPlayerMouseButtonPressed(int playerId, int button) const {
// 实现多个玩家鼠标按钮按下的检测 void *data) const {
return false;
}
};
29. 示例:使用多人游戏支持的输入类
int main(int argc, char* argv[]) {
Input input;
if (!input.Initialize()) {
return -1;
}
bool running = true;
while (running) {
input.Update();
// 处理多个玩家的输入
for (int i = 0; i < MAX_PLAYERS; ++i) {
if (input.IsPlayerKeyPressed(i, SDLK_SPACE)) {
std::cout << "Player "<< i << " pressed space key" << std::endl;
}
if (input.IsPlayerMouseButtonPressed(i, SDL_BUTTON_LEFT)) {
std::cout << "Player "<< i << " pressed left mouse button" << std::endl;
}
}
if (/* 检测到退出事件 */) {
running = false;
}
SDL_Delay(16); // 大约60 FPS
}
input.Shutdown();
return 0;
}
30. 输入的调试和测试工具
为了方便调试和测试输入系统,可以添加一些调试和测试工具。
30.1 输入调试工具
class InputDebugger {
public:
void LogInputState(const Input& input) {
// 实现输入状态的日志记录
}
};
31. 示例:使用输入调试工具
int main(int argc, char* argv[]) {
Input input;
InputDebugger debugger;
if (!input.Initialize()) {
return -1;
}
bool running = true;
while (running) {
input.Update();
debugger.LogInputState(input);
if (/* 检测到退出事件 */) {
running = false;
}
SDL_Delay(16); // 大约60 FPS
}
input.Shutdown();
return 0;
}
通过这些扩展和优化,输入类可以更好地适应不同的游戏场景和需求,提供更灵活和强大的输入处理功能。在实际的游戏引擎中,输入类可能还会包含更多的功能和优化,以满足高性能和高质量输入的需求。