上一篇中我们讲到了UE4把键盘和鼠标的映射都保存在一个全局对象FInputKeyManager里,并且也提供了通过key来获取key的虚拟码、字符的方法,下面,我们就来用用个简单例子来说明下这个的用法
首先新建一个C++工程,新建一个C++类InputTestDevice,继承自IInputDevice,这个类是输入相关的接口且为抽象基类,为啥要继承这个类下面我会说,
InputTestDevice.h
class PROJECTTEST_RE421_API FInputTestDevice : public IInputDevice
{
public:
//以下都是父类接口
FInputTestDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler);
virtual void Tick(float DeltaTime) override;
/** Poll for controller state and send events if needed */
virtual void SendControllerEvents() override;
/** Set which MessageHandler will get the events from SendControllerEvents. */
virtual void SetMessageHandler(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler) override;
/** Exec handler to allow console commands to be passed through for debugging */
virtual bool Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar) override;
/**
* IForceFeedbackSystem pass through functions
*/
virtual void SetChannelValue(int32 ControllerId, FForceFeedbackChannelType ChannelType, float Value) override;
virtual void SetChannelValues(int32 ControllerId, const FForceFeedbackValues &values) override;
//事件类型
enum class EventType
{
UNDEFINED, /** No value. */
KEY_DOWN, /** 按键按下. */
KEY_UP, /** 按键松开. */
KEY_PRESS, /** 按键按下并且输入一个字符. */
MOUSE_ENTER, /** 鼠标进入窗口画板. */
MOUSE_LEAVE, /** 鼠标离开窗口画板. */
MOUSE_MOVE, /** 鼠标在窗口画板上移动. */
MOUSE_DOWN, /** 鼠标点击. */
MOUSE_UP, /** 鼠标松开. */
MOUSE_WHEEL, /** 鼠标滚轮. */
};
/** 事件. */
struct FEvent
{
/** 事件类型. */
EventType Event;
/** A generic piece of data which is used to hold information about the
* event, specialized by making a union with an appropriate struct. */
union
{
uint64 Word;
struct /** KEY_DOWN */
{
uint8 KeyCode;
bool bIsRepeat;
} KeyDown;
struct /* KEY_UP */
{
uint8 KeyCode;
} KeyUp;
struct /** KEY_PRESSED */
{
TCHAR Character;
} Character;
struct /** MOUSE_MOVE */
{
int16 DeltaX;
int16 DeltaY;
uint16 PosX;
uint16 PosY;
} MouseMove;
struct /** MOUSE_DOWN, MOUSE_UP */
{
uint8 Button;
uint16 PosX;
uint16 PosY;
} MouseButton;
struct /** MOUSE_WHEEL */
{
int16 Delta;
uint16 PosX;
uint16 PosY;
} MouseWheel;
} Data;
/**
* Create a completely empty event.
*/
FEvent()
: Event(EventType::UNDEFINED)
{
}
/**
* Create an event of the given type.
* @param InEvent - The type of the event.
*/
FEvent(EventType InEvent)
: Event(InEvent)
{
}
/**
* An event related to a key being pushed down.
* @param InKeyCode - Numerical code identifying the pushed down key.
* @param InIsRepeat - Whether the key is being kept down and is repeating.
*/
void SetKeyDown(uint8 InKeyCode, bool InIsRepeat)
{
check(Event == EventType::KEY_DOWN);
Data.KeyDown.KeyCode = InKeyCode;
Data.KeyDown.bIsRepeat = InIsRepeat;
}
/**
* An event related to a key being released.
* @param InKeyCode - Numerical code identifying the released key.
*/
void SetKeyUp(uint8 InKeyCode)
{
check(Event == EventType::KEY_UP);
Data.KeyUp.KeyCode = InKeyCode;
}
/**
* An event related to character input.
* @param InCharacter - The character being input.
*/
void SetCharCode(TCHAR InCharacter)
{
check(Event == EventType::KEY_PRESS);
Data.Character.Character = InCharacter;
}
/**
* An event related to mouse movement.
* @param InPoxX - The X position of the mouse pointer.
* @param InPosY - The Y position of the mouse pointer.
* @param InDeltaX - The change in the X position of the mouse pointer.
* @param InDeltaY - The change in the Y position of the mouse pointer.
*/
void SetMouseDelta(uint16 InPosX, uint16 InPosY, int16 InDeltaX, int16 InDeltaY)
{
check(Event == EventType::MOUSE_MOVE);
Data.MouseMove.DeltaX = InDeltaX;
Data.MouseMove.DeltaY = InDeltaY;
Data.MouseMove.PosX = InPosX;
Data.MouseMove.PosY = InPosY;
}
/**
* An event related to mouse buttons.
* @param InButton - The button number corresponding to left, middle, right, etc.
* @param InPoxX - The X position of the mouse pointer.
* @param InPosY - The Y position of the mouse pointer.
*/
void SetMouseClick(uint8 InButton, uint16 InPosX, uint16 InPosY)
{
check(Event == EventType::MOUSE_DOWN || Event == EventType::MOUSE_UP);
Data.MouseButton.Button = InButton;
Data.MouseButton.PosX = InPosX;
Data.MouseButton.PosY = InPosY;
}
/**
* An event related to the mouse scroll wheel.
* @param InButton - The amount by which the mouse wheel was scrolled.
* @param InPoxX - The X position of the mouse pointer when the wheel was scrolled.
* @param InPosY - The Y position of the mouse pointer when the wheel was scrolled.
*/
void SetMouseWheel(int16 InDelta, uint16 InPosX, uint16 InPosY)
{
check(Event == EventType::MOUSE_WHEEL);
Data.MouseWheel.Delta = InDelta;
Data.MouseWheel.PosX = InPosX;
Data.MouseWheel.PosY = InPosY;
}
/**
* Get information about an event related to a key being pushed down.
* @param OutKeyCode - Numerical code identifying the pushed down key.
* @param OutIsRepeat - Whether the key is being kept down and is repeating.
*/
void GetKeyDown(uint8& OutKeyCode, bool& OutIsRepeat)
{
check(Event == EventType::KEY_DOWN);
OutKeyCode = Data.KeyDown.KeyCode;
OutIsRepeat = Data.KeyDown.bIsRepeat;
}
/**
* Get information about an event related to a key being released.
* @param OutKeyCode - Numerical code identifying the released key.
*/
void GetKeyUp(uint8& OutKeyCode)
{
check(Event == EventType::KEY_UP);
OutKeyCode = Data.KeyUp.KeyCode;
}
/**
* Get information about an event related to character input.
* @param OutCharacter - The character being input.
*/
void GetCharacterCode(TCHAR& OutCharacter)
{
check(Event == EventType::KEY_PRESS);
OutCharacter = Data.Character.Character;
}
/**
* Get information about an event related to mouse movement.
* @param OutPoxX - The X position of the mouse pointer.
* @param OutPosY - The Y position of the mouse pointer.
* @param OutDeltaX - The change in the X position of the mouse pointer.
* @param OutDeltaY - The change in the Y position of the mouse pointer.
*/
void GetMouseDelta(uint16& OutPosX, uint16& OutPosY, int16& OutDeltaX, int16& OutDeltaY)
{
check(Event == EventType::MOUSE_MOVE);
OutPosX = Data.MouseMove.PosX;
OutPosY = Data.MouseMove.PosY;
OutDeltaX = Data.MouseMove.DeltaX;
OutDeltaY = Data.MouseMove.DeltaY;
}
/**
* Get information about an event related to mouse buttons.
* @param OutButton - The button number corresponding to left, middle, right, etc.
* @param OutPosX - The X position of the mouse pointer.
* @param OutPosY - The Y position of the mouse pointer.
*/
void GetMouseClick(EMouseButtons::Type& OutButton, uint16& OutPosX, uint16& OutPosY)
{
check(Event == EventType::MOUSE_DOWN || Event == EventType::MOUSE_UP);
// https://developer.mozilla.org/en-US/docs/Web/Events/mousedown
uint8 Button = Data.MouseButton.Button;
switch (Button)
{
case 0:
{
OutButton = EMouseButtons::Left;
}
break;
case 1:
{
OutButton = EMouseButtons::Middle;
}
break;
case 2:
{
OutButton = EMouseButtons::Right;
}
break;
default:
{
UE_LOG(LogTemp, Error, TEXT("Unknown Pixel Streaming mouse click with button %d and word 0x%016llx"), Button, Data.Word);
}
break;
}
OutPosX = Data.MouseButton.PosX;
OutPosY = Data.MouseButton.PosY;
}
/**
* Get information about an event related to the mouse wheel.
* @param OutDelta - The amount by which the mouse wheel was scrolled.
* @param PosX - The X position of the mouse pointer when the wheel was scrolled.
* @param PosY - The Y position of the mouse pointer when the wheel was scrolled.
*/
void GetMouseWheel(int16& OutDelta, uint16& OutPosX, uint16& OutPosY)
{
check(Event == EventType::MOUSE_WHEEL);
OutDelta = Data.MouseWheel.Delta;
OutPosX = Data.MouseWheel.PosX;
OutPosY = Data.MouseWheel.PosY;
}
};
/**
* 向输入设备添加一个新事件以供稍后处理。.
* @param InEvent - The new event.
*/
void ProcessEvent(const FEvent& InEvent);
/**
* 向输入设备添加一个新的UI交互描述符,以便稍后处理.
* @param InDescriptor - The new UI interaction descriptor.
*/
void ProcessUIInteraction(const FString& InDescriptor);
private:
/**
* A special wrapper over the GenericApplication layer which allows us to
* override certain behavior.
*/
TSharedPtr<class FTestApplicationWrapper> TestApplicationWrapper;
//鼠标光标的指针
TSharedPtr<class FTestCursor> TestCursor;
/** 系统传递事件的消息处理程序的引用 */
TSharedRef<FGenericApplicationMessageHandler> MessageHandler;
/** 事件的消息队列. */
TQueue<FEvent> Events;
/**
* A queue of UI interaction descriptor strings which contain arbitrary
* information related to the interaction.
*/
TQueue<FString> UIInteractions;
};
InputTestDevice.cpp
在cpp里,我们新建了一个继承自鼠标光标的类TestCursor,以便自定义我们自己的鼠标光标事件
class FTestCursor : public ICursor
{
public:
FTestCursor() {}
virtual ~FTestCursor() = default;
virtual FVector2D GetPosition() const override { return Position; }
virtual void SetPosition(const int32 X, const int32 Y) override { Position = FVector2D(X, Y); };
virtual void SetType(const EMouseCursor::Type InNewCursor) override {};
virtual EMouseCursor::Type GetType() const override { return EMouseCursor::Type::Default; };
virtual void GetSize(int32& Width, int32& Height) const override {};
virtual void Show(bool bShow) override {};
virtual void Lock(const RECT* const Bounds) override {};
virtual void SetTypeShape(EMouseCursor::Type InCursorType, void* CursorHandle) override {};
private:
/** The cursor position sent across with mouse events. */
FVector2D Position;
};
接下来就是InputTestDevice.cpp
FInputTestDevice::FInputTestDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
: TestApplicationWrapper(MakeShareable(new FTestApplicationWrapper(FSlateApplication::Get().GetPlatformApplication())))
, TestCursor(MakeShareable(new FTestCursor()))
, MessageHandler(InMessageHandler)
{
}
void FInputTestDevice::Tick(float DeltaTime)
{
FEvent Event;
while (Events.Dequeue(Event))
{
switch (Event.Event)
{
case EventType::UNDEFINED:
{
checkNoEntry();
}
break;
case EventType::KEY_DOWN:
{
uint8 JavaScriptKeyCode;
bool IsRepeat;
Event.GetKeyDown(JavaScriptKeyCode, IsRepeat);
const FKey* AgnosticKey = AgnosticKeys[JavaScriptKeyCode];
const uint32* KeyCode;
const uint32* CharacterCode;
FInputKeyManager::Get().GetCodesFromKey(*AgnosticKey, KeyCode, CharacterCode);
MessageHandler->OnKeyDown(KeyCode ? *KeyCode : 0, CharacterCode ? *CharacterCode : 0, IsRepeat);
UE_LOG(LogTemp, Verbose, TEXT("KEY_DOWN: KeyCode = %d; CharacterCode = %d; IsRepeat = %s"), KeyCode, CharacterCode, IsRepeat ? TEXT("True") : TEXT("False"));
}
break;
case EventType::KEY_UP:
{
uint8 JavaScriptKeyCode;
Event.GetKeyUp(JavaScriptKeyCode);
const FKey* AgnosticKey = AgnosticKeys[JavaScriptKeyCode];
const uint32* KeyCode;
const uint32* CharacterCode;
FInputKeyManager::Get().GetCodesFromKey(*AgnosticKey, KeyCode, CharacterCode);
MessageHandler->OnKeyUp(KeyCode ? *KeyCode : 0, CharacterCode ? *CharacterCode : 0, false); // Key up events are never repeats.
UE_LOG(LogTemp, Verbose, TEXT("KEY_UP: KeyCode = %d; CharacterCode = %d"), KeyCode, CharacterCode);
}
break;
case EventType::KEY_PRESS:
{
TCHAR UnicodeCharacter;
Event.GetCharacterCode(UnicodeCharacter);
MessageHandler->OnKeyChar(UnicodeCharacter, false); // Key press repeat not yet available but are not intrinsically used.
UE_LOG(LogTemp, Verbose, TEXT("KEY_PRESSED: Character = '%c'"), UnicodeCharacter);
}
break;
case EventType::MOUSE_ENTER:
{
//
//FSlateApplication::Get().OverridePlatformApplication(TestApplicationWrapper);
FSlateApplication::Get().OnCursorSet();
// Make sure the viewport is active.
FSlateApplication::Get().ProcessApplicationActivationEvent(true);
// Double the number of hit test cells to cater for the possibility
// that the window will be off screen.
UGameEngine* GameEngine = Cast<UGameEngine>(GEngine);
TSharedPtr<SWindow> Window = GameEngine->SceneViewport->FindWindow();
Window->GetHittestGrid()->SetNumCellsExcess(Window->GetHittestGrid()->GetNumCells());
UE_LOG(LogTemp, Verbose, TEXT("MOUSE_ENTER"));
}
break;
case EventType::MOUSE_LEAVE:
{
// Restore normal application layer.
//FSlateApplication::Get().OverridePlatformApplication(TestApplicationWrapper->WrappedApplication);
// Reduce the number of hit test cells back to normal.
UGameEngine* GameEngine = Cast<UGameEngine>(GEngine);
TSharedPtr<SWindow> Window = GameEngine->SceneViewport->FindWindow();
Window->GetHittestGrid()->SetNumCellsExcess(FIntPoint(0, 0));
UE_LOG(LogTemp, Verbose, TEXT("MOUSE_LEAVE"));
}
break;
case EventType::MOUSE_MOVE:
{
uint16 PosX;
uint16 PosY;
int16 DeltaX;
int16 DeltaY;
Event.GetMouseDelta(PosX, PosY, DeltaX, DeltaY);
FVector2D CursorPos = GEngine->GameViewport->GetWindow()->GetPositionInScreen() + FVector2D(PosX, PosY);
//TestApplicationWrapper->Cursor->SetPosition(CursorPos.X, CursorPos.Y);
TestCursor->SetPosition(CursorPos.X, CursorPos.Y);
MessageHandler->OnRawMouseMove(DeltaX, DeltaY);
UE_LOG(LogTemp, VeryVerbose, TEXT("MOUSE_MOVE: Pos = (%d, %d); CursorPos = (%d, %d); Delta = (%d, %d)"), PosX, PosY, static_cast<int>(CursorPos.X), static_cast<int>(CursorPos.Y), DeltaX, DeltaY);
}
break;
case EventType::MOUSE_DOWN:
{
// If a user clicks on the application window and then clicks on the
// browser then this will move the focus away from the application
// window which will deactivate the application, so we need to check
// if we must reactivate the application.
if (!FSlateApplication::Get().IsActive())
{
FSlateApplication::Get().ProcessApplicationActivationEvent(true);
}
EMouseButtons::Type Button;
uint16 PosX;
uint16 PosY;
Event.GetMouseClick(Button, PosX, PosY);
FVector2D CursorPos = GEngine->GameViewport->GetWindow()->GetPositionInScreen() + FVector2D(PosX, PosY);
//TestApplicationWrapper->Cursor->SetPosition(CursorPos.X, CursorPos.Y);
TestCursor->SetPosition(CursorPos.X, CursorPos.Y);
MessageHandler->OnMouseDown(GEngine->GameViewport->GetWindow()->GetNativeWindow(), Button, CursorPos);
UE_LOG(LogTemp, Verbose, TEXT("MOUSE_DOWN: Button = %d; Pos = (%d, %d); CursorPos = (%d, %d)"), Button, PosX, PosY, static_cast<int>(CursorPos.X), static_cast<int>(CursorPos.Y));
}
break;
case EventType::MOUSE_UP:
{
EMouseButtons::Type Button;
uint16 PosX;
uint16 PosY;
Event.GetMouseClick(Button, PosX, PosY);
FVector2D CursorPos = GEngine->GameViewport->GetWindow()->GetPositionInScreen() + FVector2D(PosX, PosY);
//TestApplicationWrapper->Cursor->SetPosition(CursorPos.X, CursorPos.Y);
TestCursor->SetPosition(CursorPos.X, CursorPos.Y);
MessageHandler->OnMouseUp(Button);
UE_LOG(LogTemp, Verbose, TEXT("MOUSE_UP: Button = %d; Pos = (%d, %d); CursorPos = (%d, %d)"), Button, PosX, PosY, static_cast<int>(CursorPos.X), static_cast<int>(CursorPos.Y));
}
break;
case EventType::MOUSE_WHEEL:
{
int16 Delta;
uint16 PosX;
uint16 PosY;
Event.GetMouseWheel(Delta, PosX, PosY);
const float SpinFactor = 1 / 120.0f;
FVector2D CursorPos = GEngine->GameViewport->GetWindow()->GetPositionInScreen() + FVector2D(PosX, PosY);
MessageHandler->OnMouseWheel(Delta * SpinFactor, CursorPos);
UE_LOG(LogTemp, Verbose, TEXT("MOUSE_WHEEL: Delta = %d; Pos = (%d, %d); CursorPos = (%d, %d)"), Delta, PosX, PosY, static_cast<int>(CursorPos.X), static_cast<int>(CursorPos.Y));
}
break;
}
}
}
void FInputTestDevice::SendControllerEvents()
{
}
void FInputTestDevice::SetMessageHandler(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
{
MessageHandler = InMessageHandler;
}
bool FInputTestDevice::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
{
return true;
}
void FInputTestDevice::SetChannelValue(int32 ControllerId, FForceFeedbackChannelType ChannelType, float Value)
{
}
void FInputTestDevice::SetChannelValues(int32 ControllerId, const FForceFeedbackValues &values)
{
}
void FInputTestDevice::ProcessEvent(const FEvent& InEvent)
{
bool Success = Events.Enqueue(InEvent);
checkf(Success, TEXT("Unable to enqueue new event of type %d"), static_cast<int>(InEvent.Event));
}
void FInputTestDevice::ProcessUIInteraction(const FString& InDescriptor)
{
bool Success = UIInteractions.Enqueue(InDescriptor);
checkf(Success, TEXT("Unable to enqueue new UI Interaction %s"), *InDescriptor);
}
我们在Tick里从消息队列中执行相应的事件消息,其中对于键盘事件,我们通过一个数组AgnosticKeys来保存所有的键盘Key类型:
static const FKey* AgnosticKeys[256] =
{
/* 0 */ &EKeys::Invalid,
/* 1 */ &EKeys::Invalid,
/* 2 */ &EKeys::Invalid,
/* 3 */ &EKeys::Invalid,
/* 4 */ &EKeys::Invalid,
/* 5 */ &EKeys::Invalid,
/* 6 */ &EKeys::Invalid,
/* 7 */ &EKeys::Invalid,
/* 8 */ &EKeys::BackSpace,
/* 9 */ &EKeys::Tab,
/* 10 */ &EKeys::Invalid,
/* 11 */ &EKeys::Invalid,
/* 12 */ /*&EKeys::Clear*/ &EKeys::Invalid,
/* 13 */ &EKeys::Enter,
/* 14 */ &EKeys::Invalid,
/* 15 */ &EKeys::Invalid,
/* 16 */ &EKeys::LeftShift,
/* 17 */ &EKeys::LeftControl,
/* 18 */ &EKeys::LeftAlt,
/* 19 */ &EKeys::Pause,
/* 20 */ &EKeys::CapsLock,
/* 21 */ &EKeys::Invalid,
/* 22 */ &EKeys::Invalid,
/* 23 */ &EKeys::Invalid,
/* 24 */ &EKeys::Invalid,
/* 25 */ &EKeys::Invalid,
/* 26 */ &EKeys::Invalid,
/* 27 */ &EKeys::Escape,
/* 28 */ &EKeys::Invalid,
/* 29 */ &EKeys::Invalid,
/* 30 */ &EKeys::Invalid,
/* 31 */ &EKeys::Invalid,
/* 32 */ &EKeys::SpaceBar,
/* 33 */ &EKeys::PageUp,
/* 34 */ &EKeys::PageDown,
/* 35 */ &EKeys::End,
/* 36 */ &EKeys::Home,
/* 37 */ &EKeys::Left,
/* 38 */ &EKeys::Up,
/* 39 */ &EKeys::Right,
/* 40 */ &EKeys::Down,
/* 41 */ &EKeys::Invalid,
/* 42 */ &EKeys::Invalid,
/* 43 */ &EKeys::Invalid,
/* 44 */ /*&EKeys::PrintScreen*/ &EKeys::Invalid,
/* 45 */ &EKeys::Insert,
/* 46 */ &EKeys::Delete,
/* 47 */ &EKeys::Invalid,
/* 48 */ &EKeys::Zero,
/* 49 */ &EKeys::One,
/* 50 */ &EKeys::Two,
/* 51 */ &EKeys::Three,
/* 52 */ &EKeys::Four,
/* 53 */ &EKeys::Five,
/* 54 */ &EKeys::Six,
/* 55 */ &EKeys::Seven,
/* 56 */ &EKeys::Eight,
/* 57 */ &EKeys::Nine,
/* 58 */ &EKeys::Invalid,
/* 59 */ &EKeys::Invalid,
/* 60 */ &EKeys::Invalid,
/* 61 */ &EKeys::Invalid,
/* 62 */ &EKeys::Invalid,
/* 63 */ &EKeys::Invalid,
/* 64 */ &EKeys::Invalid,
/* 65 */ &EKeys::A,
/* 66 */ &EKeys::B,
/* 67 */ &EKeys::C,
/* 68 */ &EKeys::D,
/* 69 */ &EKeys::E,
/* 70 */ &EKeys::F,
/* 71 */ &EKeys::G,
/* 72 */ &EKeys::H,
/* 73 */ &EKeys::I,
/* 74 */ &EKeys::J,
/* 75 */ &EKeys::K,
/* 76 */ &EKeys::L,
/* 77 */ &EKeys::M,
/* 78 */ &EKeys::N,
/* 79 */ &EKeys::O,
/* 80 */ &EKeys::P,
/* 81 */ &EKeys::Q,
/* 82 */ &EKeys::R,
/* 83 */ &EKeys::S,
/* 84 */ &EKeys::T,
/* 85 */ &EKeys::U,
/* 86 */ &EKeys::V,
/* 87 */ &EKeys::W,
/* 88 */ &EKeys::X,
/* 89 */ &EKeys::Y,
/* 90 */ &EKeys::Z,
/* 91 */ /*&EKeys::LeftWindowKey*/ &EKeys::Invalid,
/* 92 */ /*&EKeys::RightWindowKey*/ &EKeys::Invalid,
/* 93 */ /*&EKeys::SelectKey*/ &EKeys::Invalid,
/* 94 */ &EKeys::Invalid,
/* 95 */ &EKeys::Invalid,
/* 96 */ &EKeys::NumPadZero,
/* 97 */ &EKeys::NumPadOne,
/* 98 */ &EKeys::NumPadTwo,
/* 99 */ &EKeys::NumPadThree,
/* 100 */ &EKeys::NumPadFour,
/* 101 */ &EKeys::NumPadFive,
/* 102 */ &EKeys::NumPadSix,
/* 103 */ &EKeys::NumPadSeven,
/* 104 */ &EKeys::NumPadEight,
/* 105 */ &EKeys::NumPadNine,
/* 106 */ &EKeys::Multiply,
/* 107 */ &EKeys::Add,
/* 108 */ &EKeys::Invalid,
/* 109 */ &EKeys::Subtract,
/* 110 */ &EKeys::Decimal,
/* 111 */ &EKeys::Divide,
/* 112 */ &EKeys::F1,
/* 113 */ &EKeys::F2,
/* 114 */ &EKeys::F3,
/* 115 */ &EKeys::F4,
/* 116 */ &EKeys::F5,
/* 117 */ &EKeys::F6,
/* 118 */ &EKeys::F7,
/* 119 */ &EKeys::F8,
/* 120 */ &EKeys::F9,
/* 121 */ &EKeys::F10,
/* 122 */ &EKeys::F11,
/* 123 */ &EKeys::F12,
/* 124 */ &EKeys::Invalid,
/* 125 */ &EKeys::Invalid,
/* 126 */ &EKeys::Invalid,
/* 127 */ &EKeys::Invalid,
/* 128 */ &EKeys::Invalid,
/* 129 */ &EKeys::Invalid,
/* 130 */ &EKeys::Invalid,
/* 131 */ &EKeys::Invalid,
/* 132 */ &EKeys::Invalid,
/* 133 */ &EKeys::Invalid,
/* 134 */ &EKeys::Invalid,
/* 135 */ &EKeys::Invalid,
/* 136 */ &EKeys::Invalid,
/* 137 */ &EKeys::Invalid,
/* 138 */ &EKeys::Invalid,
/* 139 */ &EKeys::Invalid,
/* 140 */ &EKeys::Invalid,
/* 141 */ &EKeys::Invalid,
/* 142 */ &EKeys::Invalid,
/* 143 */ &EKeys::Invalid,
/* 144 */ &EKeys::NumLock,
/* 145 */ &EKeys::ScrollLock,
/* 146 */ &EKeys::Invalid,
/* 147 */ &EKeys::Invalid,
/* 148 */ &EKeys::Invalid,
/* 149 */ &EKeys::Invalid,
/* 150 */ &EKeys::Invalid,
/* 151 */ &EKeys::Invalid,
/* 152 */ &EKeys::Invalid,
/* 153 */ &EKeys::Invalid,
/* 154 */ &EKeys::Invalid,
/* 155 */ &EKeys::Invalid,
/* 156 */ &EKeys::Invalid,
/* 157 */ &EKeys::Invalid,
/* 158 */ &EKeys::Invalid,
/* 159 */ &EKeys::Invalid,
/* 160 */ &EKeys::Invalid,
/* 161 */ &EKeys::Invalid,
/* 162 */ &EKeys::Invalid,
/* 163 */ &EKeys::Invalid,
/* 164 */ &EKeys::Invalid,
/* 165 */ &EKeys::Invalid,
/* 166 */ &EKeys::Invalid,
/* 167 */ &EKeys::Invalid,
/* 168 */ &EKeys::Invalid,
/* 169 */ &EKeys::Invalid,
/* 170 */ &EKeys::Invalid,
/* 171 */ &EKeys::Invalid,
/* 172 */ &EKeys::Invalid,
/* 173 */ &EKeys::Invalid,
/* 174 */ &EKeys::Invalid,
/* 175 */ &EKeys::Invalid,
/* 176 */ &EKeys::Invalid,
/* 177 */ &EKeys::Invalid,
/* 178 */ &EKeys::Invalid,
/* 179 */ &EKeys::Invalid,
/* 180 */ &EKeys::Invalid,
/* 181 */ &EKeys::Invalid,
/* 182 */ &EKeys::Invalid,
/* 183 */ &EKeys::Invalid,
/* 184 */ &EKeys::Invalid,
/* 185 */ &EKeys::Invalid,
/* 186 */ &EKeys::Semicolon,
/* 187 */ &EKeys::Equals,
/* 188 */ &EKeys::Comma,
/* 189 */ &EKeys::Hyphen,
/* 190 */ &EKeys::Period,
/* 191 */ &EKeys::Slash,
/* 192 */ &EKeys::Tilde,
/* 193 */ &EKeys::Invalid,
/* 194 */ &EKeys::Invalid,
/* 195 */ &EKeys::Invalid,
/* 196 */ &EKeys::Invalid,
/* 197 */ &EKeys::Invalid,
/* 198 */ &EKeys::Invalid,
/* 199 */ &EKeys::Invalid,
/* 200 */ &EKeys::Invalid,
/* 201 */ &EKeys::Invalid,
/* 202 */ &EKeys::Invalid,
/* 203 */ &EKeys::Invalid,
/* 204 */ &EKeys::Invalid,
/* 205 */ &EKeys::Invalid,
/* 206 */ &EKeys::Invalid,
/* 207 */ &EKeys::Invalid,
/* 208 */ &EKeys::Invalid,
/* 209 */ &EKeys::Invalid,
/* 210 */ &EKeys::Invalid,
/* 211 */ &EKeys::Invalid,
/* 212 */ &EKeys::Invalid,
/* 213 */ &EKeys::Invalid,
/* 214 */ &EKeys::Invalid,
/* 215 */ &EKeys::Invalid,
/* 216 */ &EKeys::Invalid,
/* 217 */ &EKeys::Invalid,
/* 218 */ &EKeys::Invalid,
/* 219 */ &EKeys::LeftBracket,
/* 220 */ &EKeys::Backslash,
/* 221 */ &EKeys::RightBracket,
/* 222 */ &EKeys::Apostrophe,
/* 223 */ &EKeys::Quote,
/* 224 */ &EKeys::Invalid,
/* 225 */ &EKeys::Invalid,
/* 226 */ &EKeys::Invalid,
/* 227 */ &EKeys::Invalid,
/* 228 */ &EKeys::Invalid,
/* 229 */ &EKeys::Invalid,
/* 230 */ &EKeys::Invalid,
/* 231 */ &EKeys::Invalid,
/* 232 */ &EKeys::Invalid,
/* 233 */ &EKeys::Invalid,
/* 234 */ &EKeys::Invalid,
/* 235 */ &EKeys::Invalid,
/* 236 */ &EKeys::Invalid,
/* 237 */ &EKeys::Invalid,
/* 238 */ &EKeys::Invalid,
/* 239 */ &EKeys::Invalid,
/* 240 */ &EKeys::Invalid,
/* 241 */ &EKeys::Invalid,
/* 242 */ &EKeys::Invalid,
/* 243 */ &EKeys::Invalid,
/* 244 */ &EKeys::Invalid,
/* 245 */ &EKeys::Invalid,
/* 246 */ &EKeys::Invalid,
/* 247 */ &EKeys::Invalid,
/* 248 */ &EKeys::Invalid,
/* 249 */ &EKeys::Invalid,
/* 250 */ &EKeys::Invalid,
/* 251 */ &EKeys::Invalid,
/* 252 */ &EKeys::Invalid,
/* 253 */ &EKeys::Invalid,
/* 254 */ &EKeys::Invalid,
/* 255 */ &EKeys::Invalid
};
通过外面传入的数组下标来获取相应的Key,然后通过Key找到对应键盘的虚拟码,最后由系统传递事件的消息处理程序的引用MessageHandler来触发系统按键消息响应,这样我们就能通过外接传入某一个值就能直接模拟按键的行为了。下面是Key_Up的代码:
case EventType::KEY_UP:
{
uint8 JavaScriptKeyCode;
Event.GetKeyUp(JavaScriptKeyCode);
const FKey* AgnosticKey = AgnosticKeys[JavaScriptKeyCode];
const uint32* KeyCode;
const uint32* CharacterCode;
FInputKeyManager::Get().GetCodesFromKey(*AgnosticKey, KeyCode, CharacterCode);
MessageHandler->OnKeyUp(KeyCode ? *KeyCode : 0, CharacterCode ? *CharacterCode : 0, false); // Key up events are never repeats.
UE_LOG(LogTemp, Verbose, TEXT("KEY_UP: KeyCode = %d; CharacterCode = %d"), KeyCode, CharacterCode);
}
接着我们在该工程模块中来创建并获取这个InputTestDevice,首先让工程模块继承自IInputDeviceModule,这个类里有个 CreateInputDevice()函数用来创建输入驱动。代码比较简单,这里就直接贴上代码:
工程模块.h
class IInputTestDeviceModule : public IInputDeviceModule
{
public:
/** 父类接口 */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
virtual TSharedPtr<class IInputDevice> CreateInputDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler) override;
/**
* 返回输入驱动的智能指针
* @return The shared pointer to the input device.
*/
TSharedPtr<FInputTestDevice> GetInputDevicePtr();
private:
TSharedPtr<FInputTestDevice> InputTestDevice;
};
工程模块.cpp
IMPLEMENT_PRIMARY_GAME_MODULE(IInputTestDeviceModule, ProjectTest_Re421, "ProjectTest_Re421" );
void IInputTestDeviceModule::StartupModule()
{
IModularFeatures::Get().RegisterModularFeature(GetModularFeatureName(), this);
}
void IInputTestDeviceModule::ShutdownModule()
{
IModularFeatures::Get().UnregisterModularFeature(GetModularFeatureName(), this);
}
TSharedPtr<class IInputDevice> IInputTestDeviceModule::CreateInputDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
{
InputTestDevice = MakeShareable(new FInputTestDevice(InMessageHandler));
return InputTestDevice;
}
TSharedPtr<FInputTestDevice> IInputTestDeviceModule::GetInputDevicePtr()
{
if (InputTestDevice.IsValid())
{
return InputTestDevice;
}
return nullptr;
}
最后我们新建一个继承自Actor的类TestActor用来测试上面的功能,代码如下:
TestActor.h
class FInputTestDevice;
UCLASS()
class PROJECTTEST_RE421_API ATestActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ATestActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
public:
UFUNCTION(BlueprintCallable,Category = Test)
void Key_Down();
UFUNCTION(BlueprintCallable, Category = Test)
void Key_Up();
UFUNCTION(BlueprintCallable, Category = Test)
void Key_Press();
public:
UPROPERTY(EditAnywhere,BlueprintReadWrite,Category = Test)
int32 TestData;
private:
TSharedPtr<FInputTestDevice> InputTestDevice;
};
在该头文件里,我定义了3个有蓝图调用的函数分别模拟按键的按下,松开,字符输入,然后定义了一个变量模拟存在AgnosticKeys数组中Key的下标值,通过改变这个变量我们可以模拟键盘上的按键的按下,松开以及输入字符
TestActor.cpp
ATestActor::ATestActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
TestData = 0;
}
// Called when the game starts or when spawned
void ATestActor::BeginPlay()
{
Super::BeginPlay();
IInputTestDeviceModule InputTestModule =FModuleManager::Get().GetModuleChecked<IInputTestDeviceModule>("ProjectTest_Re421");
if (!InputTestModule.GetInputDevicePtr())
{
InputTestModule.CreateInputDevice(MakeShareable(new FGenericApplicationMessageHandler()));
}
InputTestDevice = InputTestModule.GetInputDevicePtr();
}
// Called every frame
void ATestActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ATestActor::Key_Down()
{
FInputTestDevice::FEvent KeyEvent(FInputTestDevice::EventType::KEY_DOWN);
KeyEvent.SetKeyDown(TestData, false);
InputTestDevice->ProcessEvent(KeyEvent);
}
void ATestActor::Key_Up()
{
FInputTestDevice::FEvent KeyEvent(FInputTestDevice::EventType::KEY_UP);
KeyEvent.SetKeyUp(TestData);
InputTestDevice->ProcessEvent(KeyEvent);
}
void ATestActor::Key_Press()
{
FInputTestDevice::FEvent KeyEvent(FInputTestDevice::EventType::KEY_PRESS);
KeyEvent.SetCharCode('s');
InputTestDevice->ProcessEvent(KeyEvent);
}
我们在beginplay里获取到工程模块,并且通过工程模块获取到输入驱动类InputTestDevice,然后再定义有蓝图调用的函数中实现InputTestDevice驱动按键的事件,这里的输入字符我是固定传入按键“s”
最后编译打开UE4编辑器,创建一个继承自TestActor蓝图类放入场景里,创建Widget主界面蓝图,放置三个按钮,三个按钮分别调用TestActor里的Key_up,Key_down,Key_Press
另外,在TestActor蓝图里我把TestData设置成83,
在数组AgnosticKeys里下标83代表的是s键,意思是当点击按钮Key_down时,相当于按下键盘上的s键,在默认地图里s键是视角后移,此时,场景视角会一直后移,当点击按钮Key_up时,相当于松开s键,此时,场景视角停止后移,当点击Key_press时,相当于我们输出字符s,这里要注意的是,输入字符首先要光标聚焦在输入模式下,才能输入字符,如图,当我开启命令行,并且光标聚焦在输入模式下,点击Key_press,命令行里会输出符号s
最后还有个问题,就说这个InputTestDevice可能大家没有发现是在哪创建的(MessageHandler从哪传进来的),这个也困扰我很久了,最终发现InputDevice创建的地方:
if (!bHasLoadedInputPlugins)
{
TArray<IInputDeviceModule*> PluginImplementations = IModularFeatures::Get().GetModularFeatureImplementations<IInputDeviceModule>( IInputDeviceModule::GetModularFeatureName() );
for( auto InputPluginIt = PluginImplementations.CreateIterator(); InputPluginIt; ++InputPluginIt )
{
//创建Inputdevice
TSharedPtr<IInputDevice> Device = (*InputPluginIt)->CreateInputDevice(MessageHandler);
AddExternalInputDevice(Device);
}
bHasLoadedInputPlugins = true;
}
这段代码是在WindowsApplication.cpp的void FWindowsApplication::PollGameDeviceState( const float TimeDelta )函数里执行的,这个函数的作用和调用时机暂时还有弄明白(萌新的UE4源码爬坑之旅真的很艰辛),希望有了解的可以告知下或者一起讨论研究研究!
还有,最后大家可能依旧还是不明白在什么地方会用到这些,UE4的像素流网页端的输入同步到本地服务的的输入就是用的这种方法!