/** * Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. */ class UDKMobileInputZone extends MobileInputZone; /** Range around player that is considered a player touch */ var const float HoldPlayerDistance; /** touches that are still being pressed down */ struct ActivePress { var int Handle; var vector2d TouchLocation; var float TouchTime; }; var array CurrentPresses; /** The location that was traced to when deprojecting the screen position to the world */ var transient Vector TraceHitLocation; /** last thing we tapped (for detecting double tap) */ var transient Actor LastTappedActor; /** The time in seconds of the last time we performed an untouch event */ var transient float TimeOfLastUntouch; /** Amount of time that a touch has been held on the player pawn */ var transient float PlayerTouchStartTime; /** Returns true if we are touching the player pawn */ function bool IsTouchingPlayerPawn() { // HACK to check if we've touched the player pawn return (InputOwner.Outer.Pawn != None && VSize2D(TraceHitLocation - InputOwner.Outer.Pawn.Location) < HoldPlayerDistance); } /** Returns the actor that was traced to from projecting to the world */ function TraceFromScreenToWorld(Vector2D ScreenPos, out Actor outHitActor, out vector OutHitLocation, optional vector Extent = vect(32,32,32)) { local Actor HitActor, TestHitActor; local vector CameraLoc, CameraDir, HitLocation, TestHitLocation, HitNormal; LocalPlayer(InputOwner.Outer.Player).DeProject(ScreenPos, CameraLoc, CameraDir); // see what is underneath the tap location in the world foreach InputOwner.Outer.TraceActors(class'Actor', TestHitActor, TestHitLocation, HitNormal, CameraLoc + CameraDir * 8000.0, CameraLoc, Extent) { if (((!TestHitActor.bWorldGeometry && BlockingVolume(TestHitActor) == None) || HitActor == None) && (TestHitActor.bBlockActors || TestHitActor.bProjTarget || TouchableElement3D(TestHitActor) != None) && (Trigger(TestHitActor) == None) && (Pawn(TestHitActor) == None || Pawn(TestHitActor).Health > 0)) { HitActor = TestHitActor; HitLocation = TestHitLocation; if (!HitActor.bWorldGeometry && BlockingVolume(TestHitActor) == None) { break; } } } outHitActor = HitActor; OutHitLocation = HitLocation; } /** Handler for touch input */ function bool ProcessGameplayInput(MobileInputZone Zone, float DeltaTime, int Handle, ETouchType EventType, Vector2D TouchLocation) { local Vector2D RelLocation, ViewportSize; local Actor TraceHitActor; local int PressIndex; local bool bRedundantInput; LocalPlayer(InputOwner.Outer.Player).ViewportClient.GetViewportSize(ViewportSize); // Get the screen space in terms of 0 to 1 RelLocation.X = TouchLocation.X / ViewportSize.X; RelLocation.Y = TouchLocation.Y / ViewportSize.Y; // Project and trace to see what we hit TraceFromScreenToWorld(RelLocation, TraceHitActor, TraceHitLocation); // If this is a touch we don't want to do anything except do some bookkeeping // for delayed movement or double tapping if (EventType == Touch_Began) { PressIndex = CurrentPresses.Add(1); CurrentPresses[PressIndex].Handle = Handle; CurrentPresses[PressIndex].TouchLocation = TouchLocation; CurrentPresses[PressIndex].TouchTime = InputOwner.Outer.WorldInfo.TimeSeconds; if (CurrentPresses.length > 1) { PlayerTouchStartTime = -1.f; } else { // Check if we are touching the player pawn if (IsTouchingPlayerPawn()) { PlayerTouchStartTime = InputOwner.Outer.WorldInfo.TimeSeconds; } else { PlayerTouchStartTime = -1.f; } } } else if (EventType == Touch_Ended || EventType == Touch_Cancelled) { PressIndex = CurrentPresses.Find('Handle', Handle); `Log(LastTappedActor @ TraceHitActor @ TimeOfLastUntouch @ InputOwner.MobileDoubleTapTime @ PressIndex); if (PressIndex != INDEX_NONE) { CurrentPresses.Remove(PressIndex, 1); // See if we tapped on something we can interact with if ( TraceHitActor != None && (TraceHitActor.bBlockActors || TraceHitActor.bProjTarget || TouchableElement3D(TraceHitActor) != None) && Trigger(TraceHitActor) == None && (Pawn(TraceHitActor) == None || Pawn(TraceHitActor).Health > 0) ) { // send double tap on world geometry for area skills but not single tap if ( LastTappedActor == TraceHitActor && InputOwner.Outer.WorldInfo.TimeSeconds < TimeOfLastUntouch + InputOwner.MobileDoubleTapTime ) { if (TouchableElement3D(TraceHitActor) != None) { TouchableElement3D(TraceHitActor).HandleDoubleClick(); } } else if (!TraceHitActor.bWorldGeometry && BlockingVolume(TraceHitActor) == None) { if (PlayerTouchStartTime < 0.f) { if (TouchableElement3D(TraceHitActor) != None) { TouchableElement3D(TraceHitActor).HandleClick(); } } } } PlayerTouchStartTime = -1.f; } TimeOfLastUntouch = InputOwner.Outer.WorldInfo.TimeSeconds; } else if (EventType == Touch_Moved || EventType == Touch_Stationary) { PressIndex = CurrentPresses.Find('Handle', Handle); if (PressIndex != INDEX_NONE) { // if this is a duplicate input, ignore it if (CurrentPresses[PressIndex].TouchLocation == TouchLocation && CurrentPresses[PressIndex].TouchTime == InputOwner.Outer.WorldInfo.TimeSeconds) { bRedundantInput = true; } else { CurrentPresses[PressIndex].TouchLocation = TouchLocation; CurrentPresses[PressIndex].TouchTime = InputOwner.Outer.WorldInfo.TimeSeconds; } } if (!bRedundantInput) { // Next see if we tapped on something we can interact with if ( EventType == Touch_Moved && TouchableElement3D(TraceHitActor) != None ) { TouchableElement3D(TraceHitActor).HandleDragOver(); } } } LastTappedActor = TraceHitActor; // If we aren't touching the player pawn, clear PlayerTouchStartTime if ( !IsTouchingPlayerPawn() ) { PlayerTouchStartTime = -1.f; } return false; } defaultproperties { OnProcessInputDelegate=ProcessGameplayInput HoldPlayerDistance=150.f }