JCardGame
 
 

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

(see also "How to use Auto Zoom in Android Card Games")

How to Use Auto Zoom in Grid and Pixel Games

Applications on smartphones (Apps) must display well on very different devices from small handheld phones to large tablets. This is a special challenge that can cause lot of work for the App programmer. Devices running Android Version 3 and up can use a build-in auto zoom feature. But often the graphics becomes blurred when zoomed by the OS, especially on these new big tablets with their superb screen resolution. JGameGrid provides an auto zoom feature that adapts sprite images automatically to the current screen resolution when the sprite image is loaded from the disk. During development a virtual coordinate system can be used that will be transformed to real (screen) coordinates depending on the current screen size at runtime. The collision areas are transformed to the zoomed image automatically. Depending if the game is grid or pixel based (cell size = 1), the procedure is slightly different.

Grid based games:

For grid based games, you use one of the GameGrid constructors that takes a cellZoom(int cellSize) argument. At APK startup, the current cell size cellSizeX is calculated depending on the GameGrid layout and the current device resolution. This will provide a zoom factor z = cellSizeX / cellSize. When sprite, background and tile map images are loaded they are scaled on the fly by the factor z. If you need z, you request its value by calling GameGrid.getZoomFactor().

Guidelines (example):

  • Assume a fixed size game, e.g. 10x10 cells with cell size 50. (This is called the virtual layout.)
  • Create actor pictures with image sizes that fit well into cells of size 50 pixels
  • Create background image with size 501x501 pixels
  • Use GameGrid constructor with cellZoom(50)
  • If you need runtime screen coordinates (e.g. for Actor.setLocationOffset()) use getCellSize() to get the real cell size. Never use magic numbers that assume a fixed grid size
  • Set the touch area parameters based on the virtual window layout. They are automatically adapted at runtime.
  • GGBackground line drawings (not the background image) must be scaled manually using the current zoom factor z = GameGrid.getZoomFactor()

In the following three related examples we show how fine auto zoom works on a tablet. The size of the background image is 501 x 501 pixels, the size of the fish is 70 x 53 pixels. The class Fish for the three examples is the following:

package ch.aplu.tut;

import ch.aplu.android.Actor;

class Fish extends Actor
{
  public Fish()
  {
    super("nemo");
  }

  public void act()
  {
    move();
    if (!isMoveValid())
    {
      turn(180);
      setHorzMirror(!isHorzMirror());
    }
  }
}

 

Ex06a: Cells with fixed size 50 x 50 pixels Ex06b: Cells adapted, but images not zoomed Ex06c: Cells adapted, images zoomed  
package ch.aplu.tut;

import
 ch.aplu.android.*;

public 
class Ex06a extends GameGrid
{
  
public Ex06a()
  
{
    
super(10, 10, 50, 
      RED, 
"reef"false);
  
}

  
public void main()
  
{
    Fish nemo 
= new Fish();
    
addActor(nemo, new Location(1, 1));
    
doRun();
  
}
}
 
package ch.aplu.tut;

import
 ch.aplu.android.*;

public 
class Ex06b extends GameGrid
{
  
public Ex06b()
  
{
    
super(10, 10, 0,
      RED, 
"reef"false);
  
}

  
public void main()
  
{
    Fish nemo 
= new Fish();
    
addActor(nemo, new Location(1, 1));
    
doRun();
  
}
}
 
package ch.aplu.tut;

import
 ch.aplu.android.*;

public
 class Ex06c extends GameGrid
{
  
public Ex06c()
  
{
    
super(10, 10, cellZoom(50),
      RED, 
"reef"false);
  
}

  
public void main()
  
{
    Fish nemo 
= new Fish();
    
addActor(nemo, new Location(1, 1));
    
doRun();
  
}
}
 
 
ex06a ex06b ex06c  


Pixel based games:

For pixel based games, you use one of the GameGrid constructors that takes a windowZoom(int windowSize) argument. At APK startup, the current window size windowSizeX is calculated depending on the GameGrid layout and the current device resolution. This will provide a zoom factor z = windowSizeX / windowSize. When sprite, background and tile map images are loaded they are scaled on the fly by the factor z. If you need z, you request its value by calling GameGrid.getZoomFactor().

Guidelines (example):

  • Assume a fixed size game window, e.g. 500x500 pixels. (This is called the virtual layout.)
  • Create actor pictures with image sizes that fit well into this window
  • Create background image with size 501x501 pixels
  • Use GameGrid constructor with windowZoom(500)
  • If you need runtime screen coordinates, use getNbHorzCells() and getNbVertCells() to get the real window size. Never use magic numbers that assume a fixed window size
  • If you use full screen windows, the zoom factor is adapted to the smaller side of the window rectangle. The aspect ratio is approximately 16:9. To get the exact virtual size of the playground, use getVirtualPgWidth() and getVirtualPgHeight() (one of them is 500, which one depends on the device orientation)
  • Set the touch and collision area parameters based on the virtual window layout. They are automatically adapted at runtime
  • GGBackground line drawings (not the background image) must be scaled manually using the current zoom factor z = GameGrid.getZoomFactor()

The three following examples shows how auto zoom with 1 pixel cells (called "pixel based games") works on a tablet. The size of the background image is 501 x 501 pixels, the size of the fish is 70 x 53 pixels. The class Clownfish for the three examples is the following:

package ch.aplu.tut;

import ch.aplu.android.Actor;

class Clownfish extends Actor
{
  public Clownfish()
  {
    super("nemo");
  }

  public void act()
  {
    move();
    if (!isInGrid())
    {
      turn(180);
      setHorzMirror(!isHorzMirror());
    }
  }
}

 

Ex07a: 1 pixel cells with fixed window size
500 x 500 pixels
Ex07b: 1 pixel cells, window adapted,
but images not zoomed
Ex06c: 1 pixel cells window adapted,
images zoomed
 

package ch.aplu.tut;

import ch.aplu.android.*;

public class Ex07a extends GameGrid
{
  public Ex07a()
  {
    super(5005001
      TRANSPARENT, "reef"false);
  }

  public void main()
  {
    setSimulationPeriod(50);
    getBg().drawFrame(WHITE);
    int cX =  250;
    int cY =  250;
    Clownfish nemo = new Clownfish();
    addActor(nemo, new Location(cX, cY));
    doRun();
  }
} 
package ch.aplu.tut;

import
 ch.aplu.android.*;

public
 class Ex07b extends GameGrid
{
  
public Ex07b()
  
{
    
super("reef");
  
}

  
public void main()
  
{
    
setSimulationPeriod(50);
    
getBg().drawFrame(WHITE);
    
int cX =  getNbHorzCells() / 2;
    
int cY =  getNbVertCells() / 2;
    Clownfish nemo 
= new Clownfish();
    
addActor(nemo, new Location(cX, cY));
    
doRun();
  
}
}
 
package ch.aplu.tut;

import
 ch.aplu.android.*;

public
 class Ex07c extends GameGrid
{
  
public Ex07c()
  
{
    
super("reef", windowZoom(500));
  
}

  
public void main()
  
{
    
setSimulationPeriod(50);
    
getBg().drawFrame(WHITE);
    
int cX =  getNbHorzCells() / 2;
    
int cY =  getNbVertCells() / 2;
    Clownfish nemo 
= new Clownfish();
    
addActor(nemo, new Location(cX, cY));
    
doRun();
  
}
}
 
 
ex06a ex06b ex06c  

Of course, full screen games look more professionally. For full screen images, the zoom factor is calculated from the smaller side of the window rectangle. This means that in landscape orientation the image height is zoomed to fit the screen vertically. To make the image fill the screen horizontally too, you should provide a background image whose width is at least 16/9 its height. With windowZoom(500) an image size of approximately 500 x 900 pixels is appropriate. This is shown in the next example:

package ch.aplu.tut;

import ch.aplu.android.*;

public class Ex08 extends GameGrid
{
  public Ex08()
  {
    super("bonaire"falsetruewindowZoom(500));
    setScreenOrientation(LANDSCAPE);
  }

  public void main()
  {
    setSimulationPeriod(50);
    getBg().drawFrame(WHITE);
    int cX =  getNbHorzCells() / 2;
    int cY =  getNbVertCells() / 2;
    Clownfish nemo = new Clownfish();
    addActor(nemo, new Location(cX, cY));
    doRun();
  }
}

bonaire