49
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 [email protected] @MichaelSamarin

Developing games for Series 40 full-touch UI

Embed Size (px)

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.

Citation preview

Page 1: Developing games for Series 40 full-touch UI

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 [email protected]

@MichaelSamarin

Page 2: Developing games for Series 40 full-touch UI

» 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

Page 3: Developing games for Series 40 full-touch UI

Asha 305 Asha 306 Asha 311

Retail 70 – 120 $

New Series 40 Full Touch Platform

Page 4: Developing games for Series 40 full-touch UI

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

Page 5: Developing games for Series 40 full-touch UI

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

Page 6: Developing games for Series 40 full-touch UI

› Game API Package (MIDP)

› javax.microedition.lcdui.game › GameCanvas

› Layer

› LayerManager

› Sprite

› TiledLayer

Page 7: Developing games for Series 40 full-touch UI

› GameCanvas › Double buffered

› Convenient for minimizing code of game loop

› Methods for querying status of keys

Page 8: Developing games for Series 40 full-touch UI

› 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();

}

}

}

Page 9: Developing games for Series 40 full-touch UI

› Graphical Assets

Page 10: Developing games for Series 40 full-touch UI

› Graphical Assets – Sprite Star

Page 11: Developing games for Series 40 full-touch UI

› Graphical Assets – Sprite Lightning

Page 12: Developing games for Series 40 full-touch UI

› Layer › Abstract class, any visual game

element

› LayerManager

› Combines layers together, provides viewport

Page 13: Developing games for Series 40 full-touch UI

› Sprite › Animated game object

› TiledLayer

› Game areas, backgrounds

Page 14: Developing games for Series 40 full-touch UI

› Sprite

› Animated element of the game (character)

› Define Sequence, Delay

› Flip, Rotate

› Define Reference Point

› Detect Collisions

Page 15: Developing games for Series 40 full-touch UI

› TiledLayer

› Defines game backgrounds

› Can be animated

› Doesn’t have Sprite methods

Page 16: Developing games for Series 40 full-touch UI

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

Page 17: Developing games for Series 40 full-touch UI

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

Page 18: Developing games for Series 40 full-touch UI

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

Page 19: Developing games for Series 40 full-touch UI

Scene Graph

World

Background

Group

Mesh

Morphing Mesh

Skinned Mesh

Group

Group

Sprite 3D

Sprite 3D User Object

Camera

Light

Page 20: Developing games for Series 40 full-touch UI

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

Page 21: Developing games for Series 40 full-touch UI

public class Canvas3D extends Canvas

implements Runnable {

public Canvas3D(){

}

public void paint(Graphics g) {

}

public void run() {

}

}

Page 22: Developing games for Series 40 full-touch UI

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;

Page 23: Developing games for Series 40 full-touch UI

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();

Page 24: Developing games for Series 40 full-touch UI

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();

Page 25: Developing games for Series 40 full-touch UI

public class Canvas3D extends Canvas

implements Runnable {

public Canvas3D(){

}

public void paint(Graphics g) {

}

public void run() {

}

}

while (running){

repaint();

Thread.sleep(20);

}

Page 26: Developing games for Series 40 full-touch UI

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

› Touch events

› Multipoint-touch

› Gestures

› Sensors

Page 27: Developing games for Series 40 full-touch UI

Game keys simulation » No touch handling in Canvas?

› Drag gestures automatically trigger simulated key events

› Up, Down, Left, Right

Page 28: Developing games for Series 40 full-touch UI

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

Page 29: Developing games for Series 40 full-touch UI

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); }

Page 30: Developing games for Series 40 full-touch UI

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; } }

Page 31: Developing games for Series 40 full-touch UI

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

Page 32: Developing games for Series 40 full-touch UI

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); }

Page 33: Developing games for Series 40 full-touch UI

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; } } }

Page 34: Developing games for Series 40 full-touch UI

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

Page 35: Developing games for Series 40 full-touch UI

Sensors Modes

› Synchronous

› Poll sensor

› Example: accelerometer in game loop

› Asynchronous

› DataListener callbacks

› Example: phone charger plugged in

Page 36: Developing games for Series 40 full-touch UI

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

Page 37: Developing games for Series 40 full-touch UI

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.

Page 38: Developing games for Series 40 full-touch UI

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.

Page 39: Developing games for Series 40 full-touch UI

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;

Page 40: Developing games for Series 40 full-touch UI

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

Page 41: Developing games for Series 40 full-touch UI

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; }

Page 42: Developing games for Series 40 full-touch UI

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

Page 43: Developing games for Series 40 full-touch UI

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

Page 44: Developing games for Series 40 full-touch UI

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.

Page 45: Developing games for Series 40 full-touch UI

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

Page 46: Developing games for Series 40 full-touch UI

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.

Page 47: Developing games for Series 40 full-touch UI

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

Page 48: Developing games for Series 40 full-touch UI

Special thanks

› Andreas Jakl, Nokia

Page 49: Developing games for Series 40 full-touch UI

Thank you!

@MichaelSamarin