zoukankan      html  css  js  c++  java
  • 触屏设备上的多点触碰检测C++代码实现

    转自:http://aigo.iteye.com/blog/2272698

    代码还是参考自Epic官方的塔防项目:StrategyGame

    看了下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);  
        }  
    }  

    如果要实现多点触碰检测,比如实现两个手指捏动来缩放屏幕,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_1P_ACTION和BIND_2P_ACTION是自己定义的宏:

     
    void AStrategyPlayerController::SetupInputComponent()  
    {  
        Super::SetupInputComponent();  
      
        InputHandler = NewObject<UStrategyInput>(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<ULocalPlayer>(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<AStrategyHUD>(GetHUD());  
                AStrategyGameState const* const MyGameState = GetWorld()->GetGameState<AStrategyGameState>();  
                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<AStrategyPlayerController>(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<EGameKey::Type,FSimpleKeyState>::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));  
        }  
    }  
  • 相关阅读:
    关于postman与shiro权限验证问题
    springboot对shiro进行mock单元测试
    深入理解spring注解之@ComponentScan注解
    springboot项目启动,但是访问报404错误
    通过jedis连接redis单机成功,使用redis客户端可以连接集群,但使用JedisCluster连接redis集群一直报Could not get a resource from the pool
    重装系统后ORACLE数据库恢复的方法
    ORA-03113: end-of-file on communication channel 解决方法
    ORA-03113:通信通道的文件结尾-完美解决方案
    由于Windows和Linux行尾标识引起脚本无法运行的解决
    在cmd命令行中弹出Windows对话框(使用mshta.exe命令)
  • 原文地址:https://www.cnblogs.com/sevenyuan/p/7725791.html
Copyright © 2011-2022 走看看