[UE 4]タッチパネル装置におけるマルチタッチ検出C++コード実装
7235 ワード
コードはEpic公式タワー防衛プロジェクト:StrategyGameから参照してください.
C++のAPIを見ると、既存のAPIでは単点タッチ検出のみがサポートされているようですが、使い方は以下の通りです.
コールバックをトリガー:
複数のタッチ検出を実現するには、例えば2つの指をつまんでスクリーンをスケーリングすることを実現し、StrategyGameプロジェクトは独自にアルゴリズムを実現して複数のスクリーン検出を実現し、具体的な方法は以下の通りである:StrategyPlayerController.h
1,StrategyPlayerControllerのProcessPlayerInput()とSetupInputComponent()関数を書き換える
SetupInputComponent()関数でイベントをバインドします(このイベントメカニズムも自分で書いたものです).BIND_1 P_ACTIONとBIND_2 P_ACTIONは自分で定義したマクロです.
ProcessPlayerInput()はタッチポイントの変化を検出し、これは連続的に実行される関数であり、実行するたびに現在のスクリーンタッチ情報と前回のタッチ情報を比較し、単点タッチか、押して放さないか、多点タッチか多点押して放さないかなどを検出し、具体的な論理はInputHandler->UpdateDetection(DeltaTime)である.に表示されます.
StrategyInput.cpp
C++のAPIを見ると、既存のAPIでは単点タッチ検出のみがサポートされているようですが、使い方は以下の通りです.
// support touch devices
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &ATD_MobilePlayerController::MoveToTouchLocation);
InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &ATD_MobilePlayerController::MoveToTouchLocation);
コールバックをトリガー:
void ATD_MobilePlayerController::MoveToTouchLocation(const ETouchIndex::Type FingerIndex, const FVector Location)
{
FVector2D ScreenSpaceLocation(Location);
// Trace to see what is under the touch location
FHitResult HitResult;
GetHitResultAtScreenPosition(ScreenSpaceLocation, CurrentClickTraceChannel, true, HitResult);
if (HitResult.bBlockingHit)
{
// We hit something, move there
SetNewMoveDestination(HitResult.ImpactPoint);
}
}
複数のタッチ検出を実現するには、例えば2つの指をつまんでスクリーンをスケーリングすることを実現し、StrategyGameプロジェクトは独自にアルゴリズムを実現して複数のスクリーン検出を実現し、具体的な方法は以下の通りである:StrategyPlayerController.h
1,StrategyPlayerControllerのProcessPlayerInput()とSetupInputComponent()関数を書き換える
protected:
/** update input detection */
virtual void ProcessPlayerInput(const float DeltaTime, const bool bGamePaused) override;
virtual void SetupInputComponent() override;
SetupInputComponent()関数でイベントをバインドします(このイベントメカニズムも自分で書いたものです).BIND_1 P_ACTIONとBIND_2 P_ACTIONは自分で定義したマクロです.
void AStrategyPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
InputHandler = NewObject(this);
BIND_1P_ACTION(InputHandler, EGameKey::Tap, IE_Pressed, &AStrategyPlayerController::OnTapPressed);
BIND_1P_ACTION(InputHandler, EGameKey::Hold, IE_Pressed, &AStrategyPlayerController::OnHoldPressed);
BIND_1P_ACTION(InputHandler, EGameKey::Hold, IE_Released, &AStrategyPlayerController::OnHoldReleased);
BIND_1P_ACTION(InputHandler, EGameKey::Swipe, IE_Pressed, &AStrategyPlayerController::OnSwipeStarted);
BIND_1P_ACTION(InputHandler, EGameKey::Swipe, IE_Repeat, &AStrategyPlayerController::OnSwipeUpdate);
BIND_1P_ACTION(InputHandler, EGameKey::Swipe, IE_Released, &AStrategyPlayerController::OnSwipeReleased);
BIND_2P_ACTION(InputHandler, EGameKey::SwipeTwoPoints, IE_Pressed, &AStrategyPlayerController::OnSwipeTwoPointsStarted);
BIND_2P_ACTION(InputHandler, EGameKey::SwipeTwoPoints, IE_Repeat, &AStrategyPlayerController::OnSwipeTwoPointsUpdate);
BIND_2P_ACTION(InputHandler, EGameKey::Pinch, IE_Pressed, &AStrategyPlayerController::OnPinchStarted);
BIND_2P_ACTION(InputHandler, EGameKey::Pinch, IE_Repeat, &AStrategyPlayerController::OnPinchUpdate);
FInputActionBinding& ToggleInGameMenuBinding = InputComponent->BindAction("InGameMenu", IE_Pressed, this, &AStrategyPlayerController::OnToggleInGameMenu);
ToggleInGameMenuBinding.bExecuteWhenPaused = true;
}
ProcessPlayerInput()はタッチポイントの変化を検出し、これは連続的に実行される関数であり、実行するたびに現在のスクリーンタッチ情報と前回のタッチ情報を比較し、単点タッチか、押して放さないか、多点タッチか多点押して放さないかなどを検出し、具体的な論理はInputHandler->UpdateDetection(DeltaTime)である.に表示されます.
void AStrategyPlayerController::ProcessPlayerInput(const float DeltaTime, const bool bGamePaused)
{
if (!bGamePaused && PlayerInput && InputHandler && !bIgnoreInput)
{
InputHandler->UpdateDetection(DeltaTime);
}
Super::ProcessPlayerInput(DeltaTime, bGamePaused);
if (!bIgnoreInput )
{
const ULocalPlayer* LocalPlayer = Cast(Player);
AStrategySpectatorPawn* StrategyPawn = GetStrategySpectatorPawn();
if(( StrategyPawn != NULL ) && ( LocalPlayer != NULL ))
{
// Create the bounds for the minimap so we can add it as a 'no scroll' zone.
AStrategyHUD* const HUD = Cast(GetHUD());
AStrategyGameState const* const MyGameState = GetWorld()->GetGameState();
if( (MyGameState != NULL ) && ( MyGameState->MiniMapCamera.IsValid() == true ) )
{
if( LocalPlayer->ViewportClient != NULL )
{
const FIntPoint ViewportSize = LocalPlayer->ViewportClient->Viewport->GetSizeXY();
const uint32 ViewTop = FMath::TruncToInt(LocalPlayer->Origin.Y * ViewportSize.Y);
const uint32 ViewBottom = ViewTop + FMath::TruncToInt(LocalPlayer->Size.Y * ViewportSize.Y);
FVector TopLeft( HUD->MiniMapMargin, ViewBottom - HUD->MiniMapMargin - MyGameState->MiniMapCamera->MiniMapHeight, 0 );
FVector BottomRight( (int32)MyGameState->MiniMapCamera->MiniMapWidth, MyGameState->MiniMapCamera->MiniMapHeight, 0 );
FBox MiniMapBounds( TopLeft, TopLeft + BottomRight );
StrategyPawn->GetStrategyCameraComponent()->AddNoScrollZone( MiniMapBounds );
StrategyPawn->GetStrategyCameraComponent()->UpdateCameraMovement( this );
}
}
}
}
}
StrategyInput.cpp
void UStrategyInput::UpdateDetection(float DeltaTime)
{
UpdateGameKeys(DeltaTime);
ProcessKeyStates(DeltaTime);
}
void UStrategyInput::UpdateGameKeys(float DeltaTime)
{
AStrategyPlayerController* MyController = CastChecked(GetOuter());
// gather current states
uint32 CurrentTouchState = 0;
for (int32 i = 0; i < ARRAY_COUNT(MyController->PlayerInput->Touches); i++)
{
if (MyController->PlayerInput->Touches[i].Z != 0)
{
CurrentTouchState |= (1 << i);
}
}
// detection
FVector2D LocalPosition1 = FVector2D(MyController->PlayerInput->Touches[0]);
FVector2D LocalPosition2 = FVector2D(MyController->PlayerInput->Touches[1]);
DetectOnePointActions(CurrentTouchState & 1, PrevTouchState & 1, DeltaTime, LocalPosition1, TouchAnchors[0], Touch0DownTime);
DetectTwoPointsActions((CurrentTouchState & 1) && (CurrentTouchState & 2), (PrevTouchState & 1) && (PrevTouchState & 2),
DeltaTime, LocalPosition1, LocalPosition2);
// save states
PrevTouchState = CurrentTouchState;
}
void UStrategyInput::ProcessKeyStates(float DeltaTime)
{
for (const FActionBinding1P& AB : ActionBindings1P)
{
const FSimpleKeyState* KeyState = KeyStateMap.Find(AB.Key);
if (KeyState && KeyState->Events[AB.KeyEvent] > 0)
{
AB.ActionDelegate.ExecuteIfBound(KeyState->Position, KeyState->DownTime);
}
}
for (const FActionBinding2P& AB : ActionBindings2P)
{
const FSimpleKeyState* KeyState = KeyStateMap.Find(AB.Key);
if (KeyState && KeyState->Events[AB.KeyEvent] > 0)
{
AB.ActionDelegate.ExecuteIfBound(KeyState->Position, KeyState->Position2, KeyState->DownTime);
}
}
// update states
for (TMap<:type>::TIterator It(KeyStateMap); It; ++It)
{
FSimpleKeyState* const KeyState = &It.Value();
if (KeyState->Events[IE_Pressed])
{
KeyState->bDown = true;
}
else if (KeyState->Events[IE_Released])
{
KeyState->bDown = false;
}
FMemory::Memzero(KeyState->Events, sizeof(KeyState->Events));
}
}