developing games for series 40 full-touch ui

Post on 12-Jan-2015

2.434 Views

Category:

Technology

5 Downloads

Preview:

Click to see full reader

DESCRIPTION

This presentation introduce the processes involved in developing 2D and 3D games for the full-touch UI on Series 40 phones by using Nokia SDK 2.0 for Java™. Java expert Michael Samarin of Futurice introduces APIs of particular interest to game developers. He focus on development techniques specific to games with no keyboard input and how to use gestures and sensors in your games to increase player engagement. Performance and memory considerations are also covered.

TRANSCRIPT

Series 40 Developer Training

Getting Started with Game Development on Nokia Series 40 Full Touch Asha Phones

Michael Samarin, Ph.D Director, Developer Training and Evangelism Futurice +358 40 518 18 09 michael.samarin@futurice.com

@MichaelSamarin

» This presentation is summary and compressed extracts relevant to game development from previous webinars:

› Overview of full touch Asha Devices

› Overview of graphical APIs for 2D and 3D Game development Mobile Java

› User input in full touch devices

› Useful in games performance tips on Series 40

Today’s topics

Asha 305 Asha 306 Asha 311

Retail 70 – 120 $

New Series 40 Full Touch Platform

2 Mb 2 Mb 2 Mb

Asha 305, 306 Asha 308, 309 Asha 311

2 Mb 2 Mb 4 Mb

-- -- 1 GHz

Capacitive Multipoint-Touch

Resistive Multipoint-Touch

Capacitive Multipoint-Touch

Jar Size

Java Heap

CPU

Screen

Series 40 Graphics APIs » 2D Game Development

› Game API, part of the MIDP 2.0 standard, java package: javax.microedition.lcdui.game

› http://www.developer.nokia.com/Resources/Library/Java/#!developers-guides/ui-and-graphics/game-api.html

» 3D Game Development

› Mobile 3D Graphics API, optional JSR-184 also known as M3G

› http://www.developer.nokia.com/Resources/Library/Java/#!developers-guides/ui-and-graphics/mobile-3d-graphics.html

› Game API Package (MIDP)

› javax.microedition.lcdui.game › GameCanvas

› Layer

› LayerManager

› Sprite

› TiledLayer

› GameCanvas › Double buffered

› Convenient for minimizing code of game loop

› Methods for querying status of keys

› GameCanvas public class MyCanvas extends GameCanvas implements Runnable {

public void run() {

Graphics g = getGraphics();

while(true) {

// update the game state

int k = getKeyStates();

// respond to key events

flushGraphics();

}

}

}

› Graphical Assets

› Graphical Assets – Sprite Star

› Graphical Assets – Sprite Lightning

› Layer › Abstract class, any visual game

element

› LayerManager

› Combines layers together, provides viewport

› Sprite › Animated game object

› TiledLayer

› Game areas, backgrounds

› Sprite

› Animated element of the game (character)

› Define Sequence, Delay

› Flip, Rotate

› Define Reference Point

› Detect Collisions

› TiledLayer

› Defines game backgrounds

› Can be animated

› Doesn’t have Sprite methods

Mobile 3D Graphics API

JSR-184 or M3G

› Object-Oriented 3D

› Scene Graph based

› Optional MIDP JSR

› Very compact API

› Very fast development

› Optimized for small memory and budget CPU

› Excellent implementation on Series 40

Lightweight API, only 30 classes

AnimationController AnimationTrack Appearance Background Camera CompositingMode Fog Graphics3D Group Image2D

IndexBuffer KeyframeSequence Light Loader Material Mesh MorphingMesh Node Object3D PolygonMode

RayIntersection SkinnedMesh Sprite3D Texture2D Transform Transformable TriangleStripArray VertexArray VertexBuffer World

M3G Modes

› Immediate mode

› Similar to OpenGL ideology

› Retained mode

› Scene Graph based

› Entire Scene Graph can be restored from file

› Well defined M3G format

› Can be freely mixed

Scene Graph

World

Background

Group

Mesh

Morphing Mesh

Skinned Mesh

Group

Group

Sprite 3D

Sprite 3D User Object

Camera

Light

Most Interesting Scene Graph

Elements

› World

› Background

› Morphing and Skinned Mesh

› Animated Geometry Objects

› Mesh

› 3D Geometry of Visible Object

› Sprite 3D

› 2D image in 3D space

public class Canvas3D extends Canvas

implements Runnable {

public Canvas3D(){

}

public void paint(Graphics g) {

}

public void run() {

}

}

public class Canvas3D extends Canvas

implements Runnable {

public Canvas3D(){

}

public void paint(Graphics g) {

}

public void run() {

}

}

private Thread thread;

private long startTime;

private Graphics3D graphics3D;

private World world;

private Camera camera;

private boolean running = false;

public class Canvas3D extends Canvas

implements Runnable {

public Canvas3D(){

}

public void paint(Graphics g) {

}

public void run() {

}

}

setFullScreenMode(true);

thread = new Thread(this);

startTime = System.currentTimeMillis()

graphics3D = Graphics3D.getInstance();

world = new World();

camera = new Camera();

float aspect = (float) getWidth() / (float) getHeight();

camera.setPerspective(30.0f, aspect, 1.0f, 1000.0f);

world.addChild(camera);

world.setActiveCamera(camera);

running = true;

thread.start();

public class Canvas3D extends Canvas

implements Runnable {

public Canvas3D(){

}

public void paint(Graphics g) {

}

public void run() {

}

}

graphics3D.bindTarget(g);

world.animate(

(int)(System.currentTimeMillis() - startTime));

graphics3D.render(world);

graphics3D.releaseTarget();

public class Canvas3D extends Canvas

implements Runnable {

public Canvas3D(){

}

public void paint(Graphics g) {

}

public void run() {

}

}

while (running){

repaint();

Thread.sleep(20);

}

› User input in full touch Asha devices › Game keys simulation

› Touch events

› Multipoint-touch

› Gestures

› Sensors

Game keys simulation » No touch handling in Canvas?

› Drag gestures automatically trigger simulated key events

› Up, Down, Left, Right

Touch Gestures

› Tap: touch + release

› Long Press (& repeated): touch + hold

› Drag: touch + drag

› Drop: touch + drag + touch down (“stop”) + release

› Flick: touch + drag + release while dragging

› Pinch (new!): 2x touch + 2x drag + 2x touch down (“stop”) + 2x release

Using Gestures

› Register as gesture listener

› Zone: reacts to 1+ specified gestures

› Whole screen or rectangular area

› Overlap possible

› Received events → GestureListener

public class MainCanvas extends Canvas implements GestureListener { private int curPinchDistance = -1; public MainCanvas() { // Set this as container (gesture source) and listener GestureRegistrationManager.setListener(this, this); // Register for pinch events in the whole canvas area gestureZone = new GestureInteractiveZone(GestureInteractiveZone.GESTURE_PINCH); GestureRegistrationManager.register(this, gestureZone); }

Using Gestures

› Handling gestures

› Executed in UI thread

› Lengthy operations (scaling image, etc.) → own thread!

public void gestureAction(Object container, GestureInteractiveZone gestureInteractiveZone, GestureEvent gestureEvent) { int eventType = gestureEvent.getType(); switch (eventType) { case GestureInteractiveZone.GESTURE_PINCH: // Pinch detected curPinchDistance = gestureEvent.getPinchDistanceCurrent(); break; case GestureInteractiveZone.GESTURE_RECOGNITION_START: /* ... */ break; case GestureInteractiveZone.GESTURE_RECOGNITION_END: /* ... */ break; } }

Pointer events and

Multipoint- Touch

› Single touch

› Canvas.pointerPressed() part of MIDP

› Only tracks 1st touch point

› Multipoint Touch

› Tracks multiple touch points

› But: use Gesture API if only interested in pinch

› Each associated with unique ID, x, y and state

› Call-back for touch changes, but status available any time

Using Multipoint-Touch

› Number of touch points

› Limited accuracy of simultaneous touch points on a resistive screen (Nokia 305) → no on-screen joystick & shoot button

› Register: touch point listener

MultipointTouch mpt = MultipointTouch.getInstance(); int numTouchPoints = MultipointTouch.getMaxPointers();

2 on Nokia 305 / 306 5 on Nokia 311

public class MainCanvas extends Canvas implements MultipointTouchListener { public MainCanvas() { // ... mpt.addMultipointTouchListener(this); }

Using Multipoint-Touch › Handling touch events

public void pointersChanged(int[] pointerIds) { for(int i=0; i<pointerIds.length; i++) { // Loop through the changed touch points { int pointerId = pointerIds[i]; // Get the touch point ID int state = MultipointTouch.getState(pointerId); // Get the touch point state // Get the touch point x and y coordinates int x = MultipointTouch.getX(pointerId); int y = MultipointTouch.getY(pointerId); // Handle the UI update based on the touch point state, ID and coordinates switch(state) { case MultipointTouch.POINTER_PRESSED: // A new finger was pressed against the screen drawTouch(pointerId, x, y); break; case MultipointTouch.POINTER_DRAGGED: // A pressed finger was dragged over the screen drawTouch(pointerId, x, y); break; case MultipointTouch.POINTER_RELEASED: // A pressed finger was lifted from the screen break; } } }

Sensors

› JSR 256 Sensor API

› Optional Generic API: designed for battery, network status, also for temperature, blood pressure, etc.

› Usefull in Games

› Acceleration: –2g .. +2g, x / y / z axis

› Double Tap: 1 .. 63, phone sides

› Orientation: 0 .. 6, phone orientation

Sensors Modes

› Synchronous

› Poll sensor

› Example: accelerometer in game loop

› Asynchronous

› DataListener callbacks

› Example: phone charger plugged in

Using Multipoint-Touch › Establish sensor connection

› Check data in game loop

// Find all acceleration sensors, the contextType is left undefined SensorInfo[] sensorInfos = SensorManager.findSensors("acceleration", null); // Find an acceleration sensor that returns double values for (int i = 0; i < sensorInfos.length; i++) { if (sensorInfos[i].getChannelInfos()[0].getDataType() == ChannelInfo.TYPE_DOUBLE) { accSensor = (SensorConnection) Connector.open(sensorInfos[i].getUrl()); } }

// Use 1 as a buffer size to get exactly 1 value for each axis Data[] data = accSensor.getData(1); speedX = -data[0].getDoubleValues()[0]; // data[0] => x-axis speedY = data[1].getDoubleValues()[0]; // data[1] => y-axis

Hash Acceleration » Some iterative algorithms are slow. Proper usage of

collections types of data structures can increase performance.

» Vector.contains() is very slow, but Hashtable.containsKey() is very fast. Reconsider your algorithms to use Hashtables.

» Usage can be found in very surprising places. For example, Font.stringWidth() is slow, but necessary for drawing multiline text on Canvas. Creating a Hashtable with the width in each character you have used in the Font can transform this into a fast operation and increase Canvas.paint() speed.

Synchronized vs. Volatile Variables » When a variable or Object needs to be accessed from more

than one Thread.

» Marking a variable as volatile is the least restrictive approach and can have very high performance because no Thread is blocked.

» Only one Thread may enter the synchronized sections at any one time.

» Consider atomic operations on two variables. For example, when updating firstName and lastName from “John Smith” to “Jane Marceau”, do so within a synchronized block to avoid briefly exposing the transitional state “Jane Smith” to other threads.

Constants » We can give the compiler and Proguard more opportunities

to optimize the code at the compile step, and this will also give the ARM processor opportunities for handling these variables with more efficient byte codes.

private static int loopCount = 10; private static long startTime = System.currentTimeMillis(); private static boolean enableImages = true;

Should be

private static final int LOOP_COUNT = 10; private static final long START_TIME = System.currentTimeMillis(); private static final boolean ENABLE_IMAGES = true;

Primitives » Use int instead of short, byte or long.

for (int i = 0; i < 3000000; i++) { short/int/long a = 123; short/int/long b = -44; short/int/long c = 12; a += c; b += a; c *= b; }

Average times spent in loops on Nokia Asha 305 (obfuscated): int: 710 (580) ms short: 900 (850) ms 50% slower long: 1450 (1150) ms 100% slower

Final in methods for (int i = 0; i < 1000000; i++) { a = finalMethod(1, 2, 3); } for (int i = 0; i < 1000000; i++) { a = nonFinalMethod(1, 2, 3); } public final int finalMethod(final int a, final int b, final int c) { final float x = 1.23f, y = 0.05f; final float z = x * y; final int d = a + b + c; return d; } public int nonFinalMethod(int a, int b, int c) { float x = 1.23f, y = 0.05f; float z = x * y; int d = a + b + c; return d; }

Final in methods

Average times on a Nokia Asha 305: finalMethod: 650 ms nonFinalMethod: 940 ms 45% slower In this case, the time difference comes from final keyword before x and y. It is logical because then z value can be precalculated. The final keywords with parameters a, b, c let us not precalculate d or anything. And because we don’t use z, it being final does not help us

Static » Generally static methods and variables should be faster.

Oddly, with some combinations of ARM and JVM, instance accesses are slightly faster.

for (int i = 0; i < 1000000; i++) { staticMethod(); } for (int i = 0; i < 1000000; i++) { nonStaticMethod(); } private static void staticMethod() { b++; // static variable } private void nonStaticMethod() { a++; // instance variable }

Average times spent in loops on Nokia Asha 305 (obfuscated): nonStaticMethod: 570 ms staticMethod: 680 ms 20% slower

String Concatenation If you are going to concatenate a large number of small Strings, use: StringBuffer.append() instead of the String += operator. String is much slower because every time you concatenate a string to another with += operator, a new StringBuffer is created under the hood. Depending on the number of concatenations, a single explicit StringBuffer can be many times faster than multiple implicit StringBuffers created by String addition.

Addition vs. Multiplication vs. Division for (int i = 0; i < 500000; i++) { a = 1.23f; b = 1.45f; c = 0.004523f; c += a; a = b + c; } for (int i = 0; i < 500000; i++) { a = 1.23f; b = 1.45f; c = 0.004523f; c *= a; a = b * c; } for (int i = 0; i < 500000; i++) { a = 1.23f; b = 1.45f; c = 0.004523f; c /= a; a = b / c; }

Average times spent in loops on Nokia Asha 305: Multiplying: 330 ms Addition: 360 ms 9% slower Division: 560 ms 70% slower

Switch vs. If

The switch statement in C is implemented as a direct jump which is extremely fast. In Java on Nokia Series 40 phones, switches are implemented at the bytecode level as a series of if statements. Therefore in many cases a switch statement is less efficient than a manually created series of if..else statements in which the first positive case is selected as the one which occurs more frequently. If you prefer to use switch statements for code clarity, then arrange them so that the most frequent cases appear first.

Live Demo Session » If you are watching these slides on SlideShare, next part is live coding

demonstration with Nokia SDK 2.0 for Java and NetBeans. Full recording of the live session can be found at Nokia Developer website:

» http://www.developer.nokia.com/Resources/Multimedia/Webinars.xhtml

Special thanks

› Andreas Jakl, Nokia

Thank you!

@MichaelSamarin

top related