Date post: | 12-Jan-2015 |
Category: |
Technology |
Upload: | nokia-developer |
View: | 2,434 times |
Download: | 5 times |
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
» 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