Aegidius
 Plüss   Aplulogo
     
 www.aplu.ch      Print Text 
© 2021, V10.4
 
  JGameGrid
 
 

The source code of all examples is included in the JGameGrid distribution.

Ex18: Easy Integration of Microsoft's Xbox Controller

Do you want to add more interactivity to your JGameGrid application? Use a game controller like the Xbox gamepad from Microsoft. It has several controls among them 4 analog, 8 digital buttons and a direction pad. It may even give you the vibrating sensation of a rumbler. The Xbox controller is easy to integrate into the JGameGrid framework when using the Xbox controller library based on the Java API Wrapper framework (JAW, a simple JNI framework). This library supports the wired Xbox controller and the wireless controller as well as. It has a fully event driven design. Because it is based on the native controller library from Microsoft, it is only available for the Windows platform. (For downloading and installation instructions, see here).

Using Java listeners callbacks that are triggered by controller manipulations has advantages over polling the state of the Xbox controls. The callback code is completely decoupled from the rest of the program and controller events are queued in a internal FIFO buffer. By polling the controls, it is the reponsability of the programer to check the controller state as often as necessary in order to keep track of all possible controller manipulations. But there is a drawback with events: The callbacks are executed in a separate thread and synchronizing is needed, if the interrupted thread performs code that is not thread-safe. For your convenience, in JGameGrid all methods of the class Actor are thread-safe, so you don't bother with synchronization.

As demonstration we implement the following scenario:

Ants appear at random positions on a desert soil and move on a somewhat erratic path. The user handles the position and direction of a syringe that creates PacMan-like creatures called gluttons, that devour the ants when they collide. The ants are "intelligent": they "see" the gluttons in their neighborhood and change the moving direction to avoid to be eaten. You may easily add more competition challenge by limiting the time and/or the total number of gluttons. (The scenario is a prototype of a "shooting game".)

First we model the ants by the class Ant that extends Actor. We use two sprite images to simulate the movement of the legs. The method act() brings the ants to life: first the sprite image is flipped, then the list of all Gluttons is checked for a Glutton instance that is closer than 50 pixels. If found, the ant's moving direction is reversed. After that the ant moves 10 pixels and changes it's direction within -20 and 20 degrees to produce the erratic path. Finally the ant is removed from the GameGrid when it moves out of the visible scene.

The Glutton class is very simple. It uses 4 sprite images for the mouth animation.

// Glutton.java

import
 ch.aplu.jgamegrid.*;

public
 class Glutton extends Actor
{
  
public Glutton()
  
{
    
super(true"sprites/ghost.gif", 4);
  
}

  
public void act()
  
{
    
showNextSprite();
    
move();
    
if (!isMoveValid())
      
removeSelf();
  
}
}

 

As usual, the application class Ex18 is a subclass of GameGrid to simplify calls to methods of the GameGrid class. The constructor is responsible for all initializing work. Among others it creates a XboxController instance and checks if the controller is already connected and turned on. It then declares and registers the controller callback methods. Most important is the buttonX() method that creates a new glutton. Finally the application thread ends up in a loop that displays the current state (number of gluttons and eaten ants) in the title bar. Do not waste CPU time be performing the check too frequently, every half second or so is enough.

public Ex18()
{
  
super(800, 530, 1, null"sprites/background.gif"false);
  
setSimulationPeriod(50);
  
playSound(this, GGSound.DUMMY);
  
addActor(nozzle, new Location(xStart, yStart));
  XboxController xc 
= new XboxController();
  
if (!xc.isConnected())
    
setTitle("Xbox controller not connected");
  
else
    
setTitle(connectInfo);
  xc.
setLeftThumbDeadZone(0.1);

  
// Register controller callbacks
  xc.
addXboxControllerListener(new XboxControllerAdapter()
  
{
    
public void leftThumbMagnitude(double magnitude)
    
{
      mag 
= magnitude;
      
setPosition();
    
}

    
public void leftThumbDirection(double direction)
    
{
      dir 
= direction;
      
setPosition();
    
}

    
public void leftTrigger(double value)
    
{
      nozzle.
setDirection(360 * value);
      
refresh();
    
}

    
public void buttonX(boolean pressed)
    
{
      
if (pressed)
      
{
        nozzle.
show(1);
        Glutton glutton 
= new Glutton();
        nbGluttons
++;
        glutton.
addCollisionActors(getActors(Ant.class));
        glutton.
addActorCollisionListener(appl);
        
addActor(glutton, nozzle.getLocation(), nozzle.getDirection());
      
}
      
else
        nozzle.
show(0);
    
}

    
public void isConnected(boolean connected)
    
{
      
if (connected)
        
setTitle(connectInfo);
      
else
        
setTitle("Connection lost");
      GameGrid.
delay(2000);
    
}
  
});

  appl 
= this;
  
show();
  
doRun();
  
delay(2000);
  
while (true)
  
{
    
delay(400);
    
if (xc.isConnected())
      
setTitle("Gluttons: " + nbGluttons + ". Eaten Ants: " + nbEaten);
  
}
}

 

The GameGrid act() method creates a new ant every five simulation cycles at a random scene location with random direction.

public void act()
{
  
if (getNbCycles() % 5 == 0)  // Slow down ant creation
  
{
    Ant ant 
= new Ant();
    ant.
addCollisionActors(getActors(Glutton.class));
    ant.
addActorCollisionListener(this);
    ant.
setSlowDown(4);
    
addActor(ant, getRandomLocation(), 360 * Math.random());
  
}
}

 

Because new ants must be "collision-aware" of all existing glutton and vice-versa, every ant and every glutton instance registers the collision listener. In JGameGame collisions are handled by the collide() callback method of the GGActorCollisionListener. This is elegant because it decouples the code from the rest of the program.

public int collide(Actor a1, Actor a2)
{
  
playSound(this, GGSound.PING);
  
if (a1 instanceof Ant)
    
removeActor(a1);
  
if (a2 instanceof Ant)
    
removeActor(a2);
  nbEaten
++;
  
return 0;
}

 

We only remove the ant, but not the glutton. Because parameter a1 or a2 may be an ant reference, we have to check for the Ant class using instanceof.

jgamegridex18

Execute the program locally using WebStart.
Execute the demo program without the need of the Xbox controller using WebStart.