Date post: | 30-Dec-2015 |
Category: |
Documents |
Upload: | portia-gill |
View: | 28 times |
Download: | 0 times |
BlackBerry Game Development Challenges
Jeff Bacon, Sr. Smartphone Product Manager
Simon Dale, Smartphone Services Team Lead
J15
Agenda
• Buffering Strategies• Parallax Scrolling• Case Study: Worms A Space Oddity• Optimizing with the JDE• Navigation• API Deficiencies
Magmic Experience
Original & Licensed IPOver 45 Games
Feature Phones (J2ME/MIDP)BlackBerryFeature Phones (BREW)Windows Mobile
Licensed IP: 60+ TitlesJ2ME/MIDP BlackBerry
BREW Windows Mobile
5+ years of BlackBerry game development
Tiles
110 x Graphics.drawBitmap 11 x Graphics.drawBitmap
Buffering StrategiesWhat do we mean by buffering?
Raging Rivers, Magmic Games
• Why do we need to buffer the screen?
• Customers think big screen == big performance• Meet customer expectations• Optimization is FUN!
Buffering StrategiesMotivations
Bitmap buffer = new Bitmap(screenWidth, screenHeight);
private boolean bufferDirty = true;
private void repaintBuffer() { synchronized (buffer) { // … draw all the static content onto the buffer … bufferDirty = false; }}
public void setBufferDirty() { synchronized (buffer) { bufferDirty = true; }}
Buffering StrategiesFull Screen Image
// OPTION 1: Paint Methodpublic void paint(Graphics g) { if (buffer_is_dirty) { repaintBuffer(); } g.drawBitmap(0, 0, screenWidth, screenHeight, buffer, 0, 0); // … draw all dynamic content …}
Buffering StrategiesFull Screen Image
// OPTION 2: Game Run Loopif (buffer_is_dirty) { repaintBuffer(); }
• Good: guarantee clean paints• Bad: frame rate choppy• Bad: long synchronization in paint
• Good: control when frame rate suffers• Bad: can have dirty paints
Buffering StrategiesFull Screen Image: Working and Live buffers
• Reduces maximum paint time• Low variance in FPS• Risk of stale paints
private void repaintBuffer() { synchronized (workingBuffer) { // … draw all the static content onto the workingBuffer … synchronized (liveBuffer) { Graphics g = liveBuffer.getGraphics(); g.drawBitmap(... workingBuffer ...); } bufferDirty = false; }}public void paint(Graphics g) { synchronized (liveBuffer) { g.drawBitmap(... liveBuffer ...); } // … draw all dynamic content …}
Buffering StrategiesLarger-Than-Full Screen Image
Content Area
Viewport
• Same basic code as Full Screen
• Keep track of viewport co-ordinates
• Offset rendering in drawBitmap with viewport
Call of Duty 3, HandsOn Mobile
int numBuffers = (screenWidth/ bufferWidth) + 1;
Bitmap buffers[] = new Bitmap[numBuffers];private boolean[] bufferIsDirty = new boolean[numBuffers];private int leftBuffer = 0;private int leftOffset = 0;
public synchronized void setBufferDirty(int bufferId) { synchronized (buffers) { bufferIsDirty[bufferId] = true; }}
private void repaintBuffer(int bufferId) { synchronized (buffers) { // … draw all the static content onto the buffer … bufferIsDirty[bufferId] = false; }}
Buffering StrategiesSegmented Buffer
public void paint(Graphics g) { synchronized (buffers) { for (int i = 0; i < numBuffers; i++) { if (bufferIsDirty[i]) { repaintBuffer(i); } } } int currentBuffer = leftBuffer; for (int i = 0; i < numBuffers; i++) { g.drawBitmap(… buffers[currentBuffer % numBuffers] …); ++currentBuffer; } // … draw all dynamic content …}
Buffering StrategiesSegmented Buffer
• Remember to respect the leftOffset• Remember to start at leftBuffer index
Screen AreaScreen Area
• More buffers == smoother scrolling• Less buffers == faster painting when not scrolling
Horizontal Scroller: • buffer height == screen height • width can be varied
(ex. above)
Vertical Scroller:• buffer width == screen width • height can be varied
Indiana Jones and the Kingdom of the Crystal Skull, THQ Wireless
Buffering StrategiesSegmented Buffer, Example
• Avoid tiled backgrounds• Use fewer, larger images• Buffer the background layer(s)• Only paint visible areas
Eagle Eye, Magmic Games
Parallax ScrollingOptimization: Reduce Number of Paints
Can we Buffer?• Can’t draw to a large Bitmap• Difficult to maintain transparency in a buffer• How do we efficiently deform the world?
Why? • Large level maps (1024x512)• Repainting on every tick• Repainting using Graphics.drawRGB()• Updating all map data for all changes
• Stationary camera: 3-5 fps• Moving camera (viewport): 1-2 fps• Explosion: 0 fps
Case Study: Worms A Space OddityProblem: Very Slow Frame Rate
• Use lookup-tables for trig functions• Faster calculations, less accurate
• Localize painting to the current viewport• Remove some detail
• Decreases production quality• Performance often outweighs detail
• Full Screen buffer• Stationary camera improved dramatically
• Stationary camera: 17 fps• Moving camera (viewport): 8-12 fps• Explosion: 0-3 fps
Case Study: Worms A Space OddityStage 1: Basic Optimizations & Full Screen Buffer
Worms A Space Oddity, THQ Wireless
• Create a world buffer: 1024x512px
• Manipulate ARGB data for transparency• Painting buffer done on load• Allows parallax scrolling• Optimize explosions & bounds checking• Localize small changes
1024px
512
px
• Stationary camera: 12 fps• Moving camera: 12 fps• Explosion: 8-10 fps
Case Study: Worms A Space OddityStage 2: More Optimizations & Segmented Buffer
Worms A Space Oddity, THQ Wireless
• Stationary camera: 14-16 fps• Moving camera: 12-14 fps• Explosion: 8-10 fps
Case Study: Worms A Space OddityStage 3: Double Buffering
• Improve stationary camera frame rate• Paint 1 live buffer vs. 1-4 working buffers• Possible panning improvement• Painting off-screen faster than direct to screen