Game apps for smartphones and tablets

Research project PHBern  
HomeStart online editorAndroid TurtlegraphicsPrintJava-Online

Fling-Events

Smartphone and tablet users often perform a 'sweep' gesture by moving rapidly with one finger over the screen. The system's reaction depends on the direction and the speed of the sweep. With Android this action is called a 'fling gesture' and is well supported by the Android API and JDroidLib. To make use of it, a GGFlingListener has to be implemented, which is registered with the method addFlingListener(). The callback method flingEvent is used to handle the fling event, containing the point, on which the fling started, the point where it ended and a velocity vector of the movement in pixels per second.


In our example, a ball is thrown with a fling gesture. The finger movement has to start at the right side of the display and must end before crossing the green line. The ball then moves physically correct, taking the initial velocity and direction given from the fling. If the basket is hit, the points are incremented. The goal is to make as many points as possible.

Beispiel im Online-Editor bearbeiten

App installieren auf Smartphone oder Tablet

QR-Code

Sources downloaden (AndroidEx24.zip)

 

For computing the tracjectory, physical formulas are used. For this to go smoothly, the coordinates are converted to physical units (positions in metres, velocities in m/s and acceleration in m/s^2). These values are called user coordinates in JDroidLib. This makes it easier, to distinguish and convert between pixel coordinates and real physical coordinates. Be aware that when positioning actors pixel coordinates are used. But all drawing operations used by GGPanel can be made in user coordinates.

In touch events, pixel coordinates have to be converted into user coordinates using the methods toUserX() and toUserY(). These can be found in the class GGPanel. This is especially useful when using dynamic window size (with windowZoom(), to adapt sprite size to screen size) and still have consistent behavior.



Source code:

// AndroidEx24.java

package app.ex24;

import ch.aplu.android.*;
import android.graphics.Point;

public class AndroidEx24 extends GameGrid implements GGFlingListenerGGActorCollisionListener
{
  private final double vFactor = 50;
  private double roomHeight = 5;
  private Basket basket;
  protected GGStatusBar status;
  protected GGPanel p;
  protected int hits = 0;
  protected int shots = 0;

  public AndroidEx24()
  {
    super(WHITE, falsetruewindowZoom(600));
    setScreenOrientation(LANDSCAPE);
    status = addStatusBar(30);
  }

  public void main()
  {
    addFlingListener(this);
    setSimulationPeriod(30);
    p = getPanel(0, roomHeight, 0);  // center lower-right
    p.setAutoRefreshEnabled(
false);
    p.setLineWidth(4);
    p.setPaintColor(GREEN);
    p.line(6, 0, 6, roomHeight);
    basket = new Basket();
    addActor(basket, new Location(p.toPixelX(0.5), p.toPixelY(3)));
    basket.setCollisionRectangle(new Point(-10-20), 40, 10);
    doRun();
    status.setText("Fling the ball!");
  }

  public boolean flingEvent(Point start, Point end, GGVector velocity)
  {
    double = p.toUserX(end.x);
    double = p.toUserY(end.y);
    double vx = vFactor * velocity.x;
    double vy = -vFactor * velocity.y;

    if (x > 6)
    {
      Ball ball = new Ball(thisx, y, vx, vy);
      addActorNoRefresh(ball, new Location(end.xend.y));
      ball.addCollisionActor(basket);
      ball.addActorCollisionListener(this);
      ball.setCollisionCircle(new Point(0, 0), 24);
      shots++;

    }
    else
      showToast("Stay behind the line!");
    return true;
  }

  public int collide(Actor actor1, Actor actor2)
  {
    if (((Ball)actor1).getVelocityY() < -0.1)
    {
      hits++;
      playTone(1200, 20);
      ((Ball)actor1).setVelocityX(0);
      ((Ball)actor1).setX(0.5);
    }
    return 10;
  }

  protected void displayResult()
  {
    status.setText(String.format("#shots: %d   #hits: %d   %%: %4.1f",
      shots, hits, 100.0 * hits / shots));
  }
}

// -----------class Ball-----------------
class Ball extends Actor
{
  private final double = 9.81; // in m/s^2
  private double x, y;  // in m 
  private double vx, vy; // in  m/s
  private double dt = 0.030;  // in s (simulation period)
  private AndroidEx24 app;

  public Ball(AndroidEx24 app, double x, double y, double vx, double vy)
  {
    super("ball");
    this.app = app;
    // Initial conditions:
    this.= x;
    this.= y;
    this.vx = vx;
    this.vy = vy;
  }

  public void act()
  {
    vy = vy - * dt;
    x = + vx * dt;
    y = + vy * dt;
    setLocation(new Location(app.p.toPixelX(x), app.p.toPixelY(y)));
    if (x < 0)
      vx = -vx;
    else
    {
      if (!isInGrid())
      {
        removeSelf();
        app.displayResult();
      }
    }
  }

  protected void setX(double x)
  {
    this.= x;
  }

  protected void setVelocityX(double vx)
  {
    this.vx = vx;
  }

  protected double getVelocityY()
  {
    return vy;
  }
}

//-----------class Basket --------------
class Basket extends Actor
{
  public Basket()
  {
    super("basket");
  }
}

Explanations to the code:
vFactor = 50 Adjusts ball velocity
toPixelX(), to PixelY() Converts the user coordinates (physical unit, metres) into pixel coordinates of the device.
toUserX(), toUserY() Converts the pixel coordinates into user coordinates
int collide() Callback method of the GGActorCollisionListeners
basket.setCollisionRectangle
(new Point(-10, -20), 40, 10);
Only the upper boarder of the basket is activated as collision area. The coordinates are respective to the sprite and are automatically adjusted to screen size.
if((Ball)actor1).getVelocityY() < -0.1 Ensures that the ball is coming from above.
((Ball)actor1).setVelocityX(0)
((Ball)actor1).setX(0.5)
After collision with the upper boarder, the ball drops vertically downwards with fixed distance to the wall.
(String.format("#shots: %d   #hits: %d   %%: %4.1f",
      shots, hits, 100.0 * hits / shots)
Formats the score in the status bar nicely. %d stands for integers, %4.1f for decimal numbers exact to the first decimal place.