Game Development Community

Help with Joystick Code?

by CharlesL · in iTorque 2D · 09/17/2012 (5:02 pm) · 9 replies

I'm having trouble with my joystick code. Right now, the joystick will work as long as it stays in it's bounds, but when it gets to the edge it will get stuck. If I'm not hovering over it when I click, it will not register onTouchUp for the sprite. Are there any other callbacks that will work?

Code:
//active sprite array  
$activeSprites[0] = null;  
  
function nullSpriteArray()  
{  
    for( %i = 0; %i < 11; %i++ )  
    {  
        $activeSprites[%i] = null;  
    }  
}
  
nullSpriteArray();

//sprite reporting  
function ThumbStick::onLevelLoaded(%this, %scenegraph)
{
	ThumbStick.origin = ThumbStick.position;
	echo(">>>>>>>>" SPC ThumbStick.origin);
	ThumbStick.enableUpdateCallback();
	ThumbStick.range = 40;
	ThumbStick.dragging = false;
}
function ThumbStick::onTouchDown( %this, %touchID, %worldPos )  
{  
   $activeSprites[%touchID] = %this;
	ThumbStick.dragging = true;
}  
function ThumbStick::onUpdate(%this)
{
	%pos = %this.getPosition();
	if(t2dVectorDistance(%pos, ThumbStick.origin) >= ThumbStick.range)
	{
		ThumbStick.dragging = false;
	}
}
function ThumbStick::onTouchUp()
{
	ThumbStick.setPosition(ThumbStick.origin);
}
//screen touch functions  
function t2dSceneWindow::onTouchDragged( %this, %touchID, %worldPos )  
{  
   if( $activeSprites[%touchID] != null)  
   {
		if(ThumbStick.dragging == true)
   		$activeSprites[%touchID].setPosition( %worldPos );  
   }  
}  
  
function joystickTrigger::onTouchUp( %this, %touchID, %worldPos )  
{
	echo("UP!");
    $activeSprites[%touchID] = null;  
}

Thanks to @Joe Williams for the touch code.

#1
09/18/2012 (7:39 am)
I just had to solve this using a very silly approach. The onTouchUp won't get called because it eventually just stops calling any touch events. I was using onTouchDragged and it got maybe 10 calls before it stopped - no matter if I was still dragging or not. Anyway... I solved this by doing something like this. Be warned it's quite lengthy and you probably can't just airlift this to your own implementation.

Since you're using a joystick style, you may want to make some changes.

in game.cs:
// towards the top of the file
$touchUpCallback = "";

// ...

function registerTouchUpCallback( %object ) {
    // TODO fill in here
    $touchUpCallback = %object;
}

// ...

function touchesUp(%touchIDs, %touchesX, %touchesY) {
   %numTouches = getWordCount( %touchIDs );

   for(%count = 0; %count < %numTouches; %count++) {
      %curTouch = getWord( %touchIDs, %count );
      %curX = getWord( %touchesX, %count );
      %curY = getWord( %touchesY , %count );
      %worldPos = sceneWindow2D.getWorldPoint(%curX, %curY);

      // Print results for debuggery
      echo("Scanning touch up " @ %curTouch @ " at " @ %worldPos);
      
      if ( $touchUpCallback !$= "" ) {
          $touchUpCallback.touchUpCallback( %curTouch );
      }      
   }
}

And in your class that wants to know about touch up:

function MyClass::onLevelLoaded( %this, %scenegraph ) {
    // ...    
    %this.schedule( 1000, "onTick" );

    registerTouchUpCallback( %this );
}

function MyClass::touchUpCallback( %this, %touchId ) {
    echo("DEBUG touchUpCallback - it got called for " @ %touchId );
    
    if ( %this.moveTouch == %touchId ) {
        %this.stopMotion();        
    }
}

function MyClass::onTick( %this ) {
    %this.updateHorzMovement();
    %this.updateVertMovement();
    
    %this.schedule( 100, "onTick" );
}

function MyClass::onTouchDown(%this, %touchId, %worldPos) {
    // depending on your impl , you may want to set %this.moveLeft or %this.moveRight here
    // I do that in my touch dragged because my motion is controlled by dragging a sprite
}

function MyClass::onTouchUp(%this, %touchId, %worldPos) {
    // in the off chance that touch up does get called
    %this.stopMotion();
}

function MyClass::onTouchDragged( %this, %touchId, %worldPos ) {
    // ...
    %this.moveTouch = %touchId;
}

function MyClass::stopMotion( %this ) {
    if ( %this.moveLeft ) {
        %this.moveLeft = false;
        %this.setLinearVelocityX( 0 );
    }
    
    if ( %this.moveRight ) {
        %this.moveRight = false;
        %this.setLinearVelocityX( 0 );
    }
    
    if ( %this.moveUp ) {
                
    }
    
    if ( %this.moveDown ) {
        
    }    
}
#2
09/18/2012 (12:24 pm)
Thanks for taking time to answer my problem, Joe. I didn't get a chance to implement it yet, but I have a question: Does this code work on it's own (It's a full joystick that does't require my code above)? Thanks!

I figured out the answer to my original problem. I have to move add an t2dSceneWindow::onTouchUp somewhere in the middle of the code, and it will react to onTouchUp. It still has a problem. If the touch moves out of the bounds, any movement will not be registered to the stick.
#3
09/18/2012 (7:15 pm)
onMouseLeave is a callback that triggers when a touch drag leaves the object. It depends on the behavior you want to see. You might also use setMouseLocked to force the object to recieve all touch events - turn it on in OnTouchDown(), turn it off in OnTouchUp().

heres a behavior I wrote up before for a control disk, you'll probably need to change it to fit your game style:
if (!isObject(DPadBehavior))
{
    %template = new BehaviorTemplate(DPadBehavior);

    %template.friendlyName = "Template";
    %template.behaviorType = "Control";
    %template.description  = "Joystick-like control";
   
    %template.addBehaviorField(thumbdisk, "The disk that appears under the thumb", object, "", t2dSceneObject);
    %template.addBehaviorField(targetObject, "The object that gets moved", object, "", t2dSceneObject);

    %template.addBehaviorField(scale, "Scale the effect on the object", float , 1.0 );
    %template.addBehaviorField(flipX, "Flip Horizontal because of movement", bool, true);
}

function DPadBehavior::onBehaviorAdd(%this)
{
    if (isObject(%this.targetObject))
        %this.targetObject.setUsesPhysics(true);
        
    %this.owner.setUseMouseEvents(true);
    %this.owner.setCollisionDetect(CIRCLE);
    %this.owner.setCollisionCircleSuperscribed(true);
}

function DPadBehavior::Update(%this, %worldPos)
{
    %vec = t2dVectorSub(%worldPos, %this.owner.getPosition());

    if (t2dVectorLength(%vec) > %this.size)
        %vec = t2dVectorScale(%vec, 1/(t2dVectorLength(%vec)/%this.size));
    
    %this.thumbdisk.setPosition(t2dVectorAdd(%this.owner.getPosition(), %vec));

    %move = t2dVectorScale(%vec, %this.scale);
    %this.targetObject.setLinearVelocity(%move);

    if (%this.flipX)
        %this.targetObject.setFlipX(%move.x > 0);
}

function DPadBehavior::onTouchDown(%this, %touchId, %worldPos)
{
    if (!%this.owner.getIsPointInObject(%worldPos))
        return;

    %this.thumbpos = %worldPos;
    %this.thumbdisk.setPosition(%this.thumbpos);
    %this.tid = %touchId;

    %this.Update(%worldPos);
}

function DPadBehavior::onLevelLoaded(%this)
{
    %size = %this.owner.getSize();
    %this.size = ( %size.x > %size.y ) ? %size.x/2 : %size.y/2;
    %this.thumbdisk.setPosition(%this.owner.getPosition());
}

function DPadBehavior::onTouchDragged(%this, %touchId, %worldPos)
{
    if (%touchId != %this.tid) return;
    
    %this.Update(%worldPos);
}

function DPadBehavior::onTouchUp(%this)
{
    if (%touchId != %this.tid) return;

    %this.thumbpos = %worldPos;
    %this.thumbdisk.setPosition(%this.owner.getPosition());
    
    %this.tid = 0;
    %this.targetObject.setAtRest();
}

function DPadBehavior::onMouseLeave(%this, %touchID, %worldPos)
{
    %this.onTouchUp(%this, %touchID, %worldPos);
}
#4
09/19/2012 (10:01 am)
Cool, thanks for taking time to answer! Is there a way to set a limit for the thumbdisk? I couldn't find anything.
#5
09/19/2012 (10:55 am)
Not sure what you mean by a limit. The behavior attaches to a t2dsceneobject that defines how far the disk/stick can move by its size. I use a big empty circle t2dstaticsprite. The "thumbdisk" isn't actually required - it just goes where you touch.
#6
09/19/2012 (1:15 pm)
Ah - Got it, Thanks. I misunderstood the setup, but it has given me some new ideas.

Are there any ways to track the touch, so when the touch leaves the target area the stick will track at the edge of it's boundary?
#7
09/19/2012 (7:41 pm)
Basically you'd need to use setMouseLocked in touchdown/touchup to keep getting input once the touch has left the main sprite - and then do some math to figure where that vector intersects the border of the main sprite.
#8
10/13/2012 (10:52 pm)
By the way I've uploaded a resource that extends this code into a twin stick shooter...
www.garagegames.com/community/resources/view/21938
#9
10/14/2012 (10:51 am)
SWEET!!! I've been trying to find something like that for a while! Thanks!