JGameGrid
 
 

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

 

Ex22: Mouse Events Using a GGMouseTouchListener

In many games the player interacts with actors using the mouse. By clicking on a actor's image the actor's reference must be known to handle the click event accordingly. When using a coarse game grid this is easily done by identifying the cell using the current mouse cursor coordinates. As explained in Ex06 (lesson 5) the clicked cell location is returned by toLocationInGrid() and an actor in this cell by getOneActorAt() or getActorsAt(). The mouse event callback used for this example resides in the class Switch:

public boolean mouseEvent(GGMouse mouse)
{
  Location location =
    gameGrid.toLocationInGrid(mouse.getX(), mouse.getY());
  if (location.equals(getLocation()))
    showNextSprite();
  bulb.show(getIdVisible());
  gameGrid.refresh();
  return false;
}

This concept fails when fine-grained game grids are used, because in this case the location of an actor corresponds to one or a few pixel that are hardly hit by the mouse. To get mouse events when the mouse "touches" the actor image a GGMouseTouchListener must be used. The well-known types of mouse events are supported (press, release, click, double-click, move, drag, enter, leave). For each sprite image a specific "mouse touch area" is used where the mouse events are active (e.g. enter and leave will be triggered when entering/leaving this area). As default the mouse touch area corresponds to the non-transparent pixels of the sprite image. With setMouseTouchRectangle() or setMouseTouchCircle() the touch area may be changed to a rectangle or circle located anywhere with respect to the sprite image. Evidently when the actor moves or rotates, the mouse touch area moves or rotates accordingly.

The notification method mouseTouched(), called when a touch event with an actor occurs, reports the actor's reference, a GGMouse reference and a Point reference that contains the coordinates of the mouse cursor with respect to the sprite image (zero at center of sprite). The actor reference can may used to call all actor's public methods directly. This simplifies and clarifies the code as seen in the revised version of the class Switch:

public void mouseTouched(Actor actor, GGMouse mouse, Point hotSpot)
{
  showNextSprite();
  bulb.show(getIdVisible());
  gameGrid.refresh();
}

A MouseTouchListener is registered for every Switch instance. You can register as many MouseListeners and MouseTouchListeners as you like, but all MouseListeners should return false, to prevent that the mouse events are "swallowed". To limit the active area to the small part of the switch image, we use a rectangular touch area:

switches[i].addMouseTouchListener(switches[i], GGMouse.lPress);
switches[i].setMouseTouchRectangle(new Point(-10, 10), 30, 20);

Execute the program locally using WebStart and check the mouse touch area.

gifs/jgamegridtut25.gif

 

The next example shows how simple it is to drag an actor in a pixel-based game grid. When pressing the mouse, the location of the hot spot displacement is saved. While dragging, the actor's new location is set by using the current cursor position and the saved displacement.

import ch.aplu.jgamegrid.*;
import java.awt.*;

public class Ex22 extends GameGrid implements GGMouseTouchListener
{
  private Point hotSpot;

  public Ex22()
  {
    super(3003001false);
    setTitle("Drag Nemo!");
    Actor nemo = new Actor("sprites/nemo.gif");
    nemo.addMouseTouchListener(this,
      GGMouse.lPress | GGMouse.lDrag | GGMouse.lRelease);
    addActor(nemo, new Location(150150));
    show();
  }

  public void mouseTouched(Actor actor, GGMouse mouse, Point spot)
  {
    switch (mouse.getEvent())
    {
      case GGMouse.lPress:
        hotSpot = spot;
        break;
      case GGMouse.lDrag:
        if (hotSpot == null)  // Pressed outside sprite
          hotSpot = spot;
        Location loc = 
          new Location(mouse.getX() - hotSpot.x, mouse.getY() - hotSpot.y);
        actor.setLocation(loc);
        refresh();
        break;
      case GGMouse.lRelease:
        hotSpot = null;
        break;
    }
  }

  public static void main(String[] args)
  {
    new Ex22();
  }
}

Execute the program locally using WebStart

jgamegridtut28

 

The user must take some care to drag the image slowly because if the mouse is moved quickly outside the image, no touch event is fired anymore. To overcome this difficulty, the drag event should be generated by a MouseListener. The improved version no works perfectly.

import ch.aplu.jgamegrid.*;
import java.awt.*;

public class Ex22a extends GameGrid 
 implements GGMouseTouchListener, GGMouseListener
{
  private Point hotSpot;
  private Actor nemo = new Actor("sprites/nemo.gif"); 

  public Ex22a()
  {
    super(300, 300, 1, false);
    setTitle("Drag Nemo!");
    addMouseListener(this, GGMouse.lDrag);
    nemo.addMouseTouchListener(this,
      GGMouse.lPress | GGMouse.lRelease);
    addActor(nemo, new Location(150, 150));
    show();
  }
  
  public boolean mouseEvent(GGMouse mouse)
  {
    if (hotSpot == null)
      return true;
    Location loc =
      new Location(mouse.getX() - hotSpot.x, mouse.getY() - hotSpot.y);
    nemo.setLocation(loc);
    refresh();
    return true;
  }

  public void mouseTouched(Actor actor, GGMouse mouse, Point spot)
  {
    switch (mouse.getEvent())
    {
      case GGMouse.lPress:
        hotSpot = spot;
        break;
    
      case GGMouse.lRelease:
        hotSpot = null;
        break;
    }
  }

  public static void main(String[] args)
  {
    new Ex22a();
  }
}

Execute the program locally using WebStart.

 

For game grids with a coarse cell layout, the same approach let you drag the actor smoothly from cell to cell. Normally actors are centered in the middle of a cell, but by using setLocationOffset() or setPixelLocation() the displayed sprite image may be displaced. When lRelease is fired, the actor is "dropped" at its standard location in the middle of the cell that currently contains the center of the actor image.

The extra boolean parameter in addMouseTouchListener() ensures that if there are several actors laying on top of each other, only the actor on top gets the mouse events. Moreover in the GGMouse.lPress event, the current touchActor is set on top, so while dragging, its image appears always on top of other actors.

import ch.aplu.jgamegrid.*;
import
 java.awt.*;

public
 class Ex22b extends GameGrid
  
implements GGMouseTouchListener, GGMouseListener
{
  
private Actor touchedActor = null;
  
private Point hotSpot;

  
public Ex22b()
  
{
    
super(5, 5, 100, Color.red, false);
    
setTitle("Drag Nemo!");
    
addMouseListener(this, GGMouse.lDrag);
    
for (int i = 0; i < 4; i++)
    
{
      Actor nemo 
= new Actor("sprites/nemo.gif");
      
addActor(nemo, getRandomEmptyLocation());
      nemo.
addMouseTouchListener(this, GGMouse.lPress | GGMouse.lRelease, true);
    
}
    
show();
  
}

  
public boolean mouseEvent(GGMouse mouse)
  
{
    
if (touchedActor != null)
    
{
      
Point pt =
        
new Point(mouse.getX() - hotSpot.x, mouse.getY() - hotSpot.y);
      touchedActor.
setPixelLocation(pt);
      
refresh();
    
}
    
return false;
  
}

  
public void mouseTouched(Actor actor, GGMouse mouse, Point spot)
  
{
    
switch (mouse.getEvent())
    
{
      
case GGMouse.lPress:
        touchedActor = actor;
        touchedActor.setOnTop();
        hotSpot = spot;
        
break;
      
case GGMouse.lRelease:
        touchedActor.
setLocationOffset(new Point(0, 0));
        touchedActor 
= null;
        
break;
    
}
    
refresh();
  
}

  
public static void main(String[] args)
  
{
    
new Ex22b();
  
}

}

Execute the program locally using WebStart.

jgamegridtut26