PDA

View Full Version : How to add mobile Object Picker event as script?



Dr.Destiny
12-23-2010, 10:49 PM
I'd like like to give an (Actor extended) class the same functionality of the kismet Mobile_Object_Picker event, but in unrealscript so I can quickly and easily link it to the game's loop code

Something like:


var gameObject extends Actor

event onObjectTapped()
{
//code
}


Also, is it bad to have alot of objects simultaneously have object picker handlers on them?
Thank you!

UPDATE:
I got traditional 'touch' working (running into the object) like this




var gameObject extends Actor

simulated singular event Touch( Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal )
{
//code here
}




I just need to get the same effect using a 'screen tap/object picker' event. Any Ideas?

ffejnosliw
12-24-2010, 04:23 AM
I'm putting an example of doing this up on the UDN with a full explanation soon, but I can post the code for it here. I set it up using an interface as it seemed the best solution. Any class you create that implements the interface will be able to be touched (assuming it has the proper collision of course). The picking is done in the PlayerController using DeProject() and Trace() with input from the OnInputTouch() delegate of the MobilePlayerInput class.

ITouchable Interface


Interface ITouchable;

function OnTouch();


PlayerController


class UDNMobilePC extends GamePlayerController;

/** Holds the dimensions of the device's screen */
var vector2D ViewportSize;

/** If TRUE, a new touch was detected (must be the only touch active) */
var bool bPendingTouch;

/** Holds the handle of the most recent touch */
var int PendingTouchHandle;

/** Holds the Actor that was selected */
var Actor SelectedActor;

/** Maximum distance an Actor can be to be picked */
var float PickDistance;

/** Maximum amount the mouse can move between touch and untouch to be considered a 'click' */
var float ClickTolerance;

/** Cache a reference to the MobilePlayerInput */
var MobilePlayerInput MPI;


event InitInputSystem()
{
Super.InitInputSystem();

//Get a reference to the local MobilePlayerInput
MPI = MobilePlayerInput(PlayerInput);

//Assing the input handler function to the delegate
MPI.OnInputTouch = HandleInputTouch;

//get the screen dimensions (used to transformto relative screen coords for the DeProject)
LocalPlayer(Player).ViewportClient.GetViewportSize (ViewportSize);
}

function HandleInputTouch(int Handle, EZoneTouchEvent Type, Vector2D TouchLocation, float DeviceTimestamp)
{
local Actor PickedActor;
local int i;

//New touch event
if(Type == ZoneEvent_Touch)
{
//Specify a new touch has occurred
PendingTouchHandle = Handle;
bPendingTouch = true;
}
//Touch in progress
else if(Type == ZoneEvent_Update)
{
for(i=0; i<MPI.NumTouchDataEntries; i++)
{
//Test distance touch has moved and cancel touch if moved too far; update touch location if not
if(MPI.Touches[i].Handle == PendingTouchHandle && MPI.Touches[i].TotalMoveDistance > ClickTolerance)
{
bPendingTouch = false;
}
}
}
//End of touch
else if(Type == ZoneEvent_Untouch)
{
//Check if a touch is active
if(Handle == PendingTouchHandle && bPendingTouch)
{
//Get actor under touch
PickedActor = PickActor(TouchLocation);

//Check if actor is touchable and set it as selected; clear current selected if not
if(ITouchable(PickedActor) != none)
{
SelectedActor = PickedActor;
}
else
{
SelectedActor = none;
}

//cancel active touch
bPendingTouch = false;
}

WorldInfo.Game.Broadcast(self, "SelectedActor:"@SelectedActor);
}
}

/** find actor under touch location
*
* @PickLocation - Screen coordinates of touch
*/
function Actor PickActor(Vector2D PickLocation)
{
local Vector TouchOrigin, TouchDir;
local Vector HitLocation, HitNormal;
local Actor PickedActor;

//Transform absolute screen coordinates to relative coordinates
PickLocation.X = PickLocation.X / ViewportSize.X;
PickLocation.Y = PickLocation.Y / ViewportSize.Y;

//Transform to world coordinates to get pick ray
LocalPlayer(Player).Deproject(PickLocation, TouchOrigin, TouchDir);

//Perform trace to find touched actor
PickedActor = Trace(HitLocation, HitNormal, TouchOrigin + (TouchDir * PickDistance), TouchOrigin, true);

//Casting to ITouchable determines if the touched actor can indeed be touched
if(Itouchable(PickedActor) != none)
{
//Call the OnTouch() function on the touched actor
Itouchable(PickedActor).OnTouch(ZoneEvent_Touch, PickLocation.X, PickLocation.Y);
}

//Return the touched actor for good measure
return PickedActor;
}

defaultproperties
{
PickDistance=10000
ClickTolerance=5

InputClass=class'GameFramework.MobilePlayerInput'
}


Example Touchable Actor


class UDNMobilePawn extends MobilePlaceablePawn implements(ITouchable);


function OnTouch()
{
WorldInfo.Game.Broadcast(self, "Touched:"@self);
}


This hasn't been tested as thoroughly as I would like, but it seems to be working properly. Feel free to post back with any questions or problems you run into if you decide to try it out.

Dr.Destiny
12-24-2010, 03:28 PM
Hmm, I've implemented this code and I just get 'selected Actor:none' messages when clicking on the actors. (These actors are clickable with the Kismet object picker.)

My interface implementation:
I dropped the 2 line interface code above in the classes folder as ITouchable.uc and implemented it in my actor class as seen above. Is that correct? (I get no compiler errors.) I've added the implements(ITouchable) and OnTouch functions to my actor class and I set my Player Controller to the one above.

Thank you for sharing this code.

ffejnosliw
12-24-2010, 03:50 PM
What kind of collision setup does your class have? If you use the exact example actor class I posted, does it work? That would at least narrow it down to differences in the two classes.

You can try changing the trace line to:

PickedActor = Trace(HitLocation, HitNormal, TouchOrigin + (TouchDir * PickDistance), TouchOrigin, true, Vect(0,0,0));
The picking code is essentially identical to the Object Picker code from what I can tell.

Dr.Destiny
12-24-2010, 04:22 PM
OK, it works!
I just changed the collision on my class to COLLIDE_BlockAll (it was COLLIDE_TouchAll which works on the kismet picker version.) I didn't have to use the new line of code you gave.

Thank you again!

ffejnosliw
12-24-2010, 04:30 PM
That's odd, because changing the example actor I posted to COLLIDE_TouchAll in the editor and then using the original picking code worked for me.

Dr.Destiny
12-26-2010, 09:06 PM
Here is the WIP simple actor class I use that requires "collide_blockAll" for the code above to work. ("collide_touchAll" doesn't seem to work.)

If in testing your code further you happend to figure out the cause please share as I'd like to use the touchAll collision -- the blockAll collision messes up the actors position when I 'Base' it to another actor. :)




class J_Socket extends Actor implements(ITouchable)
placeable;

var(Attachment) StaticMeshComponent DisplayMesh;
var DynamicLightEnvironmentComponent LightEnvironment;
var() float Speed;
var Rotator rotMove;


var(Attachment) string socketTag;
var bool socketFilled;

var vector locationMoveTest;

var J_Peice attachedPeice;

function PostBeginPlay()
{
local Float RandVal;

Super.PostBeginPlay();

RandVal = RandRange(0.5,1.5);
Speed *= RandVal;
locationMoveTest.Z =0.0;

}


defaultproperties
{
Begin Object Class=DynamicLightEnvironmentComponent Name=MyLightEnvironment
End Object

Components(0)=MyLightEnvironment

Begin Object Class=StaticMeshComponent Name=MyStaticMesh
StaticMesh=StaticMesh'E3_Demo.Meshes.SM_Barrel_01'
Scale3D=(X=0.653531,Y=0.653531,Z=0.373531)
LightEnvironment=MyLightEnvironment
CastShadow=FALSE
End Object
Components(1)=MyStaticMesh
CollisionComponent=MyStaticMesh;
DisplayMesh=MyStaticMesh
Physics=PHYS_None
bStatic=false
bMovable=true
bHardAttach=true
bBlockActors=false
bDetach=false


socketTag = "empty";
socketFilled = false;

attachedPeice = none;
}

function Tick(float Delta)
{

Super.Tick(Delta);



rotMove.Yaw += 200;

//setRotation(rotMove);


}

function OnTouch()
{

WorldInfo.Game.Broadcast(self, "touched socket :" $ socketTag);
J_BFGame(WorldInfo.Game).storeLastTouchedSocket(se lf);
}



function moveTest()
{
rotMove.Yaw -= 400;
//locationMoveTest.Z += 0.01;
//setLocation(locationMoveTest);


}

Alfier
02-13-2011, 09:02 AM
Hi, I'd like to use this system to check if player's pawn is touched, instead of any other actor, but when I try to implement the ITouchable interface in my custom player pawn and touch it in-game nothing happens. :\ What am I doing wrong?
It does not only affect player's pawn, but every skeletal mesh in level too...

Alfier
02-15-2011, 04:11 AM
Bump! Need to solve this problem :\
That's odd, skeletal meshes have a collision cylinder but still they are not "touched"...

Insipidus
04-03-2011, 01:09 AM
Hey ffejnosliw,

Wondering if you ever got the chance to do your full explanation about implementing this code? I've compiled everything you've shared, yet nothing works for me. Just to make it clear, I am attempting to "touch" objects in UDK Mobile and move them. I was hoping that this code, which I also saw here (http://udn.epicgames.com/Three/MobileInputSystem.html), would do this. Maybe I'm doing something wrong?

Insipidus
04-06-2011, 01:49 PM
Hey Dr. Destiny,

Curious about this line of code you have:

function OnTouch()
{

WorldInfo.Game.Broadcast(self, "touched socket :" $ socketTag);
J_BFGame(WorldInfo.Game).storeLastTouchedSocket(se lf);

I keep getting errors with the "J_BFGame" portion (changed it to my game name). The error I get is:

D:\040411\UDK-2011-03\Development\Src\MyGame\Classes\MyActor.uc(49) : Error, Unrecognized member 'storeLastTouchedSocket' in class 'MobileGame'

Not sure what I did wrong. Also, as I posted before, I followed the tutorial located here too:

http://udn.epicgames.com/Three/MobileInputSystem.html#TouchEvents

and am unable to obtain any type of results for grabbing and moving a mesh/actor. Any input from anyone would be greatly appreciated. Wondering if I'm the only one running into problems or what.

Thanks.

ffejnosliw
04-06-2011, 01:54 PM
His gametype must have a storeLastTouchedSocket() function, whereas I am guessing yours does not. That is why you get an error. That function is not something built-in to any existing UDK classes.

The example is not meant to "grab and move" anything. It simply determines if there was a compatible actor where you touch the screen and "selects" it, meaning it stores a reference to it. Any functionality beyond that would have to be added by you.

Insipidus
04-06-2011, 01:57 PM
Ah, I see. Have to add the additional functionality. I was wondering if that might be the issue. I will see what I can do.

Thanks for the prompt response ffejnosliw.

ffejnosliw
04-06-2011, 02:09 PM
Just to add some more info...

The example currently selects on a tap, which means the touch has to end without moving from the original touch location (within reason). There are several ways you can modify that to include moving the actor.

You could have it select on first touch and then use the ZoneEvent_Update events to get the current touch location and use that to move the selected actor, if one exists.
You could keep the tap to select, but if there is a selected actor and a new touch begins over that actor, then you use the ZoneEvent_Update events to move the actor.
You could use tap to select and then use a subsequent tap to determine the location to move the actor to.

Any of these can be done fairly easily by modifying the code in the example.

Insipidus
04-06-2011, 03:06 PM
Haha, I was just looking at that, as well as other ZoneEvents. I'll have to figure out how to implement them correctly, but your information further supports my theory with that being the direction to go.

Touch Events
During the lifetime of touch, it generates events that are categorized into different types. These types can be used to determine how to react to the touch at any given point.


ZoneEvent_Touch This event type is sent when the user comes into contact, or touches, the device. It signifies a new individual touch.

ZoneEvent_Update This event type is sent each frame between when the first touch occurs and the touch ends. This event allows the touch to be tracked over its lifetime.

ZoneEvent_UnTouch This event type is sent when the touch ends, or the user stops touching the device. It signifies the end of an individual touch.

ZoneEvent_Cancelled This event type is sent when an outside force, like a system message appearing, cancels an existing touch. This also signifies the end of an existing touch.

ZoneEvent_Stationary This event type is not currently generated.


Thank you again. :)