diff --git a/9781430239871.jpg b/9781430239871.jpg new file mode 100644 index 0000000..1b409fb Binary files /dev/null and b/9781430239871.jpg differ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..f3b9117 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,27 @@ +Freeware License, some rights reserved + +Copyright (c) 2011 Mario Zechner and Robert Green + +Permission is hereby granted, free of charge, to anyone obtaining a copy +of this software and associated documentation files (the "Software"), +to work with the Software within the limits of freeware distribution and fair use. +This includes the rights to use, copy, and modify the Software for personal use. +Users are also allowed and encouraged to submit corrections and modifications +to the Software for the benefit of other users. + +It is not allowed to reuse, modify, or redistribute the Software for +commercial use in any way, or for a user’s educational materials such as books +or blog articles without prior permission from the copyright holder. + +The above copyright notice and this permission notice need to be included +in all copies or substantial portions of the software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..ef5991e --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +#Apress Source Code + +This repository accompanies [*Beginning Android 4 Games Development*](http://www.apress.com/9781430239871) by Mario Zechner and Robert Green (Apress, 2011). + +![Cover image](9781430239871.jpg) + +Download the files as a zip using the green button, or clone the repository to your machine using Git. + +##Releases + +Release v1.0 corresponds to the code in the published book, without corrections or updates. + +##Contributions + +See the file Contributing.md for more information on how you can contribute to this repository. diff --git a/ch02-hello world/.classpath b/ch02-hello world/.classpath new file mode 100644 index 0000000..609aa00 --- /dev/null +++ b/ch02-hello world/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ch02-hello world/.project b/ch02-hello world/.project new file mode 100644 index 0000000..4ba30ec --- /dev/null +++ b/ch02-hello world/.project @@ -0,0 +1,33 @@ + + + ch02-hello world + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/ch02-hello world/AndroidManifest.xml b/ch02-hello world/AndroidManifest.xml new file mode 100644 index 0000000..72d41dd --- /dev/null +++ b/ch02-hello world/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch02-hello world/default.properties b/ch02-hello world/default.properties new file mode 100644 index 0000000..0cdab95 --- /dev/null +++ b/ch02-hello world/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-12 diff --git a/ch02-hello world/res/drawable/icon.png b/ch02-hello world/res/drawable/icon.png new file mode 100644 index 0000000..a07c69f Binary files /dev/null and b/ch02-hello world/res/drawable/icon.png differ diff --git a/ch02-hello world/res/layout/main.xml b/ch02-hello world/res/layout/main.xml new file mode 100644 index 0000000..3a5f117 --- /dev/null +++ b/ch02-hello world/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/ch02-hello world/res/values/strings.xml b/ch02-hello world/res/values/strings.xml new file mode 100644 index 0000000..12dc004 --- /dev/null +++ b/ch02-hello world/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Hello World, HelloWorldActivity! + Hello World + diff --git a/ch02-hello world/src/com/helloworld/HelloWorldActivity.java b/ch02-hello world/src/com/helloworld/HelloWorldActivity.java new file mode 100644 index 0000000..9875de2 --- /dev/null +++ b/ch02-hello world/src/com/helloworld/HelloWorldActivity.java @@ -0,0 +1,26 @@ +package com.helloworld; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; + +public class HelloWorldActivity extends Activity + implements View.OnClickListener { + Button button; + int touchCount; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + button = new Button(this); + button.setText( "Touch me!" ); + button.setOnClickListener(this); + setContentView(button); + } + + public void onClick(View v) { + touchCount++; + button.setText("Touched me "+touchCount+" time(s)"); + } +} \ No newline at end of file diff --git a/ch03-game-framework/.classpath b/ch03-game-framework/.classpath new file mode 100644 index 0000000..18d70f0 --- /dev/null +++ b/ch03-game-framework/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/ch03-game-framework/.project b/ch03-game-framework/.project new file mode 100644 index 0000000..54c56bc --- /dev/null +++ b/ch03-game-framework/.project @@ -0,0 +1,17 @@ + + + ch03-game-framework + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/ch03-game-framework/src/com/badlogic/androidgames/framework/Audio.java b/ch03-game-framework/src/com/badlogic/androidgames/framework/Audio.java new file mode 100644 index 0000000..d30d722 --- /dev/null +++ b/ch03-game-framework/src/com/badlogic/androidgames/framework/Audio.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Audio { + public Music newMusic(String filename); + + public Sound newSound(String filename); +} diff --git a/ch03-game-framework/src/com/badlogic/androidgames/framework/FileIO.java b/ch03-game-framework/src/com/badlogic/androidgames/framework/FileIO.java new file mode 100644 index 0000000..d555b7d --- /dev/null +++ b/ch03-game-framework/src/com/badlogic/androidgames/framework/FileIO.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.framework; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public interface FileIO { + public InputStream readAsset(String fileName) throws IOException; + + public InputStream readFile(String fileName) throws IOException; + + public OutputStream writeFile(String fileName) throws IOException; +} diff --git a/ch03-game-framework/src/com/badlogic/androidgames/framework/Game.java b/ch03-game-framework/src/com/badlogic/androidgames/framework/Game.java new file mode 100644 index 0000000..fb5c5aa --- /dev/null +++ b/ch03-game-framework/src/com/badlogic/androidgames/framework/Game.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework; + +public interface Game { + public Input getInput(); + + public FileIO getFileIO(); + + public Graphics getGraphics(); + + public Audio getAudio(); + + public void setScreen(Screen screen); + + public Screen getCurrentScreen(); + + public Screen getStartScreen(); +} \ No newline at end of file diff --git a/ch03-game-framework/src/com/badlogic/androidgames/framework/Graphics.java b/ch03-game-framework/src/com/badlogic/androidgames/framework/Graphics.java new file mode 100644 index 0000000..c7b8f45 --- /dev/null +++ b/ch03-game-framework/src/com/badlogic/androidgames/framework/Graphics.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework; + +public interface Graphics { + public static enum PixmapFormat { + ARGB8888, ARGB4444, RGB565 + } + + public Pixmap newPixmap(String fileName, PixmapFormat format); + + public void clear(int color); + + public void drawPixel(int x, int y, int color); + + public void drawLine(int x, int y, int x2, int y2, int color); + + public void drawRect(int x, int y, int width, int height, int color); + + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight); + + public void drawPixmap(Pixmap pixmap, int x, int y); + + public int getWidth(); + + public int getHeight(); +} diff --git a/ch03-game-framework/src/com/badlogic/androidgames/framework/Input.java b/ch03-game-framework/src/com/badlogic/androidgames/framework/Input.java new file mode 100644 index 0000000..0893d62 --- /dev/null +++ b/ch03-game-framework/src/com/badlogic/androidgames/framework/Input.java @@ -0,0 +1,70 @@ +package com.badlogic.androidgames.framework; + +import java.util.List; + +public interface Input { + public static class KeyEvent { + public static final int KEY_DOWN = 0; + public static final int KEY_UP = 1; + + public int type; + public int keyCode; + public char keyChar; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == KEY_DOWN) + builder.append("key down, "); + else + builder.append("key up, "); + builder.append(keyCode); + builder.append(","); + builder.append(keyChar); + return builder.toString(); + } + } + + public static class TouchEvent { + public static final int TOUCH_DOWN = 0; + public static final int TOUCH_UP = 1; + public static final int TOUCH_DRAGGED = 2; + + public int type; + public int x, y; + public int pointer; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == TOUCH_DOWN) + builder.append("touch down, "); + else if (type == TOUCH_DRAGGED) + builder.append("touch dragged, "); + else + builder.append("touch up, "); + builder.append(pointer); + builder.append(","); + builder.append(x); + builder.append(","); + builder.append(y); + return builder.toString(); + } + } + + public boolean isKeyPressed(int keyCode); + + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public float getAccelX(); + + public float getAccelY(); + + public float getAccelZ(); + + public List getKeyEvents(); + + public List getTouchEvents(); +} diff --git a/ch03-game-framework/src/com/badlogic/androidgames/framework/Music.java b/ch03-game-framework/src/com/badlogic/androidgames/framework/Music.java new file mode 100644 index 0000000..1b95a1e --- /dev/null +++ b/ch03-game-framework/src/com/badlogic/androidgames/framework/Music.java @@ -0,0 +1,21 @@ +package com.badlogic.androidgames.framework; + +public interface Music { + public void play(); + + public void stop(); + + public void pause(); + + public void setLooping(boolean looping); + + public void setVolume(float volume); + + public boolean isPlaying(); + + public boolean isStopped(); + + public boolean isLooping(); + + public void dispose(); +} diff --git a/ch03-game-framework/src/com/badlogic/androidgames/framework/Pixmap.java b/ch03-game-framework/src/com/badlogic/androidgames/framework/Pixmap.java new file mode 100644 index 0000000..4b1eec9 --- /dev/null +++ b/ch03-game-framework/src/com/badlogic/androidgames/framework/Pixmap.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; + +public interface Pixmap { + public int getWidth(); + + public int getHeight(); + + public PixmapFormat getFormat(); + + public void dispose(); +} diff --git a/ch03-game-framework/src/com/badlogic/androidgames/framework/Screen.java b/ch03-game-framework/src/com/badlogic/androidgames/framework/Screen.java new file mode 100644 index 0000000..40a98b9 --- /dev/null +++ b/ch03-game-framework/src/com/badlogic/androidgames/framework/Screen.java @@ -0,0 +1,19 @@ +package com.badlogic.androidgames.framework; + +public abstract class Screen { + protected final Game game; + + public Screen(Game game) { + this.game = game; + } + + public abstract void update(float deltaTime); + + public abstract void present(float deltaTime); + + public abstract void pause(); + + public abstract void resume(); + + public abstract void dispose(); +} diff --git a/ch03-game-framework/src/com/badlogic/androidgames/framework/Sound.java b/ch03-game-framework/src/com/badlogic/androidgames/framework/Sound.java new file mode 100644 index 0000000..3f49835 --- /dev/null +++ b/ch03-game-framework/src/com/badlogic/androidgames/framework/Sound.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Sound { + public void play(float volume); + + public void dispose(); +} diff --git a/ch04-android-basics/.classpath b/ch04-android-basics/.classpath new file mode 100644 index 0000000..609aa00 --- /dev/null +++ b/ch04-android-basics/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ch04-android-basics/.project b/ch04-android-basics/.project new file mode 100644 index 0000000..9989b9d --- /dev/null +++ b/ch04-android-basics/.project @@ -0,0 +1,33 @@ + + + ch04-android-basics + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/ch04-android-basics/AndroidManifest.xml b/ch04-android-basics/AndroidManifest.xml new file mode 100644 index 0000000..98aafc3 --- /dev/null +++ b/ch04-android-basics/AndroidManifest.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch04-android-basics/assets/bobargb8888.png b/ch04-android-basics/assets/bobargb8888.png new file mode 100644 index 0000000..bd0294d Binary files /dev/null and b/ch04-android-basics/assets/bobargb8888.png differ diff --git a/ch04-android-basics/assets/bobrgb888.png b/ch04-android-basics/assets/bobrgb888.png new file mode 100644 index 0000000..987fe37 Binary files /dev/null and b/ch04-android-basics/assets/bobrgb888.png differ diff --git a/ch04-android-basics/assets/explosion.ogg b/ch04-android-basics/assets/explosion.ogg new file mode 100644 index 0000000..6ee14c5 Binary files /dev/null and b/ch04-android-basics/assets/explosion.ogg differ diff --git a/ch04-android-basics/assets/font.ttf b/ch04-android-basics/assets/font.ttf new file mode 100644 index 0000000..7ca93b5 Binary files /dev/null and b/ch04-android-basics/assets/font.ttf differ diff --git a/ch04-android-basics/assets/music.ogg b/ch04-android-basics/assets/music.ogg new file mode 100644 index 0000000..6ee14c5 Binary files /dev/null and b/ch04-android-basics/assets/music.ogg differ diff --git a/ch04-android-basics/assets/texts/myawesometext.txt b/ch04-android-basics/assets/texts/myawesometext.txt new file mode 100644 index 0000000..41bfe81 --- /dev/null +++ b/ch04-android-basics/assets/texts/myawesometext.txt @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. \ No newline at end of file diff --git a/ch04-android-basics/default.properties b/ch04-android-basics/default.properties new file mode 100644 index 0000000..0cdab95 --- /dev/null +++ b/ch04-android-basics/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-12 diff --git a/ch04-android-basics/res/drawable-hdpi/icon.png b/ch04-android-basics/res/drawable-hdpi/icon.png new file mode 100644 index 0000000..8074c4c Binary files /dev/null and b/ch04-android-basics/res/drawable-hdpi/icon.png differ diff --git a/ch04-android-basics/res/drawable-ldpi/icon.png b/ch04-android-basics/res/drawable-ldpi/icon.png new file mode 100644 index 0000000..1095584 Binary files /dev/null and b/ch04-android-basics/res/drawable-ldpi/icon.png differ diff --git a/ch04-android-basics/res/drawable-mdpi/icon.png b/ch04-android-basics/res/drawable-mdpi/icon.png new file mode 100644 index 0000000..a07c69f Binary files /dev/null and b/ch04-android-basics/res/drawable-mdpi/icon.png differ diff --git a/ch04-android-basics/res/drawable/icon.png b/ch04-android-basics/res/drawable/icon.png new file mode 100644 index 0000000..a07c69f Binary files /dev/null and b/ch04-android-basics/res/drawable/icon.png differ diff --git a/ch04-android-basics/res/values/strings.xml b/ch04-android-basics/res/values/strings.xml new file mode 100644 index 0000000..df67b84 --- /dev/null +++ b/ch04-android-basics/res/values/strings.xml @@ -0,0 +1,4 @@ + + + AndroidBasics + diff --git a/ch04-android-basics/src/com/badlogic/androidgames/AccelerometerTest.java b/ch04-android-basics/src/com/badlogic/androidgames/AccelerometerTest.java new file mode 100644 index 0000000..1ba36be --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/AccelerometerTest.java @@ -0,0 +1,51 @@ +package com.badlogic.androidgames; + +import android.app.Activity; +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Bundle; +import android.widget.TextView; + +public class AccelerometerTest extends Activity implements SensorEventListener { + TextView textView; + StringBuilder builder = new StringBuilder(); + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + textView = new TextView(this); + setContentView(textView); + + SensorManager manager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); + if (manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() == 0) { + textView.setText("No accelerometer installed"); + } else { + Sensor accelerometer = manager.getSensorList( + Sensor.TYPE_ACCELEROMETER).get(0); + if (!manager.registerListener(this, accelerometer, + SensorManager.SENSOR_DELAY_GAME)) { + textView.setText("Couldn't register sensor listener"); + } + } + } + + @Override + public void onSensorChanged(SensorEvent event) { + builder.setLength(0); + builder.append("x: "); + builder.append(event.values[0]); + builder.append(", y: "); + builder.append(event.values[1]); + builder.append(", z: "); + builder.append(event.values[2]); + textView.setText(builder.toString()); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // nothing to do here + } +} \ No newline at end of file diff --git a/ch04-android-basics/src/com/badlogic/androidgames/AndroidBasicsStarter.java b/ch04-android-basics/src/com/badlogic/androidgames/AndroidBasicsStarter.java new file mode 100644 index 0000000..4438584 --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/AndroidBasicsStarter.java @@ -0,0 +1,37 @@ +package com.badlogic.androidgames; + +import android.app.ListActivity; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +public class AndroidBasicsStarter extends ListActivity { + String tests[] = { "LifeCycleTest", "SingleTouchTest", "MultiTouchTest", + "KeyTest", "AccelerometerTest", "AssetsTest", + "ExternalStorageTest", "SoundPoolTest", "MediaPlayerTest", + "FullScreenTest", "WakeLockTest", "RenderViewTest", "ShapeTest", "BitmapTest", + "FontTest", "SurfaceViewTest" }; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setListAdapter(new ArrayAdapter(this, + android.R.layout.simple_list_item_1, tests)); + } + + @Override + protected void onListItemClick(ListView list, View view, int position, + long id) { + super.onListItemClick(list, view, position, id); + String testName = tests[position]; + try { + Class clazz = Class + .forName("com.badlogic.androidgames." + testName); + Intent intent = new Intent(this, clazz); + startActivity(intent); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/ch04-android-basics/src/com/badlogic/androidgames/AssetsTest.java b/ch04-android-basics/src/com/badlogic/androidgames/AssetsTest.java new file mode 100644 index 0000000..f5bef47 --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/AssetsTest.java @@ -0,0 +1,45 @@ +package com.badlogic.androidgames; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import android.app.Activity; +import android.content.res.AssetManager; +import android.os.Bundle; +import android.widget.TextView; + +public class AssetsTest extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + TextView textView = new TextView(this); + setContentView(textView); + + AssetManager assetManager = getAssets(); + InputStream inputStream = null; + try { + inputStream = assetManager.open("texts/myawesometext.txt"); + String text = loadTextFile(inputStream); + textView.setText(text); + } catch (IOException e) { + textView.setText("Couldn't load file"); + } finally { + if (inputStream != null) + try { + inputStream.close(); + } catch (IOException e) { + textView.setText("Couldn't close file"); + } + } + } + + public String loadTextFile(InputStream inputStream) throws IOException { + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + byte[] bytes = new byte[4096]; + int len = 0; + while ((len = inputStream.read(bytes)) > 0) + byteStream.write(bytes, 0, len); + return new String(byteStream.toByteArray(), "UTF8"); + } +} diff --git a/ch04-android-basics/src/com/badlogic/androidgames/BitmapTest.java b/ch04-android-basics/src/com/badlogic/androidgames/BitmapTest.java new file mode 100644 index 0000000..c047dc3 --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/BitmapTest.java @@ -0,0 +1,68 @@ +package com.badlogic.androidgames; + +import java.io.IOException; +import java.io.InputStream; + +import android.app.Activity; +import android.content.Context; +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; + +public class BitmapTest extends Activity { + class RenderView extends View { + Bitmap bob565; + Bitmap bob4444; + Rect dst = new Rect(); + + public RenderView(Context context) { + super(context); + + try { + AssetManager assetManager = context.getAssets(); + InputStream inputStream = assetManager.open("bobrgb888.png"); + bob565 = BitmapFactory.decodeStream(inputStream); + inputStream.close(); + Log.d("BitmapText", + "bobrgb888.png format: " + bob565.getConfig()); + + inputStream = assetManager.open("bobargb8888.png"); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = Bitmap.Config.ARGB_4444; + bob4444 = BitmapFactory + .decodeStream(inputStream, null, options); + inputStream.close(); + Log.d("BitmapText", + "bobargb8888.png format: " + bob4444.getConfig()); + + } catch (IOException e) { + // silently ignored, bad coder monkey, baaad! + } finally { + // we should really close our input streams here. + } + } + + protected void onDraw(Canvas canvas) { + dst.set(50, 50, 350, 350); + canvas.drawBitmap(bob565, null, dst, null); + canvas.drawBitmap(bob4444, 100, 100, null); + invalidate(); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(new RenderView(this)); + } +} diff --git a/ch04-android-basics/src/com/badlogic/androidgames/ExternalStorageTest.java b/ch04-android-basics/src/com/badlogic/androidgames/ExternalStorageTest.java new file mode 100644 index 0000000..2d24bd8 --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/ExternalStorageTest.java @@ -0,0 +1,59 @@ +package com.badlogic.androidgames; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Environment; +import android.widget.TextView; + +public class ExternalStorageTest extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + TextView textView = new TextView(this); + setContentView(textView); + + String state = Environment.getExternalStorageState(); + if (!state.equals(Environment.MEDIA_MOUNTED)) { + textView.setText("No external storage mounted"); + } else { + File externalDir = Environment.getExternalStorageDirectory(); + File textFile = new File(externalDir.getAbsolutePath() + + File.separator + "text.txt"); + try { + writeTextFile(textFile, "This is a test. Roger"); + String text = readTextFile(textFile); + textView.setText(text); + if (!textFile.delete()) { + textView.setText("Couldn't remove temporary file"); + } + } catch (IOException e) { + textView.setText("something went wrong! " + e.getMessage()); + } + } + } + + private void writeTextFile(File file, String text) throws IOException { + BufferedWriter writer = new BufferedWriter(new FileWriter(file)); + writer.write(text); + writer.close(); + } + + private String readTextFile(File file) throws IOException { + BufferedReader reader = new BufferedReader(new FileReader(file)); + StringBuilder text = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + text.append(line); + text.append("\n"); + } + reader.close(); + return text.toString(); + } +} diff --git a/ch04-android-basics/src/com/badlogic/androidgames/FontTest.java b/ch04-android-basics/src/com/badlogic/androidgames/FontTest.java new file mode 100644 index 0000000..2a29747 --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/FontTest.java @@ -0,0 +1,54 @@ +package com.badlogic.androidgames; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.os.Bundle; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; + +public class FontTest extends Activity { + class RenderView extends View { + Paint paint; + Typeface font; + Rect bounds = new Rect(); + + public RenderView(Context context) { + super(context); + paint = new Paint(); + font = Typeface.createFromAsset(context.getAssets(), "font.ttf"); + } + + protected void onDraw(Canvas canvas) { + paint.setColor(Color.YELLOW); + paint.setTypeface(font); + paint.setTextSize(28); + paint.setTextAlign(Paint.Align.CENTER); + canvas.drawText("This is a test!", canvas.getWidth() / 2, 100, + paint); + + String text = "This is another test o_O"; + paint.setColor(Color.WHITE); + paint.setTextSize(18); + paint.setTextAlign(Paint.Align.LEFT); + paint.getTextBounds(text, 0, text.length(), bounds); + canvas.drawText(text, canvas.getWidth() - bounds.width(), 140, + paint); + invalidate(); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(new RenderView(this)); + } +} diff --git a/ch04-android-basics/src/com/badlogic/androidgames/FullScreenTest.java b/ch04-android-basics/src/com/badlogic/androidgames/FullScreenTest.java new file mode 100644 index 0000000..bb834fc --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/FullScreenTest.java @@ -0,0 +1,16 @@ +package com.badlogic.androidgames; + +import android.os.Bundle; +import android.view.Window; +import android.view.WindowManager; + +public class FullScreenTest extends SingleTouchTest { + + @Override + public void onCreate(Bundle savedInstanceState) { + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + super.onCreate(savedInstanceState); + } +} diff --git a/ch04-android-basics/src/com/badlogic/androidgames/KeyTest.java b/ch04-android-basics/src/com/badlogic/androidgames/KeyTest.java new file mode 100644 index 0000000..1ed2aee --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/KeyTest.java @@ -0,0 +1,48 @@ +package com.badlogic.androidgames; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.view.View.OnKeyListener; +import android.widget.TextView; + +public class KeyTest extends Activity implements OnKeyListener { + StringBuilder builder = new StringBuilder(); + TextView textView; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + textView = new TextView(this); + textView.setText("Press keys (if you have some)!"); + textView.setOnKeyListener(this); + textView.setFocusableInTouchMode(true); + textView.requestFocus(); + setContentView(textView); + } + + @Override + public boolean onKey(View view, int keyCode, KeyEvent event) { + builder.setLength(0); + switch (event.getAction()) { + case KeyEvent.ACTION_DOWN: + builder.append("down, "); + break; + case KeyEvent.ACTION_UP: + builder.append("up, "); + break; + } + builder.append(event.getKeyCode()); + builder.append(", "); + builder.append((char) event.getUnicodeChar()); + String text = builder.toString(); + Log.d("KeyTest", text); + textView.setText(text); + + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) + return false; + else + return true; + } +} diff --git a/ch04-android-basics/src/com/badlogic/androidgames/LifeCycleTest.java b/ch04-android-basics/src/com/badlogic/androidgames/LifeCycleTest.java new file mode 100644 index 0000000..5f87514 --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/LifeCycleTest.java @@ -0,0 +1,43 @@ +package com.badlogic.androidgames; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.widget.TextView; + +public class LifeCycleTest extends Activity { + StringBuilder builder = new StringBuilder(); + TextView textView; + + private void log(String text) { + Log.d("LifeCycleTest", text); + builder.append(text); + builder.append('\n'); + textView.setText(builder.toString()); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + textView = new TextView(this); + textView.setText(builder.toString()); + setContentView(textView); + log("created"); + } + + @Override + protected void onResume() { + super.onResume(); + log("resumed"); + } + + @Override + protected void onPause() { + super.onPause(); + log("paused"); + + if (isFinishing()) { + log("finishing"); + } + } +} \ No newline at end of file diff --git a/ch04-android-basics/src/com/badlogic/androidgames/MediaPlayerTest.java b/ch04-android-basics/src/com/badlogic/androidgames/MediaPlayerTest.java new file mode 100644 index 0000000..7122d4b --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/MediaPlayerTest.java @@ -0,0 +1,55 @@ +package com.badlogic.androidgames; + +import java.io.IOException; + +import android.app.Activity; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.os.Bundle; +import android.widget.TextView; + +public class MediaPlayerTest extends Activity { + MediaPlayer mediaPlayer; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + TextView textView = new TextView(this); + setContentView(textView); + + setVolumeControlStream(AudioManager.STREAM_MUSIC); + mediaPlayer = new MediaPlayer(); + try { + AssetManager assetManager = getAssets(); + AssetFileDescriptor descriptor = assetManager.openFd("music.ogg"); + mediaPlayer.setDataSource(descriptor.getFileDescriptor(), + descriptor.getStartOffset(), descriptor.getLength()); + mediaPlayer.prepare(); + mediaPlayer.setLooping(true); + } catch (IOException e) { + textView.setText("Couldn't load music file, " + e.getMessage()); + mediaPlayer = null; + } + } + + @Override + protected void onResume() { + super.onResume(); + if (mediaPlayer != null) { + mediaPlayer.start(); + } + } + + protected void onPause() { + super.onPause(); + if (mediaPlayer != null) { + mediaPlayer.pause(); + if (isFinishing()) { + mediaPlayer.stop(); + mediaPlayer.release(); + } + } + } +} diff --git a/ch04-android-basics/src/com/badlogic/androidgames/MultiTouchTest.java b/ch04-android-basics/src/com/badlogic/androidgames/MultiTouchTest.java new file mode 100644 index 0000000..27ef0c7 --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/MultiTouchTest.java @@ -0,0 +1,93 @@ +package com.badlogic.androidgames; + +import android.app.Activity; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnTouchListener; +import android.widget.TextView; + +public class MultiTouchTest extends Activity implements OnTouchListener { + StringBuilder builder = new StringBuilder(); + TextView textView; + float[] x = new float[10]; + float[] y = new float[10]; + boolean[] touched = new boolean[10]; + int[] id = new int[10]; + + private void updateTextView() { + builder.setLength(0); + for (int i = 0; i < 10; i++) { + builder.append(touched[i]); + builder.append(", "); + builder.append(id[i]); + builder.append(", "); + builder.append(x[i]); + builder.append(", "); + builder.append(y[i]); + builder.append("\n"); + } + textView.setText(builder.toString()); + } + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + textView = new TextView(this); + textView.setText("Touch and drag (multiple fingers supported)!"); + textView.setOnTouchListener(this); + setContentView(textView); + for (int i = 0; i < 10; i++) { + id[i] = -1; + } + updateTextView(); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + int action = event.getAction() & MotionEvent.ACTION_MASK; + int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; + int pointerCount = event.getPointerCount(); + for (int i = 0; i < 10; i++) { + if (i >= pointerCount) { + touched[i] = false; + id[i] = -1; + continue; + } + if (event.getAction() != MotionEvent.ACTION_MOVE && i != pointerIndex) { + // if it's an up/down/cancel/out event, mask the id to see if we should process it for this touch point + continue; + } + int pointerId = event.getPointerId(i); + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + touched[i] = true; + id[i] = pointerId; + x[i] = (int) event.getX(i); + y[i] = (int) event.getY(i); + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_OUTSIDE: + case MotionEvent.ACTION_CANCEL: + touched[i] = false; + id[i] = -1; + x[i] = (int) event.getX(i); + y[i] = (int) event.getY(i); + break; + + case MotionEvent.ACTION_MOVE: + touched[i] = true; + id[i] = pointerId; + x[i] = (int) event.getX(i); + y[i] = (int) event.getY(i); + break; + } + } + + updateTextView(); + return true; + } +} + diff --git a/ch04-android-basics/src/com/badlogic/androidgames/RenderViewTest.java b/ch04-android-basics/src/com/badlogic/androidgames/RenderViewTest.java new file mode 100644 index 0000000..df78280 --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/RenderViewTest.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames; + +import java.util.Random; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.os.Bundle; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; + +public class RenderViewTest extends Activity { + class RenderView extends View { + Random rand = new Random(); + + public RenderView(Context context) { + super(context); + } + + protected void onDraw(Canvas canvas) { + canvas.drawRGB(rand.nextInt(256), rand.nextInt(256), + rand.nextInt(256)); + invalidate(); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(new RenderView(this)); + } +} \ No newline at end of file diff --git a/ch04-android-basics/src/com/badlogic/androidgames/ShapeTest.java b/ch04-android-basics/src/com/badlogic/androidgames/ShapeTest.java new file mode 100644 index 0000000..f262ec4 --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/ShapeTest.java @@ -0,0 +1,47 @@ +package com.badlogic.androidgames; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.os.Bundle; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; + +public class ShapeTest extends Activity { + class RenderView extends View { + Paint paint; + + public RenderView(Context context) { + super(context); + paint = new Paint(); + } + + protected void onDraw(Canvas canvas) { + canvas.drawRGB(255, 255, 255); + paint.setColor(Color.RED); + canvas.drawLine(0, 0, canvas.getWidth()-1, canvas.getHeight()-1, paint); + + paint.setStyle(Style.STROKE); + paint.setColor(0xff00ff00); + canvas.drawCircle(canvas.getWidth() / 2, canvas.getHeight() / 2, 40, paint); + + paint.setStyle(Style.FILL); + paint.setColor(0x770000ff); + canvas.drawRect(100, 100, 200, 200, paint); + invalidate(); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(new RenderView(this)); + } +} \ No newline at end of file diff --git a/ch04-android-basics/src/com/badlogic/androidgames/SingleTouchTest.java b/ch04-android-basics/src/com/badlogic/androidgames/SingleTouchTest.java new file mode 100644 index 0000000..1e7c11f --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/SingleTouchTest.java @@ -0,0 +1,48 @@ +package com.badlogic.androidgames; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnTouchListener; +import android.widget.TextView; + +public class SingleTouchTest extends Activity implements OnTouchListener { + StringBuilder builder = new StringBuilder(); + TextView textView; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + textView = new TextView(this); + textView.setText("Touch and drag (one finger only)!"); + textView.setOnTouchListener(this); + setContentView(textView); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + builder.setLength(0); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + builder.append("down, "); + break; + case MotionEvent.ACTION_MOVE: + builder.append("move, "); + break; + case MotionEvent.ACTION_CANCEL: + builder.append("cancle, "); + break; + case MotionEvent.ACTION_UP: + builder.append("up, "); + break; + } + builder.append(event.getX()); + builder.append(", "); + builder.append(event.getY()); + String text = builder.toString(); + Log.d("TouchTest", text); + textView.setText(text); + return true; + } +} diff --git a/ch04-android-basics/src/com/badlogic/androidgames/SoundPoolTest.java b/ch04-android-basics/src/com/badlogic/androidgames/SoundPoolTest.java new file mode 100644 index 0000000..cdb84d4 --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/SoundPoolTest.java @@ -0,0 +1,50 @@ +package com.badlogic.androidgames; + +import java.io.IOException; + +import android.app.Activity; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.media.AudioManager; +import android.media.SoundPool; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnTouchListener; +import android.widget.TextView; + +public class SoundPoolTest extends Activity implements OnTouchListener { + SoundPool soundPool; + int explosionId = -1; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + TextView textView = new TextView(this); + textView.setOnTouchListener(this); + setContentView(textView); + + setVolumeControlStream(AudioManager.STREAM_MUSIC); + soundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0); + + try { + AssetManager assetManager = getAssets(); + AssetFileDescriptor descriptor = assetManager + .openFd("explosion.ogg"); + explosionId = soundPool.load(descriptor, 1); + } catch (IOException e) { + textView.setText("Couldn't load sound effect from asset, " + + e.getMessage()); + } + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_UP) { + if (explosionId != -1) { + soundPool.play(explosionId, 1, 1, 0, 0, 1); + } + } + return true; + } +} diff --git a/ch04-android-basics/src/com/badlogic/androidgames/SurfaceViewTest.java b/ch04-android-basics/src/com/badlogic/androidgames/SurfaceViewTest.java new file mode 100644 index 0000000..0e796a0 --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/SurfaceViewTest.java @@ -0,0 +1,78 @@ +package com.badlogic.androidgames; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.os.Bundle; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.Window; +import android.view.WindowManager; + +public class SurfaceViewTest extends Activity { + FastRenderView renderView; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + renderView = new FastRenderView(this); + setContentView(renderView); + } + + protected void onResume() { + super.onResume(); + renderView.resume(); + } + + protected void onPause() { + super.onPause(); + renderView.pause(); + } + + class FastRenderView extends SurfaceView implements Runnable { + Thread renderThread = null; + SurfaceHolder holder; + volatile boolean running = false; + + public FastRenderView(Context context) { + super(context); + holder = getHolder(); + } + + public void resume() { + running = true; + renderThread = new Thread(this); + renderThread.start(); + } + + public void pause() { + running = false; + while(true) { + try { + renderThread.join(); + break; + } catch (InterruptedException e) { + // retry + } + } + renderThread = null; + } + + public void run() { + while(running) { + if(!holder.getSurface().isValid()) + continue; + + Canvas canvas = holder.lockCanvas(); + drawSurface(canvas); + holder.unlockCanvasAndPost(canvas); + } + } + + private void drawSurface(Canvas canvas) { + canvas.drawRGB(255, 0, 0); + } + } +} \ No newline at end of file diff --git a/ch04-android-basics/src/com/badlogic/androidgames/WakeLockTest.java b/ch04-android-basics/src/com/badlogic/androidgames/WakeLockTest.java new file mode 100644 index 0000000..89fe31a --- /dev/null +++ b/ch04-android-basics/src/com/badlogic/androidgames/WakeLockTest.java @@ -0,0 +1,28 @@ +package com.badlogic.androidgames; + +import android.content.Context; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; + +public class WakeLockTest extends FullScreenTest { + WakeLock wakeLock; + + @Override + public void onCreate(Bundle savedInstanceState) { + PowerManager powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE); + wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "My Lock"); + super.onCreate(savedInstanceState); + } + + @Override + public void onResume() { + wakeLock.acquire(); + super.onResume(); + } + + public void onPause() { + wakeLock.release(); + super.onPause(); + } +} diff --git a/ch06-mr-nom/.classpath b/ch06-mr-nom/.classpath new file mode 100644 index 0000000..609aa00 --- /dev/null +++ b/ch06-mr-nom/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ch06-mr-nom/.project b/ch06-mr-nom/.project new file mode 100644 index 0000000..79b48a2 --- /dev/null +++ b/ch06-mr-nom/.project @@ -0,0 +1,33 @@ + + + ch06-mr-nom + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/ch06-mr-nom/AndroidManifest.xml b/ch06-mr-nom/AndroidManifest.xml new file mode 100644 index 0000000..5fb3ef7 --- /dev/null +++ b/ch06-mr-nom/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch06-mr-nom/assets/background.png b/ch06-mr-nom/assets/background.png new file mode 100644 index 0000000..0662e1f Binary files /dev/null and b/ch06-mr-nom/assets/background.png differ diff --git a/ch06-mr-nom/assets/bitten.ogg b/ch06-mr-nom/assets/bitten.ogg new file mode 100644 index 0000000..070433b Binary files /dev/null and b/ch06-mr-nom/assets/bitten.ogg differ diff --git a/ch06-mr-nom/assets/bobargb8888.png b/ch06-mr-nom/assets/bobargb8888.png new file mode 100644 index 0000000..bd0294d Binary files /dev/null and b/ch06-mr-nom/assets/bobargb8888.png differ diff --git a/ch06-mr-nom/assets/bobrgb888.png b/ch06-mr-nom/assets/bobrgb888.png new file mode 100644 index 0000000..987fe37 Binary files /dev/null and b/ch06-mr-nom/assets/bobrgb888.png differ diff --git a/ch06-mr-nom/assets/buttons.png b/ch06-mr-nom/assets/buttons.png new file mode 100644 index 0000000..c0d66d1 Binary files /dev/null and b/ch06-mr-nom/assets/buttons.png differ diff --git a/ch06-mr-nom/assets/click.ogg b/ch06-mr-nom/assets/click.ogg new file mode 100644 index 0000000..23e051b Binary files /dev/null and b/ch06-mr-nom/assets/click.ogg differ diff --git a/ch06-mr-nom/assets/eat.ogg b/ch06-mr-nom/assets/eat.ogg new file mode 100644 index 0000000..d962e80 Binary files /dev/null and b/ch06-mr-nom/assets/eat.ogg differ diff --git a/ch06-mr-nom/assets/gameover.png b/ch06-mr-nom/assets/gameover.png new file mode 100644 index 0000000..e194bc9 Binary files /dev/null and b/ch06-mr-nom/assets/gameover.png differ diff --git a/ch06-mr-nom/assets/headdown.png b/ch06-mr-nom/assets/headdown.png new file mode 100644 index 0000000..35e6894 Binary files /dev/null and b/ch06-mr-nom/assets/headdown.png differ diff --git a/ch06-mr-nom/assets/headleft.png b/ch06-mr-nom/assets/headleft.png new file mode 100644 index 0000000..6ea9eae Binary files /dev/null and b/ch06-mr-nom/assets/headleft.png differ diff --git a/ch06-mr-nom/assets/headright.png b/ch06-mr-nom/assets/headright.png new file mode 100644 index 0000000..0559af1 Binary files /dev/null and b/ch06-mr-nom/assets/headright.png differ diff --git a/ch06-mr-nom/assets/headup.png b/ch06-mr-nom/assets/headup.png new file mode 100644 index 0000000..bea354a Binary files /dev/null and b/ch06-mr-nom/assets/headup.png differ diff --git a/ch06-mr-nom/assets/help1.png b/ch06-mr-nom/assets/help1.png new file mode 100644 index 0000000..8dc9e47 Binary files /dev/null and b/ch06-mr-nom/assets/help1.png differ diff --git a/ch06-mr-nom/assets/help2.png b/ch06-mr-nom/assets/help2.png new file mode 100644 index 0000000..a6b31c9 Binary files /dev/null and b/ch06-mr-nom/assets/help2.png differ diff --git a/ch06-mr-nom/assets/help3.png b/ch06-mr-nom/assets/help3.png new file mode 100644 index 0000000..1f82c5b Binary files /dev/null and b/ch06-mr-nom/assets/help3.png differ diff --git a/ch06-mr-nom/assets/logo.png b/ch06-mr-nom/assets/logo.png new file mode 100644 index 0000000..f370455 Binary files /dev/null and b/ch06-mr-nom/assets/logo.png differ diff --git a/ch06-mr-nom/assets/mainmenu.png b/ch06-mr-nom/assets/mainmenu.png new file mode 100644 index 0000000..0adfe7e Binary files /dev/null and b/ch06-mr-nom/assets/mainmenu.png differ diff --git a/ch06-mr-nom/assets/music.ogg b/ch06-mr-nom/assets/music.ogg new file mode 100644 index 0000000..6ee14c5 Binary files /dev/null and b/ch06-mr-nom/assets/music.ogg differ diff --git a/ch06-mr-nom/assets/numbers.png b/ch06-mr-nom/assets/numbers.png new file mode 100644 index 0000000..a259457 Binary files /dev/null and b/ch06-mr-nom/assets/numbers.png differ diff --git a/ch06-mr-nom/assets/pausemenu.png b/ch06-mr-nom/assets/pausemenu.png new file mode 100644 index 0000000..45a5c8b Binary files /dev/null and b/ch06-mr-nom/assets/pausemenu.png differ diff --git a/ch06-mr-nom/assets/ready.png b/ch06-mr-nom/assets/ready.png new file mode 100644 index 0000000..12878cc Binary files /dev/null and b/ch06-mr-nom/assets/ready.png differ diff --git a/ch06-mr-nom/assets/stain1.png b/ch06-mr-nom/assets/stain1.png new file mode 100644 index 0000000..43a53f8 Binary files /dev/null and b/ch06-mr-nom/assets/stain1.png differ diff --git a/ch06-mr-nom/assets/stain2.png b/ch06-mr-nom/assets/stain2.png new file mode 100644 index 0000000..f4b25b7 Binary files /dev/null and b/ch06-mr-nom/assets/stain2.png differ diff --git a/ch06-mr-nom/assets/stain3.png b/ch06-mr-nom/assets/stain3.png new file mode 100644 index 0000000..ef4a0b1 Binary files /dev/null and b/ch06-mr-nom/assets/stain3.png differ diff --git a/ch06-mr-nom/assets/tail.png b/ch06-mr-nom/assets/tail.png new file mode 100644 index 0000000..e6c5e72 Binary files /dev/null and b/ch06-mr-nom/assets/tail.png differ diff --git a/ch06-mr-nom/assets/test.txt b/ch06-mr-nom/assets/test.txt new file mode 100644 index 0000000..e88d295 --- /dev/null +++ b/ch06-mr-nom/assets/test.txt @@ -0,0 +1 @@ +test tests \ No newline at end of file diff --git a/ch06-mr-nom/default.properties b/ch06-mr-nom/default.properties new file mode 100644 index 0000000..0cdab95 --- /dev/null +++ b/ch06-mr-nom/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-12 diff --git a/ch06-mr-nom/res/drawable-hdpi/icon.png b/ch06-mr-nom/res/drawable-hdpi/icon.png new file mode 100644 index 0000000..0a77809 Binary files /dev/null and b/ch06-mr-nom/res/drawable-hdpi/icon.png differ diff --git a/ch06-mr-nom/res/drawable-ldpi/icon.png b/ch06-mr-nom/res/drawable-ldpi/icon.png new file mode 100644 index 0000000..bc76a5d Binary files /dev/null and b/ch06-mr-nom/res/drawable-ldpi/icon.png differ diff --git a/ch06-mr-nom/res/drawable-mdpi/icon.png b/ch06-mr-nom/res/drawable-mdpi/icon.png new file mode 100644 index 0000000..856e2c1 Binary files /dev/null and b/ch06-mr-nom/res/drawable-mdpi/icon.png differ diff --git a/ch06-mr-nom/res/drawable/icon.png b/ch06-mr-nom/res/drawable/icon.png new file mode 100644 index 0000000..1cf782f Binary files /dev/null and b/ch06-mr-nom/res/drawable/icon.png differ diff --git a/ch06-mr-nom/res/layout/main.xml b/ch06-mr-nom/res/layout/main.xml new file mode 100644 index 0000000..3a5f117 --- /dev/null +++ b/ch06-mr-nom/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/ch06-mr-nom/res/values/strings.xml b/ch06-mr-nom/res/values/strings.xml new file mode 100644 index 0000000..3ff66b5 --- /dev/null +++ b/ch06-mr-nom/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Hello World, AndroidGameFrameWork! + AndroidGameFrameWork + diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/Audio.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Audio.java new file mode 100644 index 0000000..d30d722 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Audio.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Audio { + public Music newMusic(String filename); + + public Sound newSound(String filename); +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/Color.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Color.java new file mode 100644 index 0000000..57d68e5 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Color.java @@ -0,0 +1,10 @@ +package com.badlogic.androidgames.framework; + +public class Color { + public static int convert (int r, int g, int b, int a) { + return ((a & 0xff) << 24) | + ((r & 0xff) << 16) | + ((g & 0xff) << 8) | + ((b & 0xff)); + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/FileIO.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/FileIO.java new file mode 100644 index 0000000..679243a --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/FileIO.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import android.content.SharedPreferences; + +public interface FileIO { + public InputStream readAsset(String fileName) throws IOException; + + public InputStream readFile(String fileName) throws IOException; + + public OutputStream writeFile(String fileName) throws IOException; + + public SharedPreferences getPreferences(); +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/Game.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Game.java new file mode 100644 index 0000000..fb5c5aa --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Game.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework; + +public interface Game { + public Input getInput(); + + public FileIO getFileIO(); + + public Graphics getGraphics(); + + public Audio getAudio(); + + public void setScreen(Screen screen); + + public Screen getCurrentScreen(); + + public Screen getStartScreen(); +} \ No newline at end of file diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/Graphics.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Graphics.java new file mode 100644 index 0000000..c7b8f45 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Graphics.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework; + +public interface Graphics { + public static enum PixmapFormat { + ARGB8888, ARGB4444, RGB565 + } + + public Pixmap newPixmap(String fileName, PixmapFormat format); + + public void clear(int color); + + public void drawPixel(int x, int y, int color); + + public void drawLine(int x, int y, int x2, int y2, int color); + + public void drawRect(int x, int y, int width, int height, int color); + + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight); + + public void drawPixmap(Pixmap pixmap, int x, int y); + + public int getWidth(); + + public int getHeight(); +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/Input.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Input.java new file mode 100644 index 0000000..0893d62 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Input.java @@ -0,0 +1,70 @@ +package com.badlogic.androidgames.framework; + +import java.util.List; + +public interface Input { + public static class KeyEvent { + public static final int KEY_DOWN = 0; + public static final int KEY_UP = 1; + + public int type; + public int keyCode; + public char keyChar; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == KEY_DOWN) + builder.append("key down, "); + else + builder.append("key up, "); + builder.append(keyCode); + builder.append(","); + builder.append(keyChar); + return builder.toString(); + } + } + + public static class TouchEvent { + public static final int TOUCH_DOWN = 0; + public static final int TOUCH_UP = 1; + public static final int TOUCH_DRAGGED = 2; + + public int type; + public int x, y; + public int pointer; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == TOUCH_DOWN) + builder.append("touch down, "); + else if (type == TOUCH_DRAGGED) + builder.append("touch dragged, "); + else + builder.append("touch up, "); + builder.append(pointer); + builder.append(","); + builder.append(x); + builder.append(","); + builder.append(y); + return builder.toString(); + } + } + + public boolean isKeyPressed(int keyCode); + + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public float getAccelX(); + + public float getAccelY(); + + public float getAccelZ(); + + public List getKeyEvents(); + + public List getTouchEvents(); +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/Music.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Music.java new file mode 100644 index 0000000..1b95a1e --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Music.java @@ -0,0 +1,21 @@ +package com.badlogic.androidgames.framework; + +public interface Music { + public void play(); + + public void stop(); + + public void pause(); + + public void setLooping(boolean looping); + + public void setVolume(float volume); + + public boolean isPlaying(); + + public boolean isStopped(); + + public boolean isLooping(); + + public void dispose(); +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/Pixmap.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Pixmap.java new file mode 100644 index 0000000..4b1eec9 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Pixmap.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; + +public interface Pixmap { + public int getWidth(); + + public int getHeight(); + + public PixmapFormat getFormat(); + + public void dispose(); +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/Pool.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Pool.java new file mode 100644 index 0000000..d0b7cec --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Pool.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework; + +import java.util.ArrayList; +import java.util.List; + +public class Pool { + public interface PoolObjectFactory { + public T createObject(); + } + + private final List freeObjects; + private final PoolObjectFactory factory; + private final int maxSize; + + public Pool(PoolObjectFactory factory, int maxSize) { + this.factory = factory; + this.maxSize = maxSize; + this.freeObjects = new ArrayList(maxSize); + } + + public T newObject() { + T object = null; + + if (freeObjects.size() == 0) + object = factory.createObject(); + else + object = freeObjects.remove(freeObjects.size() - 1); + + return object; + } + + public void free(T object) { + if (freeObjects.size() < maxSize) + freeObjects.add(object); + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/Screen.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Screen.java new file mode 100644 index 0000000..40a98b9 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Screen.java @@ -0,0 +1,19 @@ +package com.badlogic.androidgames.framework; + +public abstract class Screen { + protected final Game game; + + public Screen(Game game) { + this.game = game; + } + + public abstract void update(float deltaTime); + + public abstract void present(float deltaTime); + + public abstract void pause(); + + public abstract void resume(); + + public abstract void dispose(); +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/Sound.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Sound.java new file mode 100644 index 0000000..3f49835 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/Sound.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Sound { + public void play(float volume); + + public void dispose(); +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/TestScreen.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/TestScreen.java new file mode 100644 index 0000000..29f18cc --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/TestScreen.java @@ -0,0 +1,112 @@ +package com.badlogic.androidgames.framework; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.List; + +import android.graphics.Color; +import android.util.Log; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; +import com.badlogic.androidgames.framework.Input.KeyEvent; +import com.badlogic.androidgames.framework.Input.TouchEvent; + +public class TestScreen extends Screen { + long startTime = System.nanoTime(); + int frames; + Pixmap bob; + Pixmap bobAlpha; + Sound sound; + Music music; + + public TestScreen(Game game) { + super(game); + bob = game.getGraphics().newPixmap("bobrgb888.png", PixmapFormat.RGB565); + bobAlpha = game.getGraphics().newPixmap("bobargb8888.png", PixmapFormat.ARGB4444); + music = game.getAudio().newMusic("music.ogg"); + music.setLooping(true); + music.setVolume(0.5f); + music.play(); + sound = game.getAudio().newSound("music.ogg"); + + try { + BufferedReader in = new BufferedReader(new InputStreamReader(game.getFileIO().readAsset("test.txt"))); + String text = in.readLine(); + in.close(); + + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(game.getFileIO().writeFile("test.txt"))); + out.write("This is a freaking test"); + out.close(); + + in = new BufferedReader(new InputStreamReader(game.getFileIO().readFile("test.txt"))); + String text2 = in.readLine(); + in.close(); + + Log.d("MrNom", text + ", " + text2 ); + } catch(Exception ex) { + ex.printStackTrace(); + } + } + + @Override + public void update(float deltaTime) { + } + + @Override + public void present(float deltaTime) { + Graphics g = game.getGraphics(); + Input inp = game.getInput(); + g.clear(Color.RED); + g.drawLine(0,0,320, 480, Color.BLUE); + g.drawRect(20,20,100,100, Color.GREEN); + g.drawPixmap(bob, 100, 100); + g.drawPixmap(bobAlpha, 100, 200); + g.drawPixmap(bob, 200, 200, 0, 0, 64, 64); + for(int i=0; i < 2; i++) { + if(inp.isTouchDown(i)) { + g.drawPixmap(bob, inp.getTouchX(i), inp.getTouchY(i), 0, 0, 64, 64); + } + } + + g.drawPixmap(bob, (int)(inp.getAccelX() * 10) + 160 - 16, (int)(inp.getAccelY() * 10) + 240 - 16, 0, 0, 32, 32 ); + + List keyEvents = inp.getKeyEvents(); + int len = keyEvents.size(); + for(int i = 0; i < len; i++) { + Log.d("MrNom", keyEvents.get(i).toString()); + } + + List touchEvents = inp.getTouchEvents(); + len = touchEvents.size(); + for(int i = 0; i < len; i++) { + Log.d("MrNom", touchEvents.get(i).toString()); + if(touchEvents.get(i).type == TouchEvent.TOUCH_UP) + sound.play(1); + } + + frames++; + if(System.nanoTime() - startTime > 1000000000l) { + Log.d("MrNom", "fps: " + frames + ", delta: " + deltaTime); + frames = 0; + startTime = System.nanoTime(); + } + } + + @Override + public void pause() { + Log.d("MrNom", "pause"); + } + + @Override + public void resume() { + Log.d("MrNom", "resume"); + } + + @Override + public void dispose() { + Log.d("MrNom", "dispose"); + music.dispose(); + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java new file mode 100644 index 0000000..4820c5a --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java @@ -0,0 +1,48 @@ +package com.badlogic.androidgames.framework.impl; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + +public class AccelerometerHandler implements SensorEventListener { + float accelX; + float accelY; + float accelZ; + + public AccelerometerHandler(Context context) { + SensorManager manager = (SensorManager) context + .getSystemService(Context.SENSOR_SERVICE); + if (manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() != 0) { + Sensor accelerometer = manager.getSensorList( + Sensor.TYPE_ACCELEROMETER).get(0); + manager.registerListener(this, accelerometer, + SensorManager.SENSOR_DELAY_GAME); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // nothing to do here + } + + @Override + public void onSensorChanged(SensorEvent event) { + accelX = event.values[0]; + accelY = event.values[1]; + accelZ = event.values[2]; + } + + public float getAccelX() { + return accelX; + } + + public float getAccelY() { + return accelY; + } + + public float getAccelZ() { + return accelZ; + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java new file mode 100644 index 0000000..8a24a03 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java @@ -0,0 +1,45 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; + +import android.app.Activity; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.media.AudioManager; +import android.media.SoundPool; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.Music; +import com.badlogic.androidgames.framework.Sound; + +public class AndroidAudio implements Audio { + AssetManager assets; + SoundPool soundPool; + + public AndroidAudio(Activity activity) { + activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); + this.assets = activity.getAssets(); + this.soundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0); + } + + @Override + public Music newMusic(String filename) { + try { + AssetFileDescriptor assetDescriptor = assets.openFd(filename); + return new AndroidMusic(assetDescriptor); + } catch (IOException e) { + throw new RuntimeException("Couldn't load music '" + filename + "'"); + } + } + + @Override + public Sound newSound(String filename) { + try { + AssetFileDescriptor assetDescriptor = assets.openFd(filename); + int soundId = soundPool.load(assetDescriptor, 0); + return new AndroidSound(soundPool, soundId); + } catch (IOException e) { + throw new RuntimeException("Couldn't load sound '" + filename + "'"); + } + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java new file mode 100644 index 0000000..6171d90 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java @@ -0,0 +1,60 @@ +package com.badlogic.androidgames.framework.impl; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class AndroidFastRenderView extends SurfaceView implements Runnable { + AndroidGame game; + Bitmap framebuffer; + Thread renderThread = null; + SurfaceHolder holder; + volatile boolean running = false; + + public AndroidFastRenderView(AndroidGame game, Bitmap framebuffer) { + super(game); + this.game = game; + this.framebuffer = framebuffer; + this.holder = getHolder(); + } + + public void resume() { + running = true; + renderThread = new Thread(this); + renderThread.start(); + } + + public void run() { + Rect dstRect = new Rect(); + long startTime = System.nanoTime(); + while(running) { + if(!holder.getSurface().isValid()) + continue; + + float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; + startTime = System.nanoTime(); + + game.getCurrentScreen().update(deltaTime); + game.getCurrentScreen().present(deltaTime); + + Canvas canvas = holder.lockCanvas(); + canvas.getClipBounds(dstRect); + canvas.drawBitmap(framebuffer, null, dstRect, null); + holder.unlockCanvasAndPost(canvas); + } + } + + public void pause() { + running = false; + while(true) { + try { + renderThread.join(); + break; + } catch (InterruptedException e) { + // retry + } + } + } +} \ No newline at end of file diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java new file mode 100644 index 0000000..7401041 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java @@ -0,0 +1,48 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.AssetManager; +import android.os.Environment; +import android.preference.PreferenceManager; + +import com.badlogic.androidgames.framework.FileIO; + +public class AndroidFileIO implements FileIO { + Context context; + AssetManager assets; + String externalStoragePath; + + public AndroidFileIO(Context context) { + this.context = context; + this.assets = context.getAssets(); + this.externalStoragePath = Environment.getExternalStorageDirectory() + .getAbsolutePath() + File.separator; + } + + @Override + public InputStream readAsset(String fileName) throws IOException { + return assets.open(fileName); + } + + @Override + public InputStream readFile(String fileName) throws IOException { + return new FileInputStream(externalStoragePath + fileName); + } + + @Override + public OutputStream writeFile(String fileName) throws IOException { + return new FileOutputStream(externalStoragePath + fileName); + } + + public SharedPreferences getPreferences() { + return PreferenceManager.getDefaultSharedPreferences(context); + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidGame.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidGame.java new file mode 100644 index 0000000..4424350 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidGame.java @@ -0,0 +1,115 @@ +package com.badlogic.androidgames.framework.impl; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.view.Window; +import android.view.WindowManager; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input; +import com.badlogic.androidgames.framework.Screen; + +public abstract class AndroidGame extends Activity implements Game { + AndroidFastRenderView renderView; + Graphics graphics; + Audio audio; + Input input; + FileIO fileIO; + Screen screen; + WakeLock wakeLock; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + + boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + int frameBufferWidth = isLandscape ? 480 : 320; + int frameBufferHeight = isLandscape ? 320 : 480; + Bitmap frameBuffer = Bitmap.createBitmap(frameBufferWidth, + frameBufferHeight, Config.RGB_565); + + float scaleX = (float) frameBufferWidth + / getWindowManager().getDefaultDisplay().getWidth(); + float scaleY = (float) frameBufferHeight + / getWindowManager().getDefaultDisplay().getHeight(); + + renderView = new AndroidFastRenderView(this, frameBuffer); + graphics = new AndroidGraphics(getAssets(), frameBuffer); + fileIO = new AndroidFileIO(this); + audio = new AndroidAudio(this); + input = new AndroidInput(this, renderView, scaleX, scaleY); + screen = getStartScreen(); + setContentView(renderView); + + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GLGame"); + } + + @Override + public void onResume() { + super.onResume(); + wakeLock.acquire(); + screen.resume(); + renderView.resume(); + } + + @Override + public void onPause() { + super.onPause(); + wakeLock.release(); + renderView.pause(); + screen.pause(); + + if (isFinishing()) + screen.dispose(); + } + + @Override + public Input getInput() { + return input; + } + + @Override + public FileIO getFileIO() { + return fileIO; + } + + @Override + public Graphics getGraphics() { + return graphics; + } + + @Override + public Audio getAudio() { + return audio; + } + + @Override + public void setScreen(Screen screen) { + if (screen == null) + throw new IllegalArgumentException("Screen must not be null"); + + this.screen.pause(); + this.screen.dispose(); + screen.resume(); + screen.update(0); + this.screen = screen; + } + + public Screen getCurrentScreen() { + return screen; + } +} \ No newline at end of file diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java new file mode 100644 index 0000000..ade52b5 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java @@ -0,0 +1,133 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; +import java.io.InputStream; + +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.BitmapFactory.Options; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Rect; + +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Pixmap; + +public class AndroidGraphics implements Graphics { + AssetManager assets; + Bitmap frameBuffer; + Canvas canvas; + Paint paint; + Rect srcRect = new Rect(); + Rect dstRect = new Rect(); + + public AndroidGraphics(AssetManager assets, Bitmap frameBuffer) { + this.assets = assets; + this.frameBuffer = frameBuffer; + this.canvas = new Canvas(frameBuffer); + this.paint = new Paint(); + } + + @Override + public Pixmap newPixmap(String fileName, PixmapFormat format) { + Config config = null; + if (format == PixmapFormat.RGB565) + config = Config.RGB_565; + else if (format == PixmapFormat.ARGB4444) + config = Config.ARGB_4444; + else + config = Config.ARGB_8888; + + Options options = new Options(); + options.inPreferredConfig = config; + + InputStream in = null; + Bitmap bitmap = null; + try { + in = assets.open(fileName); + bitmap = BitmapFactory.decodeStream(in, null, options); + if (bitmap == null) + throw new RuntimeException("Couldn't load bitmap from asset '" + + fileName + "'"); + } catch (IOException e) { + throw new RuntimeException("Couldn't load bitmap from asset '" + + fileName + "'"); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + + if (bitmap.getConfig() == Config.RGB_565) + format = PixmapFormat.RGB565; + else if (bitmap.getConfig() == Config.ARGB_4444) + format = PixmapFormat.ARGB4444; + else + format = PixmapFormat.ARGB8888; + + return new AndroidPixmap(bitmap, format); + } + + @Override + public void clear(int color) { + canvas.drawRGB((color & 0xff0000) >> 16, (color & 0xff00) >> 8, + (color & 0xff)); + } + + @Override + public void drawPixel(int x, int y, int color) { + paint.setColor(color); + canvas.drawPoint(x, y, paint); + } + + @Override + public void drawLine(int x, int y, int x2, int y2, int color) { + paint.setColor(color); + canvas.drawLine(x, y, x2, y2, paint); + } + + @Override + public void drawRect(int x, int y, int width, int height, int color) { + paint.setColor(color); + paint.setStyle(Style.FILL); + canvas.drawRect(x, y, x + width - 1, y + height - 1, paint); + } + + @Override + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight) { + srcRect.left = srcX; + srcRect.top = srcY; + srcRect.right = srcX + srcWidth - 1; + srcRect.bottom = srcY + srcHeight - 1; + + dstRect.left = x; + dstRect.top = y; + dstRect.right = x + srcWidth - 1; + dstRect.bottom = y + srcHeight - 1; + + canvas.drawBitmap(((AndroidPixmap) pixmap).bitmap, srcRect, dstRect, + null); + } + + @Override + public void drawPixmap(Pixmap pixmap, int x, int y) { + canvas.drawBitmap(((AndroidPixmap)pixmap).bitmap, x, y, null); + } + + @Override + public int getWidth() { + return frameBuffer.getWidth(); + } + + @Override + public int getHeight() { + return frameBuffer.getHeight(); + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidInput.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidInput.java new file mode 100644 index 0000000..11f5863 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidInput.java @@ -0,0 +1,69 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.List; + +import android.content.Context; +import android.os.Build.VERSION; +import android.view.View; + +import com.badlogic.androidgames.framework.Input; + +public class AndroidInput implements Input { + AccelerometerHandler accelHandler; + KeyboardHandler keyHandler; + TouchHandler touchHandler; + + public AndroidInput(Context context, View view, float scaleX, float scaleY) { + accelHandler = new AccelerometerHandler(context); + keyHandler = new KeyboardHandler(view); + if(Integer.parseInt(VERSION.SDK) < 5) + touchHandler = new SingleTouchHandler(view, scaleX, scaleY); + else + touchHandler = new MultiTouchHandler(view, scaleX, scaleY); + } + + @Override + public boolean isKeyPressed(int keyCode) { + return keyHandler.isKeyPressed(keyCode); + } + + @Override + public boolean isTouchDown(int pointer) { + return touchHandler.isTouchDown(pointer); + } + + @Override + public int getTouchX(int pointer) { + return touchHandler.getTouchX(pointer); + } + + @Override + public int getTouchY(int pointer) { + return touchHandler.getTouchY(pointer); + } + + @Override + public float getAccelX() { + return accelHandler.getAccelX(); + } + + @Override + public float getAccelY() { + return accelHandler.getAccelY(); + } + + @Override + public float getAccelZ() { + return accelHandler.getAccelZ(); + } + + @Override + public List getTouchEvents() { + return touchHandler.getTouchEvents(); + } + + @Override + public List getKeyEvents() { + return keyHandler.getKeyEvents(); + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java new file mode 100644 index 0000000..fae8e32 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java @@ -0,0 +1,99 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; + +import android.content.res.AssetFileDescriptor; +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnCompletionListener; + +import com.badlogic.androidgames.framework.Music; + +public class AndroidMusic implements Music, OnCompletionListener { + MediaPlayer mediaPlayer; + boolean isPrepared = false; + + public AndroidMusic(AssetFileDescriptor assetDescriptor) { + mediaPlayer = new MediaPlayer(); + try { + mediaPlayer.setDataSource(assetDescriptor.getFileDescriptor(), + assetDescriptor.getStartOffset(), + assetDescriptor.getLength()); + mediaPlayer.prepare(); + isPrepared = true; + mediaPlayer.setOnCompletionListener(this); + } catch (Exception e) { + throw new RuntimeException("Couldn't load music"); + } + } + + @Override + public void dispose() { + if (mediaPlayer.isPlaying()) + mediaPlayer.stop(); + mediaPlayer.release(); + } + + @Override + public boolean isLooping() { + return mediaPlayer.isLooping(); + } + + @Override + public boolean isPlaying() { + return mediaPlayer.isPlaying(); + } + + @Override + public boolean isStopped() { + return !isPrepared; + } + + @Override + public void pause() { + if (mediaPlayer.isPlaying()) + mediaPlayer.pause(); + } + + @Override + public void play() { + if (mediaPlayer.isPlaying()) + return; + + try { + synchronized (this) { + if (!isPrepared) + mediaPlayer.prepare(); + mediaPlayer.start(); + } + } catch (IllegalStateException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void setLooping(boolean isLooping) { + mediaPlayer.setLooping(isLooping); + } + + @Override + public void setVolume(float volume) { + mediaPlayer.setVolume(volume, volume); + } + + @Override + public void stop() { + mediaPlayer.stop(); + synchronized (this) { + isPrepared = false; + } + } + + @Override + public void onCompletion(MediaPlayer player) { + synchronized (this) { + isPrepared = false; + } + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java new file mode 100644 index 0000000..9de5021 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework.impl; + +import android.graphics.Bitmap; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; +import com.badlogic.androidgames.framework.Pixmap; + +public class AndroidPixmap implements Pixmap { + Bitmap bitmap; + PixmapFormat format; + + public AndroidPixmap(Bitmap bitmap, PixmapFormat format) { + this.bitmap = bitmap; + this.format = format; + } + + @Override + public int getWidth() { + return bitmap.getWidth(); + } + + @Override + public int getHeight() { + return bitmap.getHeight(); + } + + @Override + public PixmapFormat getFormat() { + return format; + } + + @Override + public void dispose() { + bitmap.recycle(); + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidSound.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidSound.java new file mode 100644 index 0000000..2af9533 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/AndroidSound.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework.impl; + +import android.media.SoundPool; + +import com.badlogic.androidgames.framework.Sound; + +public class AndroidSound implements Sound { + int soundId; + SoundPool soundPool; + + public AndroidSound(SoundPool soundPool, int soundId) { + this.soundId = soundId; + this.soundPool = soundPool; + } + + @Override + public void play(float volume) { + soundPool.play(soundId, volume, volume, 0, 0, 1); + } + + @Override + public void dispose() { + soundPool.unload(soundId); + } + +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java new file mode 100644 index 0000000..f70d3e9 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java @@ -0,0 +1,73 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.View; +import android.view.View.OnKeyListener; + +import com.badlogic.androidgames.framework.Input.KeyEvent; +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class KeyboardHandler implements OnKeyListener { + boolean[] pressedKeys = new boolean[128]; + Pool keyEventPool; + List keyEventsBuffer = new ArrayList(); + List keyEvents = new ArrayList(); + + public KeyboardHandler(View view) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public KeyEvent createObject() { + return new KeyEvent(); + } + }; + keyEventPool = new Pool(factory, 100); + view.setOnKeyListener(this); + view.setFocusableInTouchMode(true); + view.requestFocus(); + } + + @Override + public boolean onKey(View v, int keyCode, android.view.KeyEvent event) { + if (event.getAction() == android.view.KeyEvent.ACTION_MULTIPLE) + return false; + + synchronized (this) { + KeyEvent keyEvent = keyEventPool.newObject(); + keyEvent.keyCode = keyCode; + keyEvent.keyChar = (char) event.getUnicodeChar(); + if (event.getAction() == android.view.KeyEvent.ACTION_DOWN) { + keyEvent.type = KeyEvent.KEY_DOWN; + if(keyCode >= 0 && keyCode < 128) + pressedKeys[keyCode] = true; + } + if (event.getAction() == android.view.KeyEvent.ACTION_UP) { + keyEvent.type = KeyEvent.KEY_UP; + if(keyCode >= 0 && keyCode < 128) + pressedKeys[keyCode] = false; + } + keyEventsBuffer.add(keyEvent); + } + return false; + } + + public boolean isKeyPressed(int keyCode) { + if (keyCode < 0 || keyCode > 127) + return false; + return pressedKeys[keyCode]; + } + + public List getKeyEvents() { + synchronized (this) { + int len = keyEvents.size(); + for (int i = 0; i < len; i++) + keyEventPool.free(keyEvents.get(i)); + keyEvents.clear(); + keyEvents.addAll(keyEventsBuffer); + keyEventsBuffer.clear(); + return keyEvents; + } + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java new file mode 100644 index 0000000..4b909a0 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java @@ -0,0 +1,156 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.MotionEvent; +import android.view.View; + +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class MultiTouchHandler implements TouchHandler { + private static final int MAX_TOUCHPOINTS = 10; + + boolean[] isTouched = new boolean[MAX_TOUCHPOINTS]; + int[] touchX = new int[MAX_TOUCHPOINTS]; + int[] touchY = new int[MAX_TOUCHPOINTS]; + int[] id = new int[MAX_TOUCHPOINTS]; + Pool touchEventPool; + List touchEvents = new ArrayList(); + List touchEventsBuffer = new ArrayList(); + float scaleX; + float scaleY; + + public MultiTouchHandler(View view, float scaleX, float scaleY) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }; + touchEventPool = new Pool(factory, 100); + view.setOnTouchListener(this); + + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + synchronized (this) { + int action = event.getAction() & MotionEvent.ACTION_MASK; + int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; + int pointerCount = event.getPointerCount(); + TouchEvent touchEvent; + for (int i = 0; i < MAX_TOUCHPOINTS; i++) { + if (i >= pointerCount) { + isTouched[i] = false; + id[i] = -1; + continue; + } + int pointerId = event.getPointerId(i); + if (event.getAction() != MotionEvent.ACTION_MOVE && i != pointerIndex) { + // if it's an up/down/cancel/out event, mask the id to see if we should process it for this touch + // point + continue; + } + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_DOWN; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[i] = (int) (event.getX(i) * scaleX); + touchEvent.y = touchY[i] = (int) (event.getY(i) * scaleY); + isTouched[i] = true; + id[i] = pointerId; + touchEventsBuffer.add(touchEvent); + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_CANCEL: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_UP; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[i] = (int) (event.getX(i) * scaleX); + touchEvent.y = touchY[i] = (int) (event.getY(i) * scaleY); + isTouched[i] = false; + id[i] = -1; + touchEventsBuffer.add(touchEvent); + break; + + case MotionEvent.ACTION_MOVE: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_DRAGGED; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[i] = (int) (event.getX(i) * scaleX); + touchEvent.y = touchY[i] = (int) (event.getY(i) * scaleY); + isTouched[i] = true; + id[i] = pointerId; + touchEventsBuffer.add(touchEvent); + break; + } + } + return true; + } + } + + @Override + public boolean isTouchDown(int pointer) { + synchronized (this) { + int index = getIndex(pointer); + if (index < 0 || index >= MAX_TOUCHPOINTS) + return false; + else + return isTouched[index]; + } + } + + @Override + public int getTouchX(int pointer) { + synchronized (this) { + int index = getIndex(pointer); + if (index < 0 || index >= MAX_TOUCHPOINTS) + return 0; + else + return touchX[index]; + } + } + + @Override + public int getTouchY(int pointer) { + synchronized (this) { + int index = getIndex(pointer); + if (index < 0 || index >= MAX_TOUCHPOINTS) + return 0; + else + return touchY[index]; + } + } + + @Override + public List getTouchEvents() { + synchronized (this) { + int len = touchEvents.size(); + for (int i = 0; i < len; i++) + touchEventPool.free(touchEvents.get(i)); + touchEvents.clear(); + touchEvents.addAll(touchEventsBuffer); + touchEventsBuffer.clear(); + return touchEvents; + } + } + + // returns the index for a given pointerId or -1 if no index. + private int getIndex(int pointerId) { + for (int i = 0; i < MAX_TOUCHPOINTS; i++) { + if (id[i] == pointerId) { + return i; + } + } + return -1; + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java new file mode 100644 index 0000000..7ff1205 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java @@ -0,0 +1,101 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.MotionEvent; +import android.view.View; + +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class SingleTouchHandler implements TouchHandler { + boolean isTouched; + int touchX; + int touchY; + Pool touchEventPool; + List touchEvents = new ArrayList(); + List touchEventsBuffer = new ArrayList(); + float scaleX; + float scaleY; + + public SingleTouchHandler(View view, float scaleX, float scaleY) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }; + touchEventPool = new Pool(factory, 100); + view.setOnTouchListener(this); + + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + synchronized(this) { + TouchEvent touchEvent = touchEventPool.newObject(); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touchEvent.type = TouchEvent.TOUCH_DOWN; + isTouched = true; + break; + case MotionEvent.ACTION_MOVE: + touchEvent.type = TouchEvent.TOUCH_DRAGGED; + isTouched = true; + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + touchEvent.type = TouchEvent.TOUCH_UP; + isTouched = false; + break; + } + + touchEvent.x = touchX = (int)(event.getX() * scaleX); + touchEvent.y = touchY = (int)(event.getY() * scaleY); + touchEventsBuffer.add(touchEvent); + + return true; + } + } + + @Override + public boolean isTouchDown(int pointer) { + synchronized(this) { + if(pointer == 0) + return isTouched; + else + return false; + } + } + + @Override + public int getTouchX(int pointer) { + synchronized(this) { + return touchX; + } + } + + @Override + public int getTouchY(int pointer) { + synchronized(this) { + return touchY; + } + } + + @Override + public List getTouchEvents() { + synchronized(this) { + int len = touchEvents.size(); + for( int i = 0; i < len; i++ ) + touchEventPool.free(touchEvents.get(i)); + touchEvents.clear(); + touchEvents.addAll(touchEventsBuffer); + touchEventsBuffer.clear(); + return touchEvents; + } + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/TouchHandler.java b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/TouchHandler.java new file mode 100644 index 0000000..cc7e754 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/framework/impl/TouchHandler.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.List; + +import android.view.View.OnTouchListener; + +import com.badlogic.androidgames.framework.Input.TouchEvent; + +public interface TouchHandler extends OnTouchListener { + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public List getTouchEvents(); +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/Assets.java b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/Assets.java new file mode 100644 index 0000000..36d4e71 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/Assets.java @@ -0,0 +1,30 @@ +package com.badlogic.androidgames.mrnom; + +import com.badlogic.androidgames.framework.Pixmap; +import com.badlogic.androidgames.framework.Sound; + +public class Assets { + public static Pixmap background; + public static Pixmap logo; + public static Pixmap mainMenu; + public static Pixmap buttons; + public static Pixmap help1; + public static Pixmap help2; + public static Pixmap help3; + public static Pixmap numbers; + public static Pixmap ready; + public static Pixmap pause; + public static Pixmap gameOver; + public static Pixmap headUp; + public static Pixmap headLeft; + public static Pixmap headDown; + public static Pixmap headRight; + public static Pixmap tail; + public static Pixmap stain1; + public static Pixmap stain2; + public static Pixmap stain3; + + public static Sound click; + public static Sound eat; + public static Sound bitten; +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/GameScreen.java b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/GameScreen.java new file mode 100644 index 0000000..51fb661 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/GameScreen.java @@ -0,0 +1,261 @@ +package com.badlogic.androidgames.mrnom; + +import java.util.List; + +import android.graphics.Color; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pixmap; +import com.badlogic.androidgames.framework.Screen; + +public class GameScreen extends Screen { + enum GameState { + Ready, + Running, + Paused, + GameOver + } + + GameState state = GameState.Ready; + World world; + int oldScore = 0; + String score = "0"; + + public GameScreen(Game game) { + super(game); + world = new World(); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + if(state == GameState.Ready) + updateReady(touchEvents); + if(state == GameState.Running) + updateRunning(touchEvents, deltaTime); + if(state == GameState.Paused) + updatePaused(touchEvents); + if(state == GameState.GameOver) + updateGameOver(touchEvents); + } + + private void updateReady(List touchEvents) { + if(touchEvents.size() > 0) + state = GameState.Running; + } + + private void updateRunning(List touchEvents, float deltaTime) { + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + if(event.type == TouchEvent.TOUCH_UP) { + if(event.x < 64 && event.y < 64) { + if(Settings.soundEnabled) + Assets.click.play(1); + state = GameState.Paused; + return; + } + } + if(event.type == TouchEvent.TOUCH_DOWN) { + if(event.x < 64 && event.y > 416) { + world.snake.turnLeft(); + } + if(event.x > 256 && event.y > 416) { + world.snake.turnRight(); + } + } + } + + world.update(deltaTime); + if(world.gameOver) { + if(Settings.soundEnabled) + Assets.bitten.play(1); + state = GameState.GameOver; + } + if(oldScore != world.score) { + oldScore = world.score; + score = "" + oldScore; + if(Settings.soundEnabled) + Assets.eat.play(1); + } + } + + private void updatePaused(List touchEvents) { + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + if(event.type == TouchEvent.TOUCH_UP) { + if(event.x > 80 && event.x <= 240) { + if(event.y > 100 && event.y <= 148) { + if(Settings.soundEnabled) + Assets.click.play(1); + state = GameState.Running; + return; + } + if(event.y > 148 && event.y < 196) { + if(Settings.soundEnabled) + Assets.click.play(1); + game.setScreen(new MainMenuScreen(game)); + return; + } + } + } + } + } + + private void updateGameOver(List touchEvents) { + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + if(event.type == TouchEvent.TOUCH_UP) { + if(event.x >= 128 && event.x <= 192 && + event.y >= 200 && event.y <= 264) { + if(Settings.soundEnabled) + Assets.click.play(1); + game.setScreen(new MainMenuScreen(game)); + return; + } + } + } + } + + + @Override + public void present(float deltaTime) { + Graphics g = game.getGraphics(); + + g.drawPixmap(Assets.background, 0, 0); + drawWorld(world); + if(state == GameState.Ready) + drawReadyUI(); + if(state == GameState.Running) + drawRunningUI(); + if(state == GameState.Paused) + drawPausedUI(); + if(state == GameState.GameOver) + drawGameOverUI(); + + drawText(g, score, g.getWidth() / 2 - score.length()*20 / 2, g.getHeight() - 42); + } + + private void drawWorld(World world) { + Graphics g = game.getGraphics(); + Snake snake = world.snake; + SnakePart head = snake.parts.get(0); + Stain stain = world.stain; + + + Pixmap stainPixmap = null; + if(stain.type == Stain.TYPE_1) + stainPixmap = Assets.stain1; + if(stain.type == Stain.TYPE_2) + stainPixmap = Assets.stain2; + if(stain.type == Stain.TYPE_3) + stainPixmap = Assets.stain3; + int x = stain.x * 32; + int y = stain.y * 32; + g.drawPixmap(stainPixmap, x, y); + + int len = snake.parts.size(); + for(int i = 1; i < len; i++) { + SnakePart part = snake.parts.get(i); + x = part.x * 32; + y = part.y * 32; + g.drawPixmap(Assets.tail, x, y); + } + + Pixmap headPixmap = null; + if(snake.direction == Snake.UP) + headPixmap = Assets.headUp; + if(snake.direction == Snake.LEFT) + headPixmap = Assets.headLeft; + if(snake.direction == Snake.DOWN) + headPixmap = Assets.headDown; + if(snake.direction == Snake.RIGHT) + headPixmap = Assets.headRight; + x = head.x * 32 + 16; + y = head.y * 32 + 16; + g.drawPixmap(headPixmap, x - headPixmap.getWidth() / 2, y - headPixmap.getHeight() / 2); + } + + private void drawReadyUI() { + Graphics g = game.getGraphics(); + + g.drawPixmap(Assets.ready, 47, 100); + g.drawLine(0, 416, 480, 416, Color.BLACK); + } + + private void drawRunningUI() { + Graphics g = game.getGraphics(); + + g.drawPixmap(Assets.buttons, 0, 0, 64, 128, 64, 64); + g.drawLine(0, 416, 480, 416, Color.BLACK); + g.drawPixmap(Assets.buttons, 0, 416, 64, 64, 64, 64); + g.drawPixmap(Assets.buttons, 256, 416, 0, 64, 64, 64); + } + + private void drawPausedUI() { + Graphics g = game.getGraphics(); + + g.drawPixmap(Assets.pause, 80, 100); + g.drawLine(0, 416, 480, 416, Color.BLACK); + } + + private void drawGameOverUI() { + Graphics g = game.getGraphics(); + + g.drawPixmap(Assets.gameOver, 62, 100); + g.drawPixmap(Assets.buttons, 128, 200, 0, 128, 64, 64); + g.drawLine(0, 416, 480, 416, Color.BLACK); + } + + public void drawText(Graphics g, String line, int x, int y) { + int len = line.length(); + for (int i = 0; i < len; i++) { + char character = line.charAt(i); + + if (character == ' ') { + x += 20; + continue; + } + + int srcX = 0; + int srcWidth = 0; + if (character == '.') { + srcX = 200; + srcWidth = 10; + } else { + srcX = (character - '0') * 20; + srcWidth = 20; + } + + g.drawPixmap(Assets.numbers, x, y, srcX, 0, srcWidth, 32); + x += srcWidth; + } + } + + @Override + public void pause() { + if(state == GameState.Running) + state = GameState.Paused; + + if(world.gameOver) { + Settings.addScore(world.score); + Settings.save(game.getFileIO()); + } + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } +} \ No newline at end of file diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/HelpScreen.java b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/HelpScreen.java new file mode 100644 index 0000000..cac5feb --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/HelpScreen.java @@ -0,0 +1,56 @@ +package com.badlogic.androidgames.mrnom; + +import java.util.List; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Screen; + +public class HelpScreen extends Screen { + public HelpScreen(Game game) { + super(game); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + if(event.type == TouchEvent.TOUCH_UP) { + if(event.x > 256 && event.y > 416 ) { + game.setScreen(new HelpScreen2(game)); + if(Settings.soundEnabled) + Assets.click.play(1); + return; + } + } + } + } + + @Override + public void present(float deltaTime) { + Graphics g = game.getGraphics(); + g.drawPixmap(Assets.background, 0, 0); + g.drawPixmap(Assets.help1, 64, 100); + g.drawPixmap(Assets.buttons, 256, 416, 0, 64, 64, 64); + } + + @Override + public void pause() { + + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } +} \ No newline at end of file diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/HelpScreen2.java b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/HelpScreen2.java new file mode 100644 index 0000000..79b8406 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/HelpScreen2.java @@ -0,0 +1,63 @@ +package com.badlogic.androidgames.mrnom; + +import java.util.List; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.Input.TouchEvent; + +public class HelpScreen2 extends Screen { + + public HelpScreen2(Game game) { + super(game); + // TODO Auto-generated constructor stub + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + Graphics g = game.getGraphics(); + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + if(event.type == TouchEvent.TOUCH_UP) { + if(event.x > g.getWidth() - 64 && event.y > g.getHeight() - 64 ) { + game.setScreen(new HelpScreen3(game)); + if(Settings.soundEnabled) + Assets.click.play(1); + return; + } + } + } + } + + @Override + public void present(float deltaTime) { + Graphics g = game.getGraphics(); + g.drawPixmap(Assets.background, 0, 0); + g.drawPixmap(Assets.help2, 64, 100); + g.drawPixmap(Assets.buttons, 256, 416, 0, 64, 64, 64); + } + + @Override + public void pause() { + // TODO Auto-generated method stub + + } + + @Override + public void resume() { + // TODO Auto-generated method stub + + } + + @Override + public void dispose() { + // TODO Auto-generated method stub + + } + +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/HelpScreen3.java b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/HelpScreen3.java new file mode 100644 index 0000000..56f4c70 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/HelpScreen3.java @@ -0,0 +1,61 @@ +package com.badlogic.androidgames.mrnom; + +import java.util.List; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.Input.TouchEvent; + +public class HelpScreen3 extends Screen { + + public HelpScreen3(Game game) { + super(game); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + if(event.type == TouchEvent.TOUCH_UP) { + if(event.x > 256 && event.y > 416 ) { + game.setScreen(new MainMenuScreen(game)); + if(Settings.soundEnabled) + Assets.click.play(1); + return; + } + } + } + } + + @Override + public void present(float deltaTime) { + Graphics g = game.getGraphics(); + g.drawPixmap(Assets.background, 0, 0); + g.drawPixmap(Assets.help3, 64, 100); + g.drawPixmap(Assets.buttons, 256, 416, 0, 128, 64, 64); + } + + @Override + public void pause() { + // TODO Auto-generated method stub + + } + + @Override + public void resume() { + // TODO Auto-generated method stub + + } + + @Override + public void dispose() { + // TODO Auto-generated method stub + + } + +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/HighscoreScreen.java b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/HighscoreScreen.java new file mode 100644 index 0000000..b14c8e8 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/HighscoreScreen.java @@ -0,0 +1,95 @@ +package com.badlogic.androidgames.mrnom; + +import java.util.List; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.Input.TouchEvent; + +public class HighscoreScreen extends Screen { + String lines[] = new String[5]; + + public HighscoreScreen(Game game) { + super(game); + + for (int i = 0; i < 5; i++) { + lines[i] = "" + (i + 1) + ". " + Settings.highscores[i]; + } + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + int len = touchEvents.size(); + for (int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + if (event.type == TouchEvent.TOUCH_UP) { + if (event.x < 64 && event.y > 416) { + if(Settings.soundEnabled) + Assets.click.play(1); + game.setScreen(new MainMenuScreen(game)); + return; + } + } + } + } + + @Override + public void present(float deltaTime) { + Graphics g = game.getGraphics(); + + g.drawPixmap(Assets.background, 0, 0); + g.drawPixmap(Assets.mainMenu, 64, 20, 0, 42, 196, 42); + + int y = 100; + for (int i = 0; i < 5; i++) { + drawText(g, lines[i], 20, y); + y += 50; + } + + g.drawPixmap(Assets.buttons, 0, 416, 64, 64, 64, 64); + } + + public void drawText(Graphics g, String line, int x, int y) { + int len = line.length(); + for (int i = 0; i < len; i++) { + char character = line.charAt(i); + + if (character == ' ') { + x += 20; + continue; + } + + int srcX = 0; + int srcWidth = 0; + if (character == '.') { + srcX = 200; + srcWidth = 10; + } else { + srcX = (character - '0') * 20; + srcWidth = 20; + } + + g.drawPixmap(Assets.numbers, x, y, srcX, 0, srcWidth, 32); + x += srcWidth; + } + } + + @Override + public void pause() { + + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/LoadingScreen.java b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/LoadingScreen.java new file mode 100644 index 0000000..70cf39a --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/LoadingScreen.java @@ -0,0 +1,61 @@ +package com.badlogic.androidgames.mrnom; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; + +public class LoadingScreen extends Screen { + public LoadingScreen(Game game) { + super(game); + } + + @Override + public void update(float deltaTime) { + Graphics g = game.getGraphics(); + Assets.background = g.newPixmap("background.png", PixmapFormat.RGB565); + Assets.logo = g.newPixmap("logo.png", PixmapFormat.ARGB4444); + Assets.mainMenu = g.newPixmap("mainmenu.png", PixmapFormat.ARGB4444); + Assets.buttons = g.newPixmap("buttons.png", PixmapFormat.ARGB4444); + Assets.help1 = g.newPixmap("help1.png", PixmapFormat.ARGB4444); + Assets.help2 = g.newPixmap("help2.png", PixmapFormat.ARGB4444); + Assets.help3 = g.newPixmap("help3.png", PixmapFormat.ARGB4444); + Assets.numbers = g.newPixmap("numbers.png", PixmapFormat.ARGB4444); + Assets.ready = g.newPixmap("ready.png", PixmapFormat.ARGB4444); + Assets.pause = g.newPixmap("pausemenu.png", PixmapFormat.ARGB4444); + Assets.gameOver = g.newPixmap("gameover.png", PixmapFormat.ARGB4444); + Assets.headUp = g.newPixmap("headup.png", PixmapFormat.ARGB4444); + Assets.headLeft = g.newPixmap("headleft.png", PixmapFormat.ARGB4444); + Assets.headDown = g.newPixmap("headdown.png", PixmapFormat.ARGB4444); + Assets.headRight = g.newPixmap("headright.png", PixmapFormat.ARGB4444); + Assets.tail = g.newPixmap("tail.png", PixmapFormat.ARGB4444); + Assets.stain1 = g.newPixmap("stain1.png", PixmapFormat.ARGB4444); + Assets.stain2 = g.newPixmap("stain2.png", PixmapFormat.ARGB4444); + Assets.stain3 = g.newPixmap("stain3.png", PixmapFormat.ARGB4444); + Assets.click = game.getAudio().newSound("click.ogg"); + Assets.eat = game.getAudio().newSound("eat.ogg"); + Assets.bitten = game.getAudio().newSound("bitten.ogg"); + Settings.load(game.getFileIO()); + game.setScreen(new MainMenuScreen(game)); + } + + @Override + public void present(float deltaTime) { + + } + + @Override + public void pause() { + + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } +} \ No newline at end of file diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/MainMenuScreen.java b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/MainMenuScreen.java new file mode 100644 index 0000000..d735ea3 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/MainMenuScreen.java @@ -0,0 +1,87 @@ +package com.badlogic.androidgames.mrnom; + +import java.util.List; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Screen; + +public class MainMenuScreen extends Screen { + public MainMenuScreen(Game game) { + super(game); + } + + @Override + public void update(float deltaTime) { + Graphics g = game.getGraphics(); + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + if(event.type == TouchEvent.TOUCH_UP) { + if(inBounds(event, 0, g.getHeight() - 64, 64, 64)) { + Settings.soundEnabled = !Settings.soundEnabled; + if(Settings.soundEnabled) + Assets.click.play(1); + } + if(inBounds(event, 64, 220, 192, 42) ) { + game.setScreen(new GameScreen(game)); + if(Settings.soundEnabled) + Assets.click.play(1); + return; + } + if(inBounds(event, 64, 220 + 42, 192, 42) ) { + game.setScreen(new HighscoreScreen(game)); + if(Settings.soundEnabled) + Assets.click.play(1); + return; + } + if(inBounds(event, 64, 220 + 84, 192, 42) ) { + game.setScreen(new HelpScreen(game)); + if(Settings.soundEnabled) + Assets.click.play(1); + return; + } + } + } + } + + private boolean inBounds(TouchEvent event, int x, int y, int width, int height) { + if(event.x > x && event.x < x + width - 1 && + event.y > y && event.y < y + height - 1) + return true; + else + return false; + } + + @Override + public void present(float deltaTime) { + Graphics g = game.getGraphics(); + + g.drawPixmap(Assets.background, 0, 0); + g.drawPixmap(Assets.logo, 32, 20); + g.drawPixmap(Assets.mainMenu, 64, 220); + if(Settings.soundEnabled) + g.drawPixmap(Assets.buttons, 0, 416, 0, 0, 64, 64); + else + g.drawPixmap(Assets.buttons, 0, 416, 64, 0, 64, 64); + } + + @Override + public void pause() { + Settings.save(game.getFileIO()); + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/MrNomGame.java b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/MrNomGame.java new file mode 100644 index 0000000..4fa38a9 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/MrNomGame.java @@ -0,0 +1,11 @@ +package com.badlogic.androidgames.mrnom; + +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.impl.AndroidGame; + +public class MrNomGame extends AndroidGame { + @Override + public Screen getStartScreen() { + return new LoadingScreen(this); + } +} \ No newline at end of file diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/Settings.java b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/Settings.java new file mode 100644 index 0000000..9fb6b82 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/Settings.java @@ -0,0 +1,69 @@ +package com.badlogic.androidgames.mrnom; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; + +import com.badlogic.androidgames.framework.FileIO; + +public class Settings { + public static boolean soundEnabled = true; + public static int[] highscores = new int[] { 100, 80, 50, 30, 10 }; + + public static void load(FileIO files) { + BufferedReader in = null; + try { + in = new BufferedReader(new InputStreamReader( + files.readFile(".mrnom"))); + soundEnabled = Boolean.parseBoolean(in.readLine()); + for (int i = 0; i < 5; i++) { + highscores[i] = Integer.parseInt(in.readLine()); + } + } catch (IOException e) { + // :( It's ok we have defaults + } catch (NumberFormatException e) { + // :/ It's ok, defaults save our day + } finally { + try { + if (in != null) + in.close(); + } catch (IOException e) { + } + } + } + + public static void save(FileIO files) { + BufferedWriter out = null; + try { + out = new BufferedWriter(new OutputStreamWriter( + files.writeFile(".mrnom"))); + out.write(Boolean.toString(soundEnabled)); + out.write("\n"); + for (int i = 0; i < 5; i++) { + out.write(Integer.toString(highscores[i])); + out.write("\n"); + } + + } catch (IOException e) { + } finally { + try { + if (out != null) + out.close(); + } catch (IOException e) { + } + } + } + + public static void addScore(int score) { + for (int i = 0; i < 5; i++) { + if (highscores[i] < score) { + for (int j = 4; j > i; j--) + highscores[j] = highscores[j - 1]; + highscores[i] = score; + break; + } + } + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/Snake.java b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/Snake.java new file mode 100644 index 0000000..8dcbbe5 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/Snake.java @@ -0,0 +1,79 @@ +package com.badlogic.androidgames.mrnom; + +import java.util.ArrayList; +import java.util.List; + +public class Snake { + public static final int UP = 0; + public static final int LEFT = 1; + public static final int DOWN = 2; + public static final int RIGHT = 3; + + public List parts = new ArrayList(); + public int direction; + + public Snake() { + direction = UP; + parts.add(new SnakePart(5, 6)); + parts.add(new SnakePart(5, 7)); + parts.add(new SnakePart(5, 8)); + } + + public void turnLeft() { + direction += 1; + if(direction > RIGHT) + direction = UP; + } + + public void turnRight() { + direction -= 1; + if(direction < UP) + direction = RIGHT; + } + + public void eat() { + SnakePart end = parts.get(parts.size()-1); + parts.add(new SnakePart(end.x, end.y)); + } + + public void advance() { + SnakePart head = parts.get(0); + + int len = parts.size() - 1; + for(int i = len; i > 0; i--) { + SnakePart before = parts.get(i-1); + SnakePart part = parts.get(i); + part.x = before.x; + part.y = before.y; + } + + if(direction == UP) + head.y -= 1; + if(direction == LEFT) + head.x -= 1; + if(direction == DOWN) + head.y += 1; + if(direction == RIGHT) + head.x += 1; + + if(head.x < 0) + head.x = 9; + if(head.x > 9) + head.x = 0; + if(head.y < 0) + head.y = 12; + if(head.y > 12) + head.y = 0; + } + + public boolean checkBitten() { + int len = parts.size(); + SnakePart head = parts.get(0); + for(int i = 1; i < len; i++) { + SnakePart part = parts.get(i); + if(part.x == head.x && part.y == head.y) + return true; + } + return false; + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/SnakePart.java b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/SnakePart.java new file mode 100644 index 0000000..3329878 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/SnakePart.java @@ -0,0 +1,11 @@ +package com.badlogic.androidgames.mrnom; + +public class SnakePart { + public int x, y; + + public SnakePart(int x, int y) { + this.x = x; + this.y = y; + } +} + diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/Stain.java b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/Stain.java new file mode 100644 index 0000000..b5d1ae9 --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/Stain.java @@ -0,0 +1,15 @@ +package com.badlogic.androidgames.mrnom; + +public class Stain { + public static final int TYPE_1 = 0; + public static final int TYPE_2 = 1; + public static final int TYPE_3 = 2; + public int x, y; + public int type; + + public Stain(int x, int y, int type) { + this.x = x; + this.y = y; + this.type = type; + } +} diff --git a/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/World.java b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/World.java new file mode 100644 index 0000000..f57944e --- /dev/null +++ b/ch06-mr-nom/src/com/badlogic/androidgames/mrnom/World.java @@ -0,0 +1,88 @@ +package com.badlogic.androidgames.mrnom; + +import java.util.Random; + +public class World { + static final int WORLD_WIDTH = 10; + static final int WORLD_HEIGHT = 13; + static final int SCORE_INCREMENT = 10; + static final float TICK_INITIAL = 0.5f; + static final float TICK_DECREMENT = 0.05f; + + public Snake snake; + public Stain stain; + public boolean gameOver = false;; + public int score = 0; + + boolean fields[][] = new boolean[WORLD_WIDTH][WORLD_HEIGHT]; + Random random = new Random(); + float tickTime = 0; + static float tick = TICK_INITIAL; + + public World() { + snake = new Snake(); + placeStain(); + } + + private void placeStain() { + for (int x = 0; x < WORLD_WIDTH; x++) { + for (int y = 0; y < WORLD_HEIGHT; y++) { + fields[x][y] = false; + } + } + + int len = snake.parts.size(); + for (int i = 0; i < len; i++) { + SnakePart part = snake.parts.get(i); + fields[part.x][part.y] = true; + } + + int stainX = random.nextInt(WORLD_WIDTH); + int stainY = random.nextInt(WORLD_HEIGHT); + while (true) { + if (fields[stainX][stainY] == false) + break; + stainX += 1; + if (stainX >= WORLD_WIDTH) { + stainX = 0; + stainY += 1; + if (stainY >= WORLD_HEIGHT) { + stainY = 0; + } + } + } + stain = new Stain(stainX, stainY, random.nextInt(3)); + } + + public void update(float deltaTime) { + if (gameOver) + return; + + tickTime += deltaTime; + + while (tickTime > tick) { + tickTime -= tick; + snake.advance(); + if (snake.checkBitten()) { + gameOver = true; + return; + } + + SnakePart head = snake.parts.get(0); + if (head.x == stain.x && head.y == stain.y) { + score += SCORE_INCREMENT; + snake.eat(); + if (snake.parts.size() == WORLD_WIDTH * WORLD_HEIGHT) { + gameOver = true; + return; + } else { + placeStain(); + } + + if (score % 100 == 0 && tick - TICK_DECREMENT > 0) { + tick -= TICK_DECREMENT; + } + } + } + } +} diff --git a/ch07-gl-basics/.classpath b/ch07-gl-basics/.classpath new file mode 100644 index 0000000..609aa00 --- /dev/null +++ b/ch07-gl-basics/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ch07-gl-basics/.project b/ch07-gl-basics/.project new file mode 100644 index 0000000..ee3dee2 --- /dev/null +++ b/ch07-gl-basics/.project @@ -0,0 +1,33 @@ + + + ch07-gl-basics + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/ch07-gl-basics/AndroidManifest.xml b/ch07-gl-basics/AndroidManifest.xml new file mode 100644 index 0000000..0b15995 --- /dev/null +++ b/ch07-gl-basics/AndroidManifest.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch07-gl-basics/assets/bobargb8888.png b/ch07-gl-basics/assets/bobargb8888.png new file mode 100644 index 0000000..a16789a Binary files /dev/null and b/ch07-gl-basics/assets/bobargb8888.png differ diff --git a/ch07-gl-basics/assets/bobrgb888-32x32.png b/ch07-gl-basics/assets/bobrgb888-32x32.png new file mode 100644 index 0000000..76896af Binary files /dev/null and b/ch07-gl-basics/assets/bobrgb888-32x32.png differ diff --git a/ch07-gl-basics/assets/bobrgb888.png b/ch07-gl-basics/assets/bobrgb888.png new file mode 100644 index 0000000..bb05647 Binary files /dev/null and b/ch07-gl-basics/assets/bobrgb888.png differ diff --git a/ch07-gl-basics/default.properties b/ch07-gl-basics/default.properties new file mode 100644 index 0000000..0cdab95 --- /dev/null +++ b/ch07-gl-basics/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-12 diff --git a/ch07-gl-basics/res/drawable-hdpi/icon.png b/ch07-gl-basics/res/drawable-hdpi/icon.png new file mode 100644 index 0000000..8074c4c Binary files /dev/null and b/ch07-gl-basics/res/drawable-hdpi/icon.png differ diff --git a/ch07-gl-basics/res/drawable-ldpi/icon.png b/ch07-gl-basics/res/drawable-ldpi/icon.png new file mode 100644 index 0000000..1095584 Binary files /dev/null and b/ch07-gl-basics/res/drawable-ldpi/icon.png differ diff --git a/ch07-gl-basics/res/drawable-mdpi/icon.png b/ch07-gl-basics/res/drawable-mdpi/icon.png new file mode 100644 index 0000000..a07c69f Binary files /dev/null and b/ch07-gl-basics/res/drawable-mdpi/icon.png differ diff --git a/ch07-gl-basics/res/layout/main.xml b/ch07-gl-basics/res/layout/main.xml new file mode 100644 index 0000000..3a5f117 --- /dev/null +++ b/ch07-gl-basics/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/ch07-gl-basics/res/values/strings.xml b/ch07-gl-basics/res/values/strings.xml new file mode 100644 index 0000000..5fdeb09 --- /dev/null +++ b/ch07-gl-basics/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Hello World, GLBasicsStarter! + GL Basics + diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/Audio.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Audio.java new file mode 100644 index 0000000..d30d722 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Audio.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Audio { + public Music newMusic(String filename); + + public Sound newSound(String filename); +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/Color.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Color.java new file mode 100644 index 0000000..57d68e5 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Color.java @@ -0,0 +1,10 @@ +package com.badlogic.androidgames.framework; + +public class Color { + public static int convert (int r, int g, int b, int a) { + return ((a & 0xff) << 24) | + ((r & 0xff) << 16) | + ((g & 0xff) << 8) | + ((b & 0xff)); + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/FileIO.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/FileIO.java new file mode 100644 index 0000000..d555b7d --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/FileIO.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.framework; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public interface FileIO { + public InputStream readAsset(String fileName) throws IOException; + + public InputStream readFile(String fileName) throws IOException; + + public OutputStream writeFile(String fileName) throws IOException; +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/Game.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Game.java new file mode 100644 index 0000000..fb5c5aa --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Game.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework; + +public interface Game { + public Input getInput(); + + public FileIO getFileIO(); + + public Graphics getGraphics(); + + public Audio getAudio(); + + public void setScreen(Screen screen); + + public Screen getCurrentScreen(); + + public Screen getStartScreen(); +} \ No newline at end of file diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/Graphics.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Graphics.java new file mode 100644 index 0000000..c7b8f45 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Graphics.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework; + +public interface Graphics { + public static enum PixmapFormat { + ARGB8888, ARGB4444, RGB565 + } + + public Pixmap newPixmap(String fileName, PixmapFormat format); + + public void clear(int color); + + public void drawPixel(int x, int y, int color); + + public void drawLine(int x, int y, int x2, int y2, int color); + + public void drawRect(int x, int y, int width, int height, int color); + + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight); + + public void drawPixmap(Pixmap pixmap, int x, int y); + + public int getWidth(); + + public int getHeight(); +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/Input.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Input.java new file mode 100644 index 0000000..0893d62 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Input.java @@ -0,0 +1,70 @@ +package com.badlogic.androidgames.framework; + +import java.util.List; + +public interface Input { + public static class KeyEvent { + public static final int KEY_DOWN = 0; + public static final int KEY_UP = 1; + + public int type; + public int keyCode; + public char keyChar; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == KEY_DOWN) + builder.append("key down, "); + else + builder.append("key up, "); + builder.append(keyCode); + builder.append(","); + builder.append(keyChar); + return builder.toString(); + } + } + + public static class TouchEvent { + public static final int TOUCH_DOWN = 0; + public static final int TOUCH_UP = 1; + public static final int TOUCH_DRAGGED = 2; + + public int type; + public int x, y; + public int pointer; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == TOUCH_DOWN) + builder.append("touch down, "); + else if (type == TOUCH_DRAGGED) + builder.append("touch dragged, "); + else + builder.append("touch up, "); + builder.append(pointer); + builder.append(","); + builder.append(x); + builder.append(","); + builder.append(y); + return builder.toString(); + } + } + + public boolean isKeyPressed(int keyCode); + + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public float getAccelX(); + + public float getAccelY(); + + public float getAccelZ(); + + public List getKeyEvents(); + + public List getTouchEvents(); +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/Music.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Music.java new file mode 100644 index 0000000..1b95a1e --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Music.java @@ -0,0 +1,21 @@ +package com.badlogic.androidgames.framework; + +public interface Music { + public void play(); + + public void stop(); + + public void pause(); + + public void setLooping(boolean looping); + + public void setVolume(float volume); + + public boolean isPlaying(); + + public boolean isStopped(); + + public boolean isLooping(); + + public void dispose(); +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/Pixmap.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Pixmap.java new file mode 100644 index 0000000..4b1eec9 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Pixmap.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; + +public interface Pixmap { + public int getWidth(); + + public int getHeight(); + + public PixmapFormat getFormat(); + + public void dispose(); +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/Pool.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Pool.java new file mode 100644 index 0000000..d0b7cec --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Pool.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework; + +import java.util.ArrayList; +import java.util.List; + +public class Pool { + public interface PoolObjectFactory { + public T createObject(); + } + + private final List freeObjects; + private final PoolObjectFactory factory; + private final int maxSize; + + public Pool(PoolObjectFactory factory, int maxSize) { + this.factory = factory; + this.maxSize = maxSize; + this.freeObjects = new ArrayList(maxSize); + } + + public T newObject() { + T object = null; + + if (freeObjects.size() == 0) + object = factory.createObject(); + else + object = freeObjects.remove(freeObjects.size() - 1); + + return object; + } + + public void free(T object) { + if (freeObjects.size() < maxSize) + freeObjects.add(object); + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/Screen.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Screen.java new file mode 100644 index 0000000..40a98b9 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Screen.java @@ -0,0 +1,19 @@ +package com.badlogic.androidgames.framework; + +public abstract class Screen { + protected final Game game; + + public Screen(Game game) { + this.game = game; + } + + public abstract void update(float deltaTime); + + public abstract void present(float deltaTime); + + public abstract void pause(); + + public abstract void resume(); + + public abstract void dispose(); +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/Sound.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Sound.java new file mode 100644 index 0000000..3f49835 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/Sound.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Sound { + public void play(float volume); + + public void dispose(); +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/TestScreen.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/TestScreen.java new file mode 100644 index 0000000..5a0bbcf --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/TestScreen.java @@ -0,0 +1,112 @@ +package com.badlogic.androidgames.framework; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.List; + +import android.graphics.Color; +import android.util.Log; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; +import com.badlogic.androidgames.framework.Input.KeyEvent; +import com.badlogic.androidgames.framework.Input.TouchEvent; + +class TestScreen extends Screen { + long startTime = System.nanoTime(); + int frames; + Pixmap bob; + Pixmap bobAlpha; + Sound sound; + Music music; + + public TestScreen(Game game) { + super(game); + bob = game.getGraphics().newPixmap("bobrgb888.png", PixmapFormat.RGB565); + bobAlpha = game.getGraphics().newPixmap("bobargb8888.png", PixmapFormat.ARGB4444); + music = game.getAudio().newMusic("music.ogg"); + music.setLooping(true); + music.setVolume(0.5f); + music.play(); + sound = game.getAudio().newSound("music.ogg"); + + try { + BufferedReader in = new BufferedReader(new InputStreamReader(game.getFileIO().readAsset("test.txt"))); + String text = in.readLine(); + in.close(); + + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(game.getFileIO().writeFile("test.txt"))); + out.write("This is a freaking test"); + out.close(); + + in = new BufferedReader(new InputStreamReader(game.getFileIO().readFile("test.txt"))); + String text2 = in.readLine(); + in.close(); + + Log.d("MrNom", text + ", " + text2 ); + } catch(Exception ex) { + ex.printStackTrace(); + } + } + + @Override + public void update(float deltaTime) { + } + + @Override + public void present(float deltaTime) { + Graphics g = game.getGraphics(); + Input inp = game.getInput(); + g.clear(Color.RED); + g.drawLine(0,0,320, 480, Color.BLUE); + g.drawRect(20,20,100,100, Color.GREEN); + g.drawPixmap(bob, 100, 100); + g.drawPixmap(bobAlpha, 100, 200); + g.drawPixmap(bob, 200, 200, 0, 0, 64, 64); + for(int i=0; i < 2; i++) { + if(inp.isTouchDown(i)) { + g.drawPixmap(bob, inp.getTouchX(i), inp.getTouchY(i), 0, 0, 64, 64); + } + } + + g.drawPixmap(bob, (int)(inp.getAccelX() * 10) + 160 - 16, (int)(inp.getAccelY() * 10) + 240 - 16, 0, 0, 32, 32 ); + + List keyEvents = inp.getKeyEvents(); + int len = keyEvents.size(); + for(int i = 0; i < len; i++) { + Log.d("MrNom", keyEvents.get(i).toString()); + } + + List touchEvents = inp.getTouchEvents(); + len = touchEvents.size(); + for(int i = 0; i < len; i++) { + Log.d("MrNom", touchEvents.get(i).toString()); + if(touchEvents.get(i).type == TouchEvent.TOUCH_UP) + sound.play(1); + } + + frames++; + if(System.nanoTime() - startTime > 1000000000l) { + Log.d("MrNom", "fps: " + frames + ", delta: " + deltaTime); + frames = 0; + startTime = System.nanoTime(); + } + } + + @Override + public void pause() { + Log.d("MrNom", "pause"); + } + + @Override + public void resume() { + Log.d("MrNom", "resume"); + } + + @Override + public void dispose() { + Log.d("MrNom", "dispose"); + music.dispose(); + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/gl/BindableVertices.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/gl/BindableVertices.java new file mode 100644 index 0000000..a24be6c --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/gl/BindableVertices.java @@ -0,0 +1,90 @@ +package com.badlogic.androidgames.framework.gl; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.ShortBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class BindableVertices { + final GLGraphics glGraphics; + final boolean hasColor; + final boolean hasTexCoords; + final int vertexSize; + final FloatBuffer vertices; + final ShortBuffer indices; + + public BindableVertices(GLGraphics glGraphics, int maxVertices, int maxIndices, boolean hasColor, boolean hasTexCoords) { + this.glGraphics = glGraphics; + this.hasColor = hasColor; + this.hasTexCoords = hasTexCoords; + this.vertexSize = (2 + (hasColor?4:0) + (hasTexCoords?2:0)) * 4; + + ByteBuffer buffer = ByteBuffer.allocateDirect(maxVertices * vertexSize); + buffer.order(ByteOrder.nativeOrder()); + vertices = buffer.asFloatBuffer(); + + if(maxIndices > 0) { + buffer = ByteBuffer.allocateDirect(maxIndices * Short.SIZE / 8); + buffer.order(ByteOrder.nativeOrder()); + indices = buffer.asShortBuffer(); + } else { + indices = null; + } + } + + public void setVertices(float[] vertices, int offset, int length) { + this.vertices.clear(); + this.vertices.put(vertices, offset, length); + this.vertices.flip(); + } + + public void setIndices(short[] indices, int offset, int length) { + this.indices.clear(); + this.indices.put(indices, offset, length); + this.indices.flip(); + } + +public void bind() { + GL10 gl = glGraphics.getGL(); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + vertices.position(0); + gl.glVertexPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + + if(hasColor) { + gl.glEnableClientState(GL10.GL_COLOR_ARRAY); + vertices.position(2); + gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices); + } + + if(hasTexCoords) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + vertices.position(hasColor?6:2); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + } +} + +public void draw(int primitiveType, int offset, int numVertices) { + GL10 gl = glGraphics.getGL(); + + if(indices!=null) { + indices.position(offset); + gl.glDrawElements(primitiveType, numVertices, GL10.GL_UNSIGNED_SHORT, indices); + } else { + gl.glDrawArrays(primitiveType, offset, numVertices); + } +} + +public void unbind() { + GL10 gl = glGraphics.getGL(); + if(hasTexCoords) + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + if(hasColor) + gl.glDisableClientState(GL10.GL_COLOR_ARRAY); +} +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/gl/FPSCounter.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/gl/FPSCounter.java new file mode 100644 index 0000000..cd816ca --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/gl/FPSCounter.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework.gl; + +import android.util.Log; + +public class FPSCounter { + long startTime = System.nanoTime(); + int frames = 0; + + public void logFrame() { + frames++; + if(System.nanoTime() - startTime >= 1000000000) { + Log.d("FPSCounter", "fps: " + frames); + frames = 0; + startTime = System.nanoTime(); + } + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/gl/Texture.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/gl/Texture.java new file mode 100644 index 0000000..174e27b --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/gl/Texture.java @@ -0,0 +1,84 @@ +package com.badlogic.androidgames.framework.gl; + +import java.io.IOException; +import java.io.InputStream; + +import javax.microedition.khronos.opengles.GL10; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.opengl.GLUtils; + +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Texture { + GLGraphics glGraphics; + FileIO fileIO; + String fileName; + int textureId; + int minFilter; + int magFilter; + public int width; + public int height; + + public Texture(GLGame glGame, String fileName) { + this.glGraphics = glGame.getGLGraphics(); + this.fileIO = glGame.getFileIO(); + this.fileName = fileName; + load(); + } + + private void load() { + GL10 gl = glGraphics.getGL(); + int[] textureIds = new int[1]; + gl.glGenTextures(1, textureIds, 0); + textureId = textureIds[0]; + + InputStream in = null; + try { + in = fileIO.readAsset(fileName); + Bitmap bitmap = BitmapFactory.decodeStream(in); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); + setFilters(GL10.GL_NEAREST, GL10.GL_NEAREST); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + width = bitmap.getWidth(); + height = bitmap.getHeight(); + bitmap.recycle(); + } catch(IOException e) { + throw new RuntimeException("Couldn't load texture '" + fileName +"'", e); + } finally { + if(in != null) + try { in.close(); } catch (IOException e) { } + } + } + + public void reload() { + load(); + bind(); + setFilters(minFilter, magFilter); + glGraphics.getGL().glBindTexture(GL10.GL_TEXTURE_2D, 0); + } + + public void setFilters(int minFilter, int magFilter) { + this.minFilter = minFilter; + this.magFilter = magFilter; + GL10 gl = glGraphics.getGL(); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, minFilter); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, magFilter); + } + + public void bind() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + } + + public void dispose() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + int[] textureIds = { textureId }; + gl.glDeleteTextures(1, textureIds, 0); + } +} \ No newline at end of file diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/gl/Vertices.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/gl/Vertices.java new file mode 100644 index 0000000..6154244 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/gl/Vertices.java @@ -0,0 +1,83 @@ +package com.badlogic.androidgames.framework.gl; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.ShortBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Vertices { + final GLGraphics glGraphics; + final boolean hasColor; + final boolean hasTexCoords; + final int vertexSize; + final FloatBuffer vertices; + final ShortBuffer indices; + + public Vertices(GLGraphics glGraphics, int maxVertices, int maxIndices, boolean hasColor, boolean hasTexCoords) { + this.glGraphics = glGraphics; + this.hasColor = hasColor; + this.hasTexCoords = hasTexCoords; + this.vertexSize = (2 + (hasColor?4:0) + (hasTexCoords?2:0)) * 4; + + ByteBuffer buffer = ByteBuffer.allocateDirect(maxVertices * vertexSize); + buffer.order(ByteOrder.nativeOrder()); + vertices = buffer.asFloatBuffer(); + + if(maxIndices > 0) { + buffer = ByteBuffer.allocateDirect(maxIndices * Short.SIZE / 8); + buffer.order(ByteOrder.nativeOrder()); + indices = buffer.asShortBuffer(); + } else { + indices = null; + } + } + + public void setVertices(float[] vertices, int offset, int length) { + this.vertices.clear(); + this.vertices.put(vertices, offset, length); + this.vertices.flip(); + } + + public void setIndices(short[] indices, int offset, int length) { + this.indices.clear(); + this.indices.put(indices, offset, length); + this.indices.flip(); + } + + public void draw(int primitiveType, int offset, int numVertices) { + GL10 gl = glGraphics.getGL(); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + vertices.position(0); + gl.glVertexPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + + if(hasColor) { + gl.glEnableClientState(GL10.GL_COLOR_ARRAY); + vertices.position(2); + gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices); + } + + if(hasTexCoords) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + vertices.position(hasColor?6:2); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + } + + if(indices!=null) { + indices.position(offset); + gl.glDrawElements(primitiveType, numVertices, GL10.GL_UNSIGNED_SHORT, indices); + } else { + gl.glDrawArrays(primitiveType, offset, numVertices); + } + + if(hasTexCoords) + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + if(hasColor) + gl.glDisableClientState(GL10.GL_COLOR_ARRAY); + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java new file mode 100644 index 0000000..4820c5a --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java @@ -0,0 +1,48 @@ +package com.badlogic.androidgames.framework.impl; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + +public class AccelerometerHandler implements SensorEventListener { + float accelX; + float accelY; + float accelZ; + + public AccelerometerHandler(Context context) { + SensorManager manager = (SensorManager) context + .getSystemService(Context.SENSOR_SERVICE); + if (manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() != 0) { + Sensor accelerometer = manager.getSensorList( + Sensor.TYPE_ACCELEROMETER).get(0); + manager.registerListener(this, accelerometer, + SensorManager.SENSOR_DELAY_GAME); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // nothing to do here + } + + @Override + public void onSensorChanged(SensorEvent event) { + accelX = event.values[0]; + accelY = event.values[1]; + accelZ = event.values[2]; + } + + public float getAccelX() { + return accelX; + } + + public float getAccelY() { + return accelY; + } + + public float getAccelZ() { + return accelZ; + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java new file mode 100644 index 0000000..42f8c1f --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java @@ -0,0 +1,45 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; + +import android.app.Activity; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.media.AudioManager; +import android.media.SoundPool; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.Music; +import com.badlogic.androidgames.framework.Sound; + +public class AndroidAudio implements Audio { + AssetManager assets; + SoundPool soundPool; + + public AndroidAudio(Activity activity) { + activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); + this.assets = activity.getAssets(); + this.soundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0); + } + + @Override + public Music newMusic(String filename) { + try { + AssetFileDescriptor assetDescriptor = assets.openFd(filename); + return new AndroidMusic(assetDescriptor); + } catch (IOException e) { + throw new RuntimeException("Couldn't load music '" + filename + "'"); + } + } + + @Override + public Sound newSound(String filename) { + try { + AssetFileDescriptor assetDescriptor = assets.openFd(filename); + int soundId = soundPool.load(assetDescriptor, 0); + return new AndroidSound(soundPool, soundId); + } catch (IOException e) { + throw new RuntimeException("Couldn't load sound '" + filename + "'"); + } + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java new file mode 100644 index 0000000..6171d90 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java @@ -0,0 +1,60 @@ +package com.badlogic.androidgames.framework.impl; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class AndroidFastRenderView extends SurfaceView implements Runnable { + AndroidGame game; + Bitmap framebuffer; + Thread renderThread = null; + SurfaceHolder holder; + volatile boolean running = false; + + public AndroidFastRenderView(AndroidGame game, Bitmap framebuffer) { + super(game); + this.game = game; + this.framebuffer = framebuffer; + this.holder = getHolder(); + } + + public void resume() { + running = true; + renderThread = new Thread(this); + renderThread.start(); + } + + public void run() { + Rect dstRect = new Rect(); + long startTime = System.nanoTime(); + while(running) { + if(!holder.getSurface().isValid()) + continue; + + float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; + startTime = System.nanoTime(); + + game.getCurrentScreen().update(deltaTime); + game.getCurrentScreen().present(deltaTime); + + Canvas canvas = holder.lockCanvas(); + canvas.getClipBounds(dstRect); + canvas.drawBitmap(framebuffer, null, dstRect, null); + holder.unlockCanvasAndPost(canvas); + } + } + + public void pause() { + running = false; + while(true) { + try { + renderThread.join(); + break; + } catch (InterruptedException e) { + // retry + } + } + } +} \ No newline at end of file diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java new file mode 100644 index 0000000..b13b115 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java @@ -0,0 +1,39 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import android.content.res.AssetManager; +import android.os.Environment; + +import com.badlogic.androidgames.framework.FileIO; + +public class AndroidFileIO implements FileIO { + AssetManager assets; + String externalStoragePath; + + public AndroidFileIO(AssetManager assets) { + this.assets = assets; + this.externalStoragePath = Environment.getExternalStorageDirectory() + .getAbsolutePath() + File.separator; + } + + @Override + public InputStream readAsset(String fileName) throws IOException { + return assets.open(fileName); + } + + @Override + public InputStream readFile(String fileName) throws IOException { + return new FileInputStream(externalStoragePath + fileName); + } + + @Override + public OutputStream writeFile(String fileName) throws IOException { + return new FileOutputStream(externalStoragePath + fileName); + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidGame.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidGame.java new file mode 100644 index 0000000..db6adc7 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidGame.java @@ -0,0 +1,115 @@ +package com.badlogic.androidgames.framework.impl; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.view.Window; +import android.view.WindowManager; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input; +import com.badlogic.androidgames.framework.Screen; + +public abstract class AndroidGame extends Activity implements Game { + AndroidFastRenderView renderView; + Graphics graphics; + Audio audio; + Input input; + FileIO fileIO; + Screen screen; + WakeLock wakeLock; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + + boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + int frameBufferWidth = isLandscape ? 480 : 320; + int frameBufferHeight = isLandscape ? 320 : 480; + Bitmap frameBuffer = Bitmap.createBitmap(frameBufferWidth, + frameBufferHeight, Config.RGB_565); + + float scaleX = (float) frameBufferWidth + / getWindowManager().getDefaultDisplay().getWidth(); + float scaleY = (float) frameBufferHeight + / getWindowManager().getDefaultDisplay().getHeight(); + + renderView = new AndroidFastRenderView(this, frameBuffer); + graphics = new AndroidGraphics(getAssets(), frameBuffer); + fileIO = new AndroidFileIO(getAssets()); + audio = new AndroidAudio(this); + input = new AndroidInput(this, renderView, scaleX, scaleY); + screen = getStartScreen(); + setContentView(renderView); + + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GLGame"); + } + + @Override + public void onResume() { + super.onResume(); + wakeLock.acquire(); + screen.resume(); + renderView.resume(); + } + + @Override + public void onPause() { + super.onPause(); + wakeLock.release(); + renderView.pause(); + screen.pause(); + + if (isFinishing()) + screen.dispose(); + } + + @Override + public Input getInput() { + return input; + } + + @Override + public FileIO getFileIO() { + return fileIO; + } + + @Override + public Graphics getGraphics() { + return graphics; + } + + @Override + public Audio getAudio() { + return audio; + } + + @Override + public void setScreen(Screen screen) { + if (screen == null) + throw new IllegalArgumentException("Screen must not be null"); + + this.screen.pause(); + this.screen.dispose(); + screen.resume(); + screen.update(0); + this.screen = screen; + } + + public Screen getCurrentScreen() { + return screen; + } +} \ No newline at end of file diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java new file mode 100644 index 0000000..ade52b5 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java @@ -0,0 +1,133 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; +import java.io.InputStream; + +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.BitmapFactory.Options; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Rect; + +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Pixmap; + +public class AndroidGraphics implements Graphics { + AssetManager assets; + Bitmap frameBuffer; + Canvas canvas; + Paint paint; + Rect srcRect = new Rect(); + Rect dstRect = new Rect(); + + public AndroidGraphics(AssetManager assets, Bitmap frameBuffer) { + this.assets = assets; + this.frameBuffer = frameBuffer; + this.canvas = new Canvas(frameBuffer); + this.paint = new Paint(); + } + + @Override + public Pixmap newPixmap(String fileName, PixmapFormat format) { + Config config = null; + if (format == PixmapFormat.RGB565) + config = Config.RGB_565; + else if (format == PixmapFormat.ARGB4444) + config = Config.ARGB_4444; + else + config = Config.ARGB_8888; + + Options options = new Options(); + options.inPreferredConfig = config; + + InputStream in = null; + Bitmap bitmap = null; + try { + in = assets.open(fileName); + bitmap = BitmapFactory.decodeStream(in, null, options); + if (bitmap == null) + throw new RuntimeException("Couldn't load bitmap from asset '" + + fileName + "'"); + } catch (IOException e) { + throw new RuntimeException("Couldn't load bitmap from asset '" + + fileName + "'"); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + + if (bitmap.getConfig() == Config.RGB_565) + format = PixmapFormat.RGB565; + else if (bitmap.getConfig() == Config.ARGB_4444) + format = PixmapFormat.ARGB4444; + else + format = PixmapFormat.ARGB8888; + + return new AndroidPixmap(bitmap, format); + } + + @Override + public void clear(int color) { + canvas.drawRGB((color & 0xff0000) >> 16, (color & 0xff00) >> 8, + (color & 0xff)); + } + + @Override + public void drawPixel(int x, int y, int color) { + paint.setColor(color); + canvas.drawPoint(x, y, paint); + } + + @Override + public void drawLine(int x, int y, int x2, int y2, int color) { + paint.setColor(color); + canvas.drawLine(x, y, x2, y2, paint); + } + + @Override + public void drawRect(int x, int y, int width, int height, int color) { + paint.setColor(color); + paint.setStyle(Style.FILL); + canvas.drawRect(x, y, x + width - 1, y + height - 1, paint); + } + + @Override + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight) { + srcRect.left = srcX; + srcRect.top = srcY; + srcRect.right = srcX + srcWidth - 1; + srcRect.bottom = srcY + srcHeight - 1; + + dstRect.left = x; + dstRect.top = y; + dstRect.right = x + srcWidth - 1; + dstRect.bottom = y + srcHeight - 1; + + canvas.drawBitmap(((AndroidPixmap) pixmap).bitmap, srcRect, dstRect, + null); + } + + @Override + public void drawPixmap(Pixmap pixmap, int x, int y) { + canvas.drawBitmap(((AndroidPixmap)pixmap).bitmap, x, y, null); + } + + @Override + public int getWidth() { + return frameBuffer.getWidth(); + } + + @Override + public int getHeight() { + return frameBuffer.getHeight(); + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidInput.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidInput.java new file mode 100644 index 0000000..11f5863 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidInput.java @@ -0,0 +1,69 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.List; + +import android.content.Context; +import android.os.Build.VERSION; +import android.view.View; + +import com.badlogic.androidgames.framework.Input; + +public class AndroidInput implements Input { + AccelerometerHandler accelHandler; + KeyboardHandler keyHandler; + TouchHandler touchHandler; + + public AndroidInput(Context context, View view, float scaleX, float scaleY) { + accelHandler = new AccelerometerHandler(context); + keyHandler = new KeyboardHandler(view); + if(Integer.parseInt(VERSION.SDK) < 5) + touchHandler = new SingleTouchHandler(view, scaleX, scaleY); + else + touchHandler = new MultiTouchHandler(view, scaleX, scaleY); + } + + @Override + public boolean isKeyPressed(int keyCode) { + return keyHandler.isKeyPressed(keyCode); + } + + @Override + public boolean isTouchDown(int pointer) { + return touchHandler.isTouchDown(pointer); + } + + @Override + public int getTouchX(int pointer) { + return touchHandler.getTouchX(pointer); + } + + @Override + public int getTouchY(int pointer) { + return touchHandler.getTouchY(pointer); + } + + @Override + public float getAccelX() { + return accelHandler.getAccelX(); + } + + @Override + public float getAccelY() { + return accelHandler.getAccelY(); + } + + @Override + public float getAccelZ() { + return accelHandler.getAccelZ(); + } + + @Override + public List getTouchEvents() { + return touchHandler.getTouchEvents(); + } + + @Override + public List getKeyEvents() { + return keyHandler.getKeyEvents(); + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java new file mode 100644 index 0000000..9e9fd1c --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java @@ -0,0 +1,99 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; + +import android.content.res.AssetFileDescriptor; +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnCompletionListener; + +import com.badlogic.androidgames.framework.Music; + +public class AndroidMusic implements Music, OnCompletionListener { + MediaPlayer mediaPlayer; + boolean isPrepared = false; + + public AndroidMusic(AssetFileDescriptor assetDescriptor) { + mediaPlayer = new MediaPlayer(); + try { + mediaPlayer.setDataSource(assetDescriptor.getFileDescriptor(), + assetDescriptor.getStartOffset(), + assetDescriptor.getLength()); + mediaPlayer.prepare(); + isPrepared = true; + mediaPlayer.setOnCompletionListener(this); + } catch (Exception e) { + throw new RuntimeException("Couldn't load music"); + } + } + + @Override + public void dispose() { + if (mediaPlayer.isPlaying()) + mediaPlayer.stop(); + mediaPlayer.release(); + } + + @Override + public boolean isLooping() { + return mediaPlayer.isLooping(); + } + + @Override + public boolean isPlaying() { + return mediaPlayer.isPlaying(); + } + + @Override + public boolean isStopped() { + return !isPrepared; + } + + @Override + public void pause() { + if (mediaPlayer.isPlaying()) + mediaPlayer.pause(); + } + + @Override + public void play() { + if (mediaPlayer.isPlaying()) + return; + + try { + synchronized (this) { + if (!isPrepared) + mediaPlayer.prepare(); + mediaPlayer.start(); + } + } catch (IllegalStateException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void setLooping(boolean isLooping) { + mediaPlayer.setLooping(isLooping); + } + + @Override + public void setVolume(float volume) { + mediaPlayer.setVolume(volume, volume); + } + + @Override + public void stop() { + mediaPlayer.stop(); + synchronized (this) { + isPrepared = false; + } + } + + @Override + public void onCompletion(MediaPlayer arg0) { + synchronized (this) { + isPrepared = false; + } + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java new file mode 100644 index 0000000..9de5021 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework.impl; + +import android.graphics.Bitmap; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; +import com.badlogic.androidgames.framework.Pixmap; + +public class AndroidPixmap implements Pixmap { + Bitmap bitmap; + PixmapFormat format; + + public AndroidPixmap(Bitmap bitmap, PixmapFormat format) { + this.bitmap = bitmap; + this.format = format; + } + + @Override + public int getWidth() { + return bitmap.getWidth(); + } + + @Override + public int getHeight() { + return bitmap.getHeight(); + } + + @Override + public PixmapFormat getFormat() { + return format; + } + + @Override + public void dispose() { + bitmap.recycle(); + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidSound.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidSound.java new file mode 100644 index 0000000..4a8fcd4 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/AndroidSound.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework.impl; + +import android.media.SoundPool; + +import com.badlogic.androidgames.framework.Sound; + +public class AndroidSound implements Sound { + int soundId; + SoundPool soundPool; + + public AndroidSound(SoundPool soundPool,int soundId) { + this.soundId = soundId; + this.soundPool = soundPool; + } + + @Override + public void play(float volume) { + soundPool.play(soundId, volume, volume, 0, 0, 1); + } + + @Override + public void dispose() { + soundPool.unload(soundId); + } + +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/GLGame.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/GLGame.java new file mode 100644 index 0000000..d07c196 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/GLGame.java @@ -0,0 +1,178 @@ +package com.badlogic.androidgames.framework.impl; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import android.app.Activity; +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.opengl.GLSurfaceView.Renderer; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.view.Window; +import android.view.WindowManager; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input; +import com.badlogic.androidgames.framework.Screen; + +public abstract class GLGame extends Activity implements Game, Renderer { + enum GLGameState { + Initialized, + Running, + Paused, + Finished, + Idle + } + + GLSurfaceView glView; + GLGraphics glGraphics; + Audio audio; + Input input; + FileIO fileIO; + Screen screen; + GLGameState state = GLGameState.Initialized; + Object stateChanged = new Object(); + long startTime = System.nanoTime(); + WakeLock wakeLock; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + glView = new GLSurfaceView(this); + glView.setRenderer(this); + setContentView(glView); + + glGraphics = new GLGraphics(glView); + fileIO = new AndroidFileIO(getAssets()); + audio = new AndroidAudio(this); + input = new AndroidInput(this, glView, 1, 1); + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GLGame"); + } + + public void onResume() { + super.onResume(); + glView.onResume(); + wakeLock.acquire(); + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + glGraphics.setGL(gl); + + synchronized(stateChanged) { + if(state == GLGameState.Initialized) + screen = getStartScreen(); + state = GLGameState.Running; + screen.resume(); + startTime = System.nanoTime(); + } + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + } + + @Override + public void onDrawFrame(GL10 gl) { + GLGameState state = null; + + synchronized(stateChanged) { + state = this.state; + } + + if(state == GLGameState.Running) { + float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; + startTime = System.nanoTime(); + + screen.update(deltaTime); + screen.present(deltaTime); + } + + if(state == GLGameState.Paused) { + screen.pause(); + synchronized(stateChanged) { + this.state = GLGameState.Idle; + stateChanged.notifyAll(); + } + } + + if(state == GLGameState.Finished) { + screen.pause(); + screen.dispose(); + synchronized(stateChanged) { + this.state = GLGameState.Idle; + stateChanged.notifyAll(); + } + } + } + + @Override + public void onPause() { + synchronized(stateChanged) { + if(isFinishing()) + state = GLGameState.Finished; + else + state = GLGameState.Paused; + while(true) { + try { + stateChanged.wait(); + break; + } catch(InterruptedException e) { + } + } + } + wakeLock.release(); + glView.onPause(); + super.onPause(); + } + + public GLGraphics getGLGraphics() { + return glGraphics; + } + + @Override + public Input getInput() { + return input; + } + + @Override + public FileIO getFileIO() { + return fileIO; + } + + @Override + public Graphics getGraphics() { + throw new IllegalStateException("We are using OpenGL!"); + } + + @Override + public Audio getAudio() { + return audio; + } + + @Override + public void setScreen(Screen screen) { + if (screen == null) + throw new IllegalArgumentException("Screen must not be null"); + + this.screen.pause(); + this.screen.dispose(); + screen.resume(); + screen.update(0); + this.screen = screen; + } + + @Override + public Screen getCurrentScreen() { + return screen; + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/GLGraphics.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/GLGraphics.java new file mode 100644 index 0000000..2eb1b43 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/GLGraphics.java @@ -0,0 +1,30 @@ +package com.badlogic.androidgames.framework.impl; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLSurfaceView; + +public class GLGraphics { + GLSurfaceView glView; + GL10 gl; + + GLGraphics(GLSurfaceView glView) { + this.glView = glView; + } + + public GL10 getGL() { + return gl; + } + + void setGL(GL10 gl) { + this.gl = gl; + } + + public int getWidth() { + return glView.getWidth(); + } + + public int getHeight() { + return glView.getHeight(); + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java new file mode 100644 index 0000000..f70d3e9 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java @@ -0,0 +1,73 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.View; +import android.view.View.OnKeyListener; + +import com.badlogic.androidgames.framework.Input.KeyEvent; +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class KeyboardHandler implements OnKeyListener { + boolean[] pressedKeys = new boolean[128]; + Pool keyEventPool; + List keyEventsBuffer = new ArrayList(); + List keyEvents = new ArrayList(); + + public KeyboardHandler(View view) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public KeyEvent createObject() { + return new KeyEvent(); + } + }; + keyEventPool = new Pool(factory, 100); + view.setOnKeyListener(this); + view.setFocusableInTouchMode(true); + view.requestFocus(); + } + + @Override + public boolean onKey(View v, int keyCode, android.view.KeyEvent event) { + if (event.getAction() == android.view.KeyEvent.ACTION_MULTIPLE) + return false; + + synchronized (this) { + KeyEvent keyEvent = keyEventPool.newObject(); + keyEvent.keyCode = keyCode; + keyEvent.keyChar = (char) event.getUnicodeChar(); + if (event.getAction() == android.view.KeyEvent.ACTION_DOWN) { + keyEvent.type = KeyEvent.KEY_DOWN; + if(keyCode >= 0 && keyCode < 128) + pressedKeys[keyCode] = true; + } + if (event.getAction() == android.view.KeyEvent.ACTION_UP) { + keyEvent.type = KeyEvent.KEY_UP; + if(keyCode >= 0 && keyCode < 128) + pressedKeys[keyCode] = false; + } + keyEventsBuffer.add(keyEvent); + } + return false; + } + + public boolean isKeyPressed(int keyCode) { + if (keyCode < 0 || keyCode > 127) + return false; + return pressedKeys[keyCode]; + } + + public List getKeyEvents() { + synchronized (this) { + int len = keyEvents.size(); + for (int i = 0; i < len; i++) + keyEventPool.free(keyEvents.get(i)); + keyEvents.clear(); + keyEvents.addAll(keyEventsBuffer); + keyEventsBuffer.clear(); + return keyEvents; + } + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java new file mode 100644 index 0000000..4668c57 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java @@ -0,0 +1,137 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.MotionEvent; +import android.view.View; + +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class MultiTouchHandler implements TouchHandler { + boolean[] isTouched = new boolean[20]; + int[] touchX = new int[20]; + int[] touchY = new int[20]; + Pool touchEventPool; + List touchEvents = new ArrayList(); + List touchEventsBuffer = new ArrayList(); + float scaleX; + float scaleY; + + public MultiTouchHandler(View view, float scaleX, float scaleY) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }; + touchEventPool = new Pool(factory, 100); + view.setOnTouchListener(this); + + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + synchronized (this) { + int action = event.getAction() & MotionEvent.ACTION_MASK; + int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; + int pointerId = event.getPointerId(pointerIndex); + TouchEvent touchEvent; + + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_DOWN; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + isTouched[pointerId] = true; + touchEventsBuffer.add(touchEvent); + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_CANCEL: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_UP; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + isTouched[pointerId] = false; + touchEventsBuffer.add(touchEvent); + break; + + case MotionEvent.ACTION_MOVE: + int pointerCount = event.getPointerCount(); + for (int i = 0; i < pointerCount; i++) { + pointerIndex = i; + pointerId = event.getPointerId(pointerIndex); + + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_DRAGGED; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + touchEventsBuffer.add(touchEvent); + } + break; + } + + return true; + } + } + + @Override + public boolean isTouchDown(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return false; + else + return isTouched[pointer]; + } + } + + @Override + public int getTouchX(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return 0; + else + return touchX[pointer]; + } + } + + @Override + public int getTouchY(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return 0; + else + return touchY[pointer]; + } + } + + @Override + public List getTouchEvents() { + synchronized (this) { + int len = touchEvents.size(); + for (int i = 0; i < len; i++) + touchEventPool.free(touchEvents.get(i)); + touchEvents.clear(); + touchEvents.addAll(touchEventsBuffer); + touchEventsBuffer.clear(); + return touchEvents; + } + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java new file mode 100644 index 0000000..7ff1205 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java @@ -0,0 +1,101 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.MotionEvent; +import android.view.View; + +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class SingleTouchHandler implements TouchHandler { + boolean isTouched; + int touchX; + int touchY; + Pool touchEventPool; + List touchEvents = new ArrayList(); + List touchEventsBuffer = new ArrayList(); + float scaleX; + float scaleY; + + public SingleTouchHandler(View view, float scaleX, float scaleY) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }; + touchEventPool = new Pool(factory, 100); + view.setOnTouchListener(this); + + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + synchronized(this) { + TouchEvent touchEvent = touchEventPool.newObject(); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touchEvent.type = TouchEvent.TOUCH_DOWN; + isTouched = true; + break; + case MotionEvent.ACTION_MOVE: + touchEvent.type = TouchEvent.TOUCH_DRAGGED; + isTouched = true; + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + touchEvent.type = TouchEvent.TOUCH_UP; + isTouched = false; + break; + } + + touchEvent.x = touchX = (int)(event.getX() * scaleX); + touchEvent.y = touchY = (int)(event.getY() * scaleY); + touchEventsBuffer.add(touchEvent); + + return true; + } + } + + @Override + public boolean isTouchDown(int pointer) { + synchronized(this) { + if(pointer == 0) + return isTouched; + else + return false; + } + } + + @Override + public int getTouchX(int pointer) { + synchronized(this) { + return touchX; + } + } + + @Override + public int getTouchY(int pointer) { + synchronized(this) { + return touchY; + } + } + + @Override + public List getTouchEvents() { + synchronized(this) { + int len = touchEvents.size(); + for( int i = 0; i < len; i++ ) + touchEventPool.free(touchEvents.get(i)); + touchEvents.clear(); + touchEvents.addAll(touchEventsBuffer); + touchEventsBuffer.clear(); + return touchEvents; + } + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/TouchHandler.java b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/TouchHandler.java new file mode 100644 index 0000000..cc7e754 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/framework/impl/TouchHandler.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.List; + +import android.view.View.OnTouchListener; + +import com.badlogic.androidgames.framework.Input.TouchEvent; + +public interface TouchHandler extends OnTouchListener { + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public List getTouchEvents(); +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/BlendingTest.java b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/BlendingTest.java new file mode 100644 index 0000000..8303a79 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/BlendingTest.java @@ -0,0 +1,89 @@ +package com.badlogic.androidgames.glbasics; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.Vertices; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class BlendingTest extends GLGame { + public Screen getStartScreen() { + return new BlendingScreen(this); + } + + class BlendingScreen extends Screen { + GLGraphics glGraphics; + Vertices vertices; + Texture textureRgb; + Texture textureRgba; + + public BlendingScreen(Game game) { + super(game); + glGraphics = ((GLGame)game).getGLGraphics(); + + textureRgb = new Texture((GLGame)game, "bobrgb888.png"); + textureRgba = new Texture((GLGame)game, "bobargb8888.png"); + + vertices = new Vertices(glGraphics, 8, 12, true, true); + float[] rects = new float[] { + 100, 100, 1, 1, 1, 0.5f, 0, 1, + 228, 100, 1, 1, 1, 0.5f, 1, 1, + 228, 228, 1, 1, 1, 0.5f, 1, 0, + 100, 228, 1, 1, 1, 0.5f, 0, 0, + + 100, 300, 1, 1, 1, 1, 0, 1, + 228, 300, 1, 1, 1, 1, 1, 1, + 228, 428, 1, 1, 1, 1, 1, 0, + 100, 428, 1, 1, 1, 1, 0, 0 + }; + vertices.setVertices(rects, 0, rects.length); + vertices.setIndices(new short[] {0, 1, 2, 2, 3, 0, + 4, 5, 6, 6, 7, 4 }, 0, 12); + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glClearColor(1,0,0,1); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(0, 320, 0, 480, 1, -1); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + gl.glEnable(GL10.GL_TEXTURE_2D); + textureRgb.bind(); + vertices.draw(GL10.GL_TRIANGLES, 0, 6 ); + + textureRgba.bind(); + vertices.draw(GL10.GL_TRIANGLES, 6, 6 ); + } + + @Override + public void update(float deltaTime) { + game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + } + + @Override + public void pause() { + + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/Bob.java b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/Bob.java new file mode 100644 index 0000000..3028f8e --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/Bob.java @@ -0,0 +1,41 @@ +package com.badlogic.androidgames.glbasics; + +import java.util.Random; + +class Bob { + static final Random rand = new Random(); + public float x, y; + float dirX, dirY; + + public Bob() { + x = rand.nextFloat() * 320; + y = rand.nextFloat() * 480; + dirX = 50; + dirY = 50; + } + + public void update(float deltaTime) { + x = x + dirX * deltaTime; + y = y + dirY * deltaTime; + + if (x < 0) { + dirX = -dirX; + x = 0; + } + + if (x > 320) { + dirX = -dirX; + x = 320; + } + + if (y < 0) { + dirY = -dirY; + y = 0; + } + + if (y > 480) { + dirY = -dirY; + y = 480; + } + } +} \ No newline at end of file diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/BobTest.java b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/BobTest.java new file mode 100644 index 0000000..f267793 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/BobTest.java @@ -0,0 +1,102 @@ +package com.badlogic.androidgames.glbasics; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.FPSCounter; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.Vertices; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class BobTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new BobScreen(this); + } + + class BobScreen extends Screen { + static final int NUM_BOBS = 100; + GLGraphics glGraphics; + Texture bobTexture; + Vertices bobModel; + Bob[] bobs; + FPSCounter fpsCounter; + + public BobScreen(Game game) { + super(game); + glGraphics = ((GLGame)game).getGLGraphics(); + + bobTexture = new Texture((GLGame)game, "bobrgb888.png"); + + bobModel = new Vertices(glGraphics, 4, 12, false, true); + bobModel.setVertices(new float[] { -16, -16, 0, 1, + 16, -16, 1, 1, + 16, 16, 1, 0, + -16, 16, 0, 0, }, 0, 16); + bobModel.setIndices(new short[] {0, 1, 2, 2, 3, 0}, 0, 6); + + + bobs = new Bob[100]; + for(int i = 0; i < 100; i++) { + bobs[i] = new Bob(); + } + + fpsCounter = new FPSCounter(); + } + + @Override + public void update(float deltaTime) { + game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + for(int i = 0; i < NUM_BOBS; i++) { + bobs[i].update(deltaTime); + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClearColor(1,0,0,1); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(0, 320, 0, 480, 1, -1); + + gl.glEnable(GL10.GL_TEXTURE_2D); + bobTexture.bind(); + + gl.glMatrixMode(GL10.GL_MODELVIEW); + for(int i = 0; i < NUM_BOBS; i++) { + gl.glLoadIdentity(); + gl.glTranslatef(bobs[i].x, bobs[i].y, 0); + gl.glRotatef(45, 0, 0, 1); + gl.glScalef(2, 0.5f, 0); + bobModel.draw(GL10.GL_TRIANGLES, 0, 6); + } + + fpsCounter.logFrame(); + } + + @Override + public void pause() { + // TODO Auto-generated method stub + + } + + @Override + public void resume() { + // TODO Auto-generated method stub + + } + + @Override + public void dispose() { + // TODO Auto-generated method stub + + } + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/ColoredTriangleTest.java b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/ColoredTriangleTest.java new file mode 100644 index 0000000..e9ce093 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/ColoredTriangleTest.java @@ -0,0 +1,79 @@ +package com.badlogic.androidgames.glbasics; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class ColoredTriangleTest extends GLGame { + @Override + public Screen getStartScreen() { + return new ColoredTriangleScreen(this); + } + + class ColoredTriangleScreen extends Screen { + final int VERTEX_SIZE = (2 + 4) * 4; + GLGraphics glGraphics; + FloatBuffer vertices; + + public ColoredTriangleScreen(Game game) { + super(game); + glGraphics = ((GLGame) game).getGLGraphics(); + + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(3 * VERTEX_SIZE); + byteBuffer.order(ByteOrder.nativeOrder()); + vertices = byteBuffer.asFloatBuffer(); + vertices.put( new float[] { 0.0f, 0.0f, 1, 0, 0, 1, + 319.0f, 0.0f, 0, 1, 0, 1, + 160.0f, 479.0f, 0, 0, 1, 1}); + vertices.flip(); + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(0, 320, 0, 480, 1, -1); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + gl.glEnableClientState(GL10.GL_COLOR_ARRAY); + + vertices.position(0); + gl.glVertexPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, vertices); + vertices.position(2); + gl.glColorPointer(4, GL10.GL_FLOAT, VERTEX_SIZE, vertices); + + gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3); + } + + @Override + public void update(float deltaTime) { + game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + } + + @Override + public void pause() { + + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/FirstTriangleTest.java b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/FirstTriangleTest.java new file mode 100644 index 0000000..454824a --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/FirstTriangleTest.java @@ -0,0 +1,73 @@ +package com.badlogic.androidgames.glbasics; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class FirstTriangleTest extends GLGame { + @Override + public Screen getStartScreen() { + return new FirstTriangleScreen(this); + } + + class FirstTriangleScreen extends Screen { + GLGraphics glGraphics; + FloatBuffer vertices; + + public FirstTriangleScreen(Game game) { + super(game); + glGraphics = ((GLGame)game).getGLGraphics(); + + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(3 * 2 * 4); + byteBuffer.order(ByteOrder.nativeOrder()); + vertices = byteBuffer.asFloatBuffer(); + vertices.put( new float[] { 0.0f, 0.0f, + 319.0f, 0.0f, + 160.0f, 479.0f}); + vertices.flip(); + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(0, 320, 0, 480, 1, -1); + + gl.glColor4f(1, 0, 0, 1); + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + gl.glVertexPointer( 2, GL10.GL_FLOAT, 0, vertices); + gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3); + } + + @Override + public void update(float deltaTime) { + game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + } + + @Override + public void pause() { + + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/GLBasicsStarter.java b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/GLBasicsStarter.java new file mode 100644 index 0000000..af37b82 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/GLBasicsStarter.java @@ -0,0 +1,34 @@ +package com.badlogic.androidgames.glbasics; + +import android.app.ListActivity; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +public class GLBasicsStarter extends ListActivity { + String tests[] = { "GLSurfaceViewTest", "GLGameTest", "FirstTriangleTest", + "ColoredTriangleTest", "TexturedTriangleTest", "IndexedTest", + "BlendingTest", "BobTest", "OptimizedBobTest"}; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setListAdapter(new ArrayAdapter(this, + android.R.layout.simple_list_item_1, tests)); + } + + @Override + protected void onListItemClick(ListView list, View view, int position,long id) { + super.onListItemClick(list, view, position, id); + String testName = tests[position]; + try { + Class clazz = Class + .forName("com.badlogic.androidgames.glbasics." + testName); + Intent intent = new Intent(this, clazz); + startActivity(intent); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/GLGameTest.java b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/GLGameTest.java new file mode 100644 index 0000000..b350da2 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/GLGameTest.java @@ -0,0 +1,51 @@ +package com.badlogic.androidgames.glbasics; + +import java.util.Random; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class GLGameTest extends GLGame { + @Override + public Screen getStartScreen() { + return new TestScreen(this); + } + + class TestScreen extends Screen { + GLGraphics glGraphics; + Random rand = new Random(); + + public TestScreen(Game game) { + super(game); + glGraphics = ((GLGame) game).getGLGraphics(); + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClearColor(rand.nextFloat(), rand.nextFloat(), + rand.nextFloat(), 1); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + } + + @Override + public void update(float deltaTime) { + } + + @Override + public void pause() { + } + + @Override + public void resume() { + } + + @Override + public void dispose() { + } + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/GLSurfaceViewTest.java b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/GLSurfaceViewTest.java new file mode 100644 index 0000000..a468bab --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/GLSurfaceViewTest.java @@ -0,0 +1,62 @@ +package com.badlogic.androidgames.glbasics; + +import java.util.Random; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import android.app.Activity; +import android.opengl.GLSurfaceView; +import android.opengl.GLSurfaceView.Renderer; +import android.os.Bundle; +import android.util.Log; +import android.view.Window; +import android.view.WindowManager; + +public class GLSurfaceViewTest extends Activity { + GLSurfaceView glView; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + glView = new GLSurfaceView(this); + glView.setRenderer(new SimpleRenderer()); + setContentView(glView); + } + + @Override + public void onResume() { + super.onResume(); + glView.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + glView.onPause(); + } + + static class SimpleRenderer implements Renderer { + Random rand = new Random(); + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + Log.d("GLSurfaceViewTest", "surface created"); + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + Log.d("GLSurfaceViewTest", "surface changed: " + width + "x" + + height); + } + + @Override + public void onDrawFrame(GL10 gl) { + gl.glClearColor(rand.nextFloat(), rand.nextFloat(), + rand.nextFloat(), 1); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + } + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/IndexedTest.java b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/IndexedTest.java new file mode 100644 index 0000000..bd0f0b7 --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/IndexedTest.java @@ -0,0 +1,97 @@ +package com.badlogic.androidgames.glbasics; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.ShortBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class IndexedTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new IndexedScreen(this); + } + +class IndexedScreen extends Screen { + final int VERTEX_SIZE = (2 + 2) * 4; + GLGraphics glGraphics; + FloatBuffer vertices; + ShortBuffer indices; + Texture texture; + + public IndexedScreen(Game game) { + super(game); + glGraphics = ((GLGame) game).getGLGraphics(); + + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(4 * VERTEX_SIZE); + byteBuffer.order(ByteOrder.nativeOrder()); + vertices = byteBuffer.asFloatBuffer(); + vertices.put(new float[] { 100.0f, 100.0f, 0.0f, 1.0f, + 228.0f, 100.0f, 1.0f, 1.0f, + 228.0f, 228.0f, 1.0f, 0.0f, + 100.0f, 228.0f, 0.0f, 0.0f }); + vertices.flip(); + + byteBuffer = ByteBuffer.allocateDirect(6 * 2); + byteBuffer.order(ByteOrder.nativeOrder()); + indices = byteBuffer.asShortBuffer(); + indices.put(new short[] { 0, 1, 2, + 2, 3, 0 }); + indices.flip(); + + texture = new Texture((GLGame)game, "bobrgb888.png"); + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(0, 320, 0, 480, 1, -1); + + gl.glEnable(GL10.GL_TEXTURE_2D); + texture.bind(); + + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + + vertices.position(0); + gl.glVertexPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, vertices); + vertices.position(2); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, vertices); + + gl.glDrawElements(GL10.GL_TRIANGLES, 6, GL10.GL_UNSIGNED_SHORT, indices); + } + + @Override + public void update(float deltaTime) { + game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + } + + @Override + public void pause() { + + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } + } +} diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/OptimizedBobTest.java b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/OptimizedBobTest.java new file mode 100644 index 0000000..755116f --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/OptimizedBobTest.java @@ -0,0 +1,105 @@ +package com.badlogic.androidgames.glbasics; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.BindableVertices; +import com.badlogic.androidgames.framework.gl.FPSCounter; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class OptimizedBobTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new OptimizedBobScreen(this); + } + + class OptimizedBobScreen extends Screen { + static final int NUM_BOBS = 100; + GLGraphics glGraphics; + Texture bobTexture; + BindableVertices bobModel; + Bob[] bobs; + FPSCounter fpsCounter; + + public OptimizedBobScreen(Game game) { + super(game); + glGraphics = ((GLGame)game).getGLGraphics(); + + bobTexture = new Texture((GLGame)game, "bobrgb888-32x32.png"); + + bobModel = new BindableVertices(glGraphics, 4, 12, false, true); + bobModel.setVertices(new float[] { -16, -16, 0, 1, + 16, -16, 1, 1, + 16, 16, 1, 0, + -16, 16, 0, 0, }, 0, 16); + bobModel.setIndices(new short[] {0, 1, 2, 2, 3, 0}, 0, 6); + + + bobs = new Bob[100]; + for(int i = 0; i < 100; i++) { + bobs[i] = new Bob(); + } + + fpsCounter = new FPSCounter(); + } + + @Override + public void update(float deltaTime) { + game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + for(int i = 0; i < NUM_BOBS; i++) { + bobs[i].update(deltaTime); + } + } + + @Override + public void resume() { + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glClearColor(1, 0, 0, 1); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(0, 320, 0, 480, 1, -1); + + gl.glMatrixMode(GL10.GL_MODELVIEW); + + bobTexture.reload(); + gl.glEnable(GL10.GL_TEXTURE_2D); + bobTexture.bind(); + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + + bobModel.bind(); + for(int i = 0; i < NUM_BOBS; i++) { + gl.glLoadIdentity(); + gl.glTranslatef((int)bobs[i].x, (int)bobs[i].y, 0); + bobModel.draw(GL10.GL_TRIANGLES, 0, 6); + } + bobModel.unbind(); + + fpsCounter.logFrame(); + } + + @Override + public void pause() { + // TODO Auto-generated method stub + + } + + @Override + public void dispose() { + // TODO Auto-generated method stub + + } + } +} + diff --git a/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/TexturedTriangleTest.java b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/TexturedTriangleTest.java new file mode 100644 index 0000000..0eabc6f --- /dev/null +++ b/ch07-gl-basics/src/com/badlogic/androidgames/glbasics/TexturedTriangleTest.java @@ -0,0 +1,110 @@ +package com.badlogic.androidgames.glbasics; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.opengl.GLUtils; +import android.util.Log; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class TexturedTriangleTest extends GLGame { + @Override + public Screen getStartScreen() { + return new TexturedTriangleScreen(this); + } + + class TexturedTriangleScreen extends Screen { + final int VERTEX_SIZE = (2 + 2) * 4; + GLGraphics glGraphics; + FloatBuffer vertices; + int textureId; + + public TexturedTriangleScreen(Game game) { + super(game); + glGraphics = ((GLGame) game).getGLGraphics(); + + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(3 * VERTEX_SIZE); + byteBuffer.order(ByteOrder.nativeOrder()); + vertices = byteBuffer.asFloatBuffer(); + vertices.put( new float[] { 0.0f, 0.0f, 0.0f, 1.0f, + 319.0f, 0.0f, 1.0f, 1.0f, + 160.0f, 479.0f, 0.5f, 0.0f}); + vertices.flip(); + textureId = loadTexture("bobrgb888.png"); + } + + public int loadTexture(String fileName) { + try { + Bitmap bitmap = BitmapFactory.decodeStream(game.getFileIO().readAsset(fileName)); + GL10 gl = glGraphics.getGL(); + int textureIds[] = new int[1]; + gl.glGenTextures(1, textureIds, 0); + int textureId = textureIds[0]; + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + bitmap.recycle(); + return textureId; + } catch(IOException e) { + Log.d("TexturedTriangleTest", "couldn't load asset 'bobrgb888.png'!"); + throw new RuntimeException("couldn't load asset '" + fileName + "'"); + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(0, 320, 0, 480, 1, -1); + + gl.glEnable(GL10.GL_TEXTURE_2D); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + vertices.position(0); + gl.glVertexPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, vertices); + vertices.position(2); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, vertices); + + gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3); + } + + @Override + public void update(float deltaTime) { + game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + } + + @Override + public void pause() { + + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } + } +} diff --git a/ch08-2d-gamedev/.classpath b/ch08-2d-gamedev/.classpath new file mode 100644 index 0000000..609aa00 --- /dev/null +++ b/ch08-2d-gamedev/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ch08-2d-gamedev/.project b/ch08-2d-gamedev/.project new file mode 100644 index 0000000..fdc040d --- /dev/null +++ b/ch08-2d-gamedev/.project @@ -0,0 +1,33 @@ + + + ch08-2d-gamedev + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/ch08-2d-gamedev/AndroidManifest.xml b/ch08-2d-gamedev/AndroidManifest.xml new file mode 100644 index 0000000..90e3b0a --- /dev/null +++ b/ch08-2d-gamedev/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch08-2d-gamedev/assets/atlas.png b/ch08-2d-gamedev/assets/atlas.png new file mode 100644 index 0000000..85007ed Binary files /dev/null and b/ch08-2d-gamedev/assets/atlas.png differ diff --git a/ch08-2d-gamedev/assets/walkanim.png b/ch08-2d-gamedev/assets/walkanim.png new file mode 100644 index 0000000..bdb814c Binary files /dev/null and b/ch08-2d-gamedev/assets/walkanim.png differ diff --git a/ch08-2d-gamedev/default.properties b/ch08-2d-gamedev/default.properties new file mode 100644 index 0000000..3ac2523 --- /dev/null +++ b/ch08-2d-gamedev/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-12 diff --git a/ch08-2d-gamedev/proguard.cfg b/ch08-2d-gamedev/proguard.cfg new file mode 100644 index 0000000..8ad7d33 --- /dev/null +++ b/ch08-2d-gamedev/proguard.cfg @@ -0,0 +1,34 @@ +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontpreverify +-verbose +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* + +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class com.android.vending.licensing.ILicensingService + +-keepclasseswithmembernames class * { + native ; +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/ch08-2d-gamedev/res/drawable-hdpi/icon.png b/ch08-2d-gamedev/res/drawable-hdpi/icon.png new file mode 100644 index 0000000..8074c4c Binary files /dev/null and b/ch08-2d-gamedev/res/drawable-hdpi/icon.png differ diff --git a/ch08-2d-gamedev/res/drawable-ldpi/icon.png b/ch08-2d-gamedev/res/drawable-ldpi/icon.png new file mode 100644 index 0000000..1095584 Binary files /dev/null and b/ch08-2d-gamedev/res/drawable-ldpi/icon.png differ diff --git a/ch08-2d-gamedev/res/drawable-mdpi/icon.png b/ch08-2d-gamedev/res/drawable-mdpi/icon.png new file mode 100644 index 0000000..a07c69f Binary files /dev/null and b/ch08-2d-gamedev/res/drawable-mdpi/icon.png differ diff --git a/ch08-2d-gamedev/res/layout/main.xml b/ch08-2d-gamedev/res/layout/main.xml new file mode 100644 index 0000000..3a5f117 --- /dev/null +++ b/ch08-2d-gamedev/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/ch08-2d-gamedev/res/values/strings.xml b/ch08-2d-gamedev/res/values/strings.xml new file mode 100644 index 0000000..3fc473e --- /dev/null +++ b/ch08-2d-gamedev/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Hello World, GameDev2DStarter! + 2D Game Dev + diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Audio.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Audio.java new file mode 100644 index 0000000..d30d722 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Audio.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Audio { + public Music newMusic(String filename); + + public Sound newSound(String filename); +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Color.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Color.java new file mode 100644 index 0000000..57d68e5 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Color.java @@ -0,0 +1,10 @@ +package com.badlogic.androidgames.framework; + +public class Color { + public static int convert (int r, int g, int b, int a) { + return ((a & 0xff) << 24) | + ((r & 0xff) << 16) | + ((g & 0xff) << 8) | + ((b & 0xff)); + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/FileIO.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/FileIO.java new file mode 100644 index 0000000..d555b7d --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/FileIO.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.framework; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public interface FileIO { + public InputStream readAsset(String fileName) throws IOException; + + public InputStream readFile(String fileName) throws IOException; + + public OutputStream writeFile(String fileName) throws IOException; +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Game.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Game.java new file mode 100644 index 0000000..fb5c5aa --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Game.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework; + +public interface Game { + public Input getInput(); + + public FileIO getFileIO(); + + public Graphics getGraphics(); + + public Audio getAudio(); + + public void setScreen(Screen screen); + + public Screen getCurrentScreen(); + + public Screen getStartScreen(); +} \ No newline at end of file diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Graphics.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Graphics.java new file mode 100644 index 0000000..c7b8f45 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Graphics.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework; + +public interface Graphics { + public static enum PixmapFormat { + ARGB8888, ARGB4444, RGB565 + } + + public Pixmap newPixmap(String fileName, PixmapFormat format); + + public void clear(int color); + + public void drawPixel(int x, int y, int color); + + public void drawLine(int x, int y, int x2, int y2, int color); + + public void drawRect(int x, int y, int width, int height, int color); + + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight); + + public void drawPixmap(Pixmap pixmap, int x, int y); + + public int getWidth(); + + public int getHeight(); +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Input.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Input.java new file mode 100644 index 0000000..0893d62 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Input.java @@ -0,0 +1,70 @@ +package com.badlogic.androidgames.framework; + +import java.util.List; + +public interface Input { + public static class KeyEvent { + public static final int KEY_DOWN = 0; + public static final int KEY_UP = 1; + + public int type; + public int keyCode; + public char keyChar; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == KEY_DOWN) + builder.append("key down, "); + else + builder.append("key up, "); + builder.append(keyCode); + builder.append(","); + builder.append(keyChar); + return builder.toString(); + } + } + + public static class TouchEvent { + public static final int TOUCH_DOWN = 0; + public static final int TOUCH_UP = 1; + public static final int TOUCH_DRAGGED = 2; + + public int type; + public int x, y; + public int pointer; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == TOUCH_DOWN) + builder.append("touch down, "); + else if (type == TOUCH_DRAGGED) + builder.append("touch dragged, "); + else + builder.append("touch up, "); + builder.append(pointer); + builder.append(","); + builder.append(x); + builder.append(","); + builder.append(y); + return builder.toString(); + } + } + + public boolean isKeyPressed(int keyCode); + + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public float getAccelX(); + + public float getAccelY(); + + public float getAccelZ(); + + public List getKeyEvents(); + + public List getTouchEvents(); +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Music.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Music.java new file mode 100644 index 0000000..1b95a1e --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Music.java @@ -0,0 +1,21 @@ +package com.badlogic.androidgames.framework; + +public interface Music { + public void play(); + + public void stop(); + + public void pause(); + + public void setLooping(boolean looping); + + public void setVolume(float volume); + + public boolean isPlaying(); + + public boolean isStopped(); + + public boolean isLooping(); + + public void dispose(); +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Pixmap.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Pixmap.java new file mode 100644 index 0000000..4b1eec9 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Pixmap.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; + +public interface Pixmap { + public int getWidth(); + + public int getHeight(); + + public PixmapFormat getFormat(); + + public void dispose(); +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Pool.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Pool.java new file mode 100644 index 0000000..d0b7cec --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Pool.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework; + +import java.util.ArrayList; +import java.util.List; + +public class Pool { + public interface PoolObjectFactory { + public T createObject(); + } + + private final List freeObjects; + private final PoolObjectFactory factory; + private final int maxSize; + + public Pool(PoolObjectFactory factory, int maxSize) { + this.factory = factory; + this.maxSize = maxSize; + this.freeObjects = new ArrayList(maxSize); + } + + public T newObject() { + T object = null; + + if (freeObjects.size() == 0) + object = factory.createObject(); + else + object = freeObjects.remove(freeObjects.size() - 1); + + return object; + } + + public void free(T object) { + if (freeObjects.size() < maxSize) + freeObjects.add(object); + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Screen.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Screen.java new file mode 100644 index 0000000..40a98b9 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Screen.java @@ -0,0 +1,19 @@ +package com.badlogic.androidgames.framework; + +public abstract class Screen { + protected final Game game; + + public Screen(Game game) { + this.game = game; + } + + public abstract void update(float deltaTime); + + public abstract void present(float deltaTime); + + public abstract void pause(); + + public abstract void resume(); + + public abstract void dispose(); +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Sound.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Sound.java new file mode 100644 index 0000000..3f49835 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/Sound.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Sound { + public void play(float volume); + + public void dispose(); +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/TestScreen.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/TestScreen.java new file mode 100644 index 0000000..5a0bbcf --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/TestScreen.java @@ -0,0 +1,112 @@ +package com.badlogic.androidgames.framework; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.List; + +import android.graphics.Color; +import android.util.Log; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; +import com.badlogic.androidgames.framework.Input.KeyEvent; +import com.badlogic.androidgames.framework.Input.TouchEvent; + +class TestScreen extends Screen { + long startTime = System.nanoTime(); + int frames; + Pixmap bob; + Pixmap bobAlpha; + Sound sound; + Music music; + + public TestScreen(Game game) { + super(game); + bob = game.getGraphics().newPixmap("bobrgb888.png", PixmapFormat.RGB565); + bobAlpha = game.getGraphics().newPixmap("bobargb8888.png", PixmapFormat.ARGB4444); + music = game.getAudio().newMusic("music.ogg"); + music.setLooping(true); + music.setVolume(0.5f); + music.play(); + sound = game.getAudio().newSound("music.ogg"); + + try { + BufferedReader in = new BufferedReader(new InputStreamReader(game.getFileIO().readAsset("test.txt"))); + String text = in.readLine(); + in.close(); + + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(game.getFileIO().writeFile("test.txt"))); + out.write("This is a freaking test"); + out.close(); + + in = new BufferedReader(new InputStreamReader(game.getFileIO().readFile("test.txt"))); + String text2 = in.readLine(); + in.close(); + + Log.d("MrNom", text + ", " + text2 ); + } catch(Exception ex) { + ex.printStackTrace(); + } + } + + @Override + public void update(float deltaTime) { + } + + @Override + public void present(float deltaTime) { + Graphics g = game.getGraphics(); + Input inp = game.getInput(); + g.clear(Color.RED); + g.drawLine(0,0,320, 480, Color.BLUE); + g.drawRect(20,20,100,100, Color.GREEN); + g.drawPixmap(bob, 100, 100); + g.drawPixmap(bobAlpha, 100, 200); + g.drawPixmap(bob, 200, 200, 0, 0, 64, 64); + for(int i=0; i < 2; i++) { + if(inp.isTouchDown(i)) { + g.drawPixmap(bob, inp.getTouchX(i), inp.getTouchY(i), 0, 0, 64, 64); + } + } + + g.drawPixmap(bob, (int)(inp.getAccelX() * 10) + 160 - 16, (int)(inp.getAccelY() * 10) + 240 - 16, 0, 0, 32, 32 ); + + List keyEvents = inp.getKeyEvents(); + int len = keyEvents.size(); + for(int i = 0; i < len; i++) { + Log.d("MrNom", keyEvents.get(i).toString()); + } + + List touchEvents = inp.getTouchEvents(); + len = touchEvents.size(); + for(int i = 0; i < len; i++) { + Log.d("MrNom", touchEvents.get(i).toString()); + if(touchEvents.get(i).type == TouchEvent.TOUCH_UP) + sound.play(1); + } + + frames++; + if(System.nanoTime() - startTime > 1000000000l) { + Log.d("MrNom", "fps: " + frames + ", delta: " + deltaTime); + frames = 0; + startTime = System.nanoTime(); + } + } + + @Override + public void pause() { + Log.d("MrNom", "pause"); + } + + @Override + public void resume() { + Log.d("MrNom", "resume"); + } + + @Override + public void dispose() { + Log.d("MrNom", "dispose"); + music.dispose(); + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/Animation.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/Animation.java new file mode 100644 index 0000000..a6ba222 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/Animation.java @@ -0,0 +1,25 @@ +package com.badlogic.androidgames.framework.gl; + +public class Animation { + public static final int ANIMATION_LOOPING = 0; + public static final int ANIMATION_NONLOOPING = 1; + + final TextureRegion[] keyFrames; + final float frameDuration; + + public Animation(float frameDuration, TextureRegion ... keyFrames) { + this.frameDuration = frameDuration; + this.keyFrames = keyFrames; + } + + public TextureRegion getKeyFrame(float stateTime, int mode) { + int frameNumber = (int)(stateTime / frameDuration); + + if(mode == ANIMATION_NONLOOPING) { + frameNumber = Math.min(keyFrames.length-1, frameNumber); + } else { + frameNumber = frameNumber % keyFrames.length; + } + return keyFrames[frameNumber]; + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/Camera2D.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/Camera2D.java new file mode 100644 index 0000000..2a58199 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/Camera2D.java @@ -0,0 +1,42 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.Vector2; + +public class Camera2D { + public final Vector2 position; + public float zoom; + public final float frustumWidth; + public final float frustumHeight; + final GLGraphics glGraphics; + + public Camera2D(GLGraphics glGraphics, float frustumWidth, float frustumHeight) { + this.glGraphics = glGraphics; + this.frustumWidth = frustumWidth; + this.frustumHeight = frustumHeight; + this.position = new Vector2(frustumWidth / 2, frustumHeight / 2); + this.zoom = 1.0f; + } + + public void setViewportAndMatrices() { + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(position.x - frustumWidth * zoom / 2, + position.x + frustumWidth * zoom/ 2, + position.y - frustumHeight * zoom / 2, + position.y + frustumHeight * zoom/ 2, + 1, -1); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + } + + public void touchToWorld(Vector2 touch) { + touch.x = (touch.x / (float) glGraphics.getWidth()) * frustumWidth * zoom; + touch.y = (1 - touch.y / (float) glGraphics.getHeight()) * frustumHeight * zoom; + touch.add(position).sub(frustumWidth * zoom / 2, frustumHeight * zoom / 2); + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/FPSCounter.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/FPSCounter.java new file mode 100644 index 0000000..cd816ca --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/FPSCounter.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework.gl; + +import android.util.Log; + +public class FPSCounter { + long startTime = System.nanoTime(); + int frames = 0; + + public void logFrame() { + frames++; + if(System.nanoTime() - startTime >= 1000000000) { + Log.d("FPSCounter", "fps: " + frames); + frames = 0; + startTime = System.nanoTime(); + } + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java new file mode 100644 index 0000000..263a403 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java @@ -0,0 +1,143 @@ +package com.badlogic.androidgames.framework.gl; + +import java.util.ArrayList; +import java.util.List; + +import com.badlogic.androidgames.gamedev2d.GameObject; + +import android.util.FloatMath; + +public class SpatialHashGrid { + List[] dynamicCells; + List[] staticCells; + int cellsPerRow; + int cellsPerCol; + float cellSize; + int[] cellIds = new int[4]; + List foundObjects; + + @SuppressWarnings("unchecked") + public SpatialHashGrid(float worldWidth, float worldHeight, float cellSize) { + this.cellSize = cellSize; + this.cellsPerRow = (int)FloatMath.ceil(worldWidth/cellSize); + this.cellsPerCol = (int)FloatMath.ceil(worldHeight/cellSize); + int numCells = cellsPerRow * cellsPerCol; + dynamicCells = new List[numCells]; + staticCells = new List[numCells]; + for(int i = 0; i < numCells; i++) { + dynamicCells[i] = new ArrayList(10); + staticCells[i] = new ArrayList(10); + } + foundObjects = new ArrayList(10); + } + + public void insertStaticObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + staticCells[cellId].add(obj); + } + } + + public void insertDynamicObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + dynamicCells[cellId].add(obj); + } + } + + public void removeObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + dynamicCells[cellId].remove(obj); + staticCells[cellId].remove(obj); + } + } + + public void clearDynamicCells(GameObject obj) { + int len = dynamicCells.length; + for(int i = 0; i < len; i++) { + dynamicCells[i].clear(); + } + } + + public List getPotentialColliders(GameObject obj) { + foundObjects.clear(); + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + int len = dynamicCells[cellId].size(); + for(int j = 0; j < len; j++) { + GameObject collider = dynamicCells[cellId].get(j); + if(!foundObjects.contains(collider)) + foundObjects.add(collider); + } + + len = staticCells[cellId].size(); + for(int j = 0; j < len; j++) { + GameObject collider = staticCells[cellId].get(j); + if(!foundObjects.contains(collider)) + foundObjects.add(collider); + } + } + return foundObjects; + } + + public int[] getCellIds(GameObject obj) { + int x1 = (int)FloatMath.floor(obj.bounds.lowerLeft.x / cellSize); + int y1 = (int)FloatMath.floor(obj.bounds.lowerLeft.y / cellSize); + int x2 = (int)FloatMath.floor((obj.bounds.lowerLeft.x + obj.bounds.width) / cellSize); + int y2 = (int)FloatMath.floor((obj.bounds.lowerLeft.y + obj.bounds.height) / cellSize); + + if(x1 == x2 && y1 == y2) { + if(x1 >= 0 && x1 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[0] = x1 + y1 * cellsPerRow; + else + cellIds[0] = -1; + cellIds[1] = -1; + cellIds[2] = -1; + cellIds[3] = -1; + } + else if(x1 == x2) { + int i = 0; + if(x1 >= 0 && x1 < cellsPerRow) { + if(y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x1 + y1 * cellsPerRow; + if(y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x1 + y2 * cellsPerRow; + } + while(i <= 3) cellIds[i++] = -1; + } + else if(y1 == y2) { + int i = 0; + if(y1 >= 0 && y1 < cellsPerCol) { + if(x1 >= 0 && x1 < cellsPerRow) + cellIds[i++] = x1 + y1 * cellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow) + cellIds[i++] = x2 + y1 * cellsPerRow; + } + while(i <= 3) cellIds[i++] = -1; + } + else { + int i = 0; + int y1CellsPerRow = y1 * cellsPerRow; + int y2CellsPerRow = y2 * cellsPerRow; + if(x1 >= 0 && x1 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x1 + y1CellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x2 + y1CellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow && y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x2 + y2CellsPerRow; + if(x1 >= 0 && x1 < cellsPerRow && y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x1 + y2CellsPerRow; + while(i <= 3) cellIds[i++] = -1; + } + return cellIds; + } +} \ No newline at end of file diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java new file mode 100644 index 0000000..8b9eee2 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java @@ -0,0 +1,128 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import android.util.FloatMath; + +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.Vector2; + +public class SpriteBatcher { + final float[] verticesBuffer; + int bufferIndex; + final Vertices vertices; + int numSprites; + + public SpriteBatcher(GLGraphics glGraphics, int maxSprites) { + this.verticesBuffer = new float[maxSprites*4*4]; + this.vertices = new Vertices(glGraphics, maxSprites*4, maxSprites*6, false, true); + this.bufferIndex = 0; + this.numSprites = 0; + + short[] indices = new short[maxSprites*6]; + int len = indices.length; + short j = 0; + for (int i = 0; i < len; i += 6, j += 4) { + indices[i + 0] = (short)(j + 0); + indices[i + 1] = (short)(j + 1); + indices[i + 2] = (short)(j + 2); + indices[i + 3] = (short)(j + 2); + indices[i + 4] = (short)(j + 3); + indices[i + 5] = (short)(j + 0); + } + vertices.setIndices(indices, 0, indices.length); + } + + public void beginBatch(Texture texture) { + texture.bind(); + numSprites = 0; + bufferIndex = 0; + } + + public void endBatch() { + vertices.setVertices(verticesBuffer, 0, bufferIndex); + vertices.bind(); + vertices.draw(GL10.GL_TRIANGLES, 0, numSprites * 6); + vertices.unbind(); + } + + public void drawSprite(float x, float y, float width, float height, TextureRegion region) { + float halfWidth = width / 2; + float halfHeight = height / 2; + float x1 = x - halfWidth; + float y1 = y - halfHeight; + float x2 = x + halfWidth; + float y2 = y + halfHeight; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v1; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v1; + + numSprites++; + } + + public void drawSprite(float x, float y, float width, float height, float angle, TextureRegion region) { + float halfWidth = width / 2; + float halfHeight = height / 2; + + float rad = angle * Vector2.TO_RADIANS; + float cos = FloatMath.cos(rad); + float sin = FloatMath.sin(rad); + + float x1 = -halfWidth * cos - (-halfHeight) * sin; + float y1 = -halfWidth * sin + (-halfHeight) * cos; + float x2 = halfWidth * cos - (-halfHeight) * sin; + float y2 = halfWidth * sin + (-halfHeight) * cos; + float x3 = halfWidth * cos - halfHeight * sin; + float y3 = halfWidth * sin + halfHeight * cos; + float x4 = -halfWidth * cos - halfHeight * sin; + float y4 = -halfWidth * sin + halfHeight * cos; + + x1 += x; + y1 += y; + x2 += x; + y2 += y; + x3 += x; + y3 += y; + x4 += x; + y4 += y; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x3; + verticesBuffer[bufferIndex++] = y3; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v1; + + verticesBuffer[bufferIndex++] = x4; + verticesBuffer[bufferIndex++] = y4; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v1; + + numSprites++; + } +} \ No newline at end of file diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/Texture.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/Texture.java new file mode 100644 index 0000000..174e27b --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/Texture.java @@ -0,0 +1,84 @@ +package com.badlogic.androidgames.framework.gl; + +import java.io.IOException; +import java.io.InputStream; + +import javax.microedition.khronos.opengles.GL10; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.opengl.GLUtils; + +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Texture { + GLGraphics glGraphics; + FileIO fileIO; + String fileName; + int textureId; + int minFilter; + int magFilter; + public int width; + public int height; + + public Texture(GLGame glGame, String fileName) { + this.glGraphics = glGame.getGLGraphics(); + this.fileIO = glGame.getFileIO(); + this.fileName = fileName; + load(); + } + + private void load() { + GL10 gl = glGraphics.getGL(); + int[] textureIds = new int[1]; + gl.glGenTextures(1, textureIds, 0); + textureId = textureIds[0]; + + InputStream in = null; + try { + in = fileIO.readAsset(fileName); + Bitmap bitmap = BitmapFactory.decodeStream(in); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); + setFilters(GL10.GL_NEAREST, GL10.GL_NEAREST); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + width = bitmap.getWidth(); + height = bitmap.getHeight(); + bitmap.recycle(); + } catch(IOException e) { + throw new RuntimeException("Couldn't load texture '" + fileName +"'", e); + } finally { + if(in != null) + try { in.close(); } catch (IOException e) { } + } + } + + public void reload() { + load(); + bind(); + setFilters(minFilter, magFilter); + glGraphics.getGL().glBindTexture(GL10.GL_TEXTURE_2D, 0); + } + + public void setFilters(int minFilter, int magFilter) { + this.minFilter = minFilter; + this.magFilter = magFilter; + GL10 gl = glGraphics.getGL(); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, minFilter); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, magFilter); + } + + public void bind() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + } + + public void dispose() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + int[] textureIds = { textureId }; + gl.glDeleteTextures(1, textureIds, 0); + } +} \ No newline at end of file diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/TextureRegion.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/TextureRegion.java new file mode 100644 index 0000000..cb91a07 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/TextureRegion.java @@ -0,0 +1,15 @@ +package com.badlogic.androidgames.framework.gl; + +public class TextureRegion { + public final float u1, v1; + public final float u2, v2; + public final Texture texture; + + public TextureRegion(Texture texture, float x, float y, float width, float height) { + this.u1 = x / texture.width; + this.v1 = y / texture.height; + this.u2 = this.u1 + width / texture.width; + this.v2 = this.v1 + height / texture.height; + this.texture = texture; + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/Vertices.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/Vertices.java new file mode 100644 index 0000000..d8bbee3 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/gl/Vertices.java @@ -0,0 +1,95 @@ +package com.badlogic.androidgames.framework.gl; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Vertices { + final GLGraphics glGraphics; + final boolean hasColor; + final boolean hasTexCoords; + final int vertexSize; + final IntBuffer vertices; + final int[] tmpBuffer; + final ShortBuffer indices; + + public Vertices(GLGraphics glGraphics, int maxVertices, int maxIndices, boolean hasColor, boolean hasTexCoords) { + this.glGraphics = glGraphics; + this.hasColor = hasColor; + this.hasTexCoords = hasTexCoords; + this.vertexSize = (2 + (hasColor?4:0) + (hasTexCoords?2:0)) * 4; + this.tmpBuffer = new int[maxVertices * vertexSize / 4]; + + ByteBuffer buffer = ByteBuffer.allocateDirect(maxVertices * vertexSize); + buffer.order(ByteOrder.nativeOrder()); + vertices = buffer.asIntBuffer(); + + if(maxIndices > 0) { + buffer = ByteBuffer.allocateDirect(maxIndices * Short.SIZE / 8); + buffer.order(ByteOrder.nativeOrder()); + indices = buffer.asShortBuffer(); + } else { + indices = null; + } + } + + public void setVertices(float[] vertices, int offset, int length) { + this.vertices.clear(); + int len = offset + length; + for(int i=offset, j=0; i < len; i++, j++) + tmpBuffer[j] = Float.floatToRawIntBits(vertices[i]); + this.vertices.put(tmpBuffer, 0, length); + this.vertices.flip(); + } + + public void setIndices(short[] indices, int offset, int length) { + this.indices.clear(); + this.indices.put(indices, offset, length); + this.indices.flip(); + } + +public void bind() { + GL10 gl = glGraphics.getGL(); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + vertices.position(0); + gl.glVertexPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + + if(hasColor) { + gl.glEnableClientState(GL10.GL_COLOR_ARRAY); + vertices.position(2); + gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices); + } + + if(hasTexCoords) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + vertices.position(hasColor?6:2); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + } +} + +public void draw(int primitiveType, int offset, int numVertices) { + GL10 gl = glGraphics.getGL(); + + if(indices!=null) { + indices.position(offset); + gl.glDrawElements(primitiveType, numVertices, GL10.GL_UNSIGNED_SHORT, indices); + } else { + gl.glDrawArrays(primitiveType, offset, numVertices); + } +} + +public void unbind() { + GL10 gl = glGraphics.getGL(); + if(hasTexCoords) + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + if(hasColor) + gl.glDisableClientState(GL10.GL_COLOR_ARRAY); +} +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java new file mode 100644 index 0000000..4820c5a --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java @@ -0,0 +1,48 @@ +package com.badlogic.androidgames.framework.impl; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + +public class AccelerometerHandler implements SensorEventListener { + float accelX; + float accelY; + float accelZ; + + public AccelerometerHandler(Context context) { + SensorManager manager = (SensorManager) context + .getSystemService(Context.SENSOR_SERVICE); + if (manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() != 0) { + Sensor accelerometer = manager.getSensorList( + Sensor.TYPE_ACCELEROMETER).get(0); + manager.registerListener(this, accelerometer, + SensorManager.SENSOR_DELAY_GAME); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // nothing to do here + } + + @Override + public void onSensorChanged(SensorEvent event) { + accelX = event.values[0]; + accelY = event.values[1]; + accelZ = event.values[2]; + } + + public float getAccelX() { + return accelX; + } + + public float getAccelY() { + return accelY; + } + + public float getAccelZ() { + return accelZ; + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java new file mode 100644 index 0000000..42f8c1f --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java @@ -0,0 +1,45 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; + +import android.app.Activity; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.media.AudioManager; +import android.media.SoundPool; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.Music; +import com.badlogic.androidgames.framework.Sound; + +public class AndroidAudio implements Audio { + AssetManager assets; + SoundPool soundPool; + + public AndroidAudio(Activity activity) { + activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); + this.assets = activity.getAssets(); + this.soundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0); + } + + @Override + public Music newMusic(String filename) { + try { + AssetFileDescriptor assetDescriptor = assets.openFd(filename); + return new AndroidMusic(assetDescriptor); + } catch (IOException e) { + throw new RuntimeException("Couldn't load music '" + filename + "'"); + } + } + + @Override + public Sound newSound(String filename) { + try { + AssetFileDescriptor assetDescriptor = assets.openFd(filename); + int soundId = soundPool.load(assetDescriptor, 0); + return new AndroidSound(soundPool, soundId); + } catch (IOException e) { + throw new RuntimeException("Couldn't load sound '" + filename + "'"); + } + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java new file mode 100644 index 0000000..6171d90 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java @@ -0,0 +1,60 @@ +package com.badlogic.androidgames.framework.impl; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class AndroidFastRenderView extends SurfaceView implements Runnable { + AndroidGame game; + Bitmap framebuffer; + Thread renderThread = null; + SurfaceHolder holder; + volatile boolean running = false; + + public AndroidFastRenderView(AndroidGame game, Bitmap framebuffer) { + super(game); + this.game = game; + this.framebuffer = framebuffer; + this.holder = getHolder(); + } + + public void resume() { + running = true; + renderThread = new Thread(this); + renderThread.start(); + } + + public void run() { + Rect dstRect = new Rect(); + long startTime = System.nanoTime(); + while(running) { + if(!holder.getSurface().isValid()) + continue; + + float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; + startTime = System.nanoTime(); + + game.getCurrentScreen().update(deltaTime); + game.getCurrentScreen().present(deltaTime); + + Canvas canvas = holder.lockCanvas(); + canvas.getClipBounds(dstRect); + canvas.drawBitmap(framebuffer, null, dstRect, null); + holder.unlockCanvasAndPost(canvas); + } + } + + public void pause() { + running = false; + while(true) { + try { + renderThread.join(); + break; + } catch (InterruptedException e) { + // retry + } + } + } +} \ No newline at end of file diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java new file mode 100644 index 0000000..b13b115 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java @@ -0,0 +1,39 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import android.content.res.AssetManager; +import android.os.Environment; + +import com.badlogic.androidgames.framework.FileIO; + +public class AndroidFileIO implements FileIO { + AssetManager assets; + String externalStoragePath; + + public AndroidFileIO(AssetManager assets) { + this.assets = assets; + this.externalStoragePath = Environment.getExternalStorageDirectory() + .getAbsolutePath() + File.separator; + } + + @Override + public InputStream readAsset(String fileName) throws IOException { + return assets.open(fileName); + } + + @Override + public InputStream readFile(String fileName) throws IOException { + return new FileInputStream(externalStoragePath + fileName); + } + + @Override + public OutputStream writeFile(String fileName) throws IOException { + return new FileOutputStream(externalStoragePath + fileName); + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidGame.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidGame.java new file mode 100644 index 0000000..a7908d4 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidGame.java @@ -0,0 +1,115 @@ +package com.badlogic.androidgames.framework.impl; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.view.Window; +import android.view.WindowManager; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input; +import com.badlogic.androidgames.framework.Screen; + +public abstract class AndroidGame extends Activity implements Game { + AndroidFastRenderView renderView; + Graphics graphics; + Audio audio; + Input input; + FileIO fileIO; + Screen screen; + WakeLock wakeLock; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + + boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + int frameBufferWidth = isLandscape ? 480 : 320; + int frameBufferHeight = isLandscape ? 320 : 480; + Bitmap frameBuffer = Bitmap.createBitmap(frameBufferWidth, + frameBufferHeight, Config.RGB_565); + + float scaleX = (float) frameBufferWidth + / getWindowManager().getDefaultDisplay().getWidth(); + float scaleY = (float) frameBufferHeight + / getWindowManager().getDefaultDisplay().getHeight(); + + renderView = new AndroidFastRenderView(this, frameBuffer); + graphics = new AndroidGraphics(getAssets(), frameBuffer); + fileIO = new AndroidFileIO(getAssets()); + audio = new AndroidAudio(this); + input = new AndroidInput(this, renderView, scaleX, scaleY); + screen = getStartScreen(); + setContentView(renderView); + + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GLGame"); + } + + @Override + public void onResume() { + super.onResume(); + wakeLock.acquire(); + screen.resume(); + renderView.resume(); + } + + @Override + public void onPause() { + super.onPause(); + wakeLock.release(); + renderView.pause(); + screen.pause(); + + if (isFinishing()) + screen.dispose(); + } + + @Override + public Input getInput() { + return input; + } + + @Override + public FileIO getFileIO() { + return fileIO; + } + + @Override + public Graphics getGraphics() { + return graphics; + } + + @Override + public Audio getAudio() { + return audio; + } + + @Override + public void setScreen(Screen screen) { + if (screen == null) + throw new IllegalArgumentException("Screen must not be null"); + + this.screen.pause(); + this.screen.dispose(); + screen.resume(); + screen.update(0); + this.screen = screen; + } + + public Screen getCurrentScreen() { + return screen; + } +} \ No newline at end of file diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java new file mode 100644 index 0000000..ade52b5 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java @@ -0,0 +1,133 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; +import java.io.InputStream; + +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.BitmapFactory.Options; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Rect; + +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Pixmap; + +public class AndroidGraphics implements Graphics { + AssetManager assets; + Bitmap frameBuffer; + Canvas canvas; + Paint paint; + Rect srcRect = new Rect(); + Rect dstRect = new Rect(); + + public AndroidGraphics(AssetManager assets, Bitmap frameBuffer) { + this.assets = assets; + this.frameBuffer = frameBuffer; + this.canvas = new Canvas(frameBuffer); + this.paint = new Paint(); + } + + @Override + public Pixmap newPixmap(String fileName, PixmapFormat format) { + Config config = null; + if (format == PixmapFormat.RGB565) + config = Config.RGB_565; + else if (format == PixmapFormat.ARGB4444) + config = Config.ARGB_4444; + else + config = Config.ARGB_8888; + + Options options = new Options(); + options.inPreferredConfig = config; + + InputStream in = null; + Bitmap bitmap = null; + try { + in = assets.open(fileName); + bitmap = BitmapFactory.decodeStream(in, null, options); + if (bitmap == null) + throw new RuntimeException("Couldn't load bitmap from asset '" + + fileName + "'"); + } catch (IOException e) { + throw new RuntimeException("Couldn't load bitmap from asset '" + + fileName + "'"); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + + if (bitmap.getConfig() == Config.RGB_565) + format = PixmapFormat.RGB565; + else if (bitmap.getConfig() == Config.ARGB_4444) + format = PixmapFormat.ARGB4444; + else + format = PixmapFormat.ARGB8888; + + return new AndroidPixmap(bitmap, format); + } + + @Override + public void clear(int color) { + canvas.drawRGB((color & 0xff0000) >> 16, (color & 0xff00) >> 8, + (color & 0xff)); + } + + @Override + public void drawPixel(int x, int y, int color) { + paint.setColor(color); + canvas.drawPoint(x, y, paint); + } + + @Override + public void drawLine(int x, int y, int x2, int y2, int color) { + paint.setColor(color); + canvas.drawLine(x, y, x2, y2, paint); + } + + @Override + public void drawRect(int x, int y, int width, int height, int color) { + paint.setColor(color); + paint.setStyle(Style.FILL); + canvas.drawRect(x, y, x + width - 1, y + height - 1, paint); + } + + @Override + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight) { + srcRect.left = srcX; + srcRect.top = srcY; + srcRect.right = srcX + srcWidth - 1; + srcRect.bottom = srcY + srcHeight - 1; + + dstRect.left = x; + dstRect.top = y; + dstRect.right = x + srcWidth - 1; + dstRect.bottom = y + srcHeight - 1; + + canvas.drawBitmap(((AndroidPixmap) pixmap).bitmap, srcRect, dstRect, + null); + } + + @Override + public void drawPixmap(Pixmap pixmap, int x, int y) { + canvas.drawBitmap(((AndroidPixmap)pixmap).bitmap, x, y, null); + } + + @Override + public int getWidth() { + return frameBuffer.getWidth(); + } + + @Override + public int getHeight() { + return frameBuffer.getHeight(); + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidInput.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidInput.java new file mode 100644 index 0000000..11f5863 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidInput.java @@ -0,0 +1,69 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.List; + +import android.content.Context; +import android.os.Build.VERSION; +import android.view.View; + +import com.badlogic.androidgames.framework.Input; + +public class AndroidInput implements Input { + AccelerometerHandler accelHandler; + KeyboardHandler keyHandler; + TouchHandler touchHandler; + + public AndroidInput(Context context, View view, float scaleX, float scaleY) { + accelHandler = new AccelerometerHandler(context); + keyHandler = new KeyboardHandler(view); + if(Integer.parseInt(VERSION.SDK) < 5) + touchHandler = new SingleTouchHandler(view, scaleX, scaleY); + else + touchHandler = new MultiTouchHandler(view, scaleX, scaleY); + } + + @Override + public boolean isKeyPressed(int keyCode) { + return keyHandler.isKeyPressed(keyCode); + } + + @Override + public boolean isTouchDown(int pointer) { + return touchHandler.isTouchDown(pointer); + } + + @Override + public int getTouchX(int pointer) { + return touchHandler.getTouchX(pointer); + } + + @Override + public int getTouchY(int pointer) { + return touchHandler.getTouchY(pointer); + } + + @Override + public float getAccelX() { + return accelHandler.getAccelX(); + } + + @Override + public float getAccelY() { + return accelHandler.getAccelY(); + } + + @Override + public float getAccelZ() { + return accelHandler.getAccelZ(); + } + + @Override + public List getTouchEvents() { + return touchHandler.getTouchEvents(); + } + + @Override + public List getKeyEvents() { + return keyHandler.getKeyEvents(); + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java new file mode 100644 index 0000000..9e9fd1c --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java @@ -0,0 +1,99 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; + +import android.content.res.AssetFileDescriptor; +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnCompletionListener; + +import com.badlogic.androidgames.framework.Music; + +public class AndroidMusic implements Music, OnCompletionListener { + MediaPlayer mediaPlayer; + boolean isPrepared = false; + + public AndroidMusic(AssetFileDescriptor assetDescriptor) { + mediaPlayer = new MediaPlayer(); + try { + mediaPlayer.setDataSource(assetDescriptor.getFileDescriptor(), + assetDescriptor.getStartOffset(), + assetDescriptor.getLength()); + mediaPlayer.prepare(); + isPrepared = true; + mediaPlayer.setOnCompletionListener(this); + } catch (Exception e) { + throw new RuntimeException("Couldn't load music"); + } + } + + @Override + public void dispose() { + if (mediaPlayer.isPlaying()) + mediaPlayer.stop(); + mediaPlayer.release(); + } + + @Override + public boolean isLooping() { + return mediaPlayer.isLooping(); + } + + @Override + public boolean isPlaying() { + return mediaPlayer.isPlaying(); + } + + @Override + public boolean isStopped() { + return !isPrepared; + } + + @Override + public void pause() { + if (mediaPlayer.isPlaying()) + mediaPlayer.pause(); + } + + @Override + public void play() { + if (mediaPlayer.isPlaying()) + return; + + try { + synchronized (this) { + if (!isPrepared) + mediaPlayer.prepare(); + mediaPlayer.start(); + } + } catch (IllegalStateException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void setLooping(boolean isLooping) { + mediaPlayer.setLooping(isLooping); + } + + @Override + public void setVolume(float volume) { + mediaPlayer.setVolume(volume, volume); + } + + @Override + public void stop() { + mediaPlayer.stop(); + synchronized (this) { + isPrepared = false; + } + } + + @Override + public void onCompletion(MediaPlayer arg0) { + synchronized (this) { + isPrepared = false; + } + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java new file mode 100644 index 0000000..9de5021 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework.impl; + +import android.graphics.Bitmap; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; +import com.badlogic.androidgames.framework.Pixmap; + +public class AndroidPixmap implements Pixmap { + Bitmap bitmap; + PixmapFormat format; + + public AndroidPixmap(Bitmap bitmap, PixmapFormat format) { + this.bitmap = bitmap; + this.format = format; + } + + @Override + public int getWidth() { + return bitmap.getWidth(); + } + + @Override + public int getHeight() { + return bitmap.getHeight(); + } + + @Override + public PixmapFormat getFormat() { + return format; + } + + @Override + public void dispose() { + bitmap.recycle(); + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidSound.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidSound.java new file mode 100644 index 0000000..4a8fcd4 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/AndroidSound.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework.impl; + +import android.media.SoundPool; + +import com.badlogic.androidgames.framework.Sound; + +public class AndroidSound implements Sound { + int soundId; + SoundPool soundPool; + + public AndroidSound(SoundPool soundPool,int soundId) { + this.soundId = soundId; + this.soundPool = soundPool; + } + + @Override + public void play(float volume) { + soundPool.play(soundId, volume, volume, 0, 0, 1); + } + + @Override + public void dispose() { + soundPool.unload(soundId); + } + +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/GLGame.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/GLGame.java new file mode 100644 index 0000000..d07c196 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/GLGame.java @@ -0,0 +1,178 @@ +package com.badlogic.androidgames.framework.impl; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import android.app.Activity; +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.opengl.GLSurfaceView.Renderer; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.view.Window; +import android.view.WindowManager; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input; +import com.badlogic.androidgames.framework.Screen; + +public abstract class GLGame extends Activity implements Game, Renderer { + enum GLGameState { + Initialized, + Running, + Paused, + Finished, + Idle + } + + GLSurfaceView glView; + GLGraphics glGraphics; + Audio audio; + Input input; + FileIO fileIO; + Screen screen; + GLGameState state = GLGameState.Initialized; + Object stateChanged = new Object(); + long startTime = System.nanoTime(); + WakeLock wakeLock; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + glView = new GLSurfaceView(this); + glView.setRenderer(this); + setContentView(glView); + + glGraphics = new GLGraphics(glView); + fileIO = new AndroidFileIO(getAssets()); + audio = new AndroidAudio(this); + input = new AndroidInput(this, glView, 1, 1); + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GLGame"); + } + + public void onResume() { + super.onResume(); + glView.onResume(); + wakeLock.acquire(); + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + glGraphics.setGL(gl); + + synchronized(stateChanged) { + if(state == GLGameState.Initialized) + screen = getStartScreen(); + state = GLGameState.Running; + screen.resume(); + startTime = System.nanoTime(); + } + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + } + + @Override + public void onDrawFrame(GL10 gl) { + GLGameState state = null; + + synchronized(stateChanged) { + state = this.state; + } + + if(state == GLGameState.Running) { + float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; + startTime = System.nanoTime(); + + screen.update(deltaTime); + screen.present(deltaTime); + } + + if(state == GLGameState.Paused) { + screen.pause(); + synchronized(stateChanged) { + this.state = GLGameState.Idle; + stateChanged.notifyAll(); + } + } + + if(state == GLGameState.Finished) { + screen.pause(); + screen.dispose(); + synchronized(stateChanged) { + this.state = GLGameState.Idle; + stateChanged.notifyAll(); + } + } + } + + @Override + public void onPause() { + synchronized(stateChanged) { + if(isFinishing()) + state = GLGameState.Finished; + else + state = GLGameState.Paused; + while(true) { + try { + stateChanged.wait(); + break; + } catch(InterruptedException e) { + } + } + } + wakeLock.release(); + glView.onPause(); + super.onPause(); + } + + public GLGraphics getGLGraphics() { + return glGraphics; + } + + @Override + public Input getInput() { + return input; + } + + @Override + public FileIO getFileIO() { + return fileIO; + } + + @Override + public Graphics getGraphics() { + throw new IllegalStateException("We are using OpenGL!"); + } + + @Override + public Audio getAudio() { + return audio; + } + + @Override + public void setScreen(Screen screen) { + if (screen == null) + throw new IllegalArgumentException("Screen must not be null"); + + this.screen.pause(); + this.screen.dispose(); + screen.resume(); + screen.update(0); + this.screen = screen; + } + + @Override + public Screen getCurrentScreen() { + return screen; + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/GLGraphics.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/GLGraphics.java new file mode 100644 index 0000000..2eb1b43 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/GLGraphics.java @@ -0,0 +1,30 @@ +package com.badlogic.androidgames.framework.impl; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLSurfaceView; + +public class GLGraphics { + GLSurfaceView glView; + GL10 gl; + + GLGraphics(GLSurfaceView glView) { + this.glView = glView; + } + + public GL10 getGL() { + return gl; + } + + void setGL(GL10 gl) { + this.gl = gl; + } + + public int getWidth() { + return glView.getWidth(); + } + + public int getHeight() { + return glView.getHeight(); + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java new file mode 100644 index 0000000..f70d3e9 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java @@ -0,0 +1,73 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.View; +import android.view.View.OnKeyListener; + +import com.badlogic.androidgames.framework.Input.KeyEvent; +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class KeyboardHandler implements OnKeyListener { + boolean[] pressedKeys = new boolean[128]; + Pool keyEventPool; + List keyEventsBuffer = new ArrayList(); + List keyEvents = new ArrayList(); + + public KeyboardHandler(View view) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public KeyEvent createObject() { + return new KeyEvent(); + } + }; + keyEventPool = new Pool(factory, 100); + view.setOnKeyListener(this); + view.setFocusableInTouchMode(true); + view.requestFocus(); + } + + @Override + public boolean onKey(View v, int keyCode, android.view.KeyEvent event) { + if (event.getAction() == android.view.KeyEvent.ACTION_MULTIPLE) + return false; + + synchronized (this) { + KeyEvent keyEvent = keyEventPool.newObject(); + keyEvent.keyCode = keyCode; + keyEvent.keyChar = (char) event.getUnicodeChar(); + if (event.getAction() == android.view.KeyEvent.ACTION_DOWN) { + keyEvent.type = KeyEvent.KEY_DOWN; + if(keyCode >= 0 && keyCode < 128) + pressedKeys[keyCode] = true; + } + if (event.getAction() == android.view.KeyEvent.ACTION_UP) { + keyEvent.type = KeyEvent.KEY_UP; + if(keyCode >= 0 && keyCode < 128) + pressedKeys[keyCode] = false; + } + keyEventsBuffer.add(keyEvent); + } + return false; + } + + public boolean isKeyPressed(int keyCode) { + if (keyCode < 0 || keyCode > 127) + return false; + return pressedKeys[keyCode]; + } + + public List getKeyEvents() { + synchronized (this) { + int len = keyEvents.size(); + for (int i = 0; i < len; i++) + keyEventPool.free(keyEvents.get(i)); + keyEvents.clear(); + keyEvents.addAll(keyEventsBuffer); + keyEventsBuffer.clear(); + return keyEvents; + } + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java new file mode 100644 index 0000000..4668c57 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java @@ -0,0 +1,137 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.MotionEvent; +import android.view.View; + +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class MultiTouchHandler implements TouchHandler { + boolean[] isTouched = new boolean[20]; + int[] touchX = new int[20]; + int[] touchY = new int[20]; + Pool touchEventPool; + List touchEvents = new ArrayList(); + List touchEventsBuffer = new ArrayList(); + float scaleX; + float scaleY; + + public MultiTouchHandler(View view, float scaleX, float scaleY) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }; + touchEventPool = new Pool(factory, 100); + view.setOnTouchListener(this); + + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + synchronized (this) { + int action = event.getAction() & MotionEvent.ACTION_MASK; + int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; + int pointerId = event.getPointerId(pointerIndex); + TouchEvent touchEvent; + + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_DOWN; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + isTouched[pointerId] = true; + touchEventsBuffer.add(touchEvent); + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_CANCEL: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_UP; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + isTouched[pointerId] = false; + touchEventsBuffer.add(touchEvent); + break; + + case MotionEvent.ACTION_MOVE: + int pointerCount = event.getPointerCount(); + for (int i = 0; i < pointerCount; i++) { + pointerIndex = i; + pointerId = event.getPointerId(pointerIndex); + + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_DRAGGED; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + touchEventsBuffer.add(touchEvent); + } + break; + } + + return true; + } + } + + @Override + public boolean isTouchDown(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return false; + else + return isTouched[pointer]; + } + } + + @Override + public int getTouchX(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return 0; + else + return touchX[pointer]; + } + } + + @Override + public int getTouchY(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return 0; + else + return touchY[pointer]; + } + } + + @Override + public List getTouchEvents() { + synchronized (this) { + int len = touchEvents.size(); + for (int i = 0; i < len; i++) + touchEventPool.free(touchEvents.get(i)); + touchEvents.clear(); + touchEvents.addAll(touchEventsBuffer); + touchEventsBuffer.clear(); + return touchEvents; + } + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java new file mode 100644 index 0000000..7ff1205 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java @@ -0,0 +1,101 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.MotionEvent; +import android.view.View; + +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class SingleTouchHandler implements TouchHandler { + boolean isTouched; + int touchX; + int touchY; + Pool touchEventPool; + List touchEvents = new ArrayList(); + List touchEventsBuffer = new ArrayList(); + float scaleX; + float scaleY; + + public SingleTouchHandler(View view, float scaleX, float scaleY) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }; + touchEventPool = new Pool(factory, 100); + view.setOnTouchListener(this); + + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + synchronized(this) { + TouchEvent touchEvent = touchEventPool.newObject(); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touchEvent.type = TouchEvent.TOUCH_DOWN; + isTouched = true; + break; + case MotionEvent.ACTION_MOVE: + touchEvent.type = TouchEvent.TOUCH_DRAGGED; + isTouched = true; + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + touchEvent.type = TouchEvent.TOUCH_UP; + isTouched = false; + break; + } + + touchEvent.x = touchX = (int)(event.getX() * scaleX); + touchEvent.y = touchY = (int)(event.getY() * scaleY); + touchEventsBuffer.add(touchEvent); + + return true; + } + } + + @Override + public boolean isTouchDown(int pointer) { + synchronized(this) { + if(pointer == 0) + return isTouched; + else + return false; + } + } + + @Override + public int getTouchX(int pointer) { + synchronized(this) { + return touchX; + } + } + + @Override + public int getTouchY(int pointer) { + synchronized(this) { + return touchY; + } + } + + @Override + public List getTouchEvents() { + synchronized(this) { + int len = touchEvents.size(); + for( int i = 0; i < len; i++ ) + touchEventPool.free(touchEvents.get(i)); + touchEvents.clear(); + touchEvents.addAll(touchEventsBuffer); + touchEventsBuffer.clear(); + return touchEvents; + } + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/TouchHandler.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/TouchHandler.java new file mode 100644 index 0000000..cc7e754 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/impl/TouchHandler.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.List; + +import android.view.View.OnTouchListener; + +import com.badlogic.androidgames.framework.Input.TouchEvent; + +public interface TouchHandler extends OnTouchListener { + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public List getTouchEvents(); +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/math/Circle.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/math/Circle.java new file mode 100644 index 0000000..2728f84 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/math/Circle.java @@ -0,0 +1,11 @@ +package com.badlogic.androidgames.framework.math; + +public class Circle { + public final Vector2 center = new Vector2(); + public float radius; + + public Circle(float x, float y, float radius) { + this.center.set(x,y); + this.radius = radius; + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/math/OverlapTester.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/math/OverlapTester.java new file mode 100644 index 0000000..5c6ace4 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/math/OverlapTester.java @@ -0,0 +1,58 @@ +package com.badlogic.androidgames.framework.math; + +public class OverlapTester { + public static boolean overlapCircles(Circle c1, Circle c2) { + float distance = c1.center.distSquared(c2.center); + float radiusSum = c1.radius + c2.radius; + return distance <= radiusSum * radiusSum; + } + + public static boolean overlapRectangles(Rectangle r1, Rectangle r2) { + if(r1.lowerLeft.x < r2.lowerLeft.x + r2.width && + r1.lowerLeft.x + r1.width > r2.lowerLeft.x && + r1.lowerLeft.y < r2.lowerLeft.y + r2.height && + r1.lowerLeft.y + r1.height > r2.lowerLeft.y) + return true; + else + return false; + } + + public static boolean overlapCircleRectangle(Circle c, Rectangle r) { + float closestX = c.center.x; + float closestY = c.center.y; + + if(c.center.x < r.lowerLeft.x) { + closestX = r.lowerLeft.x; + } + else if(c.center.x > r.lowerLeft.x + r.width) { + closestX = r.lowerLeft.x + r.width; + } + + if(c.center.y < r.lowerLeft.y) { + closestY = r.lowerLeft.y; + } + else if(c.center.y > r.lowerLeft.y + r.height) { + closestY = r.lowerLeft.y + r.height; + } + + return c.center.distSquared(closestX, closestY) < c.radius * c.radius; + } + + public static boolean pointInCircle(Circle c, Vector2 p) { + return c.center.distSquared(p) < c.radius * c.radius; + } + + public static boolean pointInCircle(Circle c, float x, float y) { + return c.center.distSquared(x, y) < c.radius * c.radius; + } + + public static boolean pointInRectangle(Rectangle r, Vector2 p) { + return r.lowerLeft.x <= p.x && r.lowerLeft.x + r.width >= p.x && + r.lowerLeft.y <= p.y && r.lowerLeft.y + r.height >= p.y; + } + + public static boolean pointInRectangle(Rectangle r, float x, float y) { + return r.lowerLeft.x <= x && r.lowerLeft.x + r.width >= x && + r.lowerLeft.y <= y && r.lowerLeft.y + r.height >= y; + } +} \ No newline at end of file diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/math/Rectangle.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/math/Rectangle.java new file mode 100644 index 0000000..9c2511f --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/math/Rectangle.java @@ -0,0 +1,12 @@ +package com.badlogic.androidgames.framework.math; + +public class Rectangle { + public final Vector2 lowerLeft; + public float width, height; + + public Rectangle(float x, float y, float width, float height) { + this.lowerLeft = new Vector2(x,y); + this.width = width; + this.height = height; + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/math/Vector2.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/math/Vector2.java new file mode 100644 index 0000000..a412397 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/framework/math/Vector2.java @@ -0,0 +1,126 @@ +package com.badlogic.androidgames.framework.math; + +import android.util.FloatMath; + +public class Vector2 { + public static float TO_RADIANS = (1 / 180.0f) * (float) Math.PI; + public static float TO_DEGREES = (1 / (float) Math.PI) * 180; + public float x, y; + + public Vector2() { + } + + public Vector2(float x, float y) { + this.x = x; + this.y = y; + } + + public Vector2(Vector2 other) { + this.x = other.x; + this.y = other.y; + } + + public Vector2 cpy() { + return new Vector2(x, y); + } + + public Vector2 set(float x, float y) { + this.x = x; + this.y = y; + return this; + } + + public Vector2 set(Vector2 other) { + this.x = other.x; + this.y = other.y; + return this; + } + + public Vector2 add(float x, float y) { + this.x += x; + this.y += y; + return this; + } + + public Vector2 add(Vector2 other) { + this.x += other.x; + this.y += other.y; + return this; + } + + public Vector2 sub(float x, float y) { + this.x -= x; + this.y -= y; + return this; + } + + public Vector2 sub(Vector2 other) { + this.x -= other.x; + this.y -= other.y; + return this; + } + + public Vector2 mul(float scalar) { + this.x *= scalar; + this.y *= scalar; + return this; + } + + public float len() { + return FloatMath.sqrt(x * x + y * y); + } + + public Vector2 nor() { + float len = len(); + if (len != 0) { + this.x /= len; + this.y /= len; + } + return this; + } + + public float angle() { + float angle = (float) Math.atan2(y, x) * TO_DEGREES; + if (angle < 0) + angle += 360; + return angle; + } + + public Vector2 rotate(float angle) { + float rad = angle * TO_RADIANS; + float cos = FloatMath.cos(rad); + float sin = FloatMath.sin(rad); + + float newX = this.x * cos - this.y * sin; + float newY = this.x * sin + this.y * cos; + + this.x = newX; + this.y = newY; + + return this; + } + + public float dist(Vector2 other) { + float distX = this.x - other.x; + float distY = this.y - other.y; + return FloatMath.sqrt(distX * distX + distY * distY); + } + + public float dist(float x, float y) { + float distX = this.x - x; + float distY = this.y - y; + return FloatMath.sqrt(distX * distX + distY * distY); + } + + public float distSquared(Vector2 other) { + float distX = this.x - other.x; + float distY = this.y - other.y; + return distX * distX + distY * distY; + } + + public float distSquared(float x, float y) { + float distX = this.x - x; + float distY = this.y - y; + return distX * distX + distY * distY; + } +} \ No newline at end of file diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/AnimationTest.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/AnimationTest.java new file mode 100644 index 0000000..dc0fba8 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/AnimationTest.java @@ -0,0 +1,109 @@ +package com.badlogic.androidgames.gamedev2d; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.Animation; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.TextureRegion; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class AnimationTest extends GLGame { + @Override + public Screen getStartScreen() { + return new AnimationScreen(this); + } + + static final float WORLD_WIDTH = 4.8f; + static final float WORLD_HEIGHT = 3.2f; + + static class Caveman extends DynamicGameObject { + public float walkingTime = 0; + + public Caveman(float x, float y, float width, float height) { + super(x, y, width, height); + this.position.set((float)Math.random() * WORLD_WIDTH, + (float)Math.random() * WORLD_HEIGHT); + this.velocity.set(Math.random() > 0.5f?-0.5f:0.5f, 0); + this.walkingTime = (float)Math.random() * 10; + } + + public void update(float deltaTime) { + position.add(velocity.x * deltaTime, velocity.y * deltaTime); + if(position.x < 0) position.x = WORLD_WIDTH; + if(position.x > WORLD_WIDTH) position.x = 0; + walkingTime += deltaTime; + } + } + + class AnimationScreen extends Screen { + static final int NUM_CAVEMEN = 10; + GLGraphics glGraphics; + Caveman[] cavemen; + SpriteBatcher batcher; + Camera2D camera; + Texture texture; + Animation walkAnim; + + public AnimationScreen(Game game) { + super(game); + glGraphics = ((GLGame)game).getGLGraphics(); + cavemen = new Caveman[NUM_CAVEMEN]; + for(int i = 0; i < NUM_CAVEMEN; i++) { + cavemen[i] = new Caveman((float)Math.random(), (float)Math.random(), 1, 1); + } + batcher = new SpriteBatcher(glGraphics, NUM_CAVEMEN); + camera = new Camera2D(glGraphics, WORLD_WIDTH, WORLD_HEIGHT); + } + + @Override + public void resume() { + texture = new Texture(((GLGame)game), "walkanim.png"); + walkAnim = new Animation( 0.2f, + new TextureRegion(texture, 0, 0, 64, 64), + new TextureRegion(texture, 64, 0, 64, 64), + new TextureRegion(texture, 128, 0, 64, 64), + new TextureRegion(texture, 192, 0, 64, 64)); + } + + @Override + public void update(float deltaTime) { + int len = cavemen.length; + for(int i = 0; i < len; i++) { + cavemen[i].update(deltaTime); + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + camera.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(texture); + int len = cavemen.length; + for(int i = 0; i < len; i++) { + Caveman caveman = cavemen[i]; + TextureRegion keyFrame = walkAnim.getKeyFrame(caveman.walkingTime, Animation.ANIMATION_LOOPING); + batcher.drawSprite(caveman.position.x, caveman.position.y, caveman.velocity.x < 0?1:-1, 1, keyFrame); + } + batcher.endBatch(); + } + + @Override + public void pause() { + } + + @Override + public void dispose() { + } + } +} \ No newline at end of file diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/Camera2DTest.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/Camera2DTest.java new file mode 100644 index 0000000..0a5b919 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/Camera2DTest.java @@ -0,0 +1,177 @@ +package com.badlogic.androidgames.gamedev2d; + +import java.util.ArrayList; +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import android.util.FloatMath; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.SpatialHashGrid; +import com.badlogic.androidgames.framework.gl.Vertices; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Vector2; + +public class Camera2DTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new Camera2DScreen(this); + } + + class Camera2DScreen extends Screen { + final int NUM_TARGETS = 20; + final float WORLD_WIDTH = 9.6f; + final float WORLD_HEIGHT = 4.8f; + GLGraphics glGraphics; + Cannon cannon; + DynamicGameObject ball; + List targets; + SpatialHashGrid grid; + Camera2D camera; + + Vertices cannonVertices; + Vertices ballVertices; + Vertices targetVertices; + + Vector2 touchPos = new Vector2(); + Vector2 gravity = new Vector2(0,-10); + + public Camera2DScreen(Game game) { + super(game); + glGraphics = ((GLGame)game).getGLGraphics(); + + cannon = new Cannon(0, 0, 1, 1); + ball = new DynamicGameObject(0, 0, 0.2f, 0.2f); + targets = new ArrayList(NUM_TARGETS); + grid = new SpatialHashGrid(WORLD_WIDTH, WORLD_HEIGHT, 2.5f); + for(int i = 0; i < NUM_TARGETS; i++) { + GameObject target = new GameObject((float)Math.random() * WORLD_WIDTH, + (float)Math.random() * WORLD_HEIGHT, + 0.5f, 0.5f); + grid.insertStaticObject(target); + targets.add(target); + } + camera = new Camera2D(glGraphics, WORLD_WIDTH, WORLD_HEIGHT); + + cannonVertices = new Vertices(glGraphics, 3, 0, false, false); + cannonVertices.setVertices(new float[] { -0.5f, -0.5f, + 0.5f, 0.0f, + -0.5f, 0.5f }, 0, 6); + + ballVertices = new Vertices(glGraphics, 4, 6, false, false); + ballVertices.setVertices(new float[] { -0.1f, -0.1f, + 0.1f, -0.1f, + 0.1f, 0.1f, + -0.1f, 0.1f }, 0, 8); + ballVertices.setIndices(new short[] {0, 1, 2, 2, 3, 0}, 0, 6); + + targetVertices = new Vertices(glGraphics, 4, 6, false, false); + targetVertices.setVertices(new float[] { -0.25f, -0.25f, + 0.25f, -0.25f, + 0.25f, 0.25f, + -0.25f, 0.25f }, 0, 8); + targetVertices.setIndices(new short[] {0, 1, 2, 2, 3, 0}, 0, 6); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + int len = touchEvents.size(); + for (int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + camera.touchToWorld(touchPos.set(event.x, event.y)); + cannon.angle = touchPos.sub(cannon.position).angle(); + + if(event.type == TouchEvent.TOUCH_UP) { + float radians = cannon.angle * Vector2.TO_RADIANS; + float ballSpeed = touchPos.len() * 2; + ball.position.set(cannon.position); + ball.velocity.x = FloatMath.cos(radians) * ballSpeed; + ball.velocity.y = FloatMath.sin(radians) * ballSpeed; + ball.bounds.lowerLeft.set(ball.position.x - 0.1f, ball.position.y - 0.1f); + } + } + + if(ball.velocity.x != 0 || ball.velocity.y != 0) { + ball.velocity.add(gravity.x * deltaTime, gravity.y * deltaTime); + ball.position.add(ball.velocity.x * deltaTime, ball.velocity.y * deltaTime); + ball.bounds.lowerLeft.add(ball.velocity.x * deltaTime, ball.velocity.y * deltaTime); + } + + List colliders = grid.getPotentialColliders(ball); + len = colliders.size(); + for(int i = 0; i < len; i++) { + GameObject collider = colliders.get(i); + if(OverlapTester.overlapRectangles(ball.bounds, collider.bounds)) { + grid.removeObject(collider); + targets.remove(collider); + } + } + + if(ball.position.y > 0) { + camera.position.set(ball.position); + camera.zoom = 1 + ball.position.y / WORLD_HEIGHT; + } else { + camera.position.set(WORLD_WIDTH / 2, WORLD_HEIGHT / 2); + camera.zoom = 1; + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + camera.setViewportAndMatrices(); + + gl.glColor4f(0, 1, 0, 1); + targetVertices.bind(); + int len = targets.size(); + for(int i = 0; i < len; i++) { + GameObject target = targets.get(i); + gl.glLoadIdentity(); + gl.glTranslatef(target.position.x, target.position.y, 0); + targetVertices.draw(GL10.GL_TRIANGLES, 0, 6); + } + targetVertices.unbind(); + + gl.glLoadIdentity(); + gl.glTranslatef(ball.position.x, ball.position.y, 0); + gl.glColor4f(1,0,0,1); + ballVertices.bind(); + ballVertices.draw(GL10.GL_TRIANGLES, 0, 6); + ballVertices.unbind(); + + gl.glLoadIdentity(); + gl.glTranslatef(cannon.position.x, cannon.position.y, 0); + gl.glRotatef(cannon.angle, 0, 0, 1); + gl.glColor4f(1,1,1,1); + cannonVertices.bind(); + cannonVertices.draw(GL10.GL_TRIANGLES, 0, 3); + cannonVertices.unbind(); + } + + @Override + public void pause() { + + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/Cannon.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/Cannon.java new file mode 100644 index 0000000..3d90405 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/Cannon.java @@ -0,0 +1,10 @@ +package com.badlogic.androidgames.gamedev2d; + +public class Cannon extends GameObject { + public float angle; + + public Cannon(float x, float y, float width, float height) { + super(x, y, width, height); + angle = 0; + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/CannonGravityTest.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/CannonGravityTest.java new file mode 100644 index 0000000..e133910 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/CannonGravityTest.java @@ -0,0 +1,122 @@ +package com.badlogic.androidgames.gamedev2d; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import android.util.FloatMath; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.Vertices; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.Vector2; + +public class CannonGravityTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new CannonGravityScreen(this); + } + + class CannonGravityScreen extends Screen { + float FRUSTUM_WIDTH = 9.6f; + float FRUSTUM_HEIGHT = 6.4f; + GLGraphics glGraphics; + Vertices cannonVertices; + Vertices ballVertices; + Vector2 cannonPos = new Vector2(); + float cannonAngle = 0; + Vector2 touchPos = new Vector2(); + Vector2 ballPos = new Vector2(0,0); + Vector2 ballVelocity = new Vector2(0,0); + Vector2 gravity = new Vector2(0,-10); + + public CannonGravityScreen(Game game) { + super(game); + glGraphics = ((GLGame) game).getGLGraphics(); + cannonVertices = new Vertices(glGraphics, 3, 0, false, false); + cannonVertices.setVertices(new float[] { -0.5f, -0.5f, + 0.5f, 0.0f, + -0.5f, 0.5f }, 0, 6); + ballVertices = new Vertices(glGraphics, 4, 6, false, false); + ballVertices.setVertices(new float[] { -0.1f, -0.1f, + 0.1f, -0.1f, + 0.1f, 0.1f, + -0.1f, 0.1f }, 0, 8); + ballVertices.setIndices(new short[] {0, 1, 2, 2, 3, 0}, 0, 6); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + int len = touchEvents.size(); + for (int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + + touchPos.x = (event.x / (float) glGraphics.getWidth()) + * FRUSTUM_WIDTH; + touchPos.y = (1 - event.y / (float) glGraphics.getHeight()) + * FRUSTUM_HEIGHT; + cannonAngle = touchPos.sub(cannonPos).angle(); + + if(event.type == TouchEvent.TOUCH_UP) { + float radians = cannonAngle * Vector2.TO_RADIANS; + float ballSpeed = touchPos.len(); + ballPos.set(cannonPos); + ballVelocity.x = FloatMath.cos(radians) * ballSpeed; + ballVelocity.y = FloatMath.sin(radians) * ballSpeed; + } + } + + ballVelocity.add(gravity.x * deltaTime, gravity.y * deltaTime); + ballPos.add(ballVelocity.x * deltaTime, ballVelocity.y * deltaTime); + } + + @Override + public void present(float deltaTime) { + + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(0, FRUSTUM_WIDTH, 0, FRUSTUM_HEIGHT, 1, -1); + gl.glMatrixMode(GL10.GL_MODELVIEW); + + gl.glLoadIdentity(); + gl.glTranslatef(cannonPos.x, cannonPos.y, 0); + gl.glRotatef(cannonAngle, 0, 0, 1); + gl.glColor4f(1,1,1,1); + cannonVertices.bind(); + cannonVertices.draw(GL10.GL_TRIANGLES, 0, 3); + cannonVertices.unbind(); + + gl.glLoadIdentity(); + gl.glTranslatef(ballPos.x, ballPos.y, 0); + gl.glColor4f(1,0,0,1); + ballVertices.bind(); + ballVertices.draw(GL10.GL_TRIANGLES, 0, 6); + ballVertices.unbind(); + } + + @Override + public void pause() { + + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/CannonTest.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/CannonTest.java new file mode 100644 index 0000000..2dce621 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/CannonTest.java @@ -0,0 +1,91 @@ +package com.badlogic.androidgames.gamedev2d; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.Vertices; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.Vector2; + +public class CannonTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new CannonScreen(this); + } + + class CannonScreen extends Screen { + float FRUSTUM_WIDTH = 4.8f; + float FRUSTUM_HEIGHT = 3.2f; + GLGraphics glGraphics; + Vertices vertices; + Vector2 cannonPos = new Vector2(2.4f, 0.5f); + float cannonAngle = 0; + Vector2 touchPos = new Vector2(); + + public CannonScreen(Game game) { + super(game); + glGraphics = ((GLGame) game).getGLGraphics(); + vertices = new Vertices(glGraphics, 3, 0, false, false); + vertices.setVertices(new float[] { -0.5f, -0.5f, + 0.5f, 0.0f, + -0.5f, 0.5f }, 0, 6); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + int len = touchEvents.size(); + for (int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + + touchPos.x = (event.x / (float) glGraphics.getWidth()) + * FRUSTUM_WIDTH; + touchPos.y = (1 - event.y / (float) glGraphics.getHeight()) + * FRUSTUM_HEIGHT; + cannonAngle = touchPos.sub(cannonPos).angle(); + } + } + + @Override + public void present(float deltaTime) { + + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(0, FRUSTUM_WIDTH, 0, FRUSTUM_HEIGHT, 1, -1); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + + gl.glTranslatef(cannonPos.x, cannonPos.y, 0); + gl.glRotatef(cannonAngle, 0, 0, 1); + vertices.bind(); + vertices.draw(GL10.GL_TRIANGLES, 0, 3); + vertices.unbind(); + } + + @Override + public void pause() { + + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/CollisionTest.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/CollisionTest.java new file mode 100644 index 0000000..736614d --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/CollisionTest.java @@ -0,0 +1,171 @@ +package com.badlogic.androidgames.gamedev2d; + +import java.util.ArrayList; +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import android.util.FloatMath; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.SpatialHashGrid; +import com.badlogic.androidgames.framework.gl.Vertices; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Vector2; + +public class CollisionTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new CollisionScreen(this); + } + +class CollisionScreen extends Screen { + final int NUM_TARGETS = 20; + final float WORLD_WIDTH = 9.6f; + final float WORLD_HEIGHT = 4.8f; + GLGraphics glGraphics; + Cannon cannon; + DynamicGameObject ball; + List targets; + SpatialHashGrid grid; + + Vertices cannonVertices; + Vertices ballVertices; + Vertices targetVertices; + + Vector2 touchPos = new Vector2(); + Vector2 gravity = new Vector2(0,-10); + + public CollisionScreen(Game game) { + super(game); + glGraphics = ((GLGame)game).getGLGraphics(); + + cannon = new Cannon(0, 0, 1, 1); + ball = new DynamicGameObject(0, 0, 0.2f, 0.2f); + targets = new ArrayList(NUM_TARGETS); + grid = new SpatialHashGrid(WORLD_WIDTH, WORLD_HEIGHT, 2.5f); + for(int i = 0; i < NUM_TARGETS; i++) { + GameObject target = new GameObject((float)Math.random() * WORLD_WIDTH, + (float)Math.random() * WORLD_HEIGHT, + 0.5f, 0.5f); + grid.insertStaticObject(target); + targets.add(target); + } + + cannonVertices = new Vertices(glGraphics, 3, 0, false, false); + cannonVertices.setVertices(new float[] { -0.5f, -0.5f, + 0.5f, 0.0f, + -0.5f, 0.5f }, 0, 6); + + ballVertices = new Vertices(glGraphics, 4, 6, false, false); + ballVertices.setVertices(new float[] { -0.1f, -0.1f, + 0.1f, -0.1f, + 0.1f, 0.1f, + -0.1f, 0.1f }, 0, 8); + ballVertices.setIndices(new short[] {0, 1, 2, 2, 3, 0}, 0, 6); + + targetVertices = new Vertices(glGraphics, 4, 6, false, false); + targetVertices.setVertices(new float[] { -0.25f, -0.25f, + 0.25f, -0.25f, + 0.25f, 0.25f, + -0.25f, 0.25f }, 0, 8); + targetVertices.setIndices(new short[] {0, 1, 2, 2, 3, 0}, 0, 6); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + int len = touchEvents.size(); + for (int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + + touchPos.x = (event.x / (float) glGraphics.getWidth())* WORLD_WIDTH; + touchPos.y = (1 - event.y / (float) glGraphics.getHeight()) * WORLD_HEIGHT; + + cannon.angle = touchPos.sub(cannon.position).angle(); + + if(event.type == TouchEvent.TOUCH_UP) { + float radians = cannon.angle * Vector2.TO_RADIANS; + float ballSpeed = touchPos.len() * 2; + ball.position.set(cannon.position); + ball.velocity.x = FloatMath.cos(radians) * ballSpeed; + ball.velocity.y = FloatMath.sin(radians) * ballSpeed; + ball.bounds.lowerLeft.set(ball.position.x - 0.1f, ball.position.y - 0.1f); + } + } + + ball.velocity.add(gravity.x * deltaTime, gravity.y * deltaTime); + ball.position.add(ball.velocity.x * deltaTime, ball.velocity.y * deltaTime); + ball.bounds.lowerLeft.add(ball.velocity.x * deltaTime, ball.velocity.y * deltaTime); + + List colliders = grid.getPotentialColliders(ball); + len = colliders.size(); + for(int i = 0; i < len; i++) { + GameObject collider = colliders.get(i); + if(OverlapTester.overlapRectangles(ball.bounds, collider.bounds)) { + grid.removeObject(collider); + targets.remove(collider); + } + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(0, WORLD_WIDTH, 0, WORLD_HEIGHT, 1, -1); + gl.glMatrixMode(GL10.GL_MODELVIEW); + + gl.glColor4f(0, 1, 0, 1); + targetVertices.bind(); + int len = targets.size(); + for(int i = 0; i < len; i++) { + GameObject target = targets.get(i); + gl.glLoadIdentity(); + gl.glTranslatef(target.position.x, target.position.y, 0); + targetVertices.draw(GL10.GL_TRIANGLES, 0, 6); + } + targetVertices.unbind(); + + gl.glLoadIdentity(); + gl.glTranslatef(ball.position.x, ball.position.y, 0); + gl.glColor4f(1,0,0,1); + ballVertices.bind(); + ballVertices.draw(GL10.GL_TRIANGLES, 0, 6); + ballVertices.unbind(); + + gl.glLoadIdentity(); + gl.glTranslatef(cannon.position.x, cannon.position.y, 0); + gl.glRotatef(cannon.angle, 0, 0, 1); + gl.glColor4f(1,1,1,1); + cannonVertices.bind(); + cannonVertices.draw(GL10.GL_TRIANGLES, 0, 3); + cannonVertices.unbind(); + } + + @Override + public void pause() { + + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } +} +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/DynamicGameObject.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/DynamicGameObject.java new file mode 100644 index 0000000..f6eb091 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/DynamicGameObject.java @@ -0,0 +1,14 @@ +package com.badlogic.androidgames.gamedev2d; + +import com.badlogic.androidgames.framework.math.Vector2; + +public class DynamicGameObject extends GameObject { + public final Vector2 velocity; + public final Vector2 accel; + + public DynamicGameObject(float x, float y, float width, float height) { + super(x, y, width, height); + velocity = new Vector2(); + accel = new Vector2(); + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/GameDev2DStarter.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/GameDev2DStarter.java new file mode 100644 index 0000000..479f438 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/GameDev2DStarter.java @@ -0,0 +1,35 @@ +package com.badlogic.androidgames.gamedev2d; + +import android.app.ListActivity; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +public class GameDev2DStarter extends ListActivity { + String tests[] = { "CannonTest", "CannonGravityTest", "CollisionTest", + "Camera2DTest", "TextureAtlasTest", "SpriteBatcherTest", + "AnimationTest"}; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setListAdapter(new ArrayAdapter(this, + android.R.layout.simple_list_item_1, tests)); + } + + @Override + protected void onListItemClick(ListView list, View view, int position, + long id) { + super.onListItemClick(list, view, position, id); + String testName = tests[position]; + try { + Class clazz = Class.forName("com.badlogic.androidgames.gamedev2d." + + testName); + Intent intent = new Intent(this, clazz); + startActivity(intent); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/GameObject.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/GameObject.java new file mode 100644 index 0000000..cebdea0 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/GameObject.java @@ -0,0 +1,14 @@ +package com.badlogic.androidgames.gamedev2d; + +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class GameObject { + public final Vector2 position; + public final Rectangle bounds; + + public GameObject(float x, float y, float width, float height) { + this.position = new Vector2(x,y); + this.bounds = new Rectangle(x-width/2, y-height/2, width, height); + } +} diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/SpatialHashGrid.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/SpatialHashGrid.java new file mode 100644 index 0000000..0b30034 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/SpatialHashGrid.java @@ -0,0 +1,141 @@ +package com.badlogic.androidgames.gamedev2d; + +import java.util.ArrayList; +import java.util.List; + +import android.util.FloatMath; + +public class SpatialHashGrid { + List[] dynamicCells; + List[] staticCells; + int cellsPerRow; + int cellsPerCol; + float cellSize; + int[] cellIds = new int[4]; + List foundObjects; + + @SuppressWarnings("unchecked") + public SpatialHashGrid(float worldWidth, float worldHeight, float cellSize) { + this.cellSize = cellSize; + this.cellsPerRow = (int)FloatMath.ceil(worldWidth/cellSize); + this.cellsPerCol = (int)FloatMath.ceil(worldHeight/cellSize); + int numCells = cellsPerRow * cellsPerCol; + dynamicCells = new List[numCells]; + staticCells = new List[numCells]; + for(int i = 0; i < numCells; i++) { + dynamicCells[i] = new ArrayList(10); + staticCells[i] = new ArrayList(10); + } + foundObjects = new ArrayList(10); + } + + public void insertStaticObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while((cellId = cellIds[i++]) != -1) { + staticCells[cellId].add(obj); + } + } + + public void insertDynamicObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while((cellId = cellIds[i++]) != -1) { + dynamicCells[cellId].add(obj); + } + } + + public void removeObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while((cellId = cellIds[i++]) != -1) { + dynamicCells[cellId].remove(obj); + staticCells[cellId].remove(obj); + } + } + + public void clearDynamicCells(GameObject obj) { + int len = dynamicCells.length; + for(int i = 0; i < len; i++) { + dynamicCells[i].clear(); + } + } + + public List getPotentialColliders(GameObject obj) { + foundObjects.clear(); + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while((cellId = cellIds[i++]) != -1) { + int len = dynamicCells[cellId].size(); + for(int j = 0; j < len; j++) { + GameObject collider = dynamicCells[cellId].get(j); + if(!foundObjects.contains(collider)) + foundObjects.add(collider); + } + + len = staticCells[cellId].size(); + for(int j = 0; j < len; j++) { + GameObject collider = staticCells[cellId].get(j); + if(!foundObjects.contains(collider)) + foundObjects.add(collider); + } + } + return foundObjects; + } + + public int[] getCellIds(GameObject obj) { + int x1 = (int)FloatMath.floor(obj.bounds.lowerLeft.x / cellSize); + int y1 = (int)FloatMath.floor(obj.bounds.lowerLeft.y / cellSize); + int x2 = (int)FloatMath.floor((obj.bounds.lowerLeft.x + obj.bounds.width) / cellSize); + int y2 = (int)FloatMath.floor((obj.bounds.lowerLeft.y + obj.bounds.height) / cellSize); + + if(x1 == x2 && y1 == y2) { + if(x1 >= 0 && x1 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[0] = x1 + y1 * cellsPerRow; + else + cellIds[0] = -1; + cellIds[1] = -1; + cellIds[2] = -1; + cellIds[3] = -1; + } + else if(x1 == x2) { + int i = 0; + if(x1 >= 0 && x1 < cellsPerRow) { + if(y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x1 + y1 * cellsPerRow; + if(y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x1 + y2 * cellsPerRow; + } + while(i <= 3) cellIds[i++] = -1; + } + else if(y1 == y2) { + int i = 0; + if(y1 >= 0 && y1 < cellsPerCol) { + if(x1 >= 0 && x1 < cellsPerRow) + cellIds[i++] = x1 + y1 * cellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow) + cellIds[i++] = x2 + y1 * cellsPerRow; + } + while(i <= 3) cellIds[i++] = -1; + } + else { + int i = 0; + int y1CellsPerRow = y1 * cellsPerRow; + int y2CellsPerRow = y2 * cellsPerRow; + if(x1 >= 0 && x1 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x1 + y1CellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x2 + y1CellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow && y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x2 + y2CellsPerRow; + if(x1 >= 0 && x1 < cellsPerRow && y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x1 + y2CellsPerRow; + while(i <= 3) cellIds[i++] = -1; + } + return cellIds; + } +} \ No newline at end of file diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/SpriteBatcherTest.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/SpriteBatcherTest.java new file mode 100644 index 0000000..6ddb39d --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/SpriteBatcherTest.java @@ -0,0 +1,162 @@ +package com.badlogic.androidgames.gamedev2d; + +import java.util.ArrayList; +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import android.util.FloatMath; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.FPSCounter; +import com.badlogic.androidgames.framework.gl.SpatialHashGrid; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.TextureRegion; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Vector2; + +public class SpriteBatcherTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new SpriteBatcherScreen(this); + } + + class SpriteBatcherScreen extends Screen { + final int NUM_TARGETS = 100; + final float WORLD_WIDTH = 9.6f; + final float WORLD_HEIGHT = 4.8f; + + Cannon cannon; + DynamicGameObject ball; + List targets; + SpatialHashGrid grid; + Vector2 touchPos = new Vector2(); + Vector2 gravity = new Vector2(0,-10); + + GLGraphics glGraphics; + Camera2D camera; + Texture texture; + TextureRegion cannonRegion; + TextureRegion ballRegion; + TextureRegion bobRegion; + SpriteBatcher batcher; + FPSCounter fpsCounter; + + public SpriteBatcherScreen(Game game) { + super(game); + glGraphics = ((GLGame)game).getGLGraphics(); + + cannon = new Cannon(0, 0, 1, 0.5f); + ball = new DynamicGameObject(0, 0, 0.2f, 0.2f); + targets = new ArrayList(NUM_TARGETS); + grid = new SpatialHashGrid(WORLD_WIDTH, WORLD_HEIGHT, 2.5f); + for(int i = 0; i < NUM_TARGETS; i++) { + GameObject target = new GameObject((float)Math.random() * WORLD_WIDTH, + (float)Math.random() * WORLD_HEIGHT, + 0.5f, 0.5f); + grid.insertStaticObject(target); + targets.add(target); + } + + camera = new Camera2D(glGraphics, WORLD_WIDTH, WORLD_HEIGHT); + batcher = new SpriteBatcher(glGraphics, 102); + fpsCounter = new FPSCounter(); + } + + @Override + public void resume() { + texture = new Texture(((GLGame)game), "atlas.png"); + cannonRegion = new TextureRegion(texture, 0, 0, 64, 32); + ballRegion = new TextureRegion(texture, 0, 32, 16, 16); + bobRegion = new TextureRegion(texture, 32, 32, 32, 32); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + int len = touchEvents.size(); + for (int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + camera.touchToWorld(touchPos.set(event.x, event.y)); + cannon.angle = touchPos.sub(cannon.position).angle(); + + if(event.type == TouchEvent.TOUCH_UP) { + float radians = cannon.angle * Vector2.TO_RADIANS; + float ballSpeed = touchPos.len() * 2; + ball.position.set(cannon.position); + ball.velocity.x = FloatMath.cos(radians) * ballSpeed; + ball.velocity.y = FloatMath.sin(radians) * ballSpeed; + ball.bounds.lowerLeft.set(ball.position.x - 0.1f, ball.position.y - 0.1f); + } + } + + if(ball.velocity.x != 0 || ball.velocity.y != 0) { + ball.velocity.add(gravity.x * deltaTime, gravity.y * deltaTime); + ball.position.add(ball.velocity.x * deltaTime, ball.velocity.y * deltaTime); + ball.bounds.lowerLeft.add(ball.velocity.x * deltaTime, ball.velocity.y * deltaTime); + } + + List colliders = grid.getPotentialColliders(ball); + len = colliders.size(); + for(int i = 0; i < len; i++) { + GameObject collider = colliders.get(i); + if(OverlapTester.overlapRectangles(ball.bounds, collider.bounds)) { + grid.removeObject(collider); + targets.remove(collider); + } + } + + if(ball.position.y > 0) { + camera.position.set(ball.position); + camera.zoom = 1 + ball.position.y / WORLD_HEIGHT; + } else { + camera.position.set(WORLD_WIDTH / 2, WORLD_HEIGHT / 2); + camera.zoom = 1; + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + camera.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(texture); + + int len = targets.size(); + for(int i = 0; i < len; i++) { + GameObject target = targets.get(i); + batcher.drawSprite(target.position.x, target.position.y, 0.5f, 0.5f, bobRegion); + } + + batcher.drawSprite(ball.position.x, ball.position.y, 0.2f, 0.2f, ballRegion); + batcher.drawSprite(cannon.position.x, cannon.position.y, 1, 0.5f, cannon.angle, cannonRegion); + batcher.endBatch(); + + fpsCounter.logFrame(); + } + + @Override + public void pause() { + + } + + @Override + public void dispose() { + + } + } +} \ No newline at end of file diff --git a/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/TextureAtlasTest.java b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/TextureAtlasTest.java new file mode 100644 index 0000000..fffdc76 --- /dev/null +++ b/ch08-2d-gamedev/src/com/badlogic/androidgames/gamedev2d/TextureAtlasTest.java @@ -0,0 +1,190 @@ +package com.badlogic.androidgames.gamedev2d; + +import java.util.ArrayList; +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import android.util.FloatMath; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.FPSCounter; +import com.badlogic.androidgames.framework.gl.SpatialHashGrid; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.Vertices; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Vector2; + +public class TextureAtlasTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new TextureAtlasScreen(this); + } + + class TextureAtlasScreen extends Screen { + final int NUM_TARGETS = 100; + final float WORLD_WIDTH = 9.6f; + final float WORLD_HEIGHT = 4.8f; + GLGraphics glGraphics; + Cannon cannon; + DynamicGameObject ball; + List targets; + SpatialHashGrid grid; + Camera2D camera; + Texture texture; + + Vertices cannonVertices; + Vertices ballVertices; + Vertices targetVertices; + + Vector2 touchPos = new Vector2(); + Vector2 gravity = new Vector2(0,-10); + FPSCounter fpsCounter; + + public TextureAtlasScreen(Game game) { + super(game); + glGraphics = ((GLGame)game).getGLGraphics(); + + cannon = new Cannon(0, 0, 1, 0.5f); + ball = new DynamicGameObject(0, 0, 0.2f, 0.2f); + targets = new ArrayList(NUM_TARGETS); + grid = new SpatialHashGrid(WORLD_WIDTH, WORLD_HEIGHT, 2.5f); + for(int i = 0; i < NUM_TARGETS; i++) { + GameObject target = new GameObject((float)Math.random() * WORLD_WIDTH, + (float)Math.random() * WORLD_HEIGHT, + 0.5f, 0.5f); + grid.insertStaticObject(target); + targets.add(target); + } + camera = new Camera2D(glGraphics, WORLD_WIDTH, WORLD_HEIGHT); + + cannonVertices = new Vertices(glGraphics, 4, 6, false, true); + cannonVertices.setVertices(new float[] { -0.5f, -0.25f, 0.0f, 0.5f, + 0.5f, -0.25f, 1.0f, 0.5f, + 0.5f, 0.25f, 1.0f, 0.0f, + -0.5f, 0.25f, 0.0f, 0.0f }, + 0, 16); + cannonVertices.setIndices(new short[] {0, 1, 2, 2, 3, 0}, 0, 6); + + ballVertices = new Vertices(glGraphics, 4, 6, false, true); + ballVertices.setVertices(new float[] { -0.1f, -0.1f, 0.0f, 0.75f, + 0.1f, -0.1f, 0.25f, 0.75f, + 0.1f, 0.1f, 0.25f, 0.5f, + -0.1f, 0.1f, 0.0f, 0.5f }, + 0, 16); + ballVertices.setIndices(new short[] {0, 1, 2, 2, 3, 0}, 0, 6); + + targetVertices = new Vertices(glGraphics, 4, 6, false, true); + targetVertices.setVertices(new float[] { -0.25f, -0.25f, 0.5f, 1.0f, + 0.25f, -0.25f, 1.0f, 1.0f, + 0.25f, 0.25f, 1.0f, 0.5f, + -0.25f, 0.25f, 0.5f, 0.5f }, + 0, 16); + targetVertices.setIndices(new short[] {0, 1, 2, 2, 3, 0}, 0, 6); + fpsCounter = new FPSCounter(); + } + + @Override + public void resume() { + texture = new Texture(((GLGame)game), "atlas.png"); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + int len = touchEvents.size(); + for (int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + camera.touchToWorld(touchPos.set(event.x, event.y)); + cannon.angle = touchPos.sub(cannon.position).angle(); + + if(event.type == TouchEvent.TOUCH_UP) { + float radians = cannon.angle * Vector2.TO_RADIANS; + float ballSpeed = touchPos.len() * 2; + ball.position.set(cannon.position); + ball.velocity.x = FloatMath.cos(radians) * ballSpeed; + ball.velocity.y = FloatMath.sin(radians) * ballSpeed; + ball.bounds.lowerLeft.set(ball.position.x - 0.1f, ball.position.y - 0.1f); + } + } + + if(ball.velocity.x != 0 || ball.velocity.y != 0) { + ball.velocity.add(gravity.x * deltaTime, gravity.y * deltaTime); + ball.position.add(ball.velocity.x * deltaTime, ball.velocity.y * deltaTime); + ball.bounds.lowerLeft.add(ball.velocity.x * deltaTime, ball.velocity.y * deltaTime); + } + + List colliders = grid.getPotentialColliders(ball); + len = colliders.size(); + for(int i = 0; i < len; i++) { + GameObject collider = colliders.get(i); + if(OverlapTester.overlapRectangles(ball.bounds, collider.bounds)) { + grid.removeObject(collider); + targets.remove(collider); + } + } + + if(ball.position.y > 0) { + camera.position.set(ball.position); + camera.zoom = 1 + ball.position.y / WORLD_HEIGHT; + } else { + camera.position.set(WORLD_WIDTH / 2, WORLD_HEIGHT / 2); + camera.zoom = 1; + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + camera.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + gl.glEnable(GL10.GL_TEXTURE_2D); + texture.bind(); + + targetVertices.bind(); + int len = targets.size(); + for(int i = 0; i < len; i++) { + GameObject target = targets.get(i); + gl.glLoadIdentity(); + gl.glTranslatef(target.position.x, target.position.y, 0); + targetVertices.draw(GL10.GL_TRIANGLES, 0, 6); + } + targetVertices.unbind(); + + gl.glLoadIdentity(); + gl.glTranslatef(ball.position.x, ball.position.y, 0); + ballVertices.bind(); + ballVertices.draw(GL10.GL_TRIANGLES, 0, 6); + ballVertices.unbind(); + + gl.glLoadIdentity(); + gl.glTranslatef(cannon.position.x, cannon.position.y, 0); + gl.glRotatef(cannon.angle, 0, 0, 1); + cannonVertices.bind(); + cannonVertices.draw(GL10.GL_TRIANGLES, 0, 6); + cannonVertices.unbind(); + fpsCounter.logFrame(); + } + + @Override + public void pause() { + + } + + @Override + public void dispose() { + + } + } +} diff --git a/ch09-jumper/.classpath b/ch09-jumper/.classpath new file mode 100644 index 0000000..609aa00 --- /dev/null +++ b/ch09-jumper/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ch09-jumper/.project b/ch09-jumper/.project new file mode 100644 index 0000000..b3301ed --- /dev/null +++ b/ch09-jumper/.project @@ -0,0 +1,33 @@ + + + ch09-jumper + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/ch09-jumper/AndroidManifest.xml b/ch09-jumper/AndroidManifest.xml new file mode 100644 index 0000000..fbabb81 --- /dev/null +++ b/ch09-jumper/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch09-jumper/assets/background.png b/ch09-jumper/assets/background.png new file mode 100644 index 0000000..bdf2e1f Binary files /dev/null and b/ch09-jumper/assets/background.png differ diff --git a/ch09-jumper/assets/click.ogg b/ch09-jumper/assets/click.ogg new file mode 100644 index 0000000..1dce73c Binary files /dev/null and b/ch09-jumper/assets/click.ogg differ diff --git a/ch09-jumper/assets/coin.ogg b/ch09-jumper/assets/coin.ogg new file mode 100644 index 0000000..a4470ec Binary files /dev/null and b/ch09-jumper/assets/coin.ogg differ diff --git a/ch09-jumper/assets/help.png b/ch09-jumper/assets/help.png new file mode 100644 index 0000000..e14f0e1 Binary files /dev/null and b/ch09-jumper/assets/help.png differ diff --git a/ch09-jumper/assets/help1.png b/ch09-jumper/assets/help1.png new file mode 100644 index 0000000..e14f0e1 Binary files /dev/null and b/ch09-jumper/assets/help1.png differ diff --git a/ch09-jumper/assets/help2.png b/ch09-jumper/assets/help2.png new file mode 100644 index 0000000..3c3948c Binary files /dev/null and b/ch09-jumper/assets/help2.png differ diff --git a/ch09-jumper/assets/help3.png b/ch09-jumper/assets/help3.png new file mode 100644 index 0000000..5612b1c Binary files /dev/null and b/ch09-jumper/assets/help3.png differ diff --git a/ch09-jumper/assets/help4.png b/ch09-jumper/assets/help4.png new file mode 100644 index 0000000..7b609da Binary files /dev/null and b/ch09-jumper/assets/help4.png differ diff --git a/ch09-jumper/assets/help5.png b/ch09-jumper/assets/help5.png new file mode 100644 index 0000000..9ad7abd Binary files /dev/null and b/ch09-jumper/assets/help5.png differ diff --git a/ch09-jumper/assets/highjump.ogg b/ch09-jumper/assets/highjump.ogg new file mode 100644 index 0000000..f96ca6d Binary files /dev/null and b/ch09-jumper/assets/highjump.ogg differ diff --git a/ch09-jumper/assets/hit.ogg b/ch09-jumper/assets/hit.ogg new file mode 100644 index 0000000..edb408c Binary files /dev/null and b/ch09-jumper/assets/hit.ogg differ diff --git a/ch09-jumper/assets/items.png b/ch09-jumper/assets/items.png new file mode 100644 index 0000000..53b0187 Binary files /dev/null and b/ch09-jumper/assets/items.png differ diff --git a/ch09-jumper/assets/jump.ogg b/ch09-jumper/assets/jump.ogg new file mode 100644 index 0000000..443ae8c Binary files /dev/null and b/ch09-jumper/assets/jump.ogg differ diff --git a/ch09-jumper/assets/music.mp3 b/ch09-jumper/assets/music.mp3 new file mode 100644 index 0000000..90f44d7 Binary files /dev/null and b/ch09-jumper/assets/music.mp3 differ diff --git a/ch09-jumper/assets/sizes.txt b/ch09-jumper/assets/sizes.txt new file mode 100644 index 0000000..6a30d6b --- /dev/null +++ b/ch09-jumper/assets/sizes.txt @@ -0,0 +1,2 @@ +main menu 300x240 +logo 274x142 \ No newline at end of file diff --git a/ch09-jumper/default.properties b/ch09-jumper/default.properties new file mode 100644 index 0000000..3ac2523 --- /dev/null +++ b/ch09-jumper/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-12 diff --git a/ch09-jumper/proguard.cfg b/ch09-jumper/proguard.cfg new file mode 100644 index 0000000..8ad7d33 --- /dev/null +++ b/ch09-jumper/proguard.cfg @@ -0,0 +1,34 @@ +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontpreverify +-verbose +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* + +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class com.android.vending.licensing.ILicensingService + +-keepclasseswithmembernames class * { + native ; +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/ch09-jumper/res/drawable-hdpi/icon.png b/ch09-jumper/res/drawable-hdpi/icon.png new file mode 100644 index 0000000..8074c4c Binary files /dev/null and b/ch09-jumper/res/drawable-hdpi/icon.png differ diff --git a/ch09-jumper/res/drawable-ldpi/icon.png b/ch09-jumper/res/drawable-ldpi/icon.png new file mode 100644 index 0000000..1095584 Binary files /dev/null and b/ch09-jumper/res/drawable-ldpi/icon.png differ diff --git a/ch09-jumper/res/drawable-mdpi/icon.png b/ch09-jumper/res/drawable-mdpi/icon.png new file mode 100644 index 0000000..a07c69f Binary files /dev/null and b/ch09-jumper/res/drawable-mdpi/icon.png differ diff --git a/ch09-jumper/res/layout/main.xml b/ch09-jumper/res/layout/main.xml new file mode 100644 index 0000000..3a5f117 --- /dev/null +++ b/ch09-jumper/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/ch09-jumper/res/values/strings.xml b/ch09-jumper/res/values/strings.xml new file mode 100644 index 0000000..319ef7c --- /dev/null +++ b/ch09-jumper/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Hello World, Jumper! + Jumper + diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/Audio.java b/ch09-jumper/src/com/badlogic/androidgames/framework/Audio.java new file mode 100644 index 0000000..d30d722 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/Audio.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Audio { + public Music newMusic(String filename); + + public Sound newSound(String filename); +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/Color.java b/ch09-jumper/src/com/badlogic/androidgames/framework/Color.java new file mode 100644 index 0000000..57d68e5 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/Color.java @@ -0,0 +1,10 @@ +package com.badlogic.androidgames.framework; + +public class Color { + public static int convert (int r, int g, int b, int a) { + return ((a & 0xff) << 24) | + ((r & 0xff) << 16) | + ((g & 0xff) << 8) | + ((b & 0xff)); + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/DynamicGameObject.java b/ch09-jumper/src/com/badlogic/androidgames/framework/DynamicGameObject.java new file mode 100644 index 0000000..00afabb --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/DynamicGameObject.java @@ -0,0 +1,14 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.math.Vector2; + +public class DynamicGameObject extends GameObject { + public final Vector2 velocity; + public final Vector2 accel; + + public DynamicGameObject(float x, float y, float width, float height) { + super(x, y, width, height); + velocity = new Vector2(); + accel = new Vector2(); + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/FileIO.java b/ch09-jumper/src/com/badlogic/androidgames/framework/FileIO.java new file mode 100644 index 0000000..679243a --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/FileIO.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import android.content.SharedPreferences; + +public interface FileIO { + public InputStream readAsset(String fileName) throws IOException; + + public InputStream readFile(String fileName) throws IOException; + + public OutputStream writeFile(String fileName) throws IOException; + + public SharedPreferences getPreferences(); +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/Game.java b/ch09-jumper/src/com/badlogic/androidgames/framework/Game.java new file mode 100644 index 0000000..fb5c5aa --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/Game.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework; + +public interface Game { + public Input getInput(); + + public FileIO getFileIO(); + + public Graphics getGraphics(); + + public Audio getAudio(); + + public void setScreen(Screen screen); + + public Screen getCurrentScreen(); + + public Screen getStartScreen(); +} \ No newline at end of file diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/GameObject.java b/ch09-jumper/src/com/badlogic/androidgames/framework/GameObject.java new file mode 100644 index 0000000..54aaa09 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/GameObject.java @@ -0,0 +1,14 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class GameObject { + public final Vector2 position; + public final Rectangle bounds; + + public GameObject(float x, float y, float width, float height) { + this.position = new Vector2(x,y); + this.bounds = new Rectangle(x-width/2, y-height/2, width, height); + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/Graphics.java b/ch09-jumper/src/com/badlogic/androidgames/framework/Graphics.java new file mode 100644 index 0000000..c7b8f45 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/Graphics.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework; + +public interface Graphics { + public static enum PixmapFormat { + ARGB8888, ARGB4444, RGB565 + } + + public Pixmap newPixmap(String fileName, PixmapFormat format); + + public void clear(int color); + + public void drawPixel(int x, int y, int color); + + public void drawLine(int x, int y, int x2, int y2, int color); + + public void drawRect(int x, int y, int width, int height, int color); + + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight); + + public void drawPixmap(Pixmap pixmap, int x, int y); + + public int getWidth(); + + public int getHeight(); +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/Input.java b/ch09-jumper/src/com/badlogic/androidgames/framework/Input.java new file mode 100644 index 0000000..0893d62 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/Input.java @@ -0,0 +1,70 @@ +package com.badlogic.androidgames.framework; + +import java.util.List; + +public interface Input { + public static class KeyEvent { + public static final int KEY_DOWN = 0; + public static final int KEY_UP = 1; + + public int type; + public int keyCode; + public char keyChar; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == KEY_DOWN) + builder.append("key down, "); + else + builder.append("key up, "); + builder.append(keyCode); + builder.append(","); + builder.append(keyChar); + return builder.toString(); + } + } + + public static class TouchEvent { + public static final int TOUCH_DOWN = 0; + public static final int TOUCH_UP = 1; + public static final int TOUCH_DRAGGED = 2; + + public int type; + public int x, y; + public int pointer; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == TOUCH_DOWN) + builder.append("touch down, "); + else if (type == TOUCH_DRAGGED) + builder.append("touch dragged, "); + else + builder.append("touch up, "); + builder.append(pointer); + builder.append(","); + builder.append(x); + builder.append(","); + builder.append(y); + return builder.toString(); + } + } + + public boolean isKeyPressed(int keyCode); + + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public float getAccelX(); + + public float getAccelY(); + + public float getAccelZ(); + + public List getKeyEvents(); + + public List getTouchEvents(); +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/Music.java b/ch09-jumper/src/com/badlogic/androidgames/framework/Music.java new file mode 100644 index 0000000..1b95a1e --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/Music.java @@ -0,0 +1,21 @@ +package com.badlogic.androidgames.framework; + +public interface Music { + public void play(); + + public void stop(); + + public void pause(); + + public void setLooping(boolean looping); + + public void setVolume(float volume); + + public boolean isPlaying(); + + public boolean isStopped(); + + public boolean isLooping(); + + public void dispose(); +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/Pixmap.java b/ch09-jumper/src/com/badlogic/androidgames/framework/Pixmap.java new file mode 100644 index 0000000..4b1eec9 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/Pixmap.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; + +public interface Pixmap { + public int getWidth(); + + public int getHeight(); + + public PixmapFormat getFormat(); + + public void dispose(); +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/Pool.java b/ch09-jumper/src/com/badlogic/androidgames/framework/Pool.java new file mode 100644 index 0000000..d0b7cec --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/Pool.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework; + +import java.util.ArrayList; +import java.util.List; + +public class Pool { + public interface PoolObjectFactory { + public T createObject(); + } + + private final List freeObjects; + private final PoolObjectFactory factory; + private final int maxSize; + + public Pool(PoolObjectFactory factory, int maxSize) { + this.factory = factory; + this.maxSize = maxSize; + this.freeObjects = new ArrayList(maxSize); + } + + public T newObject() { + T object = null; + + if (freeObjects.size() == 0) + object = factory.createObject(); + else + object = freeObjects.remove(freeObjects.size() - 1); + + return object; + } + + public void free(T object) { + if (freeObjects.size() < maxSize) + freeObjects.add(object); + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/Screen.java b/ch09-jumper/src/com/badlogic/androidgames/framework/Screen.java new file mode 100644 index 0000000..40a98b9 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/Screen.java @@ -0,0 +1,19 @@ +package com.badlogic.androidgames.framework; + +public abstract class Screen { + protected final Game game; + + public Screen(Game game) { + this.game = game; + } + + public abstract void update(float deltaTime); + + public abstract void present(float deltaTime); + + public abstract void pause(); + + public abstract void resume(); + + public abstract void dispose(); +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/Sound.java b/ch09-jumper/src/com/badlogic/androidgames/framework/Sound.java new file mode 100644 index 0000000..3f49835 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/Sound.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Sound { + public void play(float volume); + + public void dispose(); +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/TestScreen.java b/ch09-jumper/src/com/badlogic/androidgames/framework/TestScreen.java new file mode 100644 index 0000000..5a0bbcf --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/TestScreen.java @@ -0,0 +1,112 @@ +package com.badlogic.androidgames.framework; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.List; + +import android.graphics.Color; +import android.util.Log; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; +import com.badlogic.androidgames.framework.Input.KeyEvent; +import com.badlogic.androidgames.framework.Input.TouchEvent; + +class TestScreen extends Screen { + long startTime = System.nanoTime(); + int frames; + Pixmap bob; + Pixmap bobAlpha; + Sound sound; + Music music; + + public TestScreen(Game game) { + super(game); + bob = game.getGraphics().newPixmap("bobrgb888.png", PixmapFormat.RGB565); + bobAlpha = game.getGraphics().newPixmap("bobargb8888.png", PixmapFormat.ARGB4444); + music = game.getAudio().newMusic("music.ogg"); + music.setLooping(true); + music.setVolume(0.5f); + music.play(); + sound = game.getAudio().newSound("music.ogg"); + + try { + BufferedReader in = new BufferedReader(new InputStreamReader(game.getFileIO().readAsset("test.txt"))); + String text = in.readLine(); + in.close(); + + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(game.getFileIO().writeFile("test.txt"))); + out.write("This is a freaking test"); + out.close(); + + in = new BufferedReader(new InputStreamReader(game.getFileIO().readFile("test.txt"))); + String text2 = in.readLine(); + in.close(); + + Log.d("MrNom", text + ", " + text2 ); + } catch(Exception ex) { + ex.printStackTrace(); + } + } + + @Override + public void update(float deltaTime) { + } + + @Override + public void present(float deltaTime) { + Graphics g = game.getGraphics(); + Input inp = game.getInput(); + g.clear(Color.RED); + g.drawLine(0,0,320, 480, Color.BLUE); + g.drawRect(20,20,100,100, Color.GREEN); + g.drawPixmap(bob, 100, 100); + g.drawPixmap(bobAlpha, 100, 200); + g.drawPixmap(bob, 200, 200, 0, 0, 64, 64); + for(int i=0; i < 2; i++) { + if(inp.isTouchDown(i)) { + g.drawPixmap(bob, inp.getTouchX(i), inp.getTouchY(i), 0, 0, 64, 64); + } + } + + g.drawPixmap(bob, (int)(inp.getAccelX() * 10) + 160 - 16, (int)(inp.getAccelY() * 10) + 240 - 16, 0, 0, 32, 32 ); + + List keyEvents = inp.getKeyEvents(); + int len = keyEvents.size(); + for(int i = 0; i < len; i++) { + Log.d("MrNom", keyEvents.get(i).toString()); + } + + List touchEvents = inp.getTouchEvents(); + len = touchEvents.size(); + for(int i = 0; i < len; i++) { + Log.d("MrNom", touchEvents.get(i).toString()); + if(touchEvents.get(i).type == TouchEvent.TOUCH_UP) + sound.play(1); + } + + frames++; + if(System.nanoTime() - startTime > 1000000000l) { + Log.d("MrNom", "fps: " + frames + ", delta: " + deltaTime); + frames = 0; + startTime = System.nanoTime(); + } + } + + @Override + public void pause() { + Log.d("MrNom", "pause"); + } + + @Override + public void resume() { + Log.d("MrNom", "resume"); + } + + @Override + public void dispose() { + Log.d("MrNom", "dispose"); + music.dispose(); + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Animation.java b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Animation.java new file mode 100644 index 0000000..d826e39 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Animation.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework.gl; + + +public class Animation { + public static final int ANIMATION_LOOPING = 0; + public static final int ANIMATION_NONLOOPING = 1; + + final TextureRegion[] keyFrames; + final float frameDuration; + + public Animation(float frameDuration, TextureRegion ... keyFrames) { + this.frameDuration = frameDuration; + this.keyFrames = keyFrames; + } + + public TextureRegion getKeyFrame(float stateTime, int mode) { + int frameNumber = (int)(stateTime / frameDuration); + + if(mode == ANIMATION_NONLOOPING) { + frameNumber = Math.min(keyFrames.length-1, frameNumber); + } else { + frameNumber = frameNumber % keyFrames.length; + } + return keyFrames[frameNumber]; + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Camera2D.java b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Camera2D.java new file mode 100644 index 0000000..2a58199 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Camera2D.java @@ -0,0 +1,42 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.Vector2; + +public class Camera2D { + public final Vector2 position; + public float zoom; + public final float frustumWidth; + public final float frustumHeight; + final GLGraphics glGraphics; + + public Camera2D(GLGraphics glGraphics, float frustumWidth, float frustumHeight) { + this.glGraphics = glGraphics; + this.frustumWidth = frustumWidth; + this.frustumHeight = frustumHeight; + this.position = new Vector2(frustumWidth / 2, frustumHeight / 2); + this.zoom = 1.0f; + } + + public void setViewportAndMatrices() { + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(position.x - frustumWidth * zoom / 2, + position.x + frustumWidth * zoom/ 2, + position.y - frustumHeight * zoom / 2, + position.y + frustumHeight * zoom/ 2, + 1, -1); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + } + + public void touchToWorld(Vector2 touch) { + touch.x = (touch.x / (float) glGraphics.getWidth()) * frustumWidth * zoom; + touch.y = (1 - touch.y / (float) glGraphics.getHeight()) * frustumHeight * zoom; + touch.add(position).sub(frustumWidth * zoom / 2, frustumHeight * zoom / 2); + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/gl/FPSCounter.java b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/FPSCounter.java new file mode 100644 index 0000000..cd816ca --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/FPSCounter.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework.gl; + +import android.util.Log; + +public class FPSCounter { + long startTime = System.nanoTime(); + int frames = 0; + + public void logFrame() { + frames++; + if(System.nanoTime() - startTime >= 1000000000) { + Log.d("FPSCounter", "fps: " + frames); + frames = 0; + startTime = System.nanoTime(); + } + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Font.java b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Font.java new file mode 100644 index 0000000..ace1452 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Font.java @@ -0,0 +1,39 @@ +package com.badlogic.androidgames.framework.gl; + +public class Font { + public final Texture texture; + public final int glyphWidth; + public final int glyphHeight; + public final TextureRegion[] glyphs = new TextureRegion[96]; + + public Font(Texture texture, + int offsetX, int offsetY, + int glyphsPerRow, int glyphWidth, int glyphHeight) { + this.texture = texture; + this.glyphWidth = glyphWidth; + this.glyphHeight = glyphHeight; + int x = offsetX; + int y = offsetY; + for(int i = 0; i < 96; i++) { + glyphs[i] = new TextureRegion(texture, x, y, glyphWidth, glyphHeight); + x += glyphWidth; + if(x == offsetX + glyphsPerRow * glyphWidth) { + x = offsetX; + y += glyphHeight; + } + } + } + + public void drawText(SpriteBatcher batcher, String text, float x, float y) { + int len = text.length(); + for(int i = 0; i < len; i++) { + int c = text.charAt(i) - ' '; + if(c < 0 || c > glyphs.length - 1) + continue; + + TextureRegion glyph = glyphs[c]; + batcher.drawSprite(x, y, glyphWidth, glyphHeight, glyph); + x += glyphWidth; + } + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java new file mode 100644 index 0000000..da3cb4f --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java @@ -0,0 +1,143 @@ +package com.badlogic.androidgames.framework.gl; + +import java.util.ArrayList; +import java.util.List; + +import com.badlogic.androidgames.framework.GameObject; + +import android.util.FloatMath; + +public class SpatialHashGrid { + List[] dynamicCells; + List[] staticCells; + int cellsPerRow; + int cellsPerCol; + float cellSize; + int[] cellIds = new int[4]; + List foundObjects; + + @SuppressWarnings("unchecked") + public SpatialHashGrid(float worldWidth, float worldHeight, float cellSize) { + this.cellSize = cellSize; + this.cellsPerRow = (int)FloatMath.ceil(worldWidth/cellSize); + this.cellsPerCol = (int)FloatMath.ceil(worldHeight/cellSize); + int numCells = cellsPerRow * cellsPerCol; + dynamicCells = new List[numCells]; + staticCells = new List[numCells]; + for(int i = 0; i < numCells; i++) { + dynamicCells[i] = new ArrayList(10); + staticCells[i] = new ArrayList(10); + } + foundObjects = new ArrayList(10); + } + + public void insertStaticObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + staticCells[cellId].add(obj); + } + } + + public void insertDynamicObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + dynamicCells[cellId].add(obj); + } + } + + public void removeObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + dynamicCells[cellId].remove(obj); + staticCells[cellId].remove(obj); + } + } + + public void clearDynamicCells(GameObject obj) { + int len = dynamicCells.length; + for(int i = 0; i < len; i++) { + dynamicCells[i].clear(); + } + } + + public List getPotentialColliders(GameObject obj) { + foundObjects.clear(); + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + int len = dynamicCells[cellId].size(); + for(int j = 0; j < len; j++) { + GameObject collider = dynamicCells[cellId].get(j); + if(!foundObjects.contains(collider)) + foundObjects.add(collider); + } + + len = staticCells[cellId].size(); + for(int j = 0; j < len; j++) { + GameObject collider = staticCells[cellId].get(j); + if(!foundObjects.contains(collider)) + foundObjects.add(collider); + } + } + return foundObjects; + } + + public int[] getCellIds(GameObject obj) { + int x1 = (int)FloatMath.floor(obj.bounds.lowerLeft.x / cellSize); + int y1 = (int)FloatMath.floor(obj.bounds.lowerLeft.y / cellSize); + int x2 = (int)FloatMath.floor((obj.bounds.lowerLeft.x + obj.bounds.width) / cellSize); + int y2 = (int)FloatMath.floor((obj.bounds.lowerLeft.y + obj.bounds.height) / cellSize); + + if(x1 == x2 && y1 == y2) { + if(x1 >= 0 && x1 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[0] = x1 + y1 * cellsPerRow; + else + cellIds[0] = -1; + cellIds[1] = -1; + cellIds[2] = -1; + cellIds[3] = -1; + } + else if(x1 == x2) { + int i = 0; + if(x1 >= 0 && x1 < cellsPerRow) { + if(y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x1 + y1 * cellsPerRow; + if(y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x1 + y2 * cellsPerRow; + } + while(i <= 3) cellIds[i++] = -1; + } + else if(y1 == y2) { + int i = 0; + if(y1 >= 0 && y1 < cellsPerCol) { + if(x1 >= 0 && x1 < cellsPerRow) + cellIds[i++] = x1 + y1 * cellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow) + cellIds[i++] = x2 + y1 * cellsPerRow; + } + while(i <= 3) cellIds[i++] = -1; + } + else { + int i = 0; + int y1CellsPerRow = y1 * cellsPerRow; + int y2CellsPerRow = y2 * cellsPerRow; + if(x1 >= 0 && x1 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x1 + y1CellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x2 + y1CellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow && y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x2 + y2CellsPerRow; + if(x1 >= 0 && x1 < cellsPerRow && y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x1 + y2CellsPerRow; + while(i <= 3) cellIds[i++] = -1; + } + return cellIds; + } +} \ No newline at end of file diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java new file mode 100644 index 0000000..8b9eee2 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java @@ -0,0 +1,128 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import android.util.FloatMath; + +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.Vector2; + +public class SpriteBatcher { + final float[] verticesBuffer; + int bufferIndex; + final Vertices vertices; + int numSprites; + + public SpriteBatcher(GLGraphics glGraphics, int maxSprites) { + this.verticesBuffer = new float[maxSprites*4*4]; + this.vertices = new Vertices(glGraphics, maxSprites*4, maxSprites*6, false, true); + this.bufferIndex = 0; + this.numSprites = 0; + + short[] indices = new short[maxSprites*6]; + int len = indices.length; + short j = 0; + for (int i = 0; i < len; i += 6, j += 4) { + indices[i + 0] = (short)(j + 0); + indices[i + 1] = (short)(j + 1); + indices[i + 2] = (short)(j + 2); + indices[i + 3] = (short)(j + 2); + indices[i + 4] = (short)(j + 3); + indices[i + 5] = (short)(j + 0); + } + vertices.setIndices(indices, 0, indices.length); + } + + public void beginBatch(Texture texture) { + texture.bind(); + numSprites = 0; + bufferIndex = 0; + } + + public void endBatch() { + vertices.setVertices(verticesBuffer, 0, bufferIndex); + vertices.bind(); + vertices.draw(GL10.GL_TRIANGLES, 0, numSprites * 6); + vertices.unbind(); + } + + public void drawSprite(float x, float y, float width, float height, TextureRegion region) { + float halfWidth = width / 2; + float halfHeight = height / 2; + float x1 = x - halfWidth; + float y1 = y - halfHeight; + float x2 = x + halfWidth; + float y2 = y + halfHeight; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v1; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v1; + + numSprites++; + } + + public void drawSprite(float x, float y, float width, float height, float angle, TextureRegion region) { + float halfWidth = width / 2; + float halfHeight = height / 2; + + float rad = angle * Vector2.TO_RADIANS; + float cos = FloatMath.cos(rad); + float sin = FloatMath.sin(rad); + + float x1 = -halfWidth * cos - (-halfHeight) * sin; + float y1 = -halfWidth * sin + (-halfHeight) * cos; + float x2 = halfWidth * cos - (-halfHeight) * sin; + float y2 = halfWidth * sin + (-halfHeight) * cos; + float x3 = halfWidth * cos - halfHeight * sin; + float y3 = halfWidth * sin + halfHeight * cos; + float x4 = -halfWidth * cos - halfHeight * sin; + float y4 = -halfWidth * sin + halfHeight * cos; + + x1 += x; + y1 += y; + x2 += x; + y2 += y; + x3 += x; + y3 += y; + x4 += x; + y4 += y; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x3; + verticesBuffer[bufferIndex++] = y3; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v1; + + verticesBuffer[bufferIndex++] = x4; + verticesBuffer[bufferIndex++] = y4; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v1; + + numSprites++; + } +} \ No newline at end of file diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Texture.java b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Texture.java new file mode 100644 index 0000000..174e27b --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Texture.java @@ -0,0 +1,84 @@ +package com.badlogic.androidgames.framework.gl; + +import java.io.IOException; +import java.io.InputStream; + +import javax.microedition.khronos.opengles.GL10; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.opengl.GLUtils; + +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Texture { + GLGraphics glGraphics; + FileIO fileIO; + String fileName; + int textureId; + int minFilter; + int magFilter; + public int width; + public int height; + + public Texture(GLGame glGame, String fileName) { + this.glGraphics = glGame.getGLGraphics(); + this.fileIO = glGame.getFileIO(); + this.fileName = fileName; + load(); + } + + private void load() { + GL10 gl = glGraphics.getGL(); + int[] textureIds = new int[1]; + gl.glGenTextures(1, textureIds, 0); + textureId = textureIds[0]; + + InputStream in = null; + try { + in = fileIO.readAsset(fileName); + Bitmap bitmap = BitmapFactory.decodeStream(in); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); + setFilters(GL10.GL_NEAREST, GL10.GL_NEAREST); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + width = bitmap.getWidth(); + height = bitmap.getHeight(); + bitmap.recycle(); + } catch(IOException e) { + throw new RuntimeException("Couldn't load texture '" + fileName +"'", e); + } finally { + if(in != null) + try { in.close(); } catch (IOException e) { } + } + } + + public void reload() { + load(); + bind(); + setFilters(minFilter, magFilter); + glGraphics.getGL().glBindTexture(GL10.GL_TEXTURE_2D, 0); + } + + public void setFilters(int minFilter, int magFilter) { + this.minFilter = minFilter; + this.magFilter = magFilter; + GL10 gl = glGraphics.getGL(); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, minFilter); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, magFilter); + } + + public void bind() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + } + + public void dispose() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + int[] textureIds = { textureId }; + gl.glDeleteTextures(1, textureIds, 0); + } +} \ No newline at end of file diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/gl/TextureRegion.java b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/TextureRegion.java new file mode 100644 index 0000000..cb91a07 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/TextureRegion.java @@ -0,0 +1,15 @@ +package com.badlogic.androidgames.framework.gl; + +public class TextureRegion { + public final float u1, v1; + public final float u2, v2; + public final Texture texture; + + public TextureRegion(Texture texture, float x, float y, float width, float height) { + this.u1 = x / texture.width; + this.v1 = y / texture.height; + this.u2 = this.u1 + width / texture.width; + this.v2 = this.v1 + height / texture.height; + this.texture = texture; + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Vertices.java b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Vertices.java new file mode 100644 index 0000000..d8bbee3 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/gl/Vertices.java @@ -0,0 +1,95 @@ +package com.badlogic.androidgames.framework.gl; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Vertices { + final GLGraphics glGraphics; + final boolean hasColor; + final boolean hasTexCoords; + final int vertexSize; + final IntBuffer vertices; + final int[] tmpBuffer; + final ShortBuffer indices; + + public Vertices(GLGraphics glGraphics, int maxVertices, int maxIndices, boolean hasColor, boolean hasTexCoords) { + this.glGraphics = glGraphics; + this.hasColor = hasColor; + this.hasTexCoords = hasTexCoords; + this.vertexSize = (2 + (hasColor?4:0) + (hasTexCoords?2:0)) * 4; + this.tmpBuffer = new int[maxVertices * vertexSize / 4]; + + ByteBuffer buffer = ByteBuffer.allocateDirect(maxVertices * vertexSize); + buffer.order(ByteOrder.nativeOrder()); + vertices = buffer.asIntBuffer(); + + if(maxIndices > 0) { + buffer = ByteBuffer.allocateDirect(maxIndices * Short.SIZE / 8); + buffer.order(ByteOrder.nativeOrder()); + indices = buffer.asShortBuffer(); + } else { + indices = null; + } + } + + public void setVertices(float[] vertices, int offset, int length) { + this.vertices.clear(); + int len = offset + length; + for(int i=offset, j=0; i < len; i++, j++) + tmpBuffer[j] = Float.floatToRawIntBits(vertices[i]); + this.vertices.put(tmpBuffer, 0, length); + this.vertices.flip(); + } + + public void setIndices(short[] indices, int offset, int length) { + this.indices.clear(); + this.indices.put(indices, offset, length); + this.indices.flip(); + } + +public void bind() { + GL10 gl = glGraphics.getGL(); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + vertices.position(0); + gl.glVertexPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + + if(hasColor) { + gl.glEnableClientState(GL10.GL_COLOR_ARRAY); + vertices.position(2); + gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices); + } + + if(hasTexCoords) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + vertices.position(hasColor?6:2); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + } +} + +public void draw(int primitiveType, int offset, int numVertices) { + GL10 gl = glGraphics.getGL(); + + if(indices!=null) { + indices.position(offset); + gl.glDrawElements(primitiveType, numVertices, GL10.GL_UNSIGNED_SHORT, indices); + } else { + gl.glDrawArrays(primitiveType, offset, numVertices); + } +} + +public void unbind() { + GL10 gl = glGraphics.getGL(); + if(hasTexCoords) + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + if(hasColor) + gl.glDisableClientState(GL10.GL_COLOR_ARRAY); +} +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java new file mode 100644 index 0000000..4820c5a --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java @@ -0,0 +1,48 @@ +package com.badlogic.androidgames.framework.impl; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + +public class AccelerometerHandler implements SensorEventListener { + float accelX; + float accelY; + float accelZ; + + public AccelerometerHandler(Context context) { + SensorManager manager = (SensorManager) context + .getSystemService(Context.SENSOR_SERVICE); + if (manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() != 0) { + Sensor accelerometer = manager.getSensorList( + Sensor.TYPE_ACCELEROMETER).get(0); + manager.registerListener(this, accelerometer, + SensorManager.SENSOR_DELAY_GAME); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // nothing to do here + } + + @Override + public void onSensorChanged(SensorEvent event) { + accelX = event.values[0]; + accelY = event.values[1]; + accelZ = event.values[2]; + } + + public float getAccelX() { + return accelX; + } + + public float getAccelY() { + return accelY; + } + + public float getAccelZ() { + return accelZ; + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java new file mode 100644 index 0000000..42f8c1f --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java @@ -0,0 +1,45 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; + +import android.app.Activity; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.media.AudioManager; +import android.media.SoundPool; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.Music; +import com.badlogic.androidgames.framework.Sound; + +public class AndroidAudio implements Audio { + AssetManager assets; + SoundPool soundPool; + + public AndroidAudio(Activity activity) { + activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); + this.assets = activity.getAssets(); + this.soundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0); + } + + @Override + public Music newMusic(String filename) { + try { + AssetFileDescriptor assetDescriptor = assets.openFd(filename); + return new AndroidMusic(assetDescriptor); + } catch (IOException e) { + throw new RuntimeException("Couldn't load music '" + filename + "'"); + } + } + + @Override + public Sound newSound(String filename) { + try { + AssetFileDescriptor assetDescriptor = assets.openFd(filename); + int soundId = soundPool.load(assetDescriptor, 0); + return new AndroidSound(soundPool, soundId); + } catch (IOException e) { + throw new RuntimeException("Couldn't load sound '" + filename + "'"); + } + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java new file mode 100644 index 0000000..6171d90 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java @@ -0,0 +1,60 @@ +package com.badlogic.androidgames.framework.impl; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class AndroidFastRenderView extends SurfaceView implements Runnable { + AndroidGame game; + Bitmap framebuffer; + Thread renderThread = null; + SurfaceHolder holder; + volatile boolean running = false; + + public AndroidFastRenderView(AndroidGame game, Bitmap framebuffer) { + super(game); + this.game = game; + this.framebuffer = framebuffer; + this.holder = getHolder(); + } + + public void resume() { + running = true; + renderThread = new Thread(this); + renderThread.start(); + } + + public void run() { + Rect dstRect = new Rect(); + long startTime = System.nanoTime(); + while(running) { + if(!holder.getSurface().isValid()) + continue; + + float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; + startTime = System.nanoTime(); + + game.getCurrentScreen().update(deltaTime); + game.getCurrentScreen().present(deltaTime); + + Canvas canvas = holder.lockCanvas(); + canvas.getClipBounds(dstRect); + canvas.drawBitmap(framebuffer, null, dstRect, null); + holder.unlockCanvasAndPost(canvas); + } + } + + public void pause() { + running = false; + while(true) { + try { + renderThread.join(); + break; + } catch (InterruptedException e) { + // retry + } + } + } +} \ No newline at end of file diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java new file mode 100644 index 0000000..7401041 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java @@ -0,0 +1,48 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.AssetManager; +import android.os.Environment; +import android.preference.PreferenceManager; + +import com.badlogic.androidgames.framework.FileIO; + +public class AndroidFileIO implements FileIO { + Context context; + AssetManager assets; + String externalStoragePath; + + public AndroidFileIO(Context context) { + this.context = context; + this.assets = context.getAssets(); + this.externalStoragePath = Environment.getExternalStorageDirectory() + .getAbsolutePath() + File.separator; + } + + @Override + public InputStream readAsset(String fileName) throws IOException { + return assets.open(fileName); + } + + @Override + public InputStream readFile(String fileName) throws IOException { + return new FileInputStream(externalStoragePath + fileName); + } + + @Override + public OutputStream writeFile(String fileName) throws IOException { + return new FileOutputStream(externalStoragePath + fileName); + } + + public SharedPreferences getPreferences() { + return PreferenceManager.getDefaultSharedPreferences(context); + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidGame.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidGame.java new file mode 100644 index 0000000..cff8015 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidGame.java @@ -0,0 +1,107 @@ +package com.badlogic.androidgames.framework.impl; + +import android.app.Activity; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.os.Bundle; +import android.view.Window; +import android.view.WindowManager; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input; +import com.badlogic.androidgames.framework.Screen; + +public abstract class AndroidGame extends Activity implements Game { + AndroidFastRenderView renderView; + Graphics graphics; + Audio audio; + Input input; + FileIO fileIO; + Screen screen; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + + boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + int frameBufferWidth = isLandscape ? 480 : 320; + int frameBufferHeight = isLandscape ? 320 : 480; + Bitmap frameBuffer = Bitmap.createBitmap(frameBufferWidth, + frameBufferHeight, Config.RGB_565); + + float scaleX = (float) frameBufferWidth + / getWindowManager().getDefaultDisplay().getWidth(); + float scaleY = (float) frameBufferHeight + / getWindowManager().getDefaultDisplay().getHeight(); + + renderView = new AndroidFastRenderView(this, frameBuffer); + graphics = new AndroidGraphics(getAssets(), frameBuffer); + fileIO = new AndroidFileIO(this); + audio = new AndroidAudio(this); + input = new AndroidInput(this, renderView, scaleX, scaleY); + screen = getStartScreen(); + setContentView(renderView); + } + + @Override + public void onResume() { + super.onResume(); + screen.resume(); + renderView.resume(); + } + + @Override + public void onPause() { + super.onPause(); + renderView.pause(); + screen.pause(); + + if (isFinishing()) { + screen.dispose(); + } + } + + @Override + public Input getInput() { + return input; + } + + @Override + public FileIO getFileIO() { + return fileIO; + } + + @Override + public Graphics getGraphics() { + return graphics; + } + + @Override + public Audio getAudio() { + return audio; + } + + @Override + public void setScreen(Screen screen) { + if (screen == null) + throw new IllegalArgumentException("Screen must not be null"); + + this.screen.pause(); + this.screen.dispose(); + screen.resume(); + screen.update(0); + this.screen = screen; + } + + public Screen getCurrentScreen() { + return screen; + } +} \ No newline at end of file diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java new file mode 100644 index 0000000..ade52b5 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java @@ -0,0 +1,133 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; +import java.io.InputStream; + +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.BitmapFactory.Options; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Rect; + +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Pixmap; + +public class AndroidGraphics implements Graphics { + AssetManager assets; + Bitmap frameBuffer; + Canvas canvas; + Paint paint; + Rect srcRect = new Rect(); + Rect dstRect = new Rect(); + + public AndroidGraphics(AssetManager assets, Bitmap frameBuffer) { + this.assets = assets; + this.frameBuffer = frameBuffer; + this.canvas = new Canvas(frameBuffer); + this.paint = new Paint(); + } + + @Override + public Pixmap newPixmap(String fileName, PixmapFormat format) { + Config config = null; + if (format == PixmapFormat.RGB565) + config = Config.RGB_565; + else if (format == PixmapFormat.ARGB4444) + config = Config.ARGB_4444; + else + config = Config.ARGB_8888; + + Options options = new Options(); + options.inPreferredConfig = config; + + InputStream in = null; + Bitmap bitmap = null; + try { + in = assets.open(fileName); + bitmap = BitmapFactory.decodeStream(in, null, options); + if (bitmap == null) + throw new RuntimeException("Couldn't load bitmap from asset '" + + fileName + "'"); + } catch (IOException e) { + throw new RuntimeException("Couldn't load bitmap from asset '" + + fileName + "'"); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + + if (bitmap.getConfig() == Config.RGB_565) + format = PixmapFormat.RGB565; + else if (bitmap.getConfig() == Config.ARGB_4444) + format = PixmapFormat.ARGB4444; + else + format = PixmapFormat.ARGB8888; + + return new AndroidPixmap(bitmap, format); + } + + @Override + public void clear(int color) { + canvas.drawRGB((color & 0xff0000) >> 16, (color & 0xff00) >> 8, + (color & 0xff)); + } + + @Override + public void drawPixel(int x, int y, int color) { + paint.setColor(color); + canvas.drawPoint(x, y, paint); + } + + @Override + public void drawLine(int x, int y, int x2, int y2, int color) { + paint.setColor(color); + canvas.drawLine(x, y, x2, y2, paint); + } + + @Override + public void drawRect(int x, int y, int width, int height, int color) { + paint.setColor(color); + paint.setStyle(Style.FILL); + canvas.drawRect(x, y, x + width - 1, y + height - 1, paint); + } + + @Override + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight) { + srcRect.left = srcX; + srcRect.top = srcY; + srcRect.right = srcX + srcWidth - 1; + srcRect.bottom = srcY + srcHeight - 1; + + dstRect.left = x; + dstRect.top = y; + dstRect.right = x + srcWidth - 1; + dstRect.bottom = y + srcHeight - 1; + + canvas.drawBitmap(((AndroidPixmap) pixmap).bitmap, srcRect, dstRect, + null); + } + + @Override + public void drawPixmap(Pixmap pixmap, int x, int y) { + canvas.drawBitmap(((AndroidPixmap)pixmap).bitmap, x, y, null); + } + + @Override + public int getWidth() { + return frameBuffer.getWidth(); + } + + @Override + public int getHeight() { + return frameBuffer.getHeight(); + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidInput.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidInput.java new file mode 100644 index 0000000..11f5863 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidInput.java @@ -0,0 +1,69 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.List; + +import android.content.Context; +import android.os.Build.VERSION; +import android.view.View; + +import com.badlogic.androidgames.framework.Input; + +public class AndroidInput implements Input { + AccelerometerHandler accelHandler; + KeyboardHandler keyHandler; + TouchHandler touchHandler; + + public AndroidInput(Context context, View view, float scaleX, float scaleY) { + accelHandler = new AccelerometerHandler(context); + keyHandler = new KeyboardHandler(view); + if(Integer.parseInt(VERSION.SDK) < 5) + touchHandler = new SingleTouchHandler(view, scaleX, scaleY); + else + touchHandler = new MultiTouchHandler(view, scaleX, scaleY); + } + + @Override + public boolean isKeyPressed(int keyCode) { + return keyHandler.isKeyPressed(keyCode); + } + + @Override + public boolean isTouchDown(int pointer) { + return touchHandler.isTouchDown(pointer); + } + + @Override + public int getTouchX(int pointer) { + return touchHandler.getTouchX(pointer); + } + + @Override + public int getTouchY(int pointer) { + return touchHandler.getTouchY(pointer); + } + + @Override + public float getAccelX() { + return accelHandler.getAccelX(); + } + + @Override + public float getAccelY() { + return accelHandler.getAccelY(); + } + + @Override + public float getAccelZ() { + return accelHandler.getAccelZ(); + } + + @Override + public List getTouchEvents() { + return touchHandler.getTouchEvents(); + } + + @Override + public List getKeyEvents() { + return keyHandler.getKeyEvents(); + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java new file mode 100644 index 0000000..9e9fd1c --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java @@ -0,0 +1,99 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; + +import android.content.res.AssetFileDescriptor; +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnCompletionListener; + +import com.badlogic.androidgames.framework.Music; + +public class AndroidMusic implements Music, OnCompletionListener { + MediaPlayer mediaPlayer; + boolean isPrepared = false; + + public AndroidMusic(AssetFileDescriptor assetDescriptor) { + mediaPlayer = new MediaPlayer(); + try { + mediaPlayer.setDataSource(assetDescriptor.getFileDescriptor(), + assetDescriptor.getStartOffset(), + assetDescriptor.getLength()); + mediaPlayer.prepare(); + isPrepared = true; + mediaPlayer.setOnCompletionListener(this); + } catch (Exception e) { + throw new RuntimeException("Couldn't load music"); + } + } + + @Override + public void dispose() { + if (mediaPlayer.isPlaying()) + mediaPlayer.stop(); + mediaPlayer.release(); + } + + @Override + public boolean isLooping() { + return mediaPlayer.isLooping(); + } + + @Override + public boolean isPlaying() { + return mediaPlayer.isPlaying(); + } + + @Override + public boolean isStopped() { + return !isPrepared; + } + + @Override + public void pause() { + if (mediaPlayer.isPlaying()) + mediaPlayer.pause(); + } + + @Override + public void play() { + if (mediaPlayer.isPlaying()) + return; + + try { + synchronized (this) { + if (!isPrepared) + mediaPlayer.prepare(); + mediaPlayer.start(); + } + } catch (IllegalStateException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void setLooping(boolean isLooping) { + mediaPlayer.setLooping(isLooping); + } + + @Override + public void setVolume(float volume) { + mediaPlayer.setVolume(volume, volume); + } + + @Override + public void stop() { + mediaPlayer.stop(); + synchronized (this) { + isPrepared = false; + } + } + + @Override + public void onCompletion(MediaPlayer arg0) { + synchronized (this) { + isPrepared = false; + } + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java new file mode 100644 index 0000000..9de5021 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework.impl; + +import android.graphics.Bitmap; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; +import com.badlogic.androidgames.framework.Pixmap; + +public class AndroidPixmap implements Pixmap { + Bitmap bitmap; + PixmapFormat format; + + public AndroidPixmap(Bitmap bitmap, PixmapFormat format) { + this.bitmap = bitmap; + this.format = format; + } + + @Override + public int getWidth() { + return bitmap.getWidth(); + } + + @Override + public int getHeight() { + return bitmap.getHeight(); + } + + @Override + public PixmapFormat getFormat() { + return format; + } + + @Override + public void dispose() { + bitmap.recycle(); + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidSound.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidSound.java new file mode 100644 index 0000000..4a8fcd4 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/AndroidSound.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework.impl; + +import android.media.SoundPool; + +import com.badlogic.androidgames.framework.Sound; + +public class AndroidSound implements Sound { + int soundId; + SoundPool soundPool; + + public AndroidSound(SoundPool soundPool,int soundId) { + this.soundId = soundId; + this.soundPool = soundPool; + } + + @Override + public void play(float volume) { + soundPool.play(soundId, volume, volume, 0, 0, 1); + } + + @Override + public void dispose() { + soundPool.unload(soundId); + } + +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/GLGame.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/GLGame.java new file mode 100644 index 0000000..500c10f --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/GLGame.java @@ -0,0 +1,178 @@ +package com.badlogic.androidgames.framework.impl; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import android.app.Activity; +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.opengl.GLSurfaceView.Renderer; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.view.Window; +import android.view.WindowManager; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input; +import com.badlogic.androidgames.framework.Screen; + +public abstract class GLGame extends Activity implements Game, Renderer { + enum GLGameState { + Initialized, + Running, + Paused, + Finished, + Idle + } + + GLSurfaceView glView; + GLGraphics glGraphics; + Audio audio; + Input input; + FileIO fileIO; + Screen screen; + GLGameState state = GLGameState.Initialized; + Object stateChanged = new Object(); + long startTime = System.nanoTime(); + WakeLock wakeLock; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + glView = new GLSurfaceView(this); + glView.setRenderer(this); + setContentView(glView); + + glGraphics = new GLGraphics(glView); + fileIO = new AndroidFileIO(this); + audio = new AndroidAudio(this); + input = new AndroidInput(this, glView, 1, 1); + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GLGame"); + } + + public void onResume() { + super.onResume(); + glView.onResume(); + wakeLock.acquire(); + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + glGraphics.setGL(gl); + + synchronized(stateChanged) { + if(state == GLGameState.Initialized) + screen = getStartScreen(); + state = GLGameState.Running; + screen.resume(); + startTime = System.nanoTime(); + } + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + } + + @Override + public void onDrawFrame(GL10 gl) { + GLGameState state = null; + + synchronized(stateChanged) { + state = this.state; + } + + if(state == GLGameState.Running) { + float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; + startTime = System.nanoTime(); + + screen.update(deltaTime); + screen.present(deltaTime); + } + + if(state == GLGameState.Paused) { + screen.pause(); + synchronized(stateChanged) { + this.state = GLGameState.Idle; + stateChanged.notifyAll(); + } + } + + if(state == GLGameState.Finished) { + screen.pause(); + screen.dispose(); + synchronized(stateChanged) { + this.state = GLGameState.Idle; + stateChanged.notifyAll(); + } + } + } + + @Override + public void onPause() { + synchronized(stateChanged) { + if(isFinishing()) + state = GLGameState.Finished; + else + state = GLGameState.Paused; + while(true) { + try { + stateChanged.wait(); + break; + } catch(InterruptedException e) { + } + } + } + wakeLock.release(); + glView.onPause(); + super.onPause(); + } + + public GLGraphics getGLGraphics() { + return glGraphics; + } + + @Override + public Input getInput() { + return input; + } + + @Override + public FileIO getFileIO() { + return fileIO; + } + + @Override + public Graphics getGraphics() { + throw new IllegalStateException("We are using OpenGL!"); + } + + @Override + public Audio getAudio() { + return audio; + } + + @Override + public void setScreen(Screen screen) { + if (screen == null) + throw new IllegalArgumentException("Screen must not be null"); + + this.screen.pause(); + this.screen.dispose(); + screen.resume(); + screen.update(0); + this.screen = screen; + } + + @Override + public Screen getCurrentScreen() { + return screen; + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/GLGraphics.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/GLGraphics.java new file mode 100644 index 0000000..2eb1b43 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/GLGraphics.java @@ -0,0 +1,30 @@ +package com.badlogic.androidgames.framework.impl; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLSurfaceView; + +public class GLGraphics { + GLSurfaceView glView; + GL10 gl; + + GLGraphics(GLSurfaceView glView) { + this.glView = glView; + } + + public GL10 getGL() { + return gl; + } + + void setGL(GL10 gl) { + this.gl = gl; + } + + public int getWidth() { + return glView.getWidth(); + } + + public int getHeight() { + return glView.getHeight(); + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/GLScreen.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/GLScreen.java new file mode 100644 index 0000000..466de8c --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/GLScreen.java @@ -0,0 +1,16 @@ +package com.badlogic.androidgames.framework.impl; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; + +public abstract class GLScreen extends Screen { + protected final GLGraphics glGraphics; + protected final GLGame glGame; + + public GLScreen(Game game) { + super(game); + glGame = (GLGame)game; + glGraphics = ((GLGame)game).getGLGraphics(); + } + +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java new file mode 100644 index 0000000..f70d3e9 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java @@ -0,0 +1,73 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.View; +import android.view.View.OnKeyListener; + +import com.badlogic.androidgames.framework.Input.KeyEvent; +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class KeyboardHandler implements OnKeyListener { + boolean[] pressedKeys = new boolean[128]; + Pool keyEventPool; + List keyEventsBuffer = new ArrayList(); + List keyEvents = new ArrayList(); + + public KeyboardHandler(View view) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public KeyEvent createObject() { + return new KeyEvent(); + } + }; + keyEventPool = new Pool(factory, 100); + view.setOnKeyListener(this); + view.setFocusableInTouchMode(true); + view.requestFocus(); + } + + @Override + public boolean onKey(View v, int keyCode, android.view.KeyEvent event) { + if (event.getAction() == android.view.KeyEvent.ACTION_MULTIPLE) + return false; + + synchronized (this) { + KeyEvent keyEvent = keyEventPool.newObject(); + keyEvent.keyCode = keyCode; + keyEvent.keyChar = (char) event.getUnicodeChar(); + if (event.getAction() == android.view.KeyEvent.ACTION_DOWN) { + keyEvent.type = KeyEvent.KEY_DOWN; + if(keyCode >= 0 && keyCode < 128) + pressedKeys[keyCode] = true; + } + if (event.getAction() == android.view.KeyEvent.ACTION_UP) { + keyEvent.type = KeyEvent.KEY_UP; + if(keyCode >= 0 && keyCode < 128) + pressedKeys[keyCode] = false; + } + keyEventsBuffer.add(keyEvent); + } + return false; + } + + public boolean isKeyPressed(int keyCode) { + if (keyCode < 0 || keyCode > 127) + return false; + return pressedKeys[keyCode]; + } + + public List getKeyEvents() { + synchronized (this) { + int len = keyEvents.size(); + for (int i = 0; i < len; i++) + keyEventPool.free(keyEvents.get(i)); + keyEvents.clear(); + keyEvents.addAll(keyEventsBuffer); + keyEventsBuffer.clear(); + return keyEvents; + } + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java new file mode 100644 index 0000000..4668c57 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java @@ -0,0 +1,137 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.MotionEvent; +import android.view.View; + +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class MultiTouchHandler implements TouchHandler { + boolean[] isTouched = new boolean[20]; + int[] touchX = new int[20]; + int[] touchY = new int[20]; + Pool touchEventPool; + List touchEvents = new ArrayList(); + List touchEventsBuffer = new ArrayList(); + float scaleX; + float scaleY; + + public MultiTouchHandler(View view, float scaleX, float scaleY) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }; + touchEventPool = new Pool(factory, 100); + view.setOnTouchListener(this); + + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + synchronized (this) { + int action = event.getAction() & MotionEvent.ACTION_MASK; + int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; + int pointerId = event.getPointerId(pointerIndex); + TouchEvent touchEvent; + + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_DOWN; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + isTouched[pointerId] = true; + touchEventsBuffer.add(touchEvent); + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_CANCEL: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_UP; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + isTouched[pointerId] = false; + touchEventsBuffer.add(touchEvent); + break; + + case MotionEvent.ACTION_MOVE: + int pointerCount = event.getPointerCount(); + for (int i = 0; i < pointerCount; i++) { + pointerIndex = i; + pointerId = event.getPointerId(pointerIndex); + + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_DRAGGED; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + touchEventsBuffer.add(touchEvent); + } + break; + } + + return true; + } + } + + @Override + public boolean isTouchDown(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return false; + else + return isTouched[pointer]; + } + } + + @Override + public int getTouchX(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return 0; + else + return touchX[pointer]; + } + } + + @Override + public int getTouchY(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return 0; + else + return touchY[pointer]; + } + } + + @Override + public List getTouchEvents() { + synchronized (this) { + int len = touchEvents.size(); + for (int i = 0; i < len; i++) + touchEventPool.free(touchEvents.get(i)); + touchEvents.clear(); + touchEvents.addAll(touchEventsBuffer); + touchEventsBuffer.clear(); + return touchEvents; + } + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java new file mode 100644 index 0000000..7ff1205 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java @@ -0,0 +1,101 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.MotionEvent; +import android.view.View; + +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class SingleTouchHandler implements TouchHandler { + boolean isTouched; + int touchX; + int touchY; + Pool touchEventPool; + List touchEvents = new ArrayList(); + List touchEventsBuffer = new ArrayList(); + float scaleX; + float scaleY; + + public SingleTouchHandler(View view, float scaleX, float scaleY) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }; + touchEventPool = new Pool(factory, 100); + view.setOnTouchListener(this); + + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + synchronized(this) { + TouchEvent touchEvent = touchEventPool.newObject(); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touchEvent.type = TouchEvent.TOUCH_DOWN; + isTouched = true; + break; + case MotionEvent.ACTION_MOVE: + touchEvent.type = TouchEvent.TOUCH_DRAGGED; + isTouched = true; + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + touchEvent.type = TouchEvent.TOUCH_UP; + isTouched = false; + break; + } + + touchEvent.x = touchX = (int)(event.getX() * scaleX); + touchEvent.y = touchY = (int)(event.getY() * scaleY); + touchEventsBuffer.add(touchEvent); + + return true; + } + } + + @Override + public boolean isTouchDown(int pointer) { + synchronized(this) { + if(pointer == 0) + return isTouched; + else + return false; + } + } + + @Override + public int getTouchX(int pointer) { + synchronized(this) { + return touchX; + } + } + + @Override + public int getTouchY(int pointer) { + synchronized(this) { + return touchY; + } + } + + @Override + public List getTouchEvents() { + synchronized(this) { + int len = touchEvents.size(); + for( int i = 0; i < len; i++ ) + touchEventPool.free(touchEvents.get(i)); + touchEvents.clear(); + touchEvents.addAll(touchEventsBuffer); + touchEventsBuffer.clear(); + return touchEvents; + } + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/impl/TouchHandler.java b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/TouchHandler.java new file mode 100644 index 0000000..cc7e754 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/impl/TouchHandler.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.List; + +import android.view.View.OnTouchListener; + +import com.badlogic.androidgames.framework.Input.TouchEvent; + +public interface TouchHandler extends OnTouchListener { + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public List getTouchEvents(); +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/math/Circle.java b/ch09-jumper/src/com/badlogic/androidgames/framework/math/Circle.java new file mode 100644 index 0000000..2728f84 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/math/Circle.java @@ -0,0 +1,11 @@ +package com.badlogic.androidgames.framework.math; + +public class Circle { + public final Vector2 center = new Vector2(); + public float radius; + + public Circle(float x, float y, float radius) { + this.center.set(x,y); + this.radius = radius; + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/math/OverlapTester.java b/ch09-jumper/src/com/badlogic/androidgames/framework/math/OverlapTester.java new file mode 100644 index 0000000..5c6ace4 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/math/OverlapTester.java @@ -0,0 +1,58 @@ +package com.badlogic.androidgames.framework.math; + +public class OverlapTester { + public static boolean overlapCircles(Circle c1, Circle c2) { + float distance = c1.center.distSquared(c2.center); + float radiusSum = c1.radius + c2.radius; + return distance <= radiusSum * radiusSum; + } + + public static boolean overlapRectangles(Rectangle r1, Rectangle r2) { + if(r1.lowerLeft.x < r2.lowerLeft.x + r2.width && + r1.lowerLeft.x + r1.width > r2.lowerLeft.x && + r1.lowerLeft.y < r2.lowerLeft.y + r2.height && + r1.lowerLeft.y + r1.height > r2.lowerLeft.y) + return true; + else + return false; + } + + public static boolean overlapCircleRectangle(Circle c, Rectangle r) { + float closestX = c.center.x; + float closestY = c.center.y; + + if(c.center.x < r.lowerLeft.x) { + closestX = r.lowerLeft.x; + } + else if(c.center.x > r.lowerLeft.x + r.width) { + closestX = r.lowerLeft.x + r.width; + } + + if(c.center.y < r.lowerLeft.y) { + closestY = r.lowerLeft.y; + } + else if(c.center.y > r.lowerLeft.y + r.height) { + closestY = r.lowerLeft.y + r.height; + } + + return c.center.distSquared(closestX, closestY) < c.radius * c.radius; + } + + public static boolean pointInCircle(Circle c, Vector2 p) { + return c.center.distSquared(p) < c.radius * c.radius; + } + + public static boolean pointInCircle(Circle c, float x, float y) { + return c.center.distSquared(x, y) < c.radius * c.radius; + } + + public static boolean pointInRectangle(Rectangle r, Vector2 p) { + return r.lowerLeft.x <= p.x && r.lowerLeft.x + r.width >= p.x && + r.lowerLeft.y <= p.y && r.lowerLeft.y + r.height >= p.y; + } + + public static boolean pointInRectangle(Rectangle r, float x, float y) { + return r.lowerLeft.x <= x && r.lowerLeft.x + r.width >= x && + r.lowerLeft.y <= y && r.lowerLeft.y + r.height >= y; + } +} \ No newline at end of file diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/math/Rectangle.java b/ch09-jumper/src/com/badlogic/androidgames/framework/math/Rectangle.java new file mode 100644 index 0000000..9c2511f --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/math/Rectangle.java @@ -0,0 +1,12 @@ +package com.badlogic.androidgames.framework.math; + +public class Rectangle { + public final Vector2 lowerLeft; + public float width, height; + + public Rectangle(float x, float y, float width, float height) { + this.lowerLeft = new Vector2(x,y); + this.width = width; + this.height = height; + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/framework/math/Vector2.java b/ch09-jumper/src/com/badlogic/androidgames/framework/math/Vector2.java new file mode 100644 index 0000000..56d79c2 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/framework/math/Vector2.java @@ -0,0 +1,126 @@ +package com.badlogic.androidgames.framework.math; + +import android.util.FloatMath; + +public class Vector2 { + public static float TO_RADIANS = (1 / 180.0f) * (float)Math.PI; + public static float TO_DEGREES = (1 / (float)Math.PI) * 180; + public float x, y; + + public Vector2() { + } + + public Vector2(float x, float y) { + this.x = x; + this.y = y; + } + + public Vector2(Vector2 other) { + this.x = other.x; + this.y = other.y; + } + + public Vector2 cpy() { + return new Vector2(x, y); + } + + public Vector2 set(float x, float y) { + this.x = x; + this.y = y; + return this; + } + + public Vector2 set(Vector2 other) { + this.x = other.x; + this.y = other.y; + return this; + } + + public Vector2 add(float x, float y) { + this.x += x; + this.y += y; + return this; + } + + public Vector2 add(Vector2 other) { + this.x += other.x; + this.y += other.y; + return this; + } + + public Vector2 sub(float x, float y) { + this.x -= x; + this.y -= y; + return this; + } + + public Vector2 sub(Vector2 other) { + this.x -= other.x; + this.y -= other.y; + return this; + } + + public Vector2 mul(float scalar) { + this.x *= scalar; + this.y *= scalar; + return this; + } + + public float len() { + return FloatMath.sqrt(x*x + y*y); + } + + public Vector2 nor() { + float len = len(); + if(len!=0) { + this.x /= len; + this.y /= len; + } + return this; + } + + public float angle() { + float angle = (float)Math.atan2(y, x) * TO_DEGREES; + if(angle < 0) + angle += 360; + return angle; + } + + public Vector2 rotate(float angle) { + float rad = angle * TO_RADIANS; + float cos = FloatMath.cos(rad); + float sin = FloatMath.sin(rad); + + float newX = this.x * cos - this.y * sin; + float newY = this.x * sin + this.y * cos; + + this.x = newX; + this.y = newY; + + return this; + } + + public float dist(Vector2 other) { + float distX = this.x - other.x; + float distY = this.y - other.y; + return FloatMath.sqrt(distX*distX + distY*distY); + } + + public float dist(float x, float y) { + float distX = this.x - x; + float distY = this.y - y; + return FloatMath.sqrt(distX*distX + distY*distY); + } + + public float distSquared(Vector2 other) { + float distX = this.x - other.x; + float distY = this.y - other.y; + return distX*distX + distY*distY; + } + + public float distSquared(float x, float y) { + float distX = this.x - x; + float distY = this.y - y; + return distX*distX + distY*distY; + } +} \ No newline at end of file diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/Assets.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/Assets.java new file mode 100644 index 0000000..f953fb5 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/Assets.java @@ -0,0 +1,109 @@ +package com.badlogic.androidgames.jumper; + +import com.badlogic.androidgames.framework.Music; +import com.badlogic.androidgames.framework.Sound; +import com.badlogic.androidgames.framework.gl.Animation; +import com.badlogic.androidgames.framework.gl.Font; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.TextureRegion; +import com.badlogic.androidgames.framework.impl.GLGame; + +public class Assets { + public static Texture background; + public static TextureRegion backgroundRegion; + + public static Texture items; + public static TextureRegion mainMenu; + public static TextureRegion pauseMenu; + public static TextureRegion ready; + public static TextureRegion gameOver; + public static TextureRegion highScoresRegion; + public static TextureRegion logo; + public static TextureRegion soundOn; + public static TextureRegion soundOff; + public static TextureRegion arrow; + public static TextureRegion pause; + public static TextureRegion spring; + public static TextureRegion castle; + public static Animation coinAnim; + public static Animation bobJump; + public static Animation bobFall; + public static TextureRegion bobHit; + public static Animation squirrelFly; + public static TextureRegion platform; + public static Animation brakingPlatform; + public static Font font; + + public static Music music; + public static Sound jumpSound; + public static Sound highJumpSound; + public static Sound hitSound; + public static Sound coinSound; + public static Sound clickSound; + + public static void load(GLGame game) { + background = new Texture(game, "background.png"); + backgroundRegion = new TextureRegion(background, 0, 0, 320, 480); + + items = new Texture(game, "items.png"); + mainMenu = new TextureRegion(items, 0, 224, 300, 110); + pauseMenu = new TextureRegion(items, 224, 128, 192, 96); + ready = new TextureRegion(items, 320, 224, 192, 32); + gameOver = new TextureRegion(items, 352, 256, 160, 96); + highScoresRegion = new TextureRegion(Assets.items, 0, 257, 300, 110 / 3); + logo = new TextureRegion(items, 0, 352, 274, 142); + soundOff = new TextureRegion(items, 0, 0, 64, 64); + soundOn = new TextureRegion(items, 64, 0, 64, 64); + arrow = new TextureRegion(items, 0, 64, 64, 64); + pause = new TextureRegion(items, 64, 64, 64, 64); + + spring = new TextureRegion(items, 128, 0, 32, 32); + castle = new TextureRegion(items, 128, 64, 64, 64); + coinAnim = new Animation(0.2f, + new TextureRegion(items, 128, 32, 32, 32), + new TextureRegion(items, 160, 32, 32, 32), + new TextureRegion(items, 192, 32, 32, 32), + new TextureRegion(items, 160, 32, 32, 32)); + bobJump = new Animation(0.2f, + new TextureRegion(items, 0, 128, 32, 32), + new TextureRegion(items, 32, 128, 32, 32)); + bobFall = new Animation(0.2f, + new TextureRegion(items, 64, 128, 32, 32), + new TextureRegion(items, 96, 128, 32, 32)); + bobHit = new TextureRegion(items, 128, 128, 32, 32); + squirrelFly = new Animation(0.2f, + new TextureRegion(items, 0, 160, 32, 32), + new TextureRegion(items, 32, 160, 32, 32)); + platform = new TextureRegion(items, 64, 160, 64, 16); + brakingPlatform = new Animation(0.2f, + new TextureRegion(items, 64, 160, 64, 16), + new TextureRegion(items, 64, 176, 64, 16), + new TextureRegion(items, 64, 192, 64, 16), + new TextureRegion(items, 64, 208, 64, 16)); + + font = new Font(items, 224, 0, 16, 16, 20); + + music = game.getAudio().newMusic("music.mp3"); + music.setLooping(true); + music.setVolume(0.5f); + if(Settings.soundEnabled) + music.play(); + jumpSound = game.getAudio().newSound("jump.ogg"); + highJumpSound = game.getAudio().newSound("highjump.ogg"); + hitSound = game.getAudio().newSound("hit.ogg"); + coinSound = game.getAudio().newSound("coin.ogg"); + clickSound = game.getAudio().newSound("click.ogg"); + } + + public static void reload() { + background.reload(); + items.reload(); + if(Settings.soundEnabled) + music.play(); + } + + public static void playSound(Sound sound) { + if(Settings.soundEnabled) + sound.play(1); + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/Bob.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/Bob.java new file mode 100644 index 0000000..8a0dd45 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/Bob.java @@ -0,0 +1,67 @@ +package com.badlogic.androidgames.jumper; + +import com.badlogic.androidgames.framework.DynamicGameObject; + +public class Bob extends DynamicGameObject{ + public static final int BOB_STATE_JUMP = 0; + public static final int BOB_STATE_FALL = 1; + public static final int BOB_STATE_HIT = 2; + public static final float BOB_JUMP_VELOCITY = 11; + public static final float BOB_MOVE_VELOCITY = 20; + public static final float BOB_WIDTH = 0.8f; + public static final float BOB_HEIGHT = 0.8f; + + int state; + float stateTime; + + public Bob(float x, float y) { + super(x, y, BOB_WIDTH, BOB_HEIGHT); + state = BOB_STATE_FALL; + stateTime = 0; + } + + public void update(float deltaTime) { + velocity.add(World.gravity.x * deltaTime, World.gravity.y * deltaTime); + position.add(velocity.x * deltaTime, velocity.y * deltaTime); + bounds.lowerLeft.set(position).sub(bounds.width / 2, bounds.height / 2); + + if(velocity.y > 0 && state != BOB_STATE_HIT) { + if(state != BOB_STATE_JUMP) { + state = BOB_STATE_JUMP; + stateTime = 0; + } + } + + if(velocity.y < 0 && state != BOB_STATE_HIT) { + if(state != BOB_STATE_FALL) { + state = BOB_STATE_FALL; + stateTime = 0; + } + } + + if(position.x < 0) + position.x = World.WORLD_WIDTH; + if(position.x > World.WORLD_WIDTH) + position.x = 0; + + stateTime += deltaTime; + } + + public void hitSquirrel() { + velocity.set(0,0); + state = BOB_STATE_HIT; + stateTime = 0; + } + + public void hitPlatform() { + velocity.y = BOB_JUMP_VELOCITY; + state = BOB_STATE_JUMP; + stateTime = 0; + } + + public void hitSpring() { + velocity.y = BOB_JUMP_VELOCITY * 1.5f; + state = BOB_STATE_JUMP; + stateTime = 0; + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/Castle.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/Castle.java new file mode 100644 index 0000000..2950131 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/Castle.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.jumper; + +import com.badlogic.androidgames.framework.GameObject; + +public class Castle extends GameObject { + public static float CASTLE_WIDTH = 1.7f; + public static float CASTLE_HEIGHT = 1.7f; + + public Castle(float x, float y) { + super(x, y, CASTLE_WIDTH, CASTLE_HEIGHT); + } + +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/Coin.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/Coin.java new file mode 100644 index 0000000..be16689 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/Coin.java @@ -0,0 +1,19 @@ +package com.badlogic.androidgames.jumper; + +import com.badlogic.androidgames.framework.GameObject; + +public class Coin extends GameObject { + public static final float COIN_WIDTH = 0.5f; + public static final float COIN_HEIGHT = 0.8f; + public static final int COIN_SCORE = 10; + + float stateTime; + public Coin(float x, float y) { + super(x, y, COIN_WIDTH, COIN_HEIGHT); + stateTime = 0; + } + + public void update(float deltaTime) { + stateTime += deltaTime; + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/GameScreen.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/GameScreen.java new file mode 100644 index 0000000..a40e29e --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/GameScreen.java @@ -0,0 +1,270 @@ +package com.badlogic.androidgames.jumper; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.FPSCounter; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; +import com.badlogic.androidgames.jumper.World.WorldListener; + +public class GameScreen extends GLScreen { + static final int GAME_READY = 0; + static final int GAME_RUNNING = 1; + static final int GAME_PAUSED = 2; + static final int GAME_LEVEL_END = 3; + static final int GAME_OVER = 4; + + int state; + Camera2D guiCam; + Vector2 touchPoint; + SpriteBatcher batcher; + World world; + WorldListener worldListener; + WorldRenderer renderer; + Rectangle pauseBounds; + Rectangle resumeBounds; + Rectangle quitBounds; + int lastScore; + String scoreString; + FPSCounter fpsCounter; + + public GameScreen(Game game) { + super(game); + state = GAME_READY; + guiCam = new Camera2D(glGraphics, 320, 480); + touchPoint = new Vector2(); + batcher = new SpriteBatcher(glGraphics, 1000); + worldListener = new WorldListener() { + @Override + public void jump() { + Assets.playSound(Assets.jumpSound); + } + + @Override + public void highJump() { + Assets.playSound(Assets.highJumpSound); + } + + @Override + public void hit() { + Assets.playSound(Assets.hitSound); + } + + @Override + public void coin() { + Assets.playSound(Assets.coinSound); + } + }; + world = new World(worldListener); + renderer = new WorldRenderer(glGraphics, batcher, world); + pauseBounds = new Rectangle(320- 64, 480- 64, 64, 64); + resumeBounds = new Rectangle(160 - 96, 240, 192, 36); + quitBounds = new Rectangle(160 - 96, 240 - 36, 192, 36); + lastScore = 0; + scoreString = "score: 0"; + fpsCounter = new FPSCounter(); + } + + @Override + public void update(float deltaTime) { + if(deltaTime > 0.1f) + deltaTime = 0.1f; + + switch(state) { + case GAME_READY: + updateReady(); + break; + case GAME_RUNNING: + updateRunning(deltaTime); + break; + case GAME_PAUSED: + updatePaused(); + break; + case GAME_LEVEL_END: + updateLevelEnd(); + break; + case GAME_OVER: + updateGameOver(); + break; + } + } + + private void updateReady() { + if(game.getInput().getTouchEvents().size() > 0) { + state = GAME_RUNNING; + } + } + + private void updateRunning(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + if(event.type != TouchEvent.TOUCH_UP) + continue; + + touchPoint.set(event.x, event.y); + guiCam.touchToWorld(touchPoint); + + if(OverlapTester.pointInRectangle(pauseBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + state = GAME_PAUSED; + return; + } + } + + world.update(deltaTime, game.getInput().getAccelX()); + if(world.score != lastScore) { + lastScore = world.score; + scoreString = "" + lastScore; + } + if(world.state == World.WORLD_STATE_NEXT_LEVEL) { + state = GAME_LEVEL_END; + } + if(world.state == World.WORLD_STATE_GAME_OVER) { + state = GAME_OVER; + if(lastScore >= Settings.highscores[4]) + scoreString = "new highscore: " + lastScore; + else + scoreString = "score: " + lastScore; + Settings.addScore(lastScore); + Settings.save(game.getFileIO()); + } + } + + private void updatePaused() { + List touchEvents = game.getInput().getTouchEvents(); + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + if(event.type != TouchEvent.TOUCH_UP) + continue; + + touchPoint.set(event.x, event.y); + guiCam.touchToWorld(touchPoint); + + if(OverlapTester.pointInRectangle(resumeBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + state = GAME_RUNNING; + return; + } + + if(OverlapTester.pointInRectangle(quitBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + game.setScreen(new MainMenuScreen(game)); + return; + + } + } + } + + private void updateLevelEnd() { + List touchEvents = game.getInput().getTouchEvents(); + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + if(event.type != TouchEvent.TOUCH_UP) + continue; + world = new World(worldListener); + renderer = new WorldRenderer(glGraphics, batcher, world); + world.score = lastScore; + state = GAME_READY; + } + } + + private void updateGameOver() { + List touchEvents = game.getInput().getTouchEvents(); + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + if(event.type != TouchEvent.TOUCH_UP) + continue; + game.setScreen(new MainMenuScreen(game)); + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + gl.glEnable(GL10.GL_TEXTURE_2D); + + renderer.render(); + + guiCam.setViewportAndMatrices(); + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + batcher.beginBatch(Assets.items); + switch(state) { + case GAME_READY: + presentReady(); + break; + case GAME_RUNNING: + presentRunning(); + break; + case GAME_PAUSED: + presentPaused(); + break; + case GAME_LEVEL_END: + presentLevelEnd(); + break; + case GAME_OVER: + presentGameOver(); + break; + } + batcher.endBatch(); + gl.glDisable(GL10.GL_BLEND); + fpsCounter.logFrame(); + } + + private void presentReady() { + batcher.drawSprite(160, 240, 192, 32, Assets.ready); + } + + private void presentRunning() { + batcher.drawSprite(320 - 32, 480 - 32, 64, 64, Assets.pause); + Assets.font.drawText(batcher, scoreString, 16, 480-20); + } + + private void presentPaused() { + batcher.drawSprite(160, 240, 192, 96, Assets.pauseMenu); + Assets.font.drawText(batcher, scoreString, 16, 480-20); + } + + private void presentLevelEnd() { + String topText = "the princess is ..."; + String bottomText = "in another castle!"; + float topWidth = Assets.font.glyphWidth * topText.length(); + float bottomWidth = Assets.font.glyphWidth * bottomText.length(); + Assets.font.drawText(batcher, topText, 160 - topWidth / 2, 480 - 40); + Assets.font.drawText(batcher, bottomText, 160 - bottomWidth / 2, 40); + } + + private void presentGameOver() { + batcher.drawSprite(160, 240, 160, 96, Assets.gameOver); + float scoreWidth = Assets.font.glyphWidth * scoreString.length(); + Assets.font.drawText(batcher, scoreString, 160 - scoreWidth / 2, 480-20); + } + + @Override + public void pause() { + if(state == GAME_RUNNING) + state = GAME_PAUSED; + } + + @Override + public void resume() { + } + + @Override + public void dispose() { + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen.java new file mode 100644 index 0000000..5471fc8 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen.java @@ -0,0 +1,91 @@ +package com.badlogic.androidgames.jumper; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.TextureRegion; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class HelpScreen extends GLScreen { + Camera2D guiCam; + SpriteBatcher batcher; + Rectangle nextBounds; + Vector2 touchPoint; + Texture helpImage; + TextureRegion helpRegion; + + public HelpScreen(Game game) { + super(game); + + guiCam = new Camera2D(glGraphics, 320, 480); + nextBounds = new Rectangle(320 - 64, 0, 64, 64); + touchPoint = new Vector2(); + batcher = new SpriteBatcher(glGraphics, 1); + } + + @Override + public void resume() { + helpImage = new Texture(glGame, "help1.png" ); + helpRegion = new TextureRegion(helpImage, 0, 0, 320, 480); + } + + @Override + public void pause() { + helpImage.dispose(); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + touchPoint.set(event.x, event.y); + guiCam.touchToWorld(touchPoint); + + if(event.type == TouchEvent.TOUCH_UP) { + if(OverlapTester.pointInRectangle(nextBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + game.setScreen(new HelpScreen2(game)); + return; + } + } + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + guiCam.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(helpImage); + batcher.drawSprite(160, 240, 320, 480, helpRegion); + batcher.endBatch(); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + batcher.beginBatch(Assets.items); + batcher.drawSprite(320 - 32, 32, -64, 64, Assets.arrow); + batcher.endBatch(); + + gl.glDisable(GL10.GL_BLEND); + } + + @Override + public void dispose() { + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen2.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen2.java new file mode 100644 index 0000000..8582c75 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen2.java @@ -0,0 +1,92 @@ +package com.badlogic.androidgames.jumper; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.TextureRegion; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class HelpScreen2 extends GLScreen { + Camera2D guiCam; + SpriteBatcher batcher; + Rectangle nextBounds; + Vector2 touchPoint; + Texture helpImage; + TextureRegion helpRegion; + + public HelpScreen2(Game game) { + super(game); + + guiCam = new Camera2D(glGraphics, 320, 480); + nextBounds = new Rectangle(320 - 64, 0, 64, 64); + touchPoint = new Vector2(); + batcher = new SpriteBatcher(glGraphics, 1); + } + + @Override + public void resume() { + helpImage = new Texture(glGame, "help2.png" ); + helpRegion = new TextureRegion(helpImage, 0, 0, 320, 480); + } + + @Override + public void pause() { + helpImage.dispose(); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + touchPoint.set(event.x, event.y); + guiCam.touchToWorld(touchPoint); + + if(event.type == TouchEvent.TOUCH_UP) { + if(OverlapTester.pointInRectangle(nextBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + game.setScreen(new HelpScreen3(game)); + return; + } + } + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + guiCam.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(helpImage); + batcher.drawSprite(160, 240, 320, 480, helpRegion); + batcher.endBatch(); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + batcher.beginBatch(Assets.items); + batcher.drawSprite(320 - 32, 32, -64, 64, Assets.arrow); + batcher.endBatch(); + + gl.glDisable(GL10.GL_BLEND); + } + + @Override + public void dispose() { + } +} + diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen3.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen3.java new file mode 100644 index 0000000..a335feb --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen3.java @@ -0,0 +1,92 @@ +package com.badlogic.androidgames.jumper; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.TextureRegion; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class HelpScreen3 extends GLScreen { + Camera2D guiCam; + SpriteBatcher batcher; + Rectangle nextBounds; + Vector2 touchPoint; + Texture helpImage; + TextureRegion helpRegion; + + public HelpScreen3(Game game) { + super(game); + + guiCam = new Camera2D(glGraphics, 320, 480); + nextBounds = new Rectangle(320 - 64, 0, 64, 64); + touchPoint = new Vector2(); + batcher = new SpriteBatcher(glGraphics, 1); + } + + @Override + public void resume() { + helpImage = new Texture(glGame, "help3.png" ); + helpRegion = new TextureRegion(helpImage, 0, 0, 320, 480); + } + + @Override + public void pause() { + helpImage.dispose(); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + touchPoint.set(event.x, event.y); + guiCam.touchToWorld(touchPoint); + + if(event.type == TouchEvent.TOUCH_UP) { + if(OverlapTester.pointInRectangle(nextBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + game.setScreen(new HelpScreen4(game)); + return; + } + } + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + guiCam.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(helpImage); + batcher.drawSprite(160, 240, 320, 480, helpRegion); + batcher.endBatch(); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + batcher.beginBatch(Assets.items); + batcher.drawSprite(320 - 32, 32, -64, 64, Assets.arrow); + batcher.endBatch(); + + gl.glDisable(GL10.GL_BLEND); + } + + @Override + public void dispose() { + } +} + diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen4.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen4.java new file mode 100644 index 0000000..13b2975 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen4.java @@ -0,0 +1,91 @@ +package com.badlogic.androidgames.jumper; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.TextureRegion; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class HelpScreen4 extends GLScreen { + Camera2D guiCam; + SpriteBatcher batcher; + Rectangle nextBounds; + Vector2 touchPoint; + Texture helpImage; + TextureRegion helpRegion; + + public HelpScreen4(Game game) { + super(game); + + guiCam = new Camera2D(glGraphics, 320, 480); + nextBounds = new Rectangle(320 - 64, 0, 64, 64); + touchPoint = new Vector2(); + batcher = new SpriteBatcher(glGraphics, 1); + } + + @Override + public void resume() { + helpImage = new Texture(glGame, "help4.png" ); + helpRegion = new TextureRegion(helpImage, 0, 0, 320, 480); + } + + @Override + public void pause() { + helpImage.dispose(); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + touchPoint.set(event.x, event.y); + guiCam.touchToWorld(touchPoint); + + if(event.type == TouchEvent.TOUCH_UP) { + if(OverlapTester.pointInRectangle(nextBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + game.setScreen(new HelpScreen5(game)); + return; + } + } + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + guiCam.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(helpImage); + batcher.drawSprite(160, 240, 320, 480, helpRegion); + batcher.endBatch(); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + batcher.beginBatch(Assets.items); + batcher.drawSprite(320 - 32, 32, -64, 64, Assets.arrow); + batcher.endBatch(); + + gl.glDisable(GL10.GL_BLEND); + } + + @Override + public void dispose() { + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen5.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen5.java new file mode 100644 index 0000000..02a0adb --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/HelpScreen5.java @@ -0,0 +1,92 @@ +package com.badlogic.androidgames.jumper; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.TextureRegion; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class HelpScreen5 extends GLScreen { + Camera2D guiCam; + SpriteBatcher batcher; + Rectangle nextBounds; + Vector2 touchPoint; + Texture helpImage; + TextureRegion helpRegion; + + public HelpScreen5(Game game) { + super(game); + + guiCam = new Camera2D(glGraphics, 320, 480); + nextBounds = new Rectangle(320 - 64, 0, 64, 64); + touchPoint = new Vector2(); + batcher = new SpriteBatcher(glGraphics, 1); + } + + @Override + public void resume() { + helpImage = new Texture(glGame, "help5.png" ); + helpRegion = new TextureRegion(helpImage, 0, 0, 320, 480); + } + + @Override + public void pause() { + helpImage.dispose(); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + touchPoint.set(event.x, event.y); + guiCam.touchToWorld(touchPoint); + + if(event.type == TouchEvent.TOUCH_UP) { + if(OverlapTester.pointInRectangle(nextBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + game.setScreen(new MainMenuScreen(game)); + return; + } + } + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + guiCam.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(helpImage); + batcher.drawSprite(160, 240, 320, 480, helpRegion); + batcher.endBatch(); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + batcher.beginBatch(Assets.items); + batcher.drawSprite(320 - 32, 32, -64, 64, Assets.arrow); + batcher.endBatch(); + + gl.glDisable(GL10.GL_BLEND); + } + + @Override + public void dispose() { + } +} + diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/HighscoreScreen.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/HighscoreScreen.java new file mode 100644 index 0000000..1bd67cf --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/HighscoreScreen.java @@ -0,0 +1,99 @@ +package com.badlogic.androidgames.jumper; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class HighscoreScreen extends GLScreen { + Camera2D guiCam; + SpriteBatcher batcher; + Rectangle backBounds; + Vector2 touchPoint; + String[] highScores; + float xOffset = 0; + + public HighscoreScreen(Game game) { + super(game); + + guiCam = new Camera2D(glGraphics, 320, 480); + backBounds = new Rectangle(0, 0, 64, 64); + touchPoint = new Vector2(); + batcher = new SpriteBatcher(glGraphics, 100); + highScores = new String[5]; + for(int i = 0; i < 5; i++) { + highScores[i] = (i + 1) + ". " + Settings.highscores[i]; + xOffset = Math.max(highScores[i].length() * Assets.font.glyphWidth, xOffset); + } + xOffset = 160 - xOffset / 2; + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + touchPoint.set(event.x, event.y); + guiCam.touchToWorld(touchPoint); + + if(event.type == TouchEvent.TOUCH_UP) { + if(OverlapTester.pointInRectangle(backBounds, touchPoint)) { + game.setScreen(new MainMenu(game)); + return; + } + } + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + guiCam.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(Assets.background); + batcher.drawSprite(160, 240, 320, 480, Assets.backgroundRegion); + batcher.endBatch(); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + batcher.beginBatch(Assets.items); + batcher.drawSprite(160, 360, 300, 33, Assets.highScoresRegion); + + float y = 240; + for(int i = 4; i >= 0; i--) { + Assets.font.drawText(batcher, highScores[i], xOffset, y); + y += Assets.font.glyphHeight; + } + + batcher.drawSprite(32, 32, 64, 64, Assets.arrow); + batcher.endBatch(); + + gl.glDisable(GL10.GL_BLEND); + } + + @Override + public void resume() { + } + + @Override + public void pause() { + } + + @Override + public void dispose() { + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/HighscoresScreen.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/HighscoresScreen.java new file mode 100644 index 0000000..2b871f3 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/HighscoresScreen.java @@ -0,0 +1,100 @@ +package com.badlogic.androidgames.jumper; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class HighscoresScreen extends GLScreen { + Camera2D guiCam; + SpriteBatcher batcher; + Rectangle backBounds; + Vector2 touchPoint; + String[] highScores; + float xOffset = 0; + + public HighscoresScreen(Game game) { + super(game); + + guiCam = new Camera2D(glGraphics, 320, 480); + backBounds = new Rectangle(0, 0, 64, 64); + touchPoint = new Vector2(); + batcher = new SpriteBatcher(glGraphics, 100); + highScores = new String[5]; + for(int i = 0; i < 5; i++) { + highScores[i] = (i + 1) + ". " + Settings.highscores[i]; + xOffset = Math.max(highScores[i].length() * Assets.font.glyphWidth, xOffset); + } + xOffset = 160 - xOffset / 2 + Assets.font.glyphWidth / 2; + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + touchPoint.set(event.x, event.y); + guiCam.touchToWorld(touchPoint); + + if(event.type == TouchEvent.TOUCH_UP) { + if(OverlapTester.pointInRectangle(backBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + game.setScreen(new MainMenuScreen(game)); + return; + } + } + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + guiCam.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(Assets.background); + batcher.drawSprite(160, 240, 320, 480, Assets.backgroundRegion); + batcher.endBatch(); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + batcher.beginBatch(Assets.items); + batcher.drawSprite(160, 360, 300, 33, Assets.highScoresRegion); + + float y = 240; + for(int i = 4; i >= 0; i--) { + Assets.font.drawText(batcher, highScores[i], xOffset, y); + y += Assets.font.glyphHeight; + } + + batcher.drawSprite(32, 32, 64, 64, Assets.arrow); + batcher.endBatch(); + + gl.glDisable(GL10.GL_BLEND); + } + + @Override + public void resume() { + } + + @Override + public void pause() { + } + + @Override + public void dispose() { + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/MainMenu.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/MainMenu.java new file mode 100644 index 0000000..85cd3ee --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/MainMenu.java @@ -0,0 +1,107 @@ +package com.badlogic.androidgames.jumper; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class MainMenu extends GLScreen { + Camera2D guiCam; + SpriteBatcher batcher; + Rectangle soundBounds; + Rectangle playBounds; + Rectangle highscoresBounds; + Rectangle helpBounds; + Vector2 touchPoint; + + public MainMenu(Game game) { + super(game); + guiCam = new Camera2D(glGraphics, 320, 480); + batcher = new SpriteBatcher(glGraphics, 100); + soundBounds = new Rectangle(0, 0, 64, 64); + playBounds = new Rectangle(160 - 150, 200 + 18, 300, 36); + highscoresBounds = new Rectangle(160 - 150, 200 - 18, 300, 36); + helpBounds = new Rectangle(160 - 150, 200 - 18 - 36, 300, 36); + touchPoint = new Vector2(); + + Settings.load(game.getFileIO()); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + touchPoint.set(event.x, event.y); + guiCam.touchToWorld(touchPoint); + + if(event.type == TouchEvent.TOUCH_UP) { + if(OverlapTester.pointInRectangle(playBounds, touchPoint)) { + game.setScreen(new GameScreen(game)); + return; + } + if(OverlapTester.pointInRectangle(highscoresBounds, touchPoint)) { + game.setScreen(new HighscoreScreen(game)); + return; + } + if(OverlapTester.pointInRectangle(helpBounds, touchPoint)) { + game.setScreen(new HelpScreen(game)); + return; + } + if(OverlapTester.pointInRectangle(soundBounds, touchPoint)) { + Settings.soundEnabled = !Settings.soundEnabled; + } + } + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + guiCam.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(Assets.background); + batcher.drawSprite(160, 240, 320, 480, Assets.backgroundRegion); + batcher.endBatch(); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + batcher.beginBatch(Assets.items); + + batcher.drawSprite(160, 480 - 10 - 71, 274, 142, Assets.logo); + batcher.drawSprite(160, 200, 300, 110, Assets.mainMenu); + batcher.drawSprite(32, 32, 64, 64, Settings.soundEnabled?Assets.soundOn:Assets.soundOff); + + batcher.endBatch(); + + gl.glDisable(GL10.GL_BLEND); + } + + @Override + public void pause() { + Settings.save(game.getFileIO()); + } + + @Override + public void resume() { + } + + @Override + public void dispose() { + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/MainMenuScreen.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/MainMenuScreen.java new file mode 100644 index 0000000..a330613 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/MainMenuScreen.java @@ -0,0 +1,113 @@ +package com.badlogic.androidgames.jumper; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class MainMenuScreen extends GLScreen { + Camera2D guiCam; + SpriteBatcher batcher; + Rectangle soundBounds; + Rectangle playBounds; + Rectangle highscoresBounds; + Rectangle helpBounds; + Vector2 touchPoint; + + public MainMenuScreen(Game game) { + super(game); + guiCam = new Camera2D(glGraphics, 320, 480); + batcher = new SpriteBatcher(glGraphics, 100); + soundBounds = new Rectangle(0, 0, 64, 64); + playBounds = new Rectangle(160 - 150, 200 + 18, 300, 36); + highscoresBounds = new Rectangle(160 - 150, 200 - 18, 300, 36); + helpBounds = new Rectangle(160 - 150, 200 - 18 - 36, 300, 36); + touchPoint = new Vector2(); + } + + @Override + public void update(float deltaTime) { + List touchEvents = game.getInput().getTouchEvents(); + game.getInput().getKeyEvents(); + + int len = touchEvents.size(); + for(int i = 0; i < len; i++) { + TouchEvent event = touchEvents.get(i); + if(event.type == TouchEvent.TOUCH_UP) { + touchPoint.set(event.x, event.y); + guiCam.touchToWorld(touchPoint); + + if(OverlapTester.pointInRectangle(playBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + game.setScreen(new GameScreen(game)); + return; + } + if(OverlapTester.pointInRectangle(highscoresBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + game.setScreen(new HighscoresScreen(game)); + return; + } + if(OverlapTester.pointInRectangle(helpBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + game.setScreen(new HelpScreen(game)); + return; + } + if(OverlapTester.pointInRectangle(soundBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + Settings.soundEnabled = !Settings.soundEnabled; + if(Settings.soundEnabled) + Assets.music.play(); + else + Assets.music.pause(); + } + } + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + guiCam.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(Assets.background); + batcher.drawSprite(160, 240, 320, 480, Assets.backgroundRegion); + batcher.endBatch(); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + batcher.beginBatch(Assets.items); + + batcher.drawSprite(160, 480 - 10 - 71, 274, 142, Assets.logo); + batcher.drawSprite(160, 200, 300, 110, Assets.mainMenu); + batcher.drawSprite(32, 32, 64, 64, Settings.soundEnabled?Assets.soundOn:Assets.soundOff); + + batcher.endBatch(); + + gl.glDisable(GL10.GL_BLEND); + } + + @Override + public void pause() { + Settings.save(game.getFileIO()); + } + + @Override + public void resume() { + } + + @Override + public void dispose() { + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/Platform.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/Platform.java new file mode 100644 index 0000000..1bb4646 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/Platform.java @@ -0,0 +1,52 @@ +package com.badlogic.androidgames.jumper; + +import com.badlogic.androidgames.framework.DynamicGameObject; + +public class Platform extends DynamicGameObject { + public static final float PLATFORM_WIDTH = 2; + public static final float PLATFORM_HEIGHT = 0.5f; + public static final int PLATFORM_TYPE_STATIC = 0; + public static final int PLATFORM_TYPE_MOVING = 1; + public static final int PLATFORM_STATE_NORMAL = 0; + public static final int PLATFORM_STATE_PULVERIZING = 1; + public static final float PLATFORM_PULVERIZE_TIME = 0.2f * 4; + public static final float PLATFORM_VELOCITY = 2; + + int type; + int state; + float stateTime; + + public Platform(int type, float x, float y) { + super(x, y, PLATFORM_WIDTH, PLATFORM_HEIGHT); + this.type = type; + this.state = PLATFORM_STATE_NORMAL; + this.stateTime = 0; + if(type == PLATFORM_TYPE_MOVING) { + velocity.x = PLATFORM_VELOCITY; + } + } + + public void update(float deltaTime) { + if(type == PLATFORM_TYPE_MOVING) { + position.add(velocity.x * deltaTime, 0); + bounds.lowerLeft.set(position).sub(PLATFORM_WIDTH / 2, PLATFORM_HEIGHT / 2); + + if(position.x < PLATFORM_WIDTH / 2) { + velocity.x = -velocity.x; + position.x = PLATFORM_WIDTH / 2; + } + if(position.x > World.WORLD_WIDTH - PLATFORM_WIDTH / 2) { + velocity.x = -velocity.x; + position.x = World.WORLD_WIDTH - PLATFORM_WIDTH / 2; + } + } + + stateTime += deltaTime; + } + + public void pulverize() { + state = PLATFORM_STATE_PULVERIZING; + stateTime = 0; + velocity.x = 0; + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/Settings.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/Settings.java new file mode 100644 index 0000000..389be2e --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/Settings.java @@ -0,0 +1,69 @@ +package com.badlogic.androidgames.jumper; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; + +import com.badlogic.androidgames.framework.FileIO; + +public class Settings { + public static boolean soundEnabled = true; + public final static int[] highscores = new int[] { 100, 80, 50, 30, 10 }; + public final static String file = ".superjumper"; + + public static void load(FileIO files) { + BufferedReader in = null; + try { + in = new BufferedReader(new InputStreamReader(files.readFile(file))); + soundEnabled = Boolean.parseBoolean(in.readLine()); + for(int i = 0; i < 5; i++) { + highscores[i] = Integer.parseInt(in.readLine()); + } + } catch (IOException e) { + // :( It's ok we have defaults + } catch (NumberFormatException e) { + // :/ It's ok, defaults save our day + } finally { + try { + if (in != null) + in.close(); + } catch (IOException e) { + } + } + } + + public static void save(FileIO files) { + BufferedWriter out = null; + try { + out = new BufferedWriter(new OutputStreamWriter( + files.writeFile(file))); + out.write(Boolean.toString(soundEnabled)); + out.write("\n"); + for(int i = 0; i < 5; i++) { + out.write(Integer.toString(highscores[i])); + out.write("\n"); + } + + } catch (IOException e) { + } finally { + try { + if (out != null) + out.close(); + } catch (IOException e) { + } + } + } + + public static void addScore(int score) { + for(int i=0; i < 5; i++) { + if(highscores[i] < score) { + for(int j= 4; j > i; j--) + highscores[j] = highscores[j-1]; + highscores[i] = score; + break; + } + } + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/Spring.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/Spring.java new file mode 100644 index 0000000..4393995 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/Spring.java @@ -0,0 +1,12 @@ +package com.badlogic.androidgames.jumper; + +import com.badlogic.androidgames.framework.GameObject; + +public class Spring extends GameObject { + public static float SPRING_WIDTH = 0.3f; + public static float SPRING_HEIGHT = 0.3f; + + public Spring(float x, float y) { + super(x, y, SPRING_WIDTH, SPRING_HEIGHT); + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/Squirrel.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/Squirrel.java new file mode 100644 index 0000000..767d867 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/Squirrel.java @@ -0,0 +1,31 @@ +package com.badlogic.androidgames.jumper; + +import com.badlogic.androidgames.framework.DynamicGameObject; + +public class Squirrel extends DynamicGameObject { + public static final float SQUIRREL_WIDTH = 1; + public static final float SQUIRREL_HEIGHT = 0.6f; + public static final float SQUIRREL_VELOCITY = 3f; + + float stateTime = 0; + + public Squirrel(float x, float y) { + super(x, y, SQUIRREL_WIDTH, SQUIRREL_HEIGHT); + velocity.set(SQUIRREL_VELOCITY, 0); + } + + public void update(float deltaTime) { + position.add(velocity.x * deltaTime, velocity.y * deltaTime); + bounds.lowerLeft.set(position).sub(SQUIRREL_WIDTH / 2, SQUIRREL_HEIGHT / 2); + + if(position.x < SQUIRREL_WIDTH / 2 ) { + position.x = SQUIRREL_WIDTH / 2; + velocity.x = SQUIRREL_VELOCITY; + } + if(position.x > World.WORLD_WIDTH - SQUIRREL_WIDTH / 2) { + position.x = World.WORLD_WIDTH - SQUIRREL_WIDTH / 2; + velocity.x = -SQUIRREL_VELOCITY; + } + stateTime += deltaTime; + } +} diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/SuperJumper.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/SuperJumper.java new file mode 100644 index 0000000..1762a16 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/SuperJumper.java @@ -0,0 +1,35 @@ +package com.badlogic.androidgames.jumper; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.impl.GLGame; + +public class SuperJumper extends GLGame { + boolean firstTimeCreate = true; + + @Override + public Screen getStartScreen() { + return new MainMenuScreen(this); + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + super.onSurfaceCreated(gl, config); + if(firstTimeCreate) { + Settings.load(getFileIO()); + Assets.load(this); + firstTimeCreate = false; + } else { + Assets.reload(); + } + } + + @Override + public void onPause() { + super.onPause(); + if(Settings.soundEnabled) + Assets.music.pause(); + } +} \ No newline at end of file diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/World.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/World.java new file mode 100644 index 0000000..0bdbada --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/World.java @@ -0,0 +1,225 @@ +package com.badlogic.androidgames.jumper; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Vector2; + +public class World { + public interface WorldListener { + public void jump(); + + public void highJump(); + + public void hit(); + + public void coin(); + } + + public static final float WORLD_WIDTH = 10; + public static final float WORLD_HEIGHT = 15 * 20; + public static final int WORLD_STATE_RUNNING = 0; + public static final int WORLD_STATE_NEXT_LEVEL = 1; + public static final int WORLD_STATE_GAME_OVER = 2; + public static final Vector2 gravity = new Vector2(0, -12); + + public final Bob bob; + public final List platforms; + public final List springs; + public final List squirrels; + public final List coins; + public Castle castle; + public final WorldListener listener; + public final Random rand; + + public float heightSoFar; + public int score; + public int state; + + public World(WorldListener listener) { + this.bob = new Bob(5, 1); + this.platforms = new ArrayList(); + this.springs = new ArrayList(); + this.squirrels = new ArrayList(); + this.coins = new ArrayList(); + this.listener = listener; + rand = new Random(); + generateLevel(); + + this.heightSoFar = 0; + this.score = 0; + this.state = WORLD_STATE_RUNNING; + } + + private void generateLevel() { + float y = Platform.PLATFORM_HEIGHT / 2; + float maxJumpHeight = Bob.BOB_JUMP_VELOCITY * Bob.BOB_JUMP_VELOCITY + / (2 * -gravity.y); + while (y < WORLD_HEIGHT - WORLD_WIDTH / 2) { + int type = rand.nextFloat() > 0.8f ? Platform.PLATFORM_TYPE_MOVING + : Platform.PLATFORM_TYPE_STATIC; + float x = rand.nextFloat() + * (WORLD_WIDTH - Platform.PLATFORM_WIDTH) + + Platform.PLATFORM_WIDTH / 2; + + Platform platform = new Platform(type, x, y); + platforms.add(platform); + + if (rand.nextFloat() > 0.9f + && type != Platform.PLATFORM_TYPE_MOVING) { + Spring spring = new Spring(platform.position.x, + platform.position.y + Platform.PLATFORM_HEIGHT / 2 + + Spring.SPRING_HEIGHT / 2); + springs.add(spring); + } + + if (y > WORLD_HEIGHT / 3 && rand.nextFloat() > 0.8f) { + Squirrel squirrel = new Squirrel(platform.position.x + + rand.nextFloat(), platform.position.y + + Squirrel.SQUIRREL_HEIGHT + rand.nextFloat() * 2); + squirrels.add(squirrel); + } + + if (rand.nextFloat() > 0.6f) { + Coin coin = new Coin(platform.position.x + rand.nextFloat(), + platform.position.y + Coin.COIN_HEIGHT + + rand.nextFloat() * 3); + coins.add(coin); + } + + y += (maxJumpHeight - 0.5f); + y -= rand.nextFloat() * (maxJumpHeight / 3); + } + + castle = new Castle(WORLD_WIDTH / 2, y); + } + +public void update(float deltaTime, float accelX) { + updateBob(deltaTime, accelX); + updatePlatforms(deltaTime); + updateSquirrels(deltaTime); + updateCoins(deltaTime); + if (bob.state != Bob.BOB_STATE_HIT) + checkCollisions(); + checkGameOver(); +} + +private void updateBob(float deltaTime, float accelX) { + if (bob.state != Bob.BOB_STATE_HIT && bob.position.y <= 0.5f) + bob.hitPlatform(); + if (bob.state != Bob.BOB_STATE_HIT) + bob.velocity.x = -accelX / 10 * Bob.BOB_MOVE_VELOCITY; + bob.update(deltaTime); + heightSoFar = Math.max(bob.position.y, heightSoFar); +} + +private void updatePlatforms(float deltaTime) { + int len = platforms.size(); + for (int i = 0; i < len; i++) { + Platform platform = platforms.get(i); + platform.update(deltaTime); + if (platform.state == Platform.PLATFORM_STATE_PULVERIZING + && platform.stateTime > Platform.PLATFORM_PULVERIZE_TIME) { + platforms.remove(platform); + len = platforms.size(); + } + } +} + +private void updateSquirrels(float deltaTime) { + int len = squirrels.size(); + for (int i = 0; i < len; i++) { + Squirrel squirrel = squirrels.get(i); + squirrel.update(deltaTime); + } +} + +private void updateCoins(float deltaTime) { + int len = coins.size(); + for (int i = 0; i < len; i++) { + Coin coin = coins.get(i); + coin.update(deltaTime); + } +} + + private void checkCollisions() { + checkPlatformCollisions(); + checkSquirrelCollisions(); + checkItemCollisions(); + checkCastleCollisions(); + } + + private void checkPlatformCollisions() { + if (bob.velocity.y > 0) + return; + + int len = platforms.size(); + for (int i = 0; i < len; i++) { + Platform platform = platforms.get(i); + if (bob.position.y > platform.position.y) { + if (OverlapTester + .overlapRectangles(bob.bounds, platform.bounds)) { + bob.hitPlatform(); + listener.jump(); + if (rand.nextFloat() > 0.5f) { + platform.pulverize(); + } + break; + } + } + } + } + + private void checkSquirrelCollisions() { + int len = squirrels.size(); + for (int i = 0; i < len; i++) { + Squirrel squirrel = squirrels.get(i); + if (OverlapTester.overlapRectangles(squirrel.bounds, bob.bounds)) { + bob.hitSquirrel(); + listener.hit(); + } + } + } + + private void checkItemCollisions() { + int len = coins.size(); + for (int i = 0; i < len; i++) { + Coin coin = coins.get(i); + if (OverlapTester.overlapRectangles(bob.bounds, coin.bounds)) { + coins.remove(coin); + len = coins.size(); + listener.coin(); + score += Coin.COIN_SCORE; + } + + } + + if (bob.velocity.y > 0) + return; + + len = springs.size(); + for (int i = 0; i < len; i++) { + Spring spring = springs.get(i); + if (bob.position.y > spring.position.y) { + if (OverlapTester.overlapRectangles(bob.bounds, spring.bounds)) { + bob.hitSpring(); + listener.highJump(); + } + } + } + } + + private void checkCastleCollisions() { + if (OverlapTester.overlapRectangles(castle.bounds, bob.bounds)) { + state = WORLD_STATE_NEXT_LEVEL; + } + } + + private void checkGameOver() { + if (heightSoFar - 7.5f > bob.position.y) { + state = WORLD_STATE_GAME_OVER; + } + } +} \ No newline at end of file diff --git a/ch09-jumper/src/com/badlogic/androidgames/jumper/WorldRenderer.java b/ch09-jumper/src/com/badlogic/androidgames/jumper/WorldRenderer.java new file mode 100644 index 0000000..fdc9d96 --- /dev/null +++ b/ch09-jumper/src/com/badlogic/androidgames/jumper/WorldRenderer.java @@ -0,0 +1,118 @@ +package com.badlogic.androidgames.jumper; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.gl.Animation; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.gl.TextureRegion; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class WorldRenderer { + static final float FRUSTUM_WIDTH = 10; + static final float FRUSTUM_HEIGHT = 15; + GLGraphics glGraphics; + World world; + Camera2D cam; + SpriteBatcher batcher; + + public WorldRenderer(GLGraphics glGraphics, SpriteBatcher batcher, World world) { + this.glGraphics = glGraphics; + this.world = world; + this.cam = new Camera2D(glGraphics, FRUSTUM_WIDTH, FRUSTUM_HEIGHT); + this.batcher = batcher; + } + + public void render() { + if(world.bob.position.y > cam.position.y ) + cam.position.y = world.bob.position.y; + cam.setViewportAndMatrices(); + renderBackground(); + renderObjects(); + } + + public void renderBackground() { + batcher.beginBatch(Assets.background); + batcher.drawSprite(cam.position.x, cam.position.y, + FRUSTUM_WIDTH, FRUSTUM_HEIGHT, + Assets.backgroundRegion); + batcher.endBatch(); + } + + public void renderObjects() { + GL10 gl = glGraphics.getGL(); + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + batcher.beginBatch(Assets.items); + renderBob(); + renderPlatforms(); + renderItems(); + renderSquirrels(); + renderCastle(); + batcher.endBatch(); + gl.glDisable(GL10.GL_BLEND); + } + + private void renderBob() { + TextureRegion keyFrame; + switch(world.bob.state) { + case Bob.BOB_STATE_FALL: + keyFrame = Assets.bobFall.getKeyFrame(world.bob.stateTime, Animation.ANIMATION_LOOPING); + break; + case Bob.BOB_STATE_JUMP: + keyFrame = Assets.bobJump.getKeyFrame(world.bob.stateTime, Animation.ANIMATION_LOOPING); + break; + case Bob.BOB_STATE_HIT: + default: + keyFrame = Assets.bobHit; + } + + float side = world.bob.velocity.x < 0? -1: 1; + batcher.drawSprite(world.bob.position.x, world.bob.position.y, side * 1, 1, keyFrame); + } + + private void renderPlatforms() { + int len = world.platforms.size(); + for(int i = 0; i < len; i++) { + Platform platform = world.platforms.get(i); + TextureRegion keyFrame = Assets.platform; + if(platform.state == Platform.PLATFORM_STATE_PULVERIZING) { + keyFrame = Assets.brakingPlatform.getKeyFrame(platform.stateTime, Animation.ANIMATION_NONLOOPING); + } + + batcher.drawSprite(platform.position.x, platform.position.y, + 2, 0.5f, keyFrame); + } + } + + private void renderItems() { + int len = world.springs.size(); + for(int i = 0; i < len; i++) { + Spring spring = world.springs.get(i); + batcher.drawSprite(spring.position.x, spring.position.y, 1, 1, Assets.spring); + } + + len = world.coins.size(); + for(int i = 0; i < len; i++) { + Coin coin = world.coins.get(i); + TextureRegion keyFrame = Assets.coinAnim.getKeyFrame(coin.stateTime, Animation.ANIMATION_LOOPING); + batcher.drawSprite(coin.position.x, coin.position.y, 1, 1, keyFrame); + } + } + + private void renderSquirrels() { + int len = world.squirrels.size(); + for(int i = 0; i < len; i++) { + Squirrel squirrel = world.squirrels.get(i); + TextureRegion keyFrame = Assets.squirrelFly.getKeyFrame(squirrel.stateTime, Animation.ANIMATION_LOOPING); + float side = squirrel.velocity.x < 0?-1:1; + batcher.drawSprite(squirrel.position.x, squirrel.position.y, side * 1, 1, keyFrame); + } + } + + private void renderCastle() { + Castle castle = world.castle; + batcher.drawSprite(castle.position.x, castle.position.y, 2, 2, Assets.castle); + } +} diff --git a/ch10-gl-3d/.classpath b/ch10-gl-3d/.classpath new file mode 100644 index 0000000..609aa00 --- /dev/null +++ b/ch10-gl-3d/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ch10-gl-3d/.project b/ch10-gl-3d/.project new file mode 100644 index 0000000..98e7d2d --- /dev/null +++ b/ch10-gl-3d/.project @@ -0,0 +1,33 @@ + + + ch10-gl-3d + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/ch10-gl-3d/AndroidManifest.xml b/ch10-gl-3d/AndroidManifest.xml new file mode 100644 index 0000000..fe0ed12 --- /dev/null +++ b/ch10-gl-3d/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch10-gl-3d/assets/crate.png b/ch10-gl-3d/assets/crate.png new file mode 100644 index 0000000..cc0788b Binary files /dev/null and b/ch10-gl-3d/assets/crate.png differ diff --git a/ch10-gl-3d/assets/scene - Kopie.wings b/ch10-gl-3d/assets/scene - Kopie.wings new file mode 100644 index 0000000..9d23b9a Binary files /dev/null and b/ch10-gl-3d/assets/scene - Kopie.wings differ diff --git a/ch10-gl-3d/assets/scene.wings b/ch10-gl-3d/assets/scene.wings new file mode 100644 index 0000000..a5aa118 Binary files /dev/null and b/ch10-gl-3d/assets/scene.wings differ diff --git a/ch10-gl-3d/default.properties b/ch10-gl-3d/default.properties new file mode 100644 index 0000000..3ac2523 --- /dev/null +++ b/ch10-gl-3d/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-12 diff --git a/ch10-gl-3d/proguard.cfg b/ch10-gl-3d/proguard.cfg new file mode 100644 index 0000000..8ad7d33 --- /dev/null +++ b/ch10-gl-3d/proguard.cfg @@ -0,0 +1,34 @@ +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontpreverify +-verbose +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* + +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class com.android.vending.licensing.ILicensingService + +-keepclasseswithmembernames class * { + native ; +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/ch10-gl-3d/res/drawable-hdpi/icon.png b/ch10-gl-3d/res/drawable-hdpi/icon.png new file mode 100644 index 0000000..8074c4c Binary files /dev/null and b/ch10-gl-3d/res/drawable-hdpi/icon.png differ diff --git a/ch10-gl-3d/res/drawable-ldpi/icon.png b/ch10-gl-3d/res/drawable-ldpi/icon.png new file mode 100644 index 0000000..1095584 Binary files /dev/null and b/ch10-gl-3d/res/drawable-ldpi/icon.png differ diff --git a/ch10-gl-3d/res/drawable-mdpi/icon.png b/ch10-gl-3d/res/drawable-mdpi/icon.png new file mode 100644 index 0000000..a07c69f Binary files /dev/null and b/ch10-gl-3d/res/drawable-mdpi/icon.png differ diff --git a/ch10-gl-3d/res/layout/main.xml b/ch10-gl-3d/res/layout/main.xml new file mode 100644 index 0000000..3a5f117 --- /dev/null +++ b/ch10-gl-3d/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/ch10-gl-3d/res/values/strings.xml b/ch10-gl-3d/res/values/strings.xml new file mode 100644 index 0000000..51c4a84 --- /dev/null +++ b/ch10-gl-3d/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Hello World, GL3DBasicsStarter! + OpenGL 3D Basics + diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/Audio.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Audio.java new file mode 100644 index 0000000..d30d722 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Audio.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Audio { + public Music newMusic(String filename); + + public Sound newSound(String filename); +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/Color.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Color.java new file mode 100644 index 0000000..57d68e5 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Color.java @@ -0,0 +1,10 @@ +package com.badlogic.androidgames.framework; + +public class Color { + public static int convert (int r, int g, int b, int a) { + return ((a & 0xff) << 24) | + ((r & 0xff) << 16) | + ((g & 0xff) << 8) | + ((b & 0xff)); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/DynamicGameObject.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/DynamicGameObject.java new file mode 100644 index 0000000..00afabb --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/DynamicGameObject.java @@ -0,0 +1,14 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.math.Vector2; + +public class DynamicGameObject extends GameObject { + public final Vector2 velocity; + public final Vector2 accel; + + public DynamicGameObject(float x, float y, float width, float height) { + super(x, y, width, height); + velocity = new Vector2(); + accel = new Vector2(); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/FileIO.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/FileIO.java new file mode 100644 index 0000000..d555b7d --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/FileIO.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.framework; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public interface FileIO { + public InputStream readAsset(String fileName) throws IOException; + + public InputStream readFile(String fileName) throws IOException; + + public OutputStream writeFile(String fileName) throws IOException; +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/Game.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Game.java new file mode 100644 index 0000000..fb5c5aa --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Game.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework; + +public interface Game { + public Input getInput(); + + public FileIO getFileIO(); + + public Graphics getGraphics(); + + public Audio getAudio(); + + public void setScreen(Screen screen); + + public Screen getCurrentScreen(); + + public Screen getStartScreen(); +} \ No newline at end of file diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/GameObject.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/GameObject.java new file mode 100644 index 0000000..54aaa09 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/GameObject.java @@ -0,0 +1,14 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class GameObject { + public final Vector2 position; + public final Rectangle bounds; + + public GameObject(float x, float y, float width, float height) { + this.position = new Vector2(x,y); + this.bounds = new Rectangle(x-width/2, y-height/2, width, height); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/Graphics.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Graphics.java new file mode 100644 index 0000000..c7b8f45 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Graphics.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework; + +public interface Graphics { + public static enum PixmapFormat { + ARGB8888, ARGB4444, RGB565 + } + + public Pixmap newPixmap(String fileName, PixmapFormat format); + + public void clear(int color); + + public void drawPixel(int x, int y, int color); + + public void drawLine(int x, int y, int x2, int y2, int color); + + public void drawRect(int x, int y, int width, int height, int color); + + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight); + + public void drawPixmap(Pixmap pixmap, int x, int y); + + public int getWidth(); + + public int getHeight(); +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/Input.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Input.java new file mode 100644 index 0000000..0893d62 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Input.java @@ -0,0 +1,70 @@ +package com.badlogic.androidgames.framework; + +import java.util.List; + +public interface Input { + public static class KeyEvent { + public static final int KEY_DOWN = 0; + public static final int KEY_UP = 1; + + public int type; + public int keyCode; + public char keyChar; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == KEY_DOWN) + builder.append("key down, "); + else + builder.append("key up, "); + builder.append(keyCode); + builder.append(","); + builder.append(keyChar); + return builder.toString(); + } + } + + public static class TouchEvent { + public static final int TOUCH_DOWN = 0; + public static final int TOUCH_UP = 1; + public static final int TOUCH_DRAGGED = 2; + + public int type; + public int x, y; + public int pointer; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == TOUCH_DOWN) + builder.append("touch down, "); + else if (type == TOUCH_DRAGGED) + builder.append("touch dragged, "); + else + builder.append("touch up, "); + builder.append(pointer); + builder.append(","); + builder.append(x); + builder.append(","); + builder.append(y); + return builder.toString(); + } + } + + public boolean isKeyPressed(int keyCode); + + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public float getAccelX(); + + public float getAccelY(); + + public float getAccelZ(); + + public List getKeyEvents(); + + public List getTouchEvents(); +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/Music.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Music.java new file mode 100644 index 0000000..1b95a1e --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Music.java @@ -0,0 +1,21 @@ +package com.badlogic.androidgames.framework; + +public interface Music { + public void play(); + + public void stop(); + + public void pause(); + + public void setLooping(boolean looping); + + public void setVolume(float volume); + + public boolean isPlaying(); + + public boolean isStopped(); + + public boolean isLooping(); + + public void dispose(); +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/Pixmap.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Pixmap.java new file mode 100644 index 0000000..4b1eec9 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Pixmap.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; + +public interface Pixmap { + public int getWidth(); + + public int getHeight(); + + public PixmapFormat getFormat(); + + public void dispose(); +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/Pool.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Pool.java new file mode 100644 index 0000000..d0b7cec --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Pool.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework; + +import java.util.ArrayList; +import java.util.List; + +public class Pool { + public interface PoolObjectFactory { + public T createObject(); + } + + private final List freeObjects; + private final PoolObjectFactory factory; + private final int maxSize; + + public Pool(PoolObjectFactory factory, int maxSize) { + this.factory = factory; + this.maxSize = maxSize; + this.freeObjects = new ArrayList(maxSize); + } + + public T newObject() { + T object = null; + + if (freeObjects.size() == 0) + object = factory.createObject(); + else + object = freeObjects.remove(freeObjects.size() - 1); + + return object; + } + + public void free(T object) { + if (freeObjects.size() < maxSize) + freeObjects.add(object); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/Screen.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Screen.java new file mode 100644 index 0000000..40a98b9 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Screen.java @@ -0,0 +1,19 @@ +package com.badlogic.androidgames.framework; + +public abstract class Screen { + protected final Game game; + + public Screen(Game game) { + this.game = game; + } + + public abstract void update(float deltaTime); + + public abstract void present(float deltaTime); + + public abstract void pause(); + + public abstract void resume(); + + public abstract void dispose(); +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/Sound.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Sound.java new file mode 100644 index 0000000..3f49835 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/Sound.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Sound { + public void play(float volume); + + public void dispose(); +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/TestScreen.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/TestScreen.java new file mode 100644 index 0000000..5a0bbcf --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/TestScreen.java @@ -0,0 +1,112 @@ +package com.badlogic.androidgames.framework; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.List; + +import android.graphics.Color; +import android.util.Log; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; +import com.badlogic.androidgames.framework.Input.KeyEvent; +import com.badlogic.androidgames.framework.Input.TouchEvent; + +class TestScreen extends Screen { + long startTime = System.nanoTime(); + int frames; + Pixmap bob; + Pixmap bobAlpha; + Sound sound; + Music music; + + public TestScreen(Game game) { + super(game); + bob = game.getGraphics().newPixmap("bobrgb888.png", PixmapFormat.RGB565); + bobAlpha = game.getGraphics().newPixmap("bobargb8888.png", PixmapFormat.ARGB4444); + music = game.getAudio().newMusic("music.ogg"); + music.setLooping(true); + music.setVolume(0.5f); + music.play(); + sound = game.getAudio().newSound("music.ogg"); + + try { + BufferedReader in = new BufferedReader(new InputStreamReader(game.getFileIO().readAsset("test.txt"))); + String text = in.readLine(); + in.close(); + + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(game.getFileIO().writeFile("test.txt"))); + out.write("This is a freaking test"); + out.close(); + + in = new BufferedReader(new InputStreamReader(game.getFileIO().readFile("test.txt"))); + String text2 = in.readLine(); + in.close(); + + Log.d("MrNom", text + ", " + text2 ); + } catch(Exception ex) { + ex.printStackTrace(); + } + } + + @Override + public void update(float deltaTime) { + } + + @Override + public void present(float deltaTime) { + Graphics g = game.getGraphics(); + Input inp = game.getInput(); + g.clear(Color.RED); + g.drawLine(0,0,320, 480, Color.BLUE); + g.drawRect(20,20,100,100, Color.GREEN); + g.drawPixmap(bob, 100, 100); + g.drawPixmap(bobAlpha, 100, 200); + g.drawPixmap(bob, 200, 200, 0, 0, 64, 64); + for(int i=0; i < 2; i++) { + if(inp.isTouchDown(i)) { + g.drawPixmap(bob, inp.getTouchX(i), inp.getTouchY(i), 0, 0, 64, 64); + } + } + + g.drawPixmap(bob, (int)(inp.getAccelX() * 10) + 160 - 16, (int)(inp.getAccelY() * 10) + 240 - 16, 0, 0, 32, 32 ); + + List keyEvents = inp.getKeyEvents(); + int len = keyEvents.size(); + for(int i = 0; i < len; i++) { + Log.d("MrNom", keyEvents.get(i).toString()); + } + + List touchEvents = inp.getTouchEvents(); + len = touchEvents.size(); + for(int i = 0; i < len; i++) { + Log.d("MrNom", touchEvents.get(i).toString()); + if(touchEvents.get(i).type == TouchEvent.TOUCH_UP) + sound.play(1); + } + + frames++; + if(System.nanoTime() - startTime > 1000000000l) { + Log.d("MrNom", "fps: " + frames + ", delta: " + deltaTime); + frames = 0; + startTime = System.nanoTime(); + } + } + + @Override + public void pause() { + Log.d("MrNom", "pause"); + } + + @Override + public void resume() { + Log.d("MrNom", "resume"); + } + + @Override + public void dispose() { + Log.d("MrNom", "dispose"); + music.dispose(); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Animation.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Animation.java new file mode 100644 index 0000000..d826e39 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Animation.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework.gl; + + +public class Animation { + public static final int ANIMATION_LOOPING = 0; + public static final int ANIMATION_NONLOOPING = 1; + + final TextureRegion[] keyFrames; + final float frameDuration; + + public Animation(float frameDuration, TextureRegion ... keyFrames) { + this.frameDuration = frameDuration; + this.keyFrames = keyFrames; + } + + public TextureRegion getKeyFrame(float stateTime, int mode) { + int frameNumber = (int)(stateTime / frameDuration); + + if(mode == ANIMATION_NONLOOPING) { + frameNumber = Math.min(keyFrames.length-1, frameNumber); + } else { + frameNumber = frameNumber % keyFrames.length; + } + return keyFrames[frameNumber]; + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Camera2D.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Camera2D.java new file mode 100644 index 0000000..2a58199 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Camera2D.java @@ -0,0 +1,42 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.Vector2; + +public class Camera2D { + public final Vector2 position; + public float zoom; + public final float frustumWidth; + public final float frustumHeight; + final GLGraphics glGraphics; + + public Camera2D(GLGraphics glGraphics, float frustumWidth, float frustumHeight) { + this.glGraphics = glGraphics; + this.frustumWidth = frustumWidth; + this.frustumHeight = frustumHeight; + this.position = new Vector2(frustumWidth / 2, frustumHeight / 2); + this.zoom = 1.0f; + } + + public void setViewportAndMatrices() { + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(position.x - frustumWidth * zoom / 2, + position.x + frustumWidth * zoom/ 2, + position.y - frustumHeight * zoom / 2, + position.y + frustumHeight * zoom/ 2, + 1, -1); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + } + + public void touchToWorld(Vector2 touch) { + touch.x = (touch.x / (float) glGraphics.getWidth()) * frustumWidth * zoom; + touch.y = (1 - touch.y / (float) glGraphics.getHeight()) * frustumHeight * zoom; + touch.add(position).sub(frustumWidth * zoom / 2, frustumHeight * zoom / 2); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/FPSCounter.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/FPSCounter.java new file mode 100644 index 0000000..cd816ca --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/FPSCounter.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework.gl; + +import android.util.Log; + +public class FPSCounter { + long startTime = System.nanoTime(); + int frames = 0; + + public void logFrame() { + frames++; + if(System.nanoTime() - startTime >= 1000000000) { + Log.d("FPSCounter", "fps: " + frames); + frames = 0; + startTime = System.nanoTime(); + } + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Font.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Font.java new file mode 100644 index 0000000..ace1452 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Font.java @@ -0,0 +1,39 @@ +package com.badlogic.androidgames.framework.gl; + +public class Font { + public final Texture texture; + public final int glyphWidth; + public final int glyphHeight; + public final TextureRegion[] glyphs = new TextureRegion[96]; + + public Font(Texture texture, + int offsetX, int offsetY, + int glyphsPerRow, int glyphWidth, int glyphHeight) { + this.texture = texture; + this.glyphWidth = glyphWidth; + this.glyphHeight = glyphHeight; + int x = offsetX; + int y = offsetY; + for(int i = 0; i < 96; i++) { + glyphs[i] = new TextureRegion(texture, x, y, glyphWidth, glyphHeight); + x += glyphWidth; + if(x == offsetX + glyphsPerRow * glyphWidth) { + x = offsetX; + y += glyphHeight; + } + } + } + + public void drawText(SpriteBatcher batcher, String text, float x, float y) { + int len = text.length(); + for(int i = 0; i < len; i++) { + int c = text.charAt(i) - ' '; + if(c < 0 || c > glyphs.length - 1) + continue; + + TextureRegion glyph = glyphs[c]; + batcher.drawSprite(x, y, glyphWidth, glyphHeight, glyph); + x += glyphWidth; + } + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Light.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Light.java new file mode 100644 index 0000000..5c0d095 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Light.java @@ -0,0 +1,49 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Light { + final float[] ambient; + final float[] diffuse; + final float[] position; + final GLGraphics glGraphics; + + public Light(GLGraphics glGraphics, boolean isDirectional) { + this.glGraphics = glGraphics; + ambient = new float[] {0.2f, 0.2f, 0.2f, 1.0f}; + diffuse = new float[] {1.0f, 1.0f, 1.0f, 1.0f}; + position = new float[] {0, 0, 0, isDirectional?0:1}; + } + + public boolean isDirectional() { + return position[3] == 0; + } + + public void setAmbient(float r, float g, float b) { + ambient[0] = r; + ambient[1] = g; + ambient[2] = b; + } + + public void setDiffuse(float r, float g, float b) { + diffuse[0] = r; + diffuse[1] = g; + diffuse[2] = b; + } + + public void setPosition(float x, float y, float z) { + position[0] = x; + position[1] = y; + position[2] = z; + } + + public void enable(int lightNum) { + GL10 gl = glGraphics.getGL(); + gl.glEnable(lightNum); + gl.glLightfv(lightNum, GL10.GL_AMBIENT, ambient, 0); + gl.glLightfv(lightNum, GL10.GL_DIFFUSE, diffuse, 0); + gl.glLightfv(lightNum, GL10.GL_POSITION, position, 0); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/NormalVertices3.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/NormalVertices3.java new file mode 100644 index 0000000..456add4 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/NormalVertices3.java @@ -0,0 +1,113 @@ +package com.badlogic.androidgames.framework.gl; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class NormalVertices3 { + final GLGraphics glGraphics; + final boolean hasColor; + final boolean hasTexCoords; + final int vertexSize; + final IntBuffer vertices; + final int[] tmpBuffer; + final ShortBuffer indices; + final boolean hasNormals; + + public NormalVertices3(GLGraphics glGraphics, int maxVertices, int maxIndices, + boolean hasColor, boolean hasTexCoords, boolean hasNormals) { + this.glGraphics = glGraphics; + this.hasColor = hasColor; + this.hasTexCoords = hasTexCoords; + this.hasNormals = hasNormals; + this.vertexSize = (3 + (hasColor ? 4 : 0) + (hasTexCoords ? 2 : 0) + (hasNormals ? 3 : 0)) * 4; + this.tmpBuffer = new int[maxVertices * vertexSize / 4]; + + ByteBuffer buffer = ByteBuffer.allocateDirect(maxVertices * vertexSize); + buffer.order(ByteOrder.nativeOrder()); + vertices = buffer.asIntBuffer(); + + if (maxIndices > 0) { + buffer = ByteBuffer.allocateDirect(maxIndices * Short.SIZE / 8); + buffer.order(ByteOrder.nativeOrder()); + indices = buffer.asShortBuffer(); + } else { + indices = null; + } + } + + public void setVertices(float[] vertices, int offset, int length) { + this.vertices.clear(); + int len = offset + length; + for (int i = offset, j = 0; i < len; i++, j++) + tmpBuffer[j] = Float.floatToRawIntBits(vertices[i]); + this.vertices.put(tmpBuffer, 0, length); + this.vertices.flip(); + } + + public void setIndices(short[] indices, int offset, int length) { + this.indices.clear(); + this.indices.put(indices, offset, length); + this.indices.flip(); + } + + public void bind() { + GL10 gl = glGraphics.getGL(); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + vertices.position(0); + gl.glVertexPointer(3, GL10.GL_FLOAT, vertexSize, vertices); + + if (hasColor) { + gl.glEnableClientState(GL10.GL_COLOR_ARRAY); + vertices.position(3); + gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices); + } + + if (hasTexCoords) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + vertices.position(hasColor ? 7 : 3); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + } + + if (hasNormals) { + gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); + int offset = 3; + if(hasColor) + offset += 4; + if(hasTexCoords) + offset += 2; + vertices.position(offset); + gl.glNormalPointer(GL10.GL_FLOAT, vertexSize, vertices); + } + } + + public void draw(int primitiveType, int offset, int numVertices) { + GL10 gl = glGraphics.getGL(); + + if (indices != null) { + indices.position(offset); + gl.glDrawElements(primitiveType, numVertices, + GL10.GL_UNSIGNED_SHORT, indices); + } else { + gl.glDrawArrays(primitiveType, offset, numVertices); + } + } + + public void unbind() { + GL10 gl = glGraphics.getGL(); + if (hasTexCoords) + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + if (hasColor) + gl.glDisableClientState(GL10.GL_COLOR_ARRAY); + + if (hasNormals) + gl.glDisableClientState(GL10.GL_NORMAL_ARRAY); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java new file mode 100644 index 0000000..da3cb4f --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java @@ -0,0 +1,143 @@ +package com.badlogic.androidgames.framework.gl; + +import java.util.ArrayList; +import java.util.List; + +import com.badlogic.androidgames.framework.GameObject; + +import android.util.FloatMath; + +public class SpatialHashGrid { + List[] dynamicCells; + List[] staticCells; + int cellsPerRow; + int cellsPerCol; + float cellSize; + int[] cellIds = new int[4]; + List foundObjects; + + @SuppressWarnings("unchecked") + public SpatialHashGrid(float worldWidth, float worldHeight, float cellSize) { + this.cellSize = cellSize; + this.cellsPerRow = (int)FloatMath.ceil(worldWidth/cellSize); + this.cellsPerCol = (int)FloatMath.ceil(worldHeight/cellSize); + int numCells = cellsPerRow * cellsPerCol; + dynamicCells = new List[numCells]; + staticCells = new List[numCells]; + for(int i = 0; i < numCells; i++) { + dynamicCells[i] = new ArrayList(10); + staticCells[i] = new ArrayList(10); + } + foundObjects = new ArrayList(10); + } + + public void insertStaticObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + staticCells[cellId].add(obj); + } + } + + public void insertDynamicObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + dynamicCells[cellId].add(obj); + } + } + + public void removeObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + dynamicCells[cellId].remove(obj); + staticCells[cellId].remove(obj); + } + } + + public void clearDynamicCells(GameObject obj) { + int len = dynamicCells.length; + for(int i = 0; i < len; i++) { + dynamicCells[i].clear(); + } + } + + public List getPotentialColliders(GameObject obj) { + foundObjects.clear(); + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + int len = dynamicCells[cellId].size(); + for(int j = 0; j < len; j++) { + GameObject collider = dynamicCells[cellId].get(j); + if(!foundObjects.contains(collider)) + foundObjects.add(collider); + } + + len = staticCells[cellId].size(); + for(int j = 0; j < len; j++) { + GameObject collider = staticCells[cellId].get(j); + if(!foundObjects.contains(collider)) + foundObjects.add(collider); + } + } + return foundObjects; + } + + public int[] getCellIds(GameObject obj) { + int x1 = (int)FloatMath.floor(obj.bounds.lowerLeft.x / cellSize); + int y1 = (int)FloatMath.floor(obj.bounds.lowerLeft.y / cellSize); + int x2 = (int)FloatMath.floor((obj.bounds.lowerLeft.x + obj.bounds.width) / cellSize); + int y2 = (int)FloatMath.floor((obj.bounds.lowerLeft.y + obj.bounds.height) / cellSize); + + if(x1 == x2 && y1 == y2) { + if(x1 >= 0 && x1 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[0] = x1 + y1 * cellsPerRow; + else + cellIds[0] = -1; + cellIds[1] = -1; + cellIds[2] = -1; + cellIds[3] = -1; + } + else if(x1 == x2) { + int i = 0; + if(x1 >= 0 && x1 < cellsPerRow) { + if(y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x1 + y1 * cellsPerRow; + if(y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x1 + y2 * cellsPerRow; + } + while(i <= 3) cellIds[i++] = -1; + } + else if(y1 == y2) { + int i = 0; + if(y1 >= 0 && y1 < cellsPerCol) { + if(x1 >= 0 && x1 < cellsPerRow) + cellIds[i++] = x1 + y1 * cellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow) + cellIds[i++] = x2 + y1 * cellsPerRow; + } + while(i <= 3) cellIds[i++] = -1; + } + else { + int i = 0; + int y1CellsPerRow = y1 * cellsPerRow; + int y2CellsPerRow = y2 * cellsPerRow; + if(x1 >= 0 && x1 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x1 + y1CellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x2 + y1CellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow && y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x2 + y2CellsPerRow; + if(x1 >= 0 && x1 < cellsPerRow && y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x1 + y2CellsPerRow; + while(i <= 3) cellIds[i++] = -1; + } + return cellIds; + } +} \ No newline at end of file diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java new file mode 100644 index 0000000..8b9eee2 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java @@ -0,0 +1,128 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import android.util.FloatMath; + +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.Vector2; + +public class SpriteBatcher { + final float[] verticesBuffer; + int bufferIndex; + final Vertices vertices; + int numSprites; + + public SpriteBatcher(GLGraphics glGraphics, int maxSprites) { + this.verticesBuffer = new float[maxSprites*4*4]; + this.vertices = new Vertices(glGraphics, maxSprites*4, maxSprites*6, false, true); + this.bufferIndex = 0; + this.numSprites = 0; + + short[] indices = new short[maxSprites*6]; + int len = indices.length; + short j = 0; + for (int i = 0; i < len; i += 6, j += 4) { + indices[i + 0] = (short)(j + 0); + indices[i + 1] = (short)(j + 1); + indices[i + 2] = (short)(j + 2); + indices[i + 3] = (short)(j + 2); + indices[i + 4] = (short)(j + 3); + indices[i + 5] = (short)(j + 0); + } + vertices.setIndices(indices, 0, indices.length); + } + + public void beginBatch(Texture texture) { + texture.bind(); + numSprites = 0; + bufferIndex = 0; + } + + public void endBatch() { + vertices.setVertices(verticesBuffer, 0, bufferIndex); + vertices.bind(); + vertices.draw(GL10.GL_TRIANGLES, 0, numSprites * 6); + vertices.unbind(); + } + + public void drawSprite(float x, float y, float width, float height, TextureRegion region) { + float halfWidth = width / 2; + float halfHeight = height / 2; + float x1 = x - halfWidth; + float y1 = y - halfHeight; + float x2 = x + halfWidth; + float y2 = y + halfHeight; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v1; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v1; + + numSprites++; + } + + public void drawSprite(float x, float y, float width, float height, float angle, TextureRegion region) { + float halfWidth = width / 2; + float halfHeight = height / 2; + + float rad = angle * Vector2.TO_RADIANS; + float cos = FloatMath.cos(rad); + float sin = FloatMath.sin(rad); + + float x1 = -halfWidth * cos - (-halfHeight) * sin; + float y1 = -halfWidth * sin + (-halfHeight) * cos; + float x2 = halfWidth * cos - (-halfHeight) * sin; + float y2 = halfWidth * sin + (-halfHeight) * cos; + float x3 = halfWidth * cos - halfHeight * sin; + float y3 = halfWidth * sin + halfHeight * cos; + float x4 = -halfWidth * cos - halfHeight * sin; + float y4 = -halfWidth * sin + halfHeight * cos; + + x1 += x; + y1 += y; + x2 += x; + y2 += y; + x3 += x; + y3 += y; + x4 += x; + y4 += y; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x3; + verticesBuffer[bufferIndex++] = y3; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v1; + + verticesBuffer[bufferIndex++] = x4; + verticesBuffer[bufferIndex++] = y4; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v1; + + numSprites++; + } +} \ No newline at end of file diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Texture.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Texture.java new file mode 100644 index 0000000..174e27b --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Texture.java @@ -0,0 +1,84 @@ +package com.badlogic.androidgames.framework.gl; + +import java.io.IOException; +import java.io.InputStream; + +import javax.microedition.khronos.opengles.GL10; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.opengl.GLUtils; + +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Texture { + GLGraphics glGraphics; + FileIO fileIO; + String fileName; + int textureId; + int minFilter; + int magFilter; + public int width; + public int height; + + public Texture(GLGame glGame, String fileName) { + this.glGraphics = glGame.getGLGraphics(); + this.fileIO = glGame.getFileIO(); + this.fileName = fileName; + load(); + } + + private void load() { + GL10 gl = glGraphics.getGL(); + int[] textureIds = new int[1]; + gl.glGenTextures(1, textureIds, 0); + textureId = textureIds[0]; + + InputStream in = null; + try { + in = fileIO.readAsset(fileName); + Bitmap bitmap = BitmapFactory.decodeStream(in); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); + setFilters(GL10.GL_NEAREST, GL10.GL_NEAREST); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + width = bitmap.getWidth(); + height = bitmap.getHeight(); + bitmap.recycle(); + } catch(IOException e) { + throw new RuntimeException("Couldn't load texture '" + fileName +"'", e); + } finally { + if(in != null) + try { in.close(); } catch (IOException e) { } + } + } + + public void reload() { + load(); + bind(); + setFilters(minFilter, magFilter); + glGraphics.getGL().glBindTexture(GL10.GL_TEXTURE_2D, 0); + } + + public void setFilters(int minFilter, int magFilter) { + this.minFilter = minFilter; + this.magFilter = magFilter; + GL10 gl = glGraphics.getGL(); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, minFilter); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, magFilter); + } + + public void bind() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + } + + public void dispose() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + int[] textureIds = { textureId }; + gl.glDeleteTextures(1, textureIds, 0); + } +} \ No newline at end of file diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/TextureRegion.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/TextureRegion.java new file mode 100644 index 0000000..cb91a07 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/TextureRegion.java @@ -0,0 +1,15 @@ +package com.badlogic.androidgames.framework.gl; + +public class TextureRegion { + public final float u1, v1; + public final float u2, v2; + public final Texture texture; + + public TextureRegion(Texture texture, float x, float y, float width, float height) { + this.u1 = x / texture.width; + this.v1 = y / texture.height; + this.u2 = this.u1 + width / texture.width; + this.v2 = this.v1 + height / texture.height; + this.texture = texture; + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Vertices.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Vertices.java new file mode 100644 index 0000000..d8bbee3 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Vertices.java @@ -0,0 +1,95 @@ +package com.badlogic.androidgames.framework.gl; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Vertices { + final GLGraphics glGraphics; + final boolean hasColor; + final boolean hasTexCoords; + final int vertexSize; + final IntBuffer vertices; + final int[] tmpBuffer; + final ShortBuffer indices; + + public Vertices(GLGraphics glGraphics, int maxVertices, int maxIndices, boolean hasColor, boolean hasTexCoords) { + this.glGraphics = glGraphics; + this.hasColor = hasColor; + this.hasTexCoords = hasTexCoords; + this.vertexSize = (2 + (hasColor?4:0) + (hasTexCoords?2:0)) * 4; + this.tmpBuffer = new int[maxVertices * vertexSize / 4]; + + ByteBuffer buffer = ByteBuffer.allocateDirect(maxVertices * vertexSize); + buffer.order(ByteOrder.nativeOrder()); + vertices = buffer.asIntBuffer(); + + if(maxIndices > 0) { + buffer = ByteBuffer.allocateDirect(maxIndices * Short.SIZE / 8); + buffer.order(ByteOrder.nativeOrder()); + indices = buffer.asShortBuffer(); + } else { + indices = null; + } + } + + public void setVertices(float[] vertices, int offset, int length) { + this.vertices.clear(); + int len = offset + length; + for(int i=offset, j=0; i < len; i++, j++) + tmpBuffer[j] = Float.floatToRawIntBits(vertices[i]); + this.vertices.put(tmpBuffer, 0, length); + this.vertices.flip(); + } + + public void setIndices(short[] indices, int offset, int length) { + this.indices.clear(); + this.indices.put(indices, offset, length); + this.indices.flip(); + } + +public void bind() { + GL10 gl = glGraphics.getGL(); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + vertices.position(0); + gl.glVertexPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + + if(hasColor) { + gl.glEnableClientState(GL10.GL_COLOR_ARRAY); + vertices.position(2); + gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices); + } + + if(hasTexCoords) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + vertices.position(hasColor?6:2); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + } +} + +public void draw(int primitiveType, int offset, int numVertices) { + GL10 gl = glGraphics.getGL(); + + if(indices!=null) { + indices.position(offset); + gl.glDrawElements(primitiveType, numVertices, GL10.GL_UNSIGNED_SHORT, indices); + } else { + gl.glDrawArrays(primitiveType, offset, numVertices); + } +} + +public void unbind() { + GL10 gl = glGraphics.getGL(); + if(hasTexCoords) + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + if(hasColor) + gl.glDisableClientState(GL10.GL_COLOR_ARRAY); +} +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Vertices3.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Vertices3.java new file mode 100644 index 0000000..7685c18 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/gl/Vertices3.java @@ -0,0 +1,97 @@ +package com.badlogic.androidgames.framework.gl; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Vertices3 { + final GLGraphics glGraphics; + final boolean hasColor; + final boolean hasTexCoords; + final int vertexSize; + final IntBuffer vertices; + final int[] tmpBuffer; + final ShortBuffer indices; + + public Vertices3(GLGraphics glGraphics, int maxVertices, int maxIndices, + boolean hasColor, boolean hasTexCoords) { + this.glGraphics = glGraphics; + this.hasColor = hasColor; + this.hasTexCoords = hasTexCoords; + this.vertexSize = (3 + (hasColor ? 4 : 0) + (hasTexCoords ? 2 : 0)) * 4; + this.tmpBuffer = new int[maxVertices * vertexSize / 4]; + + ByteBuffer buffer = ByteBuffer.allocateDirect(maxVertices * vertexSize); + buffer.order(ByteOrder.nativeOrder()); + vertices = buffer.asIntBuffer(); + + if (maxIndices > 0) { + buffer = ByteBuffer.allocateDirect(maxIndices * Short.SIZE / 8); + buffer.order(ByteOrder.nativeOrder()); + indices = buffer.asShortBuffer(); + } else { + indices = null; + } + } + + public void setVertices(float[] vertices, int offset, int length) { + this.vertices.clear(); + int len = offset + length; + for (int i = offset, j = 0; i < len; i++, j++) + tmpBuffer[j] = Float.floatToRawIntBits(vertices[i]); + this.vertices.put(tmpBuffer, 0, length); + this.vertices.flip(); + } + + public void setIndices(short[] indices, int offset, int length) { + this.indices.clear(); + this.indices.put(indices, offset, length); + this.indices.flip(); + } + + public void bind() { + GL10 gl = glGraphics.getGL(); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + vertices.position(0); + gl.glVertexPointer(3, GL10.GL_FLOAT, vertexSize, vertices); + + if (hasColor) { + gl.glEnableClientState(GL10.GL_COLOR_ARRAY); + vertices.position(3); + gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices); + } + + if (hasTexCoords) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + vertices.position(hasColor ? 7 : 3); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + } + } + + public void draw(int primitiveType, int offset, int numVertices) { + GL10 gl = glGraphics.getGL(); + + if (indices != null) { + indices.position(offset); + gl.glDrawElements(primitiveType, numVertices, + GL10.GL_UNSIGNED_SHORT, indices); + } else { + gl.glDrawArrays(primitiveType, offset, numVertices); + } + } + + public void unbind() { + GL10 gl = glGraphics.getGL(); + if (hasTexCoords) + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + if (hasColor) + gl.glDisableClientState(GL10.GL_COLOR_ARRAY); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java new file mode 100644 index 0000000..4820c5a --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java @@ -0,0 +1,48 @@ +package com.badlogic.androidgames.framework.impl; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + +public class AccelerometerHandler implements SensorEventListener { + float accelX; + float accelY; + float accelZ; + + public AccelerometerHandler(Context context) { + SensorManager manager = (SensorManager) context + .getSystemService(Context.SENSOR_SERVICE); + if (manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() != 0) { + Sensor accelerometer = manager.getSensorList( + Sensor.TYPE_ACCELEROMETER).get(0); + manager.registerListener(this, accelerometer, + SensorManager.SENSOR_DELAY_GAME); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // nothing to do here + } + + @Override + public void onSensorChanged(SensorEvent event) { + accelX = event.values[0]; + accelY = event.values[1]; + accelZ = event.values[2]; + } + + public float getAccelX() { + return accelX; + } + + public float getAccelY() { + return accelY; + } + + public float getAccelZ() { + return accelZ; + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java new file mode 100644 index 0000000..42f8c1f --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java @@ -0,0 +1,45 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; + +import android.app.Activity; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.media.AudioManager; +import android.media.SoundPool; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.Music; +import com.badlogic.androidgames.framework.Sound; + +public class AndroidAudio implements Audio { + AssetManager assets; + SoundPool soundPool; + + public AndroidAudio(Activity activity) { + activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); + this.assets = activity.getAssets(); + this.soundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0); + } + + @Override + public Music newMusic(String filename) { + try { + AssetFileDescriptor assetDescriptor = assets.openFd(filename); + return new AndroidMusic(assetDescriptor); + } catch (IOException e) { + throw new RuntimeException("Couldn't load music '" + filename + "'"); + } + } + + @Override + public Sound newSound(String filename) { + try { + AssetFileDescriptor assetDescriptor = assets.openFd(filename); + int soundId = soundPool.load(assetDescriptor, 0); + return new AndroidSound(soundPool, soundId); + } catch (IOException e) { + throw new RuntimeException("Couldn't load sound '" + filename + "'"); + } + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java new file mode 100644 index 0000000..6171d90 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java @@ -0,0 +1,60 @@ +package com.badlogic.androidgames.framework.impl; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class AndroidFastRenderView extends SurfaceView implements Runnable { + AndroidGame game; + Bitmap framebuffer; + Thread renderThread = null; + SurfaceHolder holder; + volatile boolean running = false; + + public AndroidFastRenderView(AndroidGame game, Bitmap framebuffer) { + super(game); + this.game = game; + this.framebuffer = framebuffer; + this.holder = getHolder(); + } + + public void resume() { + running = true; + renderThread = new Thread(this); + renderThread.start(); + } + + public void run() { + Rect dstRect = new Rect(); + long startTime = System.nanoTime(); + while(running) { + if(!holder.getSurface().isValid()) + continue; + + float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; + startTime = System.nanoTime(); + + game.getCurrentScreen().update(deltaTime); + game.getCurrentScreen().present(deltaTime); + + Canvas canvas = holder.lockCanvas(); + canvas.getClipBounds(dstRect); + canvas.drawBitmap(framebuffer, null, dstRect, null); + holder.unlockCanvasAndPost(canvas); + } + } + + public void pause() { + running = false; + while(true) { + try { + renderThread.join(); + break; + } catch (InterruptedException e) { + // retry + } + } + } +} \ No newline at end of file diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java new file mode 100644 index 0000000..b13b115 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java @@ -0,0 +1,39 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import android.content.res.AssetManager; +import android.os.Environment; + +import com.badlogic.androidgames.framework.FileIO; + +public class AndroidFileIO implements FileIO { + AssetManager assets; + String externalStoragePath; + + public AndroidFileIO(AssetManager assets) { + this.assets = assets; + this.externalStoragePath = Environment.getExternalStorageDirectory() + .getAbsolutePath() + File.separator; + } + + @Override + public InputStream readAsset(String fileName) throws IOException { + return assets.open(fileName); + } + + @Override + public InputStream readFile(String fileName) throws IOException { + return new FileInputStream(externalStoragePath + fileName); + } + + @Override + public OutputStream writeFile(String fileName) throws IOException { + return new FileOutputStream(externalStoragePath + fileName); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidGame.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidGame.java new file mode 100644 index 0000000..d1fd0b3 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidGame.java @@ -0,0 +1,107 @@ +package com.badlogic.androidgames.framework.impl; + +import android.app.Activity; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.os.Bundle; +import android.view.Window; +import android.view.WindowManager; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input; +import com.badlogic.androidgames.framework.Screen; + +public abstract class AndroidGame extends Activity implements Game { + AndroidFastRenderView renderView; + Graphics graphics; + Audio audio; + Input input; + FileIO fileIO; + Screen screen; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + + boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + int frameBufferWidth = isLandscape ? 480 : 320; + int frameBufferHeight = isLandscape ? 320 : 480; + Bitmap frameBuffer = Bitmap.createBitmap(frameBufferWidth, + frameBufferHeight, Config.RGB_565); + + float scaleX = (float) frameBufferWidth + / getWindowManager().getDefaultDisplay().getWidth(); + float scaleY = (float) frameBufferHeight + / getWindowManager().getDefaultDisplay().getHeight(); + + renderView = new AndroidFastRenderView(this, frameBuffer); + graphics = new AndroidGraphics(getAssets(), frameBuffer); + fileIO = new AndroidFileIO(getAssets()); + audio = new AndroidAudio(this); + input = new AndroidInput(this, renderView, scaleX, scaleY); + screen = getStartScreen(); + setContentView(renderView); + } + + @Override + public void onResume() { + super.onResume(); + screen.resume(); + renderView.resume(); + } + + @Override + public void onPause() { + super.onPause(); + renderView.pause(); + screen.pause(); + + if (isFinishing()) { + screen.dispose(); + } + } + + @Override + public Input getInput() { + return input; + } + + @Override + public FileIO getFileIO() { + return fileIO; + } + + @Override + public Graphics getGraphics() { + return graphics; + } + + @Override + public Audio getAudio() { + return audio; + } + + @Override + public void setScreen(Screen screen) { + if (screen == null) + throw new IllegalArgumentException("Screen must not be null"); + + this.screen.pause(); + this.screen.dispose(); + screen.resume(); + screen.update(0); + this.screen = screen; + } + + public Screen getCurrentScreen() { + return screen; + } +} \ No newline at end of file diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java new file mode 100644 index 0000000..ade52b5 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java @@ -0,0 +1,133 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; +import java.io.InputStream; + +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.BitmapFactory.Options; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Rect; + +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Pixmap; + +public class AndroidGraphics implements Graphics { + AssetManager assets; + Bitmap frameBuffer; + Canvas canvas; + Paint paint; + Rect srcRect = new Rect(); + Rect dstRect = new Rect(); + + public AndroidGraphics(AssetManager assets, Bitmap frameBuffer) { + this.assets = assets; + this.frameBuffer = frameBuffer; + this.canvas = new Canvas(frameBuffer); + this.paint = new Paint(); + } + + @Override + public Pixmap newPixmap(String fileName, PixmapFormat format) { + Config config = null; + if (format == PixmapFormat.RGB565) + config = Config.RGB_565; + else if (format == PixmapFormat.ARGB4444) + config = Config.ARGB_4444; + else + config = Config.ARGB_8888; + + Options options = new Options(); + options.inPreferredConfig = config; + + InputStream in = null; + Bitmap bitmap = null; + try { + in = assets.open(fileName); + bitmap = BitmapFactory.decodeStream(in, null, options); + if (bitmap == null) + throw new RuntimeException("Couldn't load bitmap from asset '" + + fileName + "'"); + } catch (IOException e) { + throw new RuntimeException("Couldn't load bitmap from asset '" + + fileName + "'"); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + + if (bitmap.getConfig() == Config.RGB_565) + format = PixmapFormat.RGB565; + else if (bitmap.getConfig() == Config.ARGB_4444) + format = PixmapFormat.ARGB4444; + else + format = PixmapFormat.ARGB8888; + + return new AndroidPixmap(bitmap, format); + } + + @Override + public void clear(int color) { + canvas.drawRGB((color & 0xff0000) >> 16, (color & 0xff00) >> 8, + (color & 0xff)); + } + + @Override + public void drawPixel(int x, int y, int color) { + paint.setColor(color); + canvas.drawPoint(x, y, paint); + } + + @Override + public void drawLine(int x, int y, int x2, int y2, int color) { + paint.setColor(color); + canvas.drawLine(x, y, x2, y2, paint); + } + + @Override + public void drawRect(int x, int y, int width, int height, int color) { + paint.setColor(color); + paint.setStyle(Style.FILL); + canvas.drawRect(x, y, x + width - 1, y + height - 1, paint); + } + + @Override + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight) { + srcRect.left = srcX; + srcRect.top = srcY; + srcRect.right = srcX + srcWidth - 1; + srcRect.bottom = srcY + srcHeight - 1; + + dstRect.left = x; + dstRect.top = y; + dstRect.right = x + srcWidth - 1; + dstRect.bottom = y + srcHeight - 1; + + canvas.drawBitmap(((AndroidPixmap) pixmap).bitmap, srcRect, dstRect, + null); + } + + @Override + public void drawPixmap(Pixmap pixmap, int x, int y) { + canvas.drawBitmap(((AndroidPixmap)pixmap).bitmap, x, y, null); + } + + @Override + public int getWidth() { + return frameBuffer.getWidth(); + } + + @Override + public int getHeight() { + return frameBuffer.getHeight(); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidInput.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidInput.java new file mode 100644 index 0000000..11f5863 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidInput.java @@ -0,0 +1,69 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.List; + +import android.content.Context; +import android.os.Build.VERSION; +import android.view.View; + +import com.badlogic.androidgames.framework.Input; + +public class AndroidInput implements Input { + AccelerometerHandler accelHandler; + KeyboardHandler keyHandler; + TouchHandler touchHandler; + + public AndroidInput(Context context, View view, float scaleX, float scaleY) { + accelHandler = new AccelerometerHandler(context); + keyHandler = new KeyboardHandler(view); + if(Integer.parseInt(VERSION.SDK) < 5) + touchHandler = new SingleTouchHandler(view, scaleX, scaleY); + else + touchHandler = new MultiTouchHandler(view, scaleX, scaleY); + } + + @Override + public boolean isKeyPressed(int keyCode) { + return keyHandler.isKeyPressed(keyCode); + } + + @Override + public boolean isTouchDown(int pointer) { + return touchHandler.isTouchDown(pointer); + } + + @Override + public int getTouchX(int pointer) { + return touchHandler.getTouchX(pointer); + } + + @Override + public int getTouchY(int pointer) { + return touchHandler.getTouchY(pointer); + } + + @Override + public float getAccelX() { + return accelHandler.getAccelX(); + } + + @Override + public float getAccelY() { + return accelHandler.getAccelY(); + } + + @Override + public float getAccelZ() { + return accelHandler.getAccelZ(); + } + + @Override + public List getTouchEvents() { + return touchHandler.getTouchEvents(); + } + + @Override + public List getKeyEvents() { + return keyHandler.getKeyEvents(); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java new file mode 100644 index 0000000..9e9fd1c --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java @@ -0,0 +1,99 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; + +import android.content.res.AssetFileDescriptor; +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnCompletionListener; + +import com.badlogic.androidgames.framework.Music; + +public class AndroidMusic implements Music, OnCompletionListener { + MediaPlayer mediaPlayer; + boolean isPrepared = false; + + public AndroidMusic(AssetFileDescriptor assetDescriptor) { + mediaPlayer = new MediaPlayer(); + try { + mediaPlayer.setDataSource(assetDescriptor.getFileDescriptor(), + assetDescriptor.getStartOffset(), + assetDescriptor.getLength()); + mediaPlayer.prepare(); + isPrepared = true; + mediaPlayer.setOnCompletionListener(this); + } catch (Exception e) { + throw new RuntimeException("Couldn't load music"); + } + } + + @Override + public void dispose() { + if (mediaPlayer.isPlaying()) + mediaPlayer.stop(); + mediaPlayer.release(); + } + + @Override + public boolean isLooping() { + return mediaPlayer.isLooping(); + } + + @Override + public boolean isPlaying() { + return mediaPlayer.isPlaying(); + } + + @Override + public boolean isStopped() { + return !isPrepared; + } + + @Override + public void pause() { + if (mediaPlayer.isPlaying()) + mediaPlayer.pause(); + } + + @Override + public void play() { + if (mediaPlayer.isPlaying()) + return; + + try { + synchronized (this) { + if (!isPrepared) + mediaPlayer.prepare(); + mediaPlayer.start(); + } + } catch (IllegalStateException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void setLooping(boolean isLooping) { + mediaPlayer.setLooping(isLooping); + } + + @Override + public void setVolume(float volume) { + mediaPlayer.setVolume(volume, volume); + } + + @Override + public void stop() { + mediaPlayer.stop(); + synchronized (this) { + isPrepared = false; + } + } + + @Override + public void onCompletion(MediaPlayer arg0) { + synchronized (this) { + isPrepared = false; + } + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java new file mode 100644 index 0000000..9de5021 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework.impl; + +import android.graphics.Bitmap; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; +import com.badlogic.androidgames.framework.Pixmap; + +public class AndroidPixmap implements Pixmap { + Bitmap bitmap; + PixmapFormat format; + + public AndroidPixmap(Bitmap bitmap, PixmapFormat format) { + this.bitmap = bitmap; + this.format = format; + } + + @Override + public int getWidth() { + return bitmap.getWidth(); + } + + @Override + public int getHeight() { + return bitmap.getHeight(); + } + + @Override + public PixmapFormat getFormat() { + return format; + } + + @Override + public void dispose() { + bitmap.recycle(); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidSound.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidSound.java new file mode 100644 index 0000000..4a8fcd4 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/AndroidSound.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework.impl; + +import android.media.SoundPool; + +import com.badlogic.androidgames.framework.Sound; + +public class AndroidSound implements Sound { + int soundId; + SoundPool soundPool; + + public AndroidSound(SoundPool soundPool,int soundId) { + this.soundId = soundId; + this.soundPool = soundPool; + } + + @Override + public void play(float volume) { + soundPool.play(soundId, volume, volume, 0, 0, 1); + } + + @Override + public void dispose() { + soundPool.unload(soundId); + } + +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/GLGame.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/GLGame.java new file mode 100644 index 0000000..5822786 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/GLGame.java @@ -0,0 +1,178 @@ +package com.badlogic.androidgames.framework.impl; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import android.app.Activity; +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.opengl.GLSurfaceView.Renderer; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.view.Window; +import android.view.WindowManager; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input; +import com.badlogic.androidgames.framework.Screen; + +public abstract class GLGame extends Activity implements Game, Renderer { + enum GLGameState { + Initialized, + Running, + Paused, + Finished, + Idle + } + + GLSurfaceView glView; + GLGraphics glGraphics; + Audio audio; + Input input; + FileIO fileIO; + Screen screen; + GLGameState state = GLGameState.Initialized; + Object stateChanged = new Object(); + long startTime = System.nanoTime(); + WakeLock wakeLock; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + glView = new GLSurfaceView(this); + glView.setRenderer(this); + setContentView(glView); + + glGraphics = new GLGraphics(glView); + fileIO = new AndroidFileIO(getAssets()); + audio = new AndroidAudio(this); + input = new AndroidInput(this, glView, 1, 1); + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GLGame"); + } + + public void onResume() { + super.onResume(); + glView.onResume(); + wakeLock.acquire(); + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + glGraphics.setGL(gl); + + synchronized(stateChanged) { + if(state == GLGameState.Initialized) + screen = getStartScreen(); + state = GLGameState.Running; + screen.resume(); + startTime = System.nanoTime(); + } + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + } + + @Override + public void onDrawFrame(GL10 gl) { + GLGameState state = null; + + synchronized(stateChanged) { + state = this.state; + } + + if(state == GLGameState.Running) { + float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; + startTime = System.nanoTime(); + + screen.update(deltaTime); + screen.present(deltaTime); + } + + if(state == GLGameState.Paused) { + screen.pause(); + synchronized(stateChanged) { + this.state = GLGameState.Idle; + stateChanged.notifyAll(); + } + } + + if(state == GLGameState.Finished) { + screen.pause(); + screen.dispose(); + synchronized(stateChanged) { + this.state = GLGameState.Idle; + stateChanged.notifyAll(); + } + } + } + + @Override + public void onPause() { + synchronized(stateChanged) { + if(isFinishing()) + state = GLGameState.Finished; + else + state = GLGameState.Paused; + while(true) { + try { + stateChanged.wait(); + break; + } catch(InterruptedException e) { + } + } + } + wakeLock.release(); + glView.onPause(); + super.onPause(); + } + + public GLGraphics getGLGraphics() { + return glGraphics; + } + + @Override + public Input getInput() { + return input; + } + + @Override + public FileIO getFileIO() { + return fileIO; + } + + @Override + public Graphics getGraphics() { + throw new IllegalStateException("We are using OpenGL!"); + } + + @Override + public Audio getAudio() { + return audio; + } + + @Override + public void setScreen(Screen screen) { + if (screen == null) + throw new IllegalArgumentException("Screen must not be null"); + + this.screen.pause(); + this.screen.dispose(); + screen.resume(); + screen.update(0); + this.screen = screen; + } + + @Override + public Screen getCurrentScreen() { + return screen; + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/GLGraphics.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/GLGraphics.java new file mode 100644 index 0000000..2eb1b43 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/GLGraphics.java @@ -0,0 +1,30 @@ +package com.badlogic.androidgames.framework.impl; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLSurfaceView; + +public class GLGraphics { + GLSurfaceView glView; + GL10 gl; + + GLGraphics(GLSurfaceView glView) { + this.glView = glView; + } + + public GL10 getGL() { + return gl; + } + + void setGL(GL10 gl) { + this.gl = gl; + } + + public int getWidth() { + return glView.getWidth(); + } + + public int getHeight() { + return glView.getHeight(); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/GLScreen.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/GLScreen.java new file mode 100644 index 0000000..466de8c --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/GLScreen.java @@ -0,0 +1,16 @@ +package com.badlogic.androidgames.framework.impl; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; + +public abstract class GLScreen extends Screen { + protected final GLGraphics glGraphics; + protected final GLGame glGame; + + public GLScreen(Game game) { + super(game); + glGame = (GLGame)game; + glGraphics = ((GLGame)game).getGLGraphics(); + } + +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java new file mode 100644 index 0000000..f70d3e9 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java @@ -0,0 +1,73 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.View; +import android.view.View.OnKeyListener; + +import com.badlogic.androidgames.framework.Input.KeyEvent; +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class KeyboardHandler implements OnKeyListener { + boolean[] pressedKeys = new boolean[128]; + Pool keyEventPool; + List keyEventsBuffer = new ArrayList(); + List keyEvents = new ArrayList(); + + public KeyboardHandler(View view) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public KeyEvent createObject() { + return new KeyEvent(); + } + }; + keyEventPool = new Pool(factory, 100); + view.setOnKeyListener(this); + view.setFocusableInTouchMode(true); + view.requestFocus(); + } + + @Override + public boolean onKey(View v, int keyCode, android.view.KeyEvent event) { + if (event.getAction() == android.view.KeyEvent.ACTION_MULTIPLE) + return false; + + synchronized (this) { + KeyEvent keyEvent = keyEventPool.newObject(); + keyEvent.keyCode = keyCode; + keyEvent.keyChar = (char) event.getUnicodeChar(); + if (event.getAction() == android.view.KeyEvent.ACTION_DOWN) { + keyEvent.type = KeyEvent.KEY_DOWN; + if(keyCode >= 0 && keyCode < 128) + pressedKeys[keyCode] = true; + } + if (event.getAction() == android.view.KeyEvent.ACTION_UP) { + keyEvent.type = KeyEvent.KEY_UP; + if(keyCode >= 0 && keyCode < 128) + pressedKeys[keyCode] = false; + } + keyEventsBuffer.add(keyEvent); + } + return false; + } + + public boolean isKeyPressed(int keyCode) { + if (keyCode < 0 || keyCode > 127) + return false; + return pressedKeys[keyCode]; + } + + public List getKeyEvents() { + synchronized (this) { + int len = keyEvents.size(); + for (int i = 0; i < len; i++) + keyEventPool.free(keyEvents.get(i)); + keyEvents.clear(); + keyEvents.addAll(keyEventsBuffer); + keyEventsBuffer.clear(); + return keyEvents; + } + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java new file mode 100644 index 0000000..4668c57 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java @@ -0,0 +1,137 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.MotionEvent; +import android.view.View; + +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class MultiTouchHandler implements TouchHandler { + boolean[] isTouched = new boolean[20]; + int[] touchX = new int[20]; + int[] touchY = new int[20]; + Pool touchEventPool; + List touchEvents = new ArrayList(); + List touchEventsBuffer = new ArrayList(); + float scaleX; + float scaleY; + + public MultiTouchHandler(View view, float scaleX, float scaleY) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }; + touchEventPool = new Pool(factory, 100); + view.setOnTouchListener(this); + + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + synchronized (this) { + int action = event.getAction() & MotionEvent.ACTION_MASK; + int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; + int pointerId = event.getPointerId(pointerIndex); + TouchEvent touchEvent; + + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_DOWN; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + isTouched[pointerId] = true; + touchEventsBuffer.add(touchEvent); + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_CANCEL: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_UP; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + isTouched[pointerId] = false; + touchEventsBuffer.add(touchEvent); + break; + + case MotionEvent.ACTION_MOVE: + int pointerCount = event.getPointerCount(); + for (int i = 0; i < pointerCount; i++) { + pointerIndex = i; + pointerId = event.getPointerId(pointerIndex); + + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_DRAGGED; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + touchEventsBuffer.add(touchEvent); + } + break; + } + + return true; + } + } + + @Override + public boolean isTouchDown(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return false; + else + return isTouched[pointer]; + } + } + + @Override + public int getTouchX(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return 0; + else + return touchX[pointer]; + } + } + + @Override + public int getTouchY(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return 0; + else + return touchY[pointer]; + } + } + + @Override + public List getTouchEvents() { + synchronized (this) { + int len = touchEvents.size(); + for (int i = 0; i < len; i++) + touchEventPool.free(touchEvents.get(i)); + touchEvents.clear(); + touchEvents.addAll(touchEventsBuffer); + touchEventsBuffer.clear(); + return touchEvents; + } + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java new file mode 100644 index 0000000..7ff1205 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java @@ -0,0 +1,101 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.MotionEvent; +import android.view.View; + +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class SingleTouchHandler implements TouchHandler { + boolean isTouched; + int touchX; + int touchY; + Pool touchEventPool; + List touchEvents = new ArrayList(); + List touchEventsBuffer = new ArrayList(); + float scaleX; + float scaleY; + + public SingleTouchHandler(View view, float scaleX, float scaleY) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }; + touchEventPool = new Pool(factory, 100); + view.setOnTouchListener(this); + + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + synchronized(this) { + TouchEvent touchEvent = touchEventPool.newObject(); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touchEvent.type = TouchEvent.TOUCH_DOWN; + isTouched = true; + break; + case MotionEvent.ACTION_MOVE: + touchEvent.type = TouchEvent.TOUCH_DRAGGED; + isTouched = true; + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + touchEvent.type = TouchEvent.TOUCH_UP; + isTouched = false; + break; + } + + touchEvent.x = touchX = (int)(event.getX() * scaleX); + touchEvent.y = touchY = (int)(event.getY() * scaleY); + touchEventsBuffer.add(touchEvent); + + return true; + } + } + + @Override + public boolean isTouchDown(int pointer) { + synchronized(this) { + if(pointer == 0) + return isTouched; + else + return false; + } + } + + @Override + public int getTouchX(int pointer) { + synchronized(this) { + return touchX; + } + } + + @Override + public int getTouchY(int pointer) { + synchronized(this) { + return touchY; + } + } + + @Override + public List getTouchEvents() { + synchronized(this) { + int len = touchEvents.size(); + for( int i = 0; i < len; i++ ) + touchEventPool.free(touchEvents.get(i)); + touchEvents.clear(); + touchEvents.addAll(touchEventsBuffer); + touchEventsBuffer.clear(); + return touchEvents; + } + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/TouchHandler.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/TouchHandler.java new file mode 100644 index 0000000..cc7e754 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/impl/TouchHandler.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.List; + +import android.view.View.OnTouchListener; + +import com.badlogic.androidgames.framework.Input.TouchEvent; + +public interface TouchHandler extends OnTouchListener { + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public List getTouchEvents(); +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/math/Circle.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/math/Circle.java new file mode 100644 index 0000000..2728f84 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/math/Circle.java @@ -0,0 +1,11 @@ +package com.badlogic.androidgames.framework.math; + +public class Circle { + public final Vector2 center = new Vector2(); + public float radius; + + public Circle(float x, float y, float radius) { + this.center.set(x,y); + this.radius = radius; + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/math/OverlapTester.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/math/OverlapTester.java new file mode 100644 index 0000000..5c6ace4 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/math/OverlapTester.java @@ -0,0 +1,58 @@ +package com.badlogic.androidgames.framework.math; + +public class OverlapTester { + public static boolean overlapCircles(Circle c1, Circle c2) { + float distance = c1.center.distSquared(c2.center); + float radiusSum = c1.radius + c2.radius; + return distance <= radiusSum * radiusSum; + } + + public static boolean overlapRectangles(Rectangle r1, Rectangle r2) { + if(r1.lowerLeft.x < r2.lowerLeft.x + r2.width && + r1.lowerLeft.x + r1.width > r2.lowerLeft.x && + r1.lowerLeft.y < r2.lowerLeft.y + r2.height && + r1.lowerLeft.y + r1.height > r2.lowerLeft.y) + return true; + else + return false; + } + + public static boolean overlapCircleRectangle(Circle c, Rectangle r) { + float closestX = c.center.x; + float closestY = c.center.y; + + if(c.center.x < r.lowerLeft.x) { + closestX = r.lowerLeft.x; + } + else if(c.center.x > r.lowerLeft.x + r.width) { + closestX = r.lowerLeft.x + r.width; + } + + if(c.center.y < r.lowerLeft.y) { + closestY = r.lowerLeft.y; + } + else if(c.center.y > r.lowerLeft.y + r.height) { + closestY = r.lowerLeft.y + r.height; + } + + return c.center.distSquared(closestX, closestY) < c.radius * c.radius; + } + + public static boolean pointInCircle(Circle c, Vector2 p) { + return c.center.distSquared(p) < c.radius * c.radius; + } + + public static boolean pointInCircle(Circle c, float x, float y) { + return c.center.distSquared(x, y) < c.radius * c.radius; + } + + public static boolean pointInRectangle(Rectangle r, Vector2 p) { + return r.lowerLeft.x <= p.x && r.lowerLeft.x + r.width >= p.x && + r.lowerLeft.y <= p.y && r.lowerLeft.y + r.height >= p.y; + } + + public static boolean pointInRectangle(Rectangle r, float x, float y) { + return r.lowerLeft.x <= x && r.lowerLeft.x + r.width >= x && + r.lowerLeft.y <= y && r.lowerLeft.y + r.height >= y; + } +} \ No newline at end of file diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/math/Rectangle.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/math/Rectangle.java new file mode 100644 index 0000000..9c2511f --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/math/Rectangle.java @@ -0,0 +1,12 @@ +package com.badlogic.androidgames.framework.math; + +public class Rectangle { + public final Vector2 lowerLeft; + public float width, height; + + public Rectangle(float x, float y, float width, float height) { + this.lowerLeft = new Vector2(x,y); + this.width = width; + this.height = height; + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/framework/math/Vector2.java b/ch10-gl-3d/src/com/badlogic/androidgames/framework/math/Vector2.java new file mode 100644 index 0000000..56d79c2 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/framework/math/Vector2.java @@ -0,0 +1,126 @@ +package com.badlogic.androidgames.framework.math; + +import android.util.FloatMath; + +public class Vector2 { + public static float TO_RADIANS = (1 / 180.0f) * (float)Math.PI; + public static float TO_DEGREES = (1 / (float)Math.PI) * 180; + public float x, y; + + public Vector2() { + } + + public Vector2(float x, float y) { + this.x = x; + this.y = y; + } + + public Vector2(Vector2 other) { + this.x = other.x; + this.y = other.y; + } + + public Vector2 cpy() { + return new Vector2(x, y); + } + + public Vector2 set(float x, float y) { + this.x = x; + this.y = y; + return this; + } + + public Vector2 set(Vector2 other) { + this.x = other.x; + this.y = other.y; + return this; + } + + public Vector2 add(float x, float y) { + this.x += x; + this.y += y; + return this; + } + + public Vector2 add(Vector2 other) { + this.x += other.x; + this.y += other.y; + return this; + } + + public Vector2 sub(float x, float y) { + this.x -= x; + this.y -= y; + return this; + } + + public Vector2 sub(Vector2 other) { + this.x -= other.x; + this.y -= other.y; + return this; + } + + public Vector2 mul(float scalar) { + this.x *= scalar; + this.y *= scalar; + return this; + } + + public float len() { + return FloatMath.sqrt(x*x + y*y); + } + + public Vector2 nor() { + float len = len(); + if(len!=0) { + this.x /= len; + this.y /= len; + } + return this; + } + + public float angle() { + float angle = (float)Math.atan2(y, x) * TO_DEGREES; + if(angle < 0) + angle += 360; + return angle; + } + + public Vector2 rotate(float angle) { + float rad = angle * TO_RADIANS; + float cos = FloatMath.cos(rad); + float sin = FloatMath.sin(rad); + + float newX = this.x * cos - this.y * sin; + float newY = this.x * sin + this.y * cos; + + this.x = newX; + this.y = newY; + + return this; + } + + public float dist(Vector2 other) { + float distX = this.x - other.x; + float distY = this.y - other.y; + return FloatMath.sqrt(distX*distX + distY*distY); + } + + public float dist(float x, float y) { + float distX = this.x - x; + float distY = this.y - y; + return FloatMath.sqrt(distX*distX + distY*distY); + } + + public float distSquared(Vector2 other) { + float distX = this.x - other.x; + float distY = this.y - other.y; + return distX*distX + distY*distY; + } + + public float distSquared(float x, float y) { + float distX = this.x - x; + float distY = this.y - y; + return distX*distX + distY*distY; + } +} \ No newline at end of file diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/CubeTest.java b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/CubeTest.java new file mode 100644 index 0000000..45b3796 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/CubeTest.java @@ -0,0 +1,121 @@ +package com.badlogic.androidgames.gl3d; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLU; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.Vertices3; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLScreen; + +public class CubeTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new CubeScreen(this); + } + + class CubeScreen extends GLScreen { + Vertices3 cube; + Texture texture; + float angle = 0; + + public CubeScreen(Game game) { + super(game); + cube = createCube(); + texture = new Texture(glGame, "crate.png"); + } + + private Vertices3 createCube() { + float[] vertices = { -0.5f, -0.5f, 0.5f, 0, 1, + 0.5f, -0.5f, 0.5f, 1, 1, + 0.5f, 0.5f, 0.5f, 1, 0, + -0.5f, 0.5f, 0.5f, 0, 0, + + 0.5f, -0.5f, 0.5f, 0, 1, + 0.5f, -0.5f, -0.5f, 1, 1, + 0.5f, 0.5f, -0.5f, 1, 0, + 0.5f, 0.5f, 0.5f, 0, 0, + + 0.5f, -0.5f, -0.5f, 0, 1, + -0.5f, -0.5f, -0.5f, 1, 1, + -0.5f, 0.5f, -0.5f, 1, 0, + 0.5f, 0.5f, -0.5f, 0, 0, + + -0.5f, -0.5f, -0.5f, 0, 1, + -0.5f, -0.5f, 0.5f, 1, 1, + -0.5f, 0.5f, 0.5f, 1, 0, + -0.5f, 0.5f, -0.5f, 0, 0, + + -0.5f, 0.5f, 0.5f, 0, 1, + 0.5f, 0.5f, 0.5f, 1, 1, + 0.5f, 0.5f, -0.5f, 1, 0, + -0.5f, 0.5f, -0.5f, 0, 0, + + -0.5f, -0.5f, 0.5f, 0, 1, + 0.5f, -0.5f, 0.5f, 1, 1, + 0.5f, -0.5f, -0.5f, 1, 0, + -0.5f, -0.5f, -0.5f, 0, 0 + }; + + short[] indices = { 0, 1, 3, 1, 2, 3, + 4, 5, 7, 5, 6, 7, + 8, 9, 11, 9, 10, 11, + 12, 13, 15, 13, 14, 15, + 16, 17, 19, 17, 18, 19, + 20, 21, 23, 21, 22, 23, + }; + + Vertices3 cube = new Vertices3(glGraphics, 24, 36, false, true); + cube.setVertices(vertices, 0, vertices.length); + cube.setIndices(indices, 0, indices.length); + return cube; + } + + @Override + public void resume() { + texture.reload(); + } + + @Override + public void update(float deltaTime) { + angle += 45 * deltaTime; + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + GLU.gluPerspective(gl, 67, + glGraphics.getWidth() / (float) glGraphics.getHeight(), + 0.1f, 10.0f); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + + gl.glEnable(GL10.GL_DEPTH_TEST); + gl.glEnable(GL10.GL_TEXTURE_2D); + texture.bind(); + cube.bind(); + gl.glTranslatef(0,0,-3); + gl.glRotatef(angle, 0, 1, 0); + cube.draw(GL10.GL_TRIANGLES, 0, 36); + cube.unbind(); + gl.glDisable(GL10.GL_TEXTURE_2D); + gl.glDisable(GL10.GL_DEPTH_TEST); + } + + @Override + public void pause() { + } + + @Override + public void dispose() { + } + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/GL3DBasicsStarter.java b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/GL3DBasicsStarter.java new file mode 100644 index 0000000..64bdb3c --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/GL3DBasicsStarter.java @@ -0,0 +1,34 @@ +package com.badlogic.androidgames.gl3d; + +import android.app.ListActivity; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +public class GL3DBasicsStarter extends ListActivity { + String tests[] = { "Vertices3Test", "PerspectiveTest", "ZBufferTest", "ZBlendingTest", + "CubeTest", "HierarchyTest"}; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setListAdapter(new ArrayAdapter(this, + android.R.layout.simple_list_item_1, tests)); + } + + @Override + protected void onListItemClick(ListView list, View view, int position, + long id) { + super.onListItemClick(list, view, position, id); + String testName = tests[position]; + try { + Class clazz = Class.forName("com.badlogic.androidgames.gl3d." + + testName); + Intent intent = new Intent(this, clazz); + startActivity(intent); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/HierarchicalObject.java b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/HierarchicalObject.java new file mode 100644 index 0000000..85be44c --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/HierarchicalObject.java @@ -0,0 +1,50 @@ +package com.badlogic.androidgames.gl3d; + +import java.util.ArrayList; +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.gl.Vertices3; + +public class HierarchicalObject { + public float x, y, z; + public float scale = 1; + public float rotationY, rotationParent; + public boolean hasParent; + public final List children = new ArrayList(); + public final Vertices3 mesh; + + public HierarchicalObject(Vertices3 mesh, boolean hasParent) { + this.mesh = mesh; + this.hasParent = hasParent; + } + + public void update(float deltaTime) { + rotationY += 45 * deltaTime; + rotationParent += 20 * deltaTime; + int len = children.size(); + for (int i = 0; i < len; i++) { + children.get(i).update(deltaTime); + } + } + + public void render(GL10 gl) { + gl.glPushMatrix(); + if (hasParent) + gl.glRotatef(rotationParent, 0, 1, 0); + gl.glTranslatef(x, y, z); + + gl.glPushMatrix(); + gl.glRotatef(rotationY, 0, 1, 0); + gl.glScalef(scale, scale, scale); + mesh.draw(GL10.GL_TRIANGLES, 0, 36); + gl.glPopMatrix(); + + int len = children.size(); + for (int i = 0; i < len; i++) { + children.get(i).render(gl); + } + gl.glPopMatrix(); + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/HierarchyTest.java b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/HierarchyTest.java new file mode 100644 index 0000000..9a4df3e --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/HierarchyTest.java @@ -0,0 +1,134 @@ +package com.badlogic.androidgames.gl3d; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLU; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.Vertices3; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLScreen; + +public class HierarchyTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new HierarchyScreen(this); + } + + class HierarchyScreen extends GLScreen { + Vertices3 cube; + Texture texture; + HierarchicalObject sun; + + public HierarchyScreen(Game game) { + super(game); + cube = createCube(); + texture = new Texture(glGame, "crate.png"); + + sun = new HierarchicalObject(cube, false); + sun.z = -5; + + HierarchicalObject planet = new HierarchicalObject(cube, true); + planet.x = 3; + planet.scale = 0.2f; + sun.children.add(planet); + + HierarchicalObject moon = new HierarchicalObject(cube, true); + moon.x = 1; + moon.scale = 0.1f; + planet.children.add(moon); + } + + private Vertices3 createCube() { + float[] vertices = { -0.5f, -0.5f, 0.5f, 0, 1, + 0.5f, -0.5f, 0.5f, 1, 1, + 0.5f, 0.5f, 0.5f, 1, 0, + -0.5f, 0.5f, 0.5f, 0, 0, + + 0.5f, -0.5f, 0.5f, 0, 1, + 0.5f, -0.5f, -0.5f, 1, 1, + 0.5f, 0.5f, -0.5f, 1, 0, + 0.5f, 0.5f, 0.5f, 0, 0, + + 0.5f, -0.5f, -0.5f, 0, 1, + -0.5f, -0.5f, -0.5f, 1, 1, + -0.5f, 0.5f, -0.5f, 1, 0, + 0.5f, 0.5f, -0.5f, 0, 0, + + -0.5f, -0.5f, -0.5f, 0, 1, + -0.5f, -0.5f, 0.5f, 1, 1, + -0.5f, 0.5f, 0.5f, 1, 0, + -0.5f, 0.5f, -0.5f, 0, 0, + + -0.5f, 0.5f, 0.5f, 0, 1, + 0.5f, 0.5f, 0.5f, 1, 1, + 0.5f, 0.5f, -0.5f, 1, 0, + -0.5f, 0.5f, -0.5f, 0, 0, + + -0.5f, -0.5f, 0.5f, 0, 1, + 0.5f, -0.5f, 0.5f, 1, 1, + 0.5f, -0.5f, -0.5f, 1, 0, + -0.5f, -0.5f, -0.5f, 0, 0 + }; + + short[] indices = { 0, 1, 3, 1, 2, 3, + 4, 5, 7, 5, 6, 7, + 8, 9, 11, 9, 10, 11, + 12, 13, 15, 13, 14, 15, + 16, 17, 19, 17, 18, 19, + 20, 21, 23, 21, 22, 23, + }; + + Vertices3 cube = new Vertices3(glGraphics, 24, 36, false, true); + cube.setVertices(vertices, 0, vertices.length); + cube.setIndices(indices, 0, indices.length); + return cube; + } + + @Override + public void resume() { + texture.reload(); + } + + @Override + public void update(float deltaTime) { + sun.update(deltaTime); + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + GLU.gluPerspective(gl, 67, glGraphics.getWidth() + / (float) glGraphics.getHeight(), 0.1f, 10.0f); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + GLU.gluLookAt(gl, 3, 3, 0, 0, 0, -5, 0, 1, 0); + + gl.glEnable(GL10.GL_DEPTH_TEST); + gl.glEnable(GL10.GL_TEXTURE_2D); + texture.bind(); + cube.bind(); + + sun.render(gl); + + cube.unbind(); + gl.glDisable(GL10.GL_TEXTURE_2D); + gl.glDisable(GL10.GL_DEPTH_TEST); + } + + @Override + public void pause() { + } + + @Override + public void dispose() { + } + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/PerspectiveTest.java b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/PerspectiveTest.java new file mode 100644 index 0000000..7ef4d48 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/PerspectiveTest.java @@ -0,0 +1,68 @@ +package com.badlogic.androidgames.gl3d; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLU; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.NormalVertices3; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLScreen; + +public class PerspectiveTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new PerspectiveScreen(this); + } + + class PerspectiveScreen extends GLScreen { + NormalVertices3 vertices; + + public PerspectiveScreen(Game game) { + super(game); + + vertices = new NormalVertices3(glGraphics, 6, 0, true, false, false); + vertices.setVertices(new float[] { -0.5f, -0.5f, -3, 1, 0, 0, 1, + 0.5f, -0.5f, -3, 1, 0, 0, 1, + 0.0f, 0.5f, -3, 1, 0, 0, 1, + 0.0f, -0.5f, -5, 0, 1, 0, 1, + 1.0f, -0.5f, -5, 0, 1, 0, 1, + 0.5f, 0.5f, -5, 0, 1, 0, 1}, 0, 7 * 6); + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + GLU.gluPerspective(gl, 67, + glGraphics.getWidth() / (float)glGraphics.getHeight(), + 0.1f, 10f); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + vertices.bind(); + vertices.draw(GL10.GL_TRIANGLES, 0, 6); + vertices.unbind(); + } + + @Override + public void update(float deltaTime) { + } + + @Override + public void pause() { + } + + @Override + public void resume() { + } + + @Override + public void dispose() { + } + } +} \ No newline at end of file diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/Vertices3Test.java b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/Vertices3Test.java new file mode 100644 index 0000000..d3b9773 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/Vertices3Test.java @@ -0,0 +1,65 @@ +package com.badlogic.androidgames.gl3d; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.Vertices3; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLScreen; + +public class Vertices3Test extends GLGame { + + @Override + public Screen getStartScreen() { + return new Vertices3Screen(this); + } + + class Vertices3Screen extends GLScreen { + Vertices3 vertices; + + public Vertices3Screen(Game game) { + super(game); + + vertices = new Vertices3(glGraphics, 6, 0, true, false); + vertices.setVertices(new float[] { -0.5f, -0.5f, -3, 1, 0, 0, 1, + 0.5f, -0.5f, -3, 1, 0, 0, 1, + 0.0f, 0.5f, -3, 1, 0, 0, 1, + + 0.0f, -0.5f, -5, 0, 1, 0, 1, + 1.0f, -0.5f, -5, 0, 1, 0, 1, + 0.5f, 0.5f, -5, 0, 1, 0, 1}, 0, 7 * 6); + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(-1, 1, -1, 1, 10, -10); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + vertices.bind(); + vertices.draw(GL10.GL_TRIANGLES, 0, 6); + vertices.unbind(); + } + + @Override + public void update(float deltaTime) { + } + + @Override + public void pause() { + } + + @Override + public void resume() { + } + + @Override + public void dispose() { + } + } +} \ No newline at end of file diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/ZBlendingTest.java b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/ZBlendingTest.java new file mode 100644 index 0000000..71c80b6 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/ZBlendingTest.java @@ -0,0 +1,77 @@ +package com.badlogic.androidgames.gl3d; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLU; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.Vertices3; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLScreen; + +public class ZBlendingTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new ZBlendingScreen(this); + } + + class ZBlendingScreen extends GLScreen { + Vertices3 vertices; + + public ZBlendingScreen(Game game) { + super(game); + + vertices = new Vertices3(glGraphics, 6, 0, true, false); + vertices.setVertices(new float[] { -0.5f, -0.5f, -3, 1, 0, 0, 0.5f, + 0.5f, -0.5f, -3, 1, 0, 0, 0.5f, + 0.0f, 0.5f, -3, 1, 0, 0, 0.5f, + 0.0f, -0.5f, -5, 0, 1, 0, 1, + 1.0f, -0.5f, -5, 0, 1, 0, 1, + 0.5f, 0.5f, -5, 0, 1, 0, 1}, 0, 7 * 6); + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + GLU.gluPerspective(gl, 67, + glGraphics.getWidth() / (float)glGraphics.getHeight(), + 0.1f, 10f); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + + gl.glEnable(GL10.GL_DEPTH_TEST); + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + vertices.bind(); + vertices.draw(GL10.GL_TRIANGLES, 0, 3); + vertices.draw(GL10.GL_TRIANGLES, 3, 3); + vertices.unbind(); + + gl.glDisable(GL10.GL_BLEND); + gl.glDisable(GL10.GL_DEPTH_TEST); + } + + @Override + public void update(float deltaTime) { + } + + @Override + public void pause() { + } + + @Override + public void resume() { + } + + @Override + public void dispose() { + } + } +} diff --git a/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/ZBufferTest.java b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/ZBufferTest.java new file mode 100644 index 0000000..829e8c9 --- /dev/null +++ b/ch10-gl-3d/src/com/badlogic/androidgames/gl3d/ZBufferTest.java @@ -0,0 +1,73 @@ +package com.badlogic.androidgames.gl3d; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLU; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.Vertices3; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLScreen; + +public class ZBufferTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new ZBufferScreen(this); + } + + class ZBufferScreen extends GLScreen { + Vertices3 vertices; + + public ZBufferScreen(Game game) { + super(game); + + vertices = new Vertices3(glGraphics, 6, 0, true, false); + vertices.setVertices(new float[] { -0.5f, -0.5f, -3, 1, 0, 0, 1, + 0.5f, -0.5f, -3, 1, 0, 0, 1, + 0.0f, 0.5f, -3, 1, 0, 0, 1, + 0.0f, -0.5f, -5, 0, 1, 0, 1, + 1.0f, -0.5f, -5, 0, 1, 0, 1, + 0.5f, 0.5f, -5, 0, 1, 0, 1}, 0, 7 * 6); + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + GLU.gluPerspective(gl, 67, + glGraphics.getWidth() / (float)glGraphics.getHeight(), + 0.1f, 10f); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + + gl.glEnable(GL10.GL_DEPTH_TEST); + + vertices.bind(); + vertices.draw(GL10.GL_TRIANGLES, 0, 6); + vertices.unbind(); + + gl.glDisable(GL10.GL_DEPTH_TEST); + } + + @Override + public void update(float deltaTime) { + } + + @Override + public void pause() { + } + + @Override + public void resume() { + } + + @Override + public void dispose() { + } + } +} diff --git a/ch11-gl-advanced/.classpath b/ch11-gl-advanced/.classpath new file mode 100644 index 0000000..609aa00 --- /dev/null +++ b/ch11-gl-advanced/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ch11-gl-advanced/.project b/ch11-gl-advanced/.project new file mode 100644 index 0000000..243e35f --- /dev/null +++ b/ch11-gl-advanced/.project @@ -0,0 +1,33 @@ + + + ch11-gl-advanced + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/ch11-gl-advanced/AndroidManifest.xml b/ch11-gl-advanced/AndroidManifest.xml new file mode 100644 index 0000000..d2067bb --- /dev/null +++ b/ch11-gl-advanced/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch11-gl-advanced/assets/button.png b/ch11-gl-advanced/assets/button.png new file mode 100644 index 0000000..71ac0e4 Binary files /dev/null and b/ch11-gl-advanced/assets/button.png differ diff --git a/ch11-gl-advanced/assets/crate.png b/ch11-gl-advanced/assets/crate.png new file mode 100644 index 0000000..cc0788b Binary files /dev/null and b/ch11-gl-advanced/assets/crate.png differ diff --git a/ch11-gl-advanced/assets/cube.obj b/ch11-gl-advanced/assets/cube.obj new file mode 100644 index 0000000..4f6b4f5 --- /dev/null +++ b/ch11-gl-advanced/assets/cube.obj @@ -0,0 +1,79 @@ +# Exported from Wings 3D 1.2 +mtllib cube.mtl +o cube1 +#8 vertices, 12 faces +v -0.50000000 -0.50000000 0.50000000 +v -0.50000000 0.50000000 0.50000000 +v 0.50000000 0.50000000 0.50000000 +v 0.50000000 -0.50000000 0.50000000 +v -0.50000000 -0.50000000 -0.50000000 +v -0.50000000 0.50000000 -0.50000000 +v 0.50000000 0.50000000 -0.50000000 +v 0.50000000 -0.50000000 -0.50000000 +vt 0.0000000e+0 1.1102230e-16 +vt 0.0000000e+0 2.2204460e-16 +vt 0.0000000e+0 1.00000000 +vt 2.2204460e-16 2.2204460e-16 +vt 2.2204460e-16 4.4408921e-16 +vt 2.2204460e-16 1.00000000 +vt 4.4408921e-16 0.0000000e+0 +vt 4.4408921e-16 2.2204460e-16 +vt 4.4408921e-16 1.00000000 +vt 1.00000000 0.0000000e+0 +vt 1.00000000 1.1102230e-16 +vt 1.00000000 2.2204460e-16 +vt 1.00000000 4.4408921e-16 +vt 1.00000000 1.00000000 +vn 0.0000000e+0 -1.00000000 0.0000000e+0 +vn 0.0000000e+0 0.0000000e+0 1.00000000 +vn -1.00000000 0.0000000e+0 0.0000000e+0 +vn 0.0000000e+0 1.00000000 0.0000000e+0 +vn 0.0000000e+0 0.0000000e+0 1.00000000 +vn 0.0000000e+0 1.00000000 0.0000000e+0 +vn 0.0000000e+0 0.0000000e+0 1.00000000 +vn -1.00000000 0.0000000e+0 0.0000000e+0 +vn -1.00000000 0.0000000e+0 0.0000000e+0 +vn 1.00000000 0.0000000e+0 0.0000000e+0 +vn 0.0000000e+0 1.00000000 0.0000000e+0 +vn 0.0000000e+0 0.0000000e+0 1.00000000 +vn 0.0000000e+0 -1.00000000 0.0000000e+0 +vn 1.00000000 0.0000000e+0 0.0000000e+0 +vn 0.0000000e+0 0.0000000e+0 1.00000000 +vn 0.0000000e+0 -1.00000000 0.0000000e+0 +vn 0.0000000e+0 0.0000000e+0 1.00000000 +vn 1.00000000 0.0000000e+0 0.0000000e+0 +vn 0.0000000e+0 -1.00000000 0.0000000e+0 +vn 0.0000000e+0 0.0000000e+0 -1.00000000 +vn 0.0000000e+0 -1.00000000 0.0000000e+0 +vn 0.0000000e+0 0.0000000e+0 -1.00000000 +vn -1.00000000 0.0000000e+0 0.0000000e+0 +vn -1.00000000 0.0000000e+0 0.0000000e+0 +vn 0.0000000e+0 1.00000000 0.0000000e+0 +vn 0.0000000e+0 0.0000000e+0 -1.00000000 +vn -1.00000000 0.0000000e+0 0.0000000e+0 +vn 0.0000000e+0 1.00000000 0.0000000e+0 +vn 1.00000000 0.0000000e+0 0.0000000e+0 +vn 0.0000000e+0 0.0000000e+0 -1.00000000 +vn 0.0000000e+0 1.00000000 0.0000000e+0 +vn 0.0000000e+0 0.0000000e+0 -1.00000000 +vn 1.00000000 0.0000000e+0 0.0000000e+0 +vn 0.0000000e+0 0.0000000e+0 -1.00000000 +vn 0.0000000e+0 -1.00000000 0.0000000e+0 +vn 1.00000000 0.0000000e+0 0.0000000e+0 +g cube1_cube1_auv +usemtl cube1_auv +s 1 +f 1/8/1 5/9/19 4/12/13 +f 3/5/11 7/6/31 2/12/6 +f 4/11/18 8/2/36 7/3/33 +f 6/9/26 7/14/32 5/7/22 +s 2 +f 2/14/7 4/4/17 3/6/12 +f 2/3/8 5/11/23 1/1/3 +f 2/12/4 7/6/28 6/14/25 +f 5/9/21 8/14/35 4/12/16 +s 3 +f 1/13/2 4/4/15 2/14/5 +f 2/3/9 6/14/27 5/11/24 +f 4/11/14 7/3/29 3/14/10 +f 7/14/30 8/10/34 5/7/20 diff --git a/ch11-gl-advanced/default.properties b/ch11-gl-advanced/default.properties new file mode 100644 index 0000000..3ac2523 --- /dev/null +++ b/ch11-gl-advanced/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-12 diff --git a/ch11-gl-advanced/proguard.cfg b/ch11-gl-advanced/proguard.cfg new file mode 100644 index 0000000..8ad7d33 --- /dev/null +++ b/ch11-gl-advanced/proguard.cfg @@ -0,0 +1,34 @@ +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontpreverify +-verbose +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* + +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class com.android.vending.licensing.ILicensingService + +-keepclasseswithmembernames class * { + native ; +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/ch11-gl-advanced/res/drawable-hdpi/icon.png b/ch11-gl-advanced/res/drawable-hdpi/icon.png new file mode 100644 index 0000000..8074c4c Binary files /dev/null and b/ch11-gl-advanced/res/drawable-hdpi/icon.png differ diff --git a/ch11-gl-advanced/res/drawable-ldpi/icon.png b/ch11-gl-advanced/res/drawable-ldpi/icon.png new file mode 100644 index 0000000..1095584 Binary files /dev/null and b/ch11-gl-advanced/res/drawable-ldpi/icon.png differ diff --git a/ch11-gl-advanced/res/drawable-mdpi/icon.png b/ch11-gl-advanced/res/drawable-mdpi/icon.png new file mode 100644 index 0000000..a07c69f Binary files /dev/null and b/ch11-gl-advanced/res/drawable-mdpi/icon.png differ diff --git a/ch11-gl-advanced/res/layout/main.xml b/ch11-gl-advanced/res/layout/main.xml new file mode 100644 index 0000000..3a5f117 --- /dev/null +++ b/ch11-gl-advanced/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/ch11-gl-advanced/res/values/strings.xml b/ch11-gl-advanced/res/values/strings.xml new file mode 100644 index 0000000..51c3075 --- /dev/null +++ b/ch11-gl-advanced/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Hello World, GLAdvancedStarter! + Advanced OpenGL ES + diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Audio.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Audio.java new file mode 100644 index 0000000..d30d722 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Audio.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Audio { + public Music newMusic(String filename); + + public Sound newSound(String filename); +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Color.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Color.java new file mode 100644 index 0000000..57d68e5 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Color.java @@ -0,0 +1,10 @@ +package com.badlogic.androidgames.framework; + +public class Color { + public static int convert (int r, int g, int b, int a) { + return ((a & 0xff) << 24) | + ((r & 0xff) << 16) | + ((g & 0xff) << 8) | + ((b & 0xff)); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/DynamicGameObject.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/DynamicGameObject.java new file mode 100644 index 0000000..00afabb --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/DynamicGameObject.java @@ -0,0 +1,14 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.math.Vector2; + +public class DynamicGameObject extends GameObject { + public final Vector2 velocity; + public final Vector2 accel; + + public DynamicGameObject(float x, float y, float width, float height) { + super(x, y, width, height); + velocity = new Vector2(); + accel = new Vector2(); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/DynamicGameObject3D.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/DynamicGameObject3D.java new file mode 100644 index 0000000..75801ff --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/DynamicGameObject3D.java @@ -0,0 +1,14 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.math.Vector3; + +public class DynamicGameObject3D extends GameObject { + public final Vector3 velocity; + public final Vector3 accel; + + public DynamicGameObject3D(float x, float y, float z, float radius) { + super(x, y, z, radius); + velocity = new Vector3(); + accel = new Vector3(); + } +} \ No newline at end of file diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/FileIO.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/FileIO.java new file mode 100644 index 0000000..d555b7d --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/FileIO.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.framework; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public interface FileIO { + public InputStream readAsset(String fileName) throws IOException; + + public InputStream readFile(String fileName) throws IOException; + + public OutputStream writeFile(String fileName) throws IOException; +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Game.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Game.java new file mode 100644 index 0000000..fb5c5aa --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Game.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework; + +public interface Game { + public Input getInput(); + + public FileIO getFileIO(); + + public Graphics getGraphics(); + + public Audio getAudio(); + + public void setScreen(Screen screen); + + public Screen getCurrentScreen(); + + public Screen getStartScreen(); +} \ No newline at end of file diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/GameObject.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/GameObject.java new file mode 100644 index 0000000..54aaa09 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/GameObject.java @@ -0,0 +1,14 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class GameObject { + public final Vector2 position; + public final Rectangle bounds; + + public GameObject(float x, float y, float width, float height) { + this.position = new Vector2(x,y); + this.bounds = new Rectangle(x-width/2, y-height/2, width, height); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/GameObject3D.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/GameObject3D.java new file mode 100644 index 0000000..1cc5f93 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/GameObject3D.java @@ -0,0 +1,14 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.math.Sphere; +import com.badlogic.androidgames.framework.math.Vector3; + +public class GameObject3D { + public final Vector3 position; + public final Sphere bounds; + + public GameObject3D(float x, float y, float z, float radius) { + this.position = new Vector3(x,y,z); + this.bounds = new Sphere(x, y, z, radius); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Graphics.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Graphics.java new file mode 100644 index 0000000..c7b8f45 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Graphics.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework; + +public interface Graphics { + public static enum PixmapFormat { + ARGB8888, ARGB4444, RGB565 + } + + public Pixmap newPixmap(String fileName, PixmapFormat format); + + public void clear(int color); + + public void drawPixel(int x, int y, int color); + + public void drawLine(int x, int y, int x2, int y2, int color); + + public void drawRect(int x, int y, int width, int height, int color); + + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight); + + public void drawPixmap(Pixmap pixmap, int x, int y); + + public int getWidth(); + + public int getHeight(); +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Input.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Input.java new file mode 100644 index 0000000..0893d62 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Input.java @@ -0,0 +1,70 @@ +package com.badlogic.androidgames.framework; + +import java.util.List; + +public interface Input { + public static class KeyEvent { + public static final int KEY_DOWN = 0; + public static final int KEY_UP = 1; + + public int type; + public int keyCode; + public char keyChar; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == KEY_DOWN) + builder.append("key down, "); + else + builder.append("key up, "); + builder.append(keyCode); + builder.append(","); + builder.append(keyChar); + return builder.toString(); + } + } + + public static class TouchEvent { + public static final int TOUCH_DOWN = 0; + public static final int TOUCH_UP = 1; + public static final int TOUCH_DRAGGED = 2; + + public int type; + public int x, y; + public int pointer; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == TOUCH_DOWN) + builder.append("touch down, "); + else if (type == TOUCH_DRAGGED) + builder.append("touch dragged, "); + else + builder.append("touch up, "); + builder.append(pointer); + builder.append(","); + builder.append(x); + builder.append(","); + builder.append(y); + return builder.toString(); + } + } + + public boolean isKeyPressed(int keyCode); + + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public float getAccelX(); + + public float getAccelY(); + + public float getAccelZ(); + + public List getKeyEvents(); + + public List getTouchEvents(); +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Music.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Music.java new file mode 100644 index 0000000..1b95a1e --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Music.java @@ -0,0 +1,21 @@ +package com.badlogic.androidgames.framework; + +public interface Music { + public void play(); + + public void stop(); + + public void pause(); + + public void setLooping(boolean looping); + + public void setVolume(float volume); + + public boolean isPlaying(); + + public boolean isStopped(); + + public boolean isLooping(); + + public void dispose(); +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Pixmap.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Pixmap.java new file mode 100644 index 0000000..4b1eec9 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Pixmap.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; + +public interface Pixmap { + public int getWidth(); + + public int getHeight(); + + public PixmapFormat getFormat(); + + public void dispose(); +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Pool.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Pool.java new file mode 100644 index 0000000..d0b7cec --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Pool.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework; + +import java.util.ArrayList; +import java.util.List; + +public class Pool { + public interface PoolObjectFactory { + public T createObject(); + } + + private final List freeObjects; + private final PoolObjectFactory factory; + private final int maxSize; + + public Pool(PoolObjectFactory factory, int maxSize) { + this.factory = factory; + this.maxSize = maxSize; + this.freeObjects = new ArrayList(maxSize); + } + + public T newObject() { + T object = null; + + if (freeObjects.size() == 0) + object = factory.createObject(); + else + object = freeObjects.remove(freeObjects.size() - 1); + + return object; + } + + public void free(T object) { + if (freeObjects.size() < maxSize) + freeObjects.add(object); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Screen.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Screen.java new file mode 100644 index 0000000..40a98b9 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Screen.java @@ -0,0 +1,19 @@ +package com.badlogic.androidgames.framework; + +public abstract class Screen { + protected final Game game; + + public Screen(Game game) { + this.game = game; + } + + public abstract void update(float deltaTime); + + public abstract void present(float deltaTime); + + public abstract void pause(); + + public abstract void resume(); + + public abstract void dispose(); +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Sound.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Sound.java new file mode 100644 index 0000000..3f49835 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/Sound.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Sound { + public void play(float volume); + + public void dispose(); +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/TestScreen.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/TestScreen.java new file mode 100644 index 0000000..5a0bbcf --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/TestScreen.java @@ -0,0 +1,112 @@ +package com.badlogic.androidgames.framework; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.List; + +import android.graphics.Color; +import android.util.Log; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; +import com.badlogic.androidgames.framework.Input.KeyEvent; +import com.badlogic.androidgames.framework.Input.TouchEvent; + +class TestScreen extends Screen { + long startTime = System.nanoTime(); + int frames; + Pixmap bob; + Pixmap bobAlpha; + Sound sound; + Music music; + + public TestScreen(Game game) { + super(game); + bob = game.getGraphics().newPixmap("bobrgb888.png", PixmapFormat.RGB565); + bobAlpha = game.getGraphics().newPixmap("bobargb8888.png", PixmapFormat.ARGB4444); + music = game.getAudio().newMusic("music.ogg"); + music.setLooping(true); + music.setVolume(0.5f); + music.play(); + sound = game.getAudio().newSound("music.ogg"); + + try { + BufferedReader in = new BufferedReader(new InputStreamReader(game.getFileIO().readAsset("test.txt"))); + String text = in.readLine(); + in.close(); + + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(game.getFileIO().writeFile("test.txt"))); + out.write("This is a freaking test"); + out.close(); + + in = new BufferedReader(new InputStreamReader(game.getFileIO().readFile("test.txt"))); + String text2 = in.readLine(); + in.close(); + + Log.d("MrNom", text + ", " + text2 ); + } catch(Exception ex) { + ex.printStackTrace(); + } + } + + @Override + public void update(float deltaTime) { + } + + @Override + public void present(float deltaTime) { + Graphics g = game.getGraphics(); + Input inp = game.getInput(); + g.clear(Color.RED); + g.drawLine(0,0,320, 480, Color.BLUE); + g.drawRect(20,20,100,100, Color.GREEN); + g.drawPixmap(bob, 100, 100); + g.drawPixmap(bobAlpha, 100, 200); + g.drawPixmap(bob, 200, 200, 0, 0, 64, 64); + for(int i=0; i < 2; i++) { + if(inp.isTouchDown(i)) { + g.drawPixmap(bob, inp.getTouchX(i), inp.getTouchY(i), 0, 0, 64, 64); + } + } + + g.drawPixmap(bob, (int)(inp.getAccelX() * 10) + 160 - 16, (int)(inp.getAccelY() * 10) + 240 - 16, 0, 0, 32, 32 ); + + List keyEvents = inp.getKeyEvents(); + int len = keyEvents.size(); + for(int i = 0; i < len; i++) { + Log.d("MrNom", keyEvents.get(i).toString()); + } + + List touchEvents = inp.getTouchEvents(); + len = touchEvents.size(); + for(int i = 0; i < len; i++) { + Log.d("MrNom", touchEvents.get(i).toString()); + if(touchEvents.get(i).type == TouchEvent.TOUCH_UP) + sound.play(1); + } + + frames++; + if(System.nanoTime() - startTime > 1000000000l) { + Log.d("MrNom", "fps: " + frames + ", delta: " + deltaTime); + frames = 0; + startTime = System.nanoTime(); + } + } + + @Override + public void pause() { + Log.d("MrNom", "pause"); + } + + @Override + public void resume() { + Log.d("MrNom", "resume"); + } + + @Override + public void dispose() { + Log.d("MrNom", "dispose"); + music.dispose(); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/AmbientLight.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/AmbientLight.java new file mode 100644 index 0000000..4e2b41a --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/AmbientLight.java @@ -0,0 +1,18 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +public class AmbientLight { + float[] color = {0.2f, 0.2f, 0.2f, 1}; + + public void setColor(float r, float g, float b, float a) { + color[0] = r; + color[1] = g; + color[2] = b; + color[3] = a; + } + + public void enable(GL10 gl) { + gl.glLightModelfv(GL10.GL_LIGHT_MODEL_AMBIENT, color, 0); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Animation.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Animation.java new file mode 100644 index 0000000..d826e39 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Animation.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework.gl; + + +public class Animation { + public static final int ANIMATION_LOOPING = 0; + public static final int ANIMATION_NONLOOPING = 1; + + final TextureRegion[] keyFrames; + final float frameDuration; + + public Animation(float frameDuration, TextureRegion ... keyFrames) { + this.frameDuration = frameDuration; + this.keyFrames = keyFrames; + } + + public TextureRegion getKeyFrame(float stateTime, int mode) { + int frameNumber = (int)(stateTime / frameDuration); + + if(mode == ANIMATION_NONLOOPING) { + frameNumber = Math.min(keyFrames.length-1, frameNumber); + } else { + frameNumber = frameNumber % keyFrames.length; + } + return keyFrames[frameNumber]; + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Camera2D.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Camera2D.java new file mode 100644 index 0000000..2a58199 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Camera2D.java @@ -0,0 +1,42 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.Vector2; + +public class Camera2D { + public final Vector2 position; + public float zoom; + public final float frustumWidth; + public final float frustumHeight; + final GLGraphics glGraphics; + + public Camera2D(GLGraphics glGraphics, float frustumWidth, float frustumHeight) { + this.glGraphics = glGraphics; + this.frustumWidth = frustumWidth; + this.frustumHeight = frustumHeight; + this.position = new Vector2(frustumWidth / 2, frustumHeight / 2); + this.zoom = 1.0f; + } + + public void setViewportAndMatrices() { + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(position.x - frustumWidth * zoom / 2, + position.x + frustumWidth * zoom/ 2, + position.y - frustumHeight * zoom / 2, + position.y + frustumHeight * zoom/ 2, + 1, -1); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + } + + public void touchToWorld(Vector2 touch) { + touch.x = (touch.x / (float) glGraphics.getWidth()) * frustumWidth * zoom; + touch.y = (1 - touch.y / (float) glGraphics.getHeight()) * frustumHeight * zoom; + touch.add(position).sub(frustumWidth * zoom / 2, frustumHeight * zoom / 2); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/DirectionalLight.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/DirectionalLight.java new file mode 100644 index 0000000..d87e71c --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/DirectionalLight.java @@ -0,0 +1,51 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +public class DirectionalLight { + float[] ambient = { 0.2f, 0.2f, 0.2f, 1.0f }; + float[] diffuse = { 1.0f, 1.0f, 1.0f, 1.0f }; + float[] specular = { 0.0f, 0.0f, 0.0f, 0.0f }; + float[] direction = { 0, 0, -1, 0 }; + int lastLightId = 0; + + public void setAmbient(float r, float g, float b, float a) { + ambient[0] = r; + ambient[1] = g; + ambient[2] = b; + ambient[3] = a; + } + + public void setDiffuse(float r, float g, float b, float a) { + diffuse[0] = r; + diffuse[1] = g; + diffuse[2] = b; + diffuse[3] = a; + } + + public void setSpecular(float r, float g, float b, float a) { + specular[0] = r; + specular[1] = g; + specular[2] = b; + specular[3] = a; + } + + public void setDirection(float x, float y, float z) { + direction[0] = -x; + direction[1] = -y; + direction[2] = -z; + } + + public void enable(GL10 gl, int lightId) { + gl.glEnable(lightId); + gl.glLightfv(lightId, GL10.GL_AMBIENT, ambient, 0); + gl.glLightfv(lightId, GL10.GL_DIFFUSE, diffuse, 0); + gl.glLightfv(lightId, GL10.GL_SPECULAR, specular, 0); + gl.glLightfv(lightId, GL10.GL_POSITION, direction, 0); + lastLightId = lightId; + } + + public void disable(GL10 gl) { + gl.glDisable(lastLightId); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/EulerCamera.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/EulerCamera.java new file mode 100644 index 0000000..7dc41b4 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/EulerCamera.java @@ -0,0 +1,80 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLU; +import android.opengl.Matrix; + +import com.badlogic.androidgames.framework.math.Vector3; + +public class EulerCamera { + final Vector3 position = new Vector3(); + float yaw; + float pitch; + float fieldOfView; + float aspectRatio; + float near; + float far; + + public EulerCamera(float fieldOfView, float aspectRatio, float near, float far) { + this.fieldOfView = fieldOfView; + this.aspectRatio = aspectRatio; + this.near = near; + this.far = far; + } + + public Vector3 getPosition() { + return position; + } + + public float getYaw() { + return yaw; + } + + public float getPitch() { + return pitch; + } + + public void setAngles(float yaw, float pitch) { + if (pitch < -90) + pitch = -90; + if (pitch > 90) + pitch = 90; + this.yaw = yaw; + this.pitch = pitch; + } + + public void rotate(float yawInc, float pitchInc) { + this.yaw += yawInc; + this.pitch += pitchInc; + if (pitch < -90) + pitch = -90; + if (pitch > 90) + pitch = 90; + } + + public void setMatrices(GL10 gl) { + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + GLU.gluPerspective(gl, fieldOfView, aspectRatio, near, far); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + gl.glRotatef(-pitch, 1, 0, 0); + gl.glRotatef(-yaw, 0, 1, 0); + gl.glTranslatef(-position.x, -position.y, -position.z); + } + + final float[] matrix = new float[16]; + final float[] inVec = { 0, 0, -1, 1 }; + final float[] outVec = new float[4]; + final Vector3 direction = new Vector3(); + + public Vector3 getDirection() { + Matrix.setIdentityM(matrix, 0); + Matrix.rotateM(matrix, 0, yaw, 0, 1, 0); + Matrix.rotateM(matrix, 0, pitch, 1, 0, 0); + Matrix.multiplyMV(outVec, 0, matrix, 0, inVec, 0); + direction.set(outVec[0], outVec[1], outVec[2]); + return direction; + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/FPSCounter.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/FPSCounter.java new file mode 100644 index 0000000..cd816ca --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/FPSCounter.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework.gl; + +import android.util.Log; + +public class FPSCounter { + long startTime = System.nanoTime(); + int frames = 0; + + public void logFrame() { + frames++; + if(System.nanoTime() - startTime >= 1000000000) { + Log.d("FPSCounter", "fps: " + frames); + frames = 0; + startTime = System.nanoTime(); + } + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Font.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Font.java new file mode 100644 index 0000000..ace1452 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Font.java @@ -0,0 +1,39 @@ +package com.badlogic.androidgames.framework.gl; + +public class Font { + public final Texture texture; + public final int glyphWidth; + public final int glyphHeight; + public final TextureRegion[] glyphs = new TextureRegion[96]; + + public Font(Texture texture, + int offsetX, int offsetY, + int glyphsPerRow, int glyphWidth, int glyphHeight) { + this.texture = texture; + this.glyphWidth = glyphWidth; + this.glyphHeight = glyphHeight; + int x = offsetX; + int y = offsetY; + for(int i = 0; i < 96; i++) { + glyphs[i] = new TextureRegion(texture, x, y, glyphWidth, glyphHeight); + x += glyphWidth; + if(x == offsetX + glyphsPerRow * glyphWidth) { + x = offsetX; + y += glyphHeight; + } + } + } + + public void drawText(SpriteBatcher batcher, String text, float x, float y) { + int len = text.length(); + for(int i = 0; i < len; i++) { + int c = text.charAt(i) - ' '; + if(c < 0 || c > glyphs.length - 1) + continue; + + TextureRegion glyph = glyphs[c]; + batcher.drawSprite(x, y, glyphWidth, glyphHeight, glyph); + x += glyphWidth; + } + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Light.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Light.java new file mode 100644 index 0000000..5c0d095 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Light.java @@ -0,0 +1,49 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Light { + final float[] ambient; + final float[] diffuse; + final float[] position; + final GLGraphics glGraphics; + + public Light(GLGraphics glGraphics, boolean isDirectional) { + this.glGraphics = glGraphics; + ambient = new float[] {0.2f, 0.2f, 0.2f, 1.0f}; + diffuse = new float[] {1.0f, 1.0f, 1.0f, 1.0f}; + position = new float[] {0, 0, 0, isDirectional?0:1}; + } + + public boolean isDirectional() { + return position[3] == 0; + } + + public void setAmbient(float r, float g, float b) { + ambient[0] = r; + ambient[1] = g; + ambient[2] = b; + } + + public void setDiffuse(float r, float g, float b) { + diffuse[0] = r; + diffuse[1] = g; + diffuse[2] = b; + } + + public void setPosition(float x, float y, float z) { + position[0] = x; + position[1] = y; + position[2] = z; + } + + public void enable(int lightNum) { + GL10 gl = glGraphics.getGL(); + gl.glEnable(lightNum); + gl.glLightfv(lightNum, GL10.GL_AMBIENT, ambient, 0); + gl.glLightfv(lightNum, GL10.GL_DIFFUSE, diffuse, 0); + gl.glLightfv(lightNum, GL10.GL_POSITION, position, 0); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/LookAtCamera.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/LookAtCamera.java new file mode 100644 index 0000000..addcbf4 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/LookAtCamera.java @@ -0,0 +1,49 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLU; + +import com.badlogic.androidgames.framework.math.Vector3; + +public class LookAtCamera { + final Vector3 position; + final Vector3 up; + final Vector3 lookAt; + float fieldOfView; + float aspectRatio; + float near; + float far; + + public LookAtCamera(float fieldOfView, float aspectRatio, float near, float far) { + this.fieldOfView = fieldOfView; + this.aspectRatio = aspectRatio; + this.near = near; + this.far = far; + + position = new Vector3(); + up = new Vector3(0, 1, 0); + lookAt = new Vector3(0,0,-1); + } + + public Vector3 getPosition() { + return position; + } + + public Vector3 getUp() { + return up; + } + + public Vector3 getLookAt() { + return lookAt; + } + + public void setMatrices(GL10 gl) { + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + GLU.gluPerspective(gl, fieldOfView, aspectRatio, near, far); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + GLU.gluLookAt(gl, position.x, position.y, position.z, lookAt.x, lookAt.y, lookAt.z, up.x, up.y, up.z); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Material.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Material.java new file mode 100644 index 0000000..5f6d8e0 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Material.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +public class Material { + float[] ambient = { 0.2f, 0.2f, 0.2f, 1.0f }; + float[] diffuse = { 1.0f, 1.0f, 1.0f, 1.0f }; + float[] specular = { 0.0f, 0.0f, 0.0f, 1.0f }; + + public void setAmbient(float r, float g, float b, float a) { + ambient[0] = r; + ambient[1] = g; + ambient[2] = b; + ambient[3] = a; + } + + public void setDiffuse(float r, float g, float b, float a) { + diffuse[0] = r; + diffuse[1] = g; + diffuse[2] = b; + diffuse[3] = a; + } + + public void setSpecular(float r, float g, float b, float a) { + specular[0] = r; + specular[1] = g; + specular[2] = b; + specular[3] = a; + } + + public void enable(GL10 gl) { + gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, ambient, 0); + gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, diffuse, 0); + gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, specular, 0); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/MipMappedTexture.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/MipMappedTexture.java new file mode 100644 index 0000000..59ed667 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/MipMappedTexture.java @@ -0,0 +1,122 @@ +package com.badlogic.androidgames.framework.gl; + +import java.io.IOException; +import java.io.InputStream; + +import javax.microedition.khronos.opengles.GL10; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.opengl.GLUtils; + +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class MipMappedTexture { + GLGraphics glGraphics; + FileIO fileIO; + String fileName; + int textureId; + int minFilter; + int magFilter; + public int width; + public int height; + boolean mipmapped; + + public MipMappedTexture(GLGame glGame, String fileName, boolean mipmapped) { + this.glGraphics = glGame.getGLGraphics(); + this.fileIO = glGame.getFileIO(); + this.fileName = fileName; + this.mipmapped = mipmapped; + load(); + } + + private void load() { + GL10 gl = glGraphics.getGL(); + int[] textureIds = new int[1]; + gl.glGenTextures(1, textureIds, 0); + textureId = textureIds[0]; + + InputStream in = null; + try { + in = fileIO.readAsset(fileName); + Bitmap bitmap = BitmapFactory.decodeStream(in); + if(mipmapped) { + createMipmaps(gl, bitmap); + } else { + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); + setFilters(GL10.GL_NEAREST, GL10.GL_NEAREST); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + width = bitmap.getWidth(); + height = bitmap.getHeight(); + bitmap.recycle(); + } + } catch(IOException e) { + throw new RuntimeException("Couldn't load texture '" + fileName +"'", e); + } finally { + if(in != null) + try { in.close(); } catch (IOException e) { } + } + } + + private void createMipmaps(GL10 gl, Bitmap bitmap) { + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + width = bitmap.getWidth(); + height = bitmap.getHeight(); + setFilters(GL10.GL_LINEAR_MIPMAP_NEAREST, GL10.GL_LINEAR); + + int level = 0; + int newWidth = width; + int newHeight = height; + while(true) { + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0); + newWidth = newWidth / 2; + newHeight = newHeight / 2; + if(newWidth <= 0) + break; + Bitmap newBitmap = Bitmap.createBitmap(newWidth, newHeight, bitmap.getConfig()); + Canvas canvas = new Canvas(newBitmap); + canvas.drawBitmap(bitmap, + new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), + new Rect(0, 0, newWidth, newHeight), + null); + bitmap.recycle(); + bitmap = newBitmap; + level++; + } + + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + bitmap.recycle(); + } + + public void reload() { + load(); + bind(); + setFilters(minFilter, magFilter); + glGraphics.getGL().glBindTexture(GL10.GL_TEXTURE_2D, 0); + } + + public void setFilters(int minFilter, int magFilter) { + this.minFilter = minFilter; + this.magFilter = magFilter; + GL10 gl = glGraphics.getGL(); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, minFilter); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, magFilter); + } + + public void bind() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + } + + public void dispose() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + int[] textureIds = { textureId }; + gl.glDeleteTextures(1, textureIds, 0); + } +} \ No newline at end of file diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/ObjLoader.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/ObjLoader.java new file mode 100644 index 0000000..a2d2658 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/ObjLoader.java @@ -0,0 +1,154 @@ +package com.badlogic.androidgames.framework.gl; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +import com.badlogic.androidgames.framework.impl.GLGame; + +public class ObjLoader { + public static Vertices3 load(GLGame game, String file) { + InputStream in = null; + try { + in = game.getFileIO().readAsset(file); + List lines = readLines(in); + + float[] vertices = new float[lines.size() * 3]; + float[] normals = new float[lines.size() * 3]; + float[] uv = new float[lines.size() * 2]; + + int numVertices = 0; + int numNormals = 0; + int numUV = 0; + int numFaces = 0; + + int[] facesVerts = new int[lines.size() * 3]; + int[] facesNormals = new int[lines.size() * 3]; + int[] facesUV = new int[lines.size() * 3]; + int vertexIndex = 0; + int normalIndex = 0; + int uvIndex = 0; + int faceIndex = 0; + + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i); + if (line.startsWith("v ")) { + String[] tokens = line.split("[ ]+"); + vertices[vertexIndex] = Float.parseFloat(tokens[1]); + vertices[vertexIndex + 1] = Float.parseFloat(tokens[2]); + vertices[vertexIndex + 2] = Float.parseFloat(tokens[3]); + vertexIndex += 3; + numVertices++; + continue; + } + + if (line.startsWith("vn ")) { + String[] tokens = line.split("[ ]+"); + normals[normalIndex] = Float.parseFloat(tokens[1]); + normals[normalIndex + 1] = Float.parseFloat(tokens[2]); + normals[normalIndex + 2] = Float.parseFloat(tokens[3]); + normalIndex += 3; + numNormals++; + continue; + } + + if (line.startsWith("vt")) { + String[] tokens = line.split("[ ]+"); + uv[uvIndex] = Float.parseFloat(tokens[1]); + uv[uvIndex + 1] = Float.parseFloat(tokens[2]); + uvIndex += 2; + numUV++; + continue; + } + + if (line.startsWith("f ")) { + String[] tokens = line.split("[ ]+"); + + String[] parts = tokens[1].split("/"); + facesVerts[faceIndex] = getIndex(parts[0], numVertices); + if (parts.length > 2) + facesNormals[faceIndex] = getIndex(parts[2], numNormals); + if (parts.length > 1) + facesUV[faceIndex] = getIndex(parts[1], numUV); + faceIndex++; + + parts = tokens[2].split("/"); + facesVerts[faceIndex] = getIndex(parts[0], numVertices); + if (parts.length > 2) + facesNormals[faceIndex] = getIndex(parts[2], numNormals); + if (parts.length > 1) + facesUV[faceIndex] = getIndex(parts[1], numUV); + faceIndex++; + + parts = tokens[3].split("/"); + facesVerts[faceIndex] = getIndex(parts[0], numVertices); + if (parts.length > 2) + facesNormals[faceIndex] = getIndex(parts[2], numNormals); + if (parts.length > 1) + facesUV[faceIndex] = getIndex(parts[1], numUV); + faceIndex++; + numFaces++; + continue; + } + } + + float[] verts = new float[(numFaces * 3) + * (3 + (numNormals > 0 ? 3 : 0) + (numUV > 0 ? 2 : 0))]; + + for (int i = 0, vi = 0; i < numFaces * 3; i++) { + int vertexIdx = facesVerts[i] * 3; + verts[vi++] = vertices[vertexIdx]; + verts[vi++] = vertices[vertexIdx + 1]; + verts[vi++] = vertices[vertexIdx + 2]; + + if (numUV > 0) { + int uvIdx = facesUV[i] * 2; + verts[vi++] = uv[uvIdx]; + verts[vi++] = 1 - uv[uvIdx + 1]; + } + + if (numNormals > 0) { + int normalIdx = facesNormals[i] * 3; + verts[vi++] = normals[normalIdx]; + verts[vi++] = normals[normalIdx + 1]; + verts[vi++] = normals[normalIdx + 2]; + } + } + + Vertices3 model = new Vertices3(game.getGLGraphics(), numFaces * 3, + 0, false, numUV > 0, numNormals > 0); + model.setVertices(verts, 0, verts.length); + return model; + } catch (Exception ex) { + throw new RuntimeException("couldn't load '" + file + "'", ex); + } finally { + if (in != null) + try { + in.close(); + } catch (Exception ex) { + + } + } + } + + static int getIndex(String index, int size) { + int idx = Integer.parseInt(index); + if (idx < 0) + return size + idx; + else + return idx - 1; + } + + static List readLines(InputStream in) throws IOException { + List lines = new ArrayList(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + String line = null; + while ((line = reader.readLine()) != null) + lines.add(line); + return lines; + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/PointLight.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/PointLight.java new file mode 100644 index 0000000..de55943 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/PointLight.java @@ -0,0 +1,51 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +public class PointLight { + float[] ambient = { 0.2f, 0.2f, 0.2f, 1.0f }; + float[] diffuse = { 1.0f, 1.0f, 1.0f, 1.0f }; + float[] specular = { 0.0f, 0.0f, 0.0f, 1.0f }; + float[] position = { 0, 0, 0, 1 }; + int lastLightId = 0; + + public void setAmbient(float r, float g, float b, float a) { + ambient[0] = r; + ambient[1] = g; + ambient[2] = b; + ambient[3] = a; + } + + public void setDiffuse(float r, float g, float b, float a) { + diffuse[0] = r; + diffuse[1] = g; + diffuse[2] = b; + diffuse[3] = a; + } + + public void setSpecular(float r, float g, float b, float a) { + specular[0] = r; + specular[1] = g; + specular[2] = b; + specular[3] = a; + } + + public void setPosition(float x, float y, float z) { + position[0] = x; + position[1] = y; + position[2] = z; + } + + public void enable(GL10 gl, int lightId) { + gl.glEnable(lightId); + gl.glLightfv(lightId, GL10.GL_AMBIENT, ambient, 0); + gl.glLightfv(lightId, GL10.GL_DIFFUSE, diffuse, 0); + gl.glLightfv(lightId, GL10.GL_SPECULAR, specular, 0); + gl.glLightfv(lightId, GL10.GL_POSITION, position, 0); + lastLightId = lightId; + } + + public void disable(GL10 gl) { + gl.glDisable(lastLightId); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java new file mode 100644 index 0000000..da3cb4f --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java @@ -0,0 +1,143 @@ +package com.badlogic.androidgames.framework.gl; + +import java.util.ArrayList; +import java.util.List; + +import com.badlogic.androidgames.framework.GameObject; + +import android.util.FloatMath; + +public class SpatialHashGrid { + List[] dynamicCells; + List[] staticCells; + int cellsPerRow; + int cellsPerCol; + float cellSize; + int[] cellIds = new int[4]; + List foundObjects; + + @SuppressWarnings("unchecked") + public SpatialHashGrid(float worldWidth, float worldHeight, float cellSize) { + this.cellSize = cellSize; + this.cellsPerRow = (int)FloatMath.ceil(worldWidth/cellSize); + this.cellsPerCol = (int)FloatMath.ceil(worldHeight/cellSize); + int numCells = cellsPerRow * cellsPerCol; + dynamicCells = new List[numCells]; + staticCells = new List[numCells]; + for(int i = 0; i < numCells; i++) { + dynamicCells[i] = new ArrayList(10); + staticCells[i] = new ArrayList(10); + } + foundObjects = new ArrayList(10); + } + + public void insertStaticObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + staticCells[cellId].add(obj); + } + } + + public void insertDynamicObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + dynamicCells[cellId].add(obj); + } + } + + public void removeObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + dynamicCells[cellId].remove(obj); + staticCells[cellId].remove(obj); + } + } + + public void clearDynamicCells(GameObject obj) { + int len = dynamicCells.length; + for(int i = 0; i < len; i++) { + dynamicCells[i].clear(); + } + } + + public List getPotentialColliders(GameObject obj) { + foundObjects.clear(); + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + int len = dynamicCells[cellId].size(); + for(int j = 0; j < len; j++) { + GameObject collider = dynamicCells[cellId].get(j); + if(!foundObjects.contains(collider)) + foundObjects.add(collider); + } + + len = staticCells[cellId].size(); + for(int j = 0; j < len; j++) { + GameObject collider = staticCells[cellId].get(j); + if(!foundObjects.contains(collider)) + foundObjects.add(collider); + } + } + return foundObjects; + } + + public int[] getCellIds(GameObject obj) { + int x1 = (int)FloatMath.floor(obj.bounds.lowerLeft.x / cellSize); + int y1 = (int)FloatMath.floor(obj.bounds.lowerLeft.y / cellSize); + int x2 = (int)FloatMath.floor((obj.bounds.lowerLeft.x + obj.bounds.width) / cellSize); + int y2 = (int)FloatMath.floor((obj.bounds.lowerLeft.y + obj.bounds.height) / cellSize); + + if(x1 == x2 && y1 == y2) { + if(x1 >= 0 && x1 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[0] = x1 + y1 * cellsPerRow; + else + cellIds[0] = -1; + cellIds[1] = -1; + cellIds[2] = -1; + cellIds[3] = -1; + } + else if(x1 == x2) { + int i = 0; + if(x1 >= 0 && x1 < cellsPerRow) { + if(y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x1 + y1 * cellsPerRow; + if(y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x1 + y2 * cellsPerRow; + } + while(i <= 3) cellIds[i++] = -1; + } + else if(y1 == y2) { + int i = 0; + if(y1 >= 0 && y1 < cellsPerCol) { + if(x1 >= 0 && x1 < cellsPerRow) + cellIds[i++] = x1 + y1 * cellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow) + cellIds[i++] = x2 + y1 * cellsPerRow; + } + while(i <= 3) cellIds[i++] = -1; + } + else { + int i = 0; + int y1CellsPerRow = y1 * cellsPerRow; + int y2CellsPerRow = y2 * cellsPerRow; + if(x1 >= 0 && x1 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x1 + y1CellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x2 + y1CellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow && y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x2 + y2CellsPerRow; + if(x1 >= 0 && x1 < cellsPerRow && y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x1 + y2CellsPerRow; + while(i <= 3) cellIds[i++] = -1; + } + return cellIds; + } +} \ No newline at end of file diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java new file mode 100644 index 0000000..8b9eee2 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java @@ -0,0 +1,128 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import android.util.FloatMath; + +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.Vector2; + +public class SpriteBatcher { + final float[] verticesBuffer; + int bufferIndex; + final Vertices vertices; + int numSprites; + + public SpriteBatcher(GLGraphics glGraphics, int maxSprites) { + this.verticesBuffer = new float[maxSprites*4*4]; + this.vertices = new Vertices(glGraphics, maxSprites*4, maxSprites*6, false, true); + this.bufferIndex = 0; + this.numSprites = 0; + + short[] indices = new short[maxSprites*6]; + int len = indices.length; + short j = 0; + for (int i = 0; i < len; i += 6, j += 4) { + indices[i + 0] = (short)(j + 0); + indices[i + 1] = (short)(j + 1); + indices[i + 2] = (short)(j + 2); + indices[i + 3] = (short)(j + 2); + indices[i + 4] = (short)(j + 3); + indices[i + 5] = (short)(j + 0); + } + vertices.setIndices(indices, 0, indices.length); + } + + public void beginBatch(Texture texture) { + texture.bind(); + numSprites = 0; + bufferIndex = 0; + } + + public void endBatch() { + vertices.setVertices(verticesBuffer, 0, bufferIndex); + vertices.bind(); + vertices.draw(GL10.GL_TRIANGLES, 0, numSprites * 6); + vertices.unbind(); + } + + public void drawSprite(float x, float y, float width, float height, TextureRegion region) { + float halfWidth = width / 2; + float halfHeight = height / 2; + float x1 = x - halfWidth; + float y1 = y - halfHeight; + float x2 = x + halfWidth; + float y2 = y + halfHeight; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v1; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v1; + + numSprites++; + } + + public void drawSprite(float x, float y, float width, float height, float angle, TextureRegion region) { + float halfWidth = width / 2; + float halfHeight = height / 2; + + float rad = angle * Vector2.TO_RADIANS; + float cos = FloatMath.cos(rad); + float sin = FloatMath.sin(rad); + + float x1 = -halfWidth * cos - (-halfHeight) * sin; + float y1 = -halfWidth * sin + (-halfHeight) * cos; + float x2 = halfWidth * cos - (-halfHeight) * sin; + float y2 = halfWidth * sin + (-halfHeight) * cos; + float x3 = halfWidth * cos - halfHeight * sin; + float y3 = halfWidth * sin + halfHeight * cos; + float x4 = -halfWidth * cos - halfHeight * sin; + float y4 = -halfWidth * sin + halfHeight * cos; + + x1 += x; + y1 += y; + x2 += x; + y2 += y; + x3 += x; + y3 += y; + x4 += x; + y4 += y; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x3; + verticesBuffer[bufferIndex++] = y3; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v1; + + verticesBuffer[bufferIndex++] = x4; + verticesBuffer[bufferIndex++] = y4; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v1; + + numSprites++; + } +} \ No newline at end of file diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Texture.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Texture.java new file mode 100644 index 0000000..da82e50 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Texture.java @@ -0,0 +1,132 @@ +package com.badlogic.androidgames.framework.gl; + +import java.io.IOException; +import java.io.InputStream; + +import javax.microedition.khronos.opengles.GL10; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.opengl.GLUtils; + +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Texture { + GLGraphics glGraphics; + FileIO fileIO; + String fileName; + int textureId; + int minFilter; + int magFilter; + public int width; + public int height; + boolean mipmapped; + + public Texture(GLGame glGame, String fileName) { + this(glGame, fileName, false); + } + + public Texture(GLGame glGame, String fileName, boolean mipmapped) { + this.glGraphics = glGame.getGLGraphics(); + this.fileIO = glGame.getFileIO(); + this.fileName = fileName; + this.mipmapped = mipmapped; + load(); + } + + private void load() { + GL10 gl = glGraphics.getGL(); + int[] textureIds = new int[1]; + gl.glGenTextures(1, textureIds, 0); + textureId = textureIds[0]; + + InputStream in = null; + try { + in = fileIO.readAsset(fileName); + Bitmap bitmap = BitmapFactory.decodeStream(in); + if (mipmapped) { + createMipmaps(gl, bitmap); + } else { + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); + setFilters(GL10.GL_NEAREST, GL10.GL_NEAREST); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + width = bitmap.getWidth(); + height = bitmap.getHeight(); + bitmap.recycle(); + } + } catch (IOException e) { + throw new RuntimeException("Couldn't load texture '" + fileName + + "'", e); + } finally { + if (in != null) + try { + in.close(); + } catch (IOException e) { + } + } + } + + private void createMipmaps(GL10 gl, Bitmap bitmap) { + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + width = bitmap.getWidth(); + height = bitmap.getHeight(); + setFilters(GL10.GL_LINEAR_MIPMAP_NEAREST, GL10.GL_LINEAR); + + int level = 0; + int newWidth = width; + int newHeight = height; + while (true) { + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0); + newWidth = newWidth / 2; + newHeight = newHeight / 2; + if (newWidth <= 0) + break; + Bitmap newBitmap = Bitmap.createBitmap(newWidth, newHeight, + bitmap.getConfig()); + Canvas canvas = new Canvas(newBitmap); + canvas.drawBitmap(bitmap, + new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), + new Rect(0, 0, newWidth, newHeight), null); + bitmap.recycle(); + bitmap = newBitmap; + level++; + } + + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + bitmap.recycle(); + } + + public void reload() { + load(); + bind(); + setFilters(minFilter, magFilter); + glGraphics.getGL().glBindTexture(GL10.GL_TEXTURE_2D, 0); + } + + public void setFilters(int minFilter, int magFilter) { + this.minFilter = minFilter; + this.magFilter = magFilter; + GL10 gl = glGraphics.getGL(); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, + minFilter); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, + magFilter); + } + + public void bind() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + } + + public void dispose() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + int[] textureIds = { textureId }; + gl.glDeleteTextures(1, textureIds, 0); + } +} \ No newline at end of file diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/TextureRegion.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/TextureRegion.java new file mode 100644 index 0000000..cb91a07 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/TextureRegion.java @@ -0,0 +1,15 @@ +package com.badlogic.androidgames.framework.gl; + +public class TextureRegion { + public final float u1, v1; + public final float u2, v2; + public final Texture texture; + + public TextureRegion(Texture texture, float x, float y, float width, float height) { + this.u1 = x / texture.width; + this.v1 = y / texture.height; + this.u2 = this.u1 + width / texture.width; + this.v2 = this.v1 + height / texture.height; + this.texture = texture; + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Vertices.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Vertices.java new file mode 100644 index 0000000..d8bbee3 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Vertices.java @@ -0,0 +1,95 @@ +package com.badlogic.androidgames.framework.gl; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Vertices { + final GLGraphics glGraphics; + final boolean hasColor; + final boolean hasTexCoords; + final int vertexSize; + final IntBuffer vertices; + final int[] tmpBuffer; + final ShortBuffer indices; + + public Vertices(GLGraphics glGraphics, int maxVertices, int maxIndices, boolean hasColor, boolean hasTexCoords) { + this.glGraphics = glGraphics; + this.hasColor = hasColor; + this.hasTexCoords = hasTexCoords; + this.vertexSize = (2 + (hasColor?4:0) + (hasTexCoords?2:0)) * 4; + this.tmpBuffer = new int[maxVertices * vertexSize / 4]; + + ByteBuffer buffer = ByteBuffer.allocateDirect(maxVertices * vertexSize); + buffer.order(ByteOrder.nativeOrder()); + vertices = buffer.asIntBuffer(); + + if(maxIndices > 0) { + buffer = ByteBuffer.allocateDirect(maxIndices * Short.SIZE / 8); + buffer.order(ByteOrder.nativeOrder()); + indices = buffer.asShortBuffer(); + } else { + indices = null; + } + } + + public void setVertices(float[] vertices, int offset, int length) { + this.vertices.clear(); + int len = offset + length; + for(int i=offset, j=0; i < len; i++, j++) + tmpBuffer[j] = Float.floatToRawIntBits(vertices[i]); + this.vertices.put(tmpBuffer, 0, length); + this.vertices.flip(); + } + + public void setIndices(short[] indices, int offset, int length) { + this.indices.clear(); + this.indices.put(indices, offset, length); + this.indices.flip(); + } + +public void bind() { + GL10 gl = glGraphics.getGL(); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + vertices.position(0); + gl.glVertexPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + + if(hasColor) { + gl.glEnableClientState(GL10.GL_COLOR_ARRAY); + vertices.position(2); + gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices); + } + + if(hasTexCoords) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + vertices.position(hasColor?6:2); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + } +} + +public void draw(int primitiveType, int offset, int numVertices) { + GL10 gl = glGraphics.getGL(); + + if(indices!=null) { + indices.position(offset); + gl.glDrawElements(primitiveType, numVertices, GL10.GL_UNSIGNED_SHORT, indices); + } else { + gl.glDrawArrays(primitiveType, offset, numVertices); + } +} + +public void unbind() { + GL10 gl = glGraphics.getGL(); + if(hasTexCoords) + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + if(hasColor) + gl.glDisableClientState(GL10.GL_COLOR_ARRAY); +} +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Vertices3.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Vertices3.java new file mode 100644 index 0000000..81c321d --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/gl/Vertices3.java @@ -0,0 +1,122 @@ +package com.badlogic.androidgames.framework.gl; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Vertices3 { + final GLGraphics glGraphics; + final boolean hasColor; + final boolean hasTexCoords; + final boolean hasNormals; + final int vertexSize; + final IntBuffer vertices; + final int[] tmpBuffer; + final ShortBuffer indices; + + public Vertices3(GLGraphics glGraphics, int maxVertices, int maxIndices, + boolean hasColor, boolean hasTexCoords, boolean hasNormals) { + this.glGraphics = glGraphics; + this.hasColor = hasColor; + this.hasTexCoords = hasTexCoords; + this.hasNormals = hasNormals; + this.vertexSize = (3 + (hasColor ? 4 : 0) + (hasTexCoords ? 2 : 0) + (hasNormals ? 3 + : 0)) * 4; + this.tmpBuffer = new int[maxVertices * vertexSize / 4]; + + ByteBuffer buffer = ByteBuffer.allocateDirect(maxVertices * vertexSize); + buffer.order(ByteOrder.nativeOrder()); + vertices = buffer.asIntBuffer(); + + if (maxIndices > 0) { + buffer = ByteBuffer.allocateDirect(maxIndices * Short.SIZE / 8); + buffer.order(ByteOrder.nativeOrder()); + indices = buffer.asShortBuffer(); + } else { + indices = null; + } + } + + public void setVertices(float[] vertices, int offset, int length) { + this.vertices.clear(); + int len = offset + length; + for (int i = offset, j = 0; i < len; i++, j++) + tmpBuffer[j] = Float.floatToRawIntBits(vertices[i]); + this.vertices.put(tmpBuffer, 0, length); + this.vertices.flip(); + } + + public void setIndices(short[] indices, int offset, int length) { + this.indices.clear(); + this.indices.put(indices, offset, length); + this.indices.flip(); + } + + public void bind() { + GL10 gl = glGraphics.getGL(); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + vertices.position(0); + gl.glVertexPointer(3, GL10.GL_FLOAT, vertexSize, vertices); + + if (hasColor) { + gl.glEnableClientState(GL10.GL_COLOR_ARRAY); + vertices.position(3); + gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices); + } + + if (hasTexCoords) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + vertices.position(hasColor ? 7 : 3); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + } + + if (hasNormals) { + gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); + int offset = 3; + if (hasColor) + offset += 4; + if (hasTexCoords) + offset += 2; + vertices.position(offset); + gl.glNormalPointer(GL10.GL_FLOAT, vertexSize, vertices); + } + } + + public void draw(int primitiveType, int offset, int numVertices) { + GL10 gl = glGraphics.getGL(); + + if (indices != null) { + indices.position(offset); + gl.glDrawElements(primitiveType, numVertices, + GL10.GL_UNSIGNED_SHORT, indices); + } else { + gl.glDrawArrays(primitiveType, offset, numVertices); + } + } + + public void unbind() { + GL10 gl = glGraphics.getGL(); + if (hasTexCoords) + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + if (hasColor) + gl.glDisableClientState(GL10.GL_COLOR_ARRAY); + + if (hasNormals) + gl.glDisableClientState(GL10.GL_NORMAL_ARRAY); + } + + public int getNumIndices() { + return indices.limit(); + } + + public int getNumVertices() { + return vertices.limit() / (vertexSize / 4); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java new file mode 100644 index 0000000..4820c5a --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java @@ -0,0 +1,48 @@ +package com.badlogic.androidgames.framework.impl; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + +public class AccelerometerHandler implements SensorEventListener { + float accelX; + float accelY; + float accelZ; + + public AccelerometerHandler(Context context) { + SensorManager manager = (SensorManager) context + .getSystemService(Context.SENSOR_SERVICE); + if (manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() != 0) { + Sensor accelerometer = manager.getSensorList( + Sensor.TYPE_ACCELEROMETER).get(0); + manager.registerListener(this, accelerometer, + SensorManager.SENSOR_DELAY_GAME); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // nothing to do here + } + + @Override + public void onSensorChanged(SensorEvent event) { + accelX = event.values[0]; + accelY = event.values[1]; + accelZ = event.values[2]; + } + + public float getAccelX() { + return accelX; + } + + public float getAccelY() { + return accelY; + } + + public float getAccelZ() { + return accelZ; + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java new file mode 100644 index 0000000..42f8c1f --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java @@ -0,0 +1,45 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; + +import android.app.Activity; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.media.AudioManager; +import android.media.SoundPool; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.Music; +import com.badlogic.androidgames.framework.Sound; + +public class AndroidAudio implements Audio { + AssetManager assets; + SoundPool soundPool; + + public AndroidAudio(Activity activity) { + activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); + this.assets = activity.getAssets(); + this.soundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0); + } + + @Override + public Music newMusic(String filename) { + try { + AssetFileDescriptor assetDescriptor = assets.openFd(filename); + return new AndroidMusic(assetDescriptor); + } catch (IOException e) { + throw new RuntimeException("Couldn't load music '" + filename + "'"); + } + } + + @Override + public Sound newSound(String filename) { + try { + AssetFileDescriptor assetDescriptor = assets.openFd(filename); + int soundId = soundPool.load(assetDescriptor, 0); + return new AndroidSound(soundPool, soundId); + } catch (IOException e) { + throw new RuntimeException("Couldn't load sound '" + filename + "'"); + } + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java new file mode 100644 index 0000000..6171d90 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java @@ -0,0 +1,60 @@ +package com.badlogic.androidgames.framework.impl; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class AndroidFastRenderView extends SurfaceView implements Runnable { + AndroidGame game; + Bitmap framebuffer; + Thread renderThread = null; + SurfaceHolder holder; + volatile boolean running = false; + + public AndroidFastRenderView(AndroidGame game, Bitmap framebuffer) { + super(game); + this.game = game; + this.framebuffer = framebuffer; + this.holder = getHolder(); + } + + public void resume() { + running = true; + renderThread = new Thread(this); + renderThread.start(); + } + + public void run() { + Rect dstRect = new Rect(); + long startTime = System.nanoTime(); + while(running) { + if(!holder.getSurface().isValid()) + continue; + + float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; + startTime = System.nanoTime(); + + game.getCurrentScreen().update(deltaTime); + game.getCurrentScreen().present(deltaTime); + + Canvas canvas = holder.lockCanvas(); + canvas.getClipBounds(dstRect); + canvas.drawBitmap(framebuffer, null, dstRect, null); + holder.unlockCanvasAndPost(canvas); + } + } + + public void pause() { + running = false; + while(true) { + try { + renderThread.join(); + break; + } catch (InterruptedException e) { + // retry + } + } + } +} \ No newline at end of file diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java new file mode 100644 index 0000000..b13b115 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java @@ -0,0 +1,39 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import android.content.res.AssetManager; +import android.os.Environment; + +import com.badlogic.androidgames.framework.FileIO; + +public class AndroidFileIO implements FileIO { + AssetManager assets; + String externalStoragePath; + + public AndroidFileIO(AssetManager assets) { + this.assets = assets; + this.externalStoragePath = Environment.getExternalStorageDirectory() + .getAbsolutePath() + File.separator; + } + + @Override + public InputStream readAsset(String fileName) throws IOException { + return assets.open(fileName); + } + + @Override + public InputStream readFile(String fileName) throws IOException { + return new FileInputStream(externalStoragePath + fileName); + } + + @Override + public OutputStream writeFile(String fileName) throws IOException { + return new FileOutputStream(externalStoragePath + fileName); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidGame.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidGame.java new file mode 100644 index 0000000..d1fd0b3 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidGame.java @@ -0,0 +1,107 @@ +package com.badlogic.androidgames.framework.impl; + +import android.app.Activity; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.os.Bundle; +import android.view.Window; +import android.view.WindowManager; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input; +import com.badlogic.androidgames.framework.Screen; + +public abstract class AndroidGame extends Activity implements Game { + AndroidFastRenderView renderView; + Graphics graphics; + Audio audio; + Input input; + FileIO fileIO; + Screen screen; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + + boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + int frameBufferWidth = isLandscape ? 480 : 320; + int frameBufferHeight = isLandscape ? 320 : 480; + Bitmap frameBuffer = Bitmap.createBitmap(frameBufferWidth, + frameBufferHeight, Config.RGB_565); + + float scaleX = (float) frameBufferWidth + / getWindowManager().getDefaultDisplay().getWidth(); + float scaleY = (float) frameBufferHeight + / getWindowManager().getDefaultDisplay().getHeight(); + + renderView = new AndroidFastRenderView(this, frameBuffer); + graphics = new AndroidGraphics(getAssets(), frameBuffer); + fileIO = new AndroidFileIO(getAssets()); + audio = new AndroidAudio(this); + input = new AndroidInput(this, renderView, scaleX, scaleY); + screen = getStartScreen(); + setContentView(renderView); + } + + @Override + public void onResume() { + super.onResume(); + screen.resume(); + renderView.resume(); + } + + @Override + public void onPause() { + super.onPause(); + renderView.pause(); + screen.pause(); + + if (isFinishing()) { + screen.dispose(); + } + } + + @Override + public Input getInput() { + return input; + } + + @Override + public FileIO getFileIO() { + return fileIO; + } + + @Override + public Graphics getGraphics() { + return graphics; + } + + @Override + public Audio getAudio() { + return audio; + } + + @Override + public void setScreen(Screen screen) { + if (screen == null) + throw new IllegalArgumentException("Screen must not be null"); + + this.screen.pause(); + this.screen.dispose(); + screen.resume(); + screen.update(0); + this.screen = screen; + } + + public Screen getCurrentScreen() { + return screen; + } +} \ No newline at end of file diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java new file mode 100644 index 0000000..ade52b5 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java @@ -0,0 +1,133 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; +import java.io.InputStream; + +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.BitmapFactory.Options; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Rect; + +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Pixmap; + +public class AndroidGraphics implements Graphics { + AssetManager assets; + Bitmap frameBuffer; + Canvas canvas; + Paint paint; + Rect srcRect = new Rect(); + Rect dstRect = new Rect(); + + public AndroidGraphics(AssetManager assets, Bitmap frameBuffer) { + this.assets = assets; + this.frameBuffer = frameBuffer; + this.canvas = new Canvas(frameBuffer); + this.paint = new Paint(); + } + + @Override + public Pixmap newPixmap(String fileName, PixmapFormat format) { + Config config = null; + if (format == PixmapFormat.RGB565) + config = Config.RGB_565; + else if (format == PixmapFormat.ARGB4444) + config = Config.ARGB_4444; + else + config = Config.ARGB_8888; + + Options options = new Options(); + options.inPreferredConfig = config; + + InputStream in = null; + Bitmap bitmap = null; + try { + in = assets.open(fileName); + bitmap = BitmapFactory.decodeStream(in, null, options); + if (bitmap == null) + throw new RuntimeException("Couldn't load bitmap from asset '" + + fileName + "'"); + } catch (IOException e) { + throw new RuntimeException("Couldn't load bitmap from asset '" + + fileName + "'"); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + + if (bitmap.getConfig() == Config.RGB_565) + format = PixmapFormat.RGB565; + else if (bitmap.getConfig() == Config.ARGB_4444) + format = PixmapFormat.ARGB4444; + else + format = PixmapFormat.ARGB8888; + + return new AndroidPixmap(bitmap, format); + } + + @Override + public void clear(int color) { + canvas.drawRGB((color & 0xff0000) >> 16, (color & 0xff00) >> 8, + (color & 0xff)); + } + + @Override + public void drawPixel(int x, int y, int color) { + paint.setColor(color); + canvas.drawPoint(x, y, paint); + } + + @Override + public void drawLine(int x, int y, int x2, int y2, int color) { + paint.setColor(color); + canvas.drawLine(x, y, x2, y2, paint); + } + + @Override + public void drawRect(int x, int y, int width, int height, int color) { + paint.setColor(color); + paint.setStyle(Style.FILL); + canvas.drawRect(x, y, x + width - 1, y + height - 1, paint); + } + + @Override + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight) { + srcRect.left = srcX; + srcRect.top = srcY; + srcRect.right = srcX + srcWidth - 1; + srcRect.bottom = srcY + srcHeight - 1; + + dstRect.left = x; + dstRect.top = y; + dstRect.right = x + srcWidth - 1; + dstRect.bottom = y + srcHeight - 1; + + canvas.drawBitmap(((AndroidPixmap) pixmap).bitmap, srcRect, dstRect, + null); + } + + @Override + public void drawPixmap(Pixmap pixmap, int x, int y) { + canvas.drawBitmap(((AndroidPixmap)pixmap).bitmap, x, y, null); + } + + @Override + public int getWidth() { + return frameBuffer.getWidth(); + } + + @Override + public int getHeight() { + return frameBuffer.getHeight(); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidInput.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidInput.java new file mode 100644 index 0000000..11f5863 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidInput.java @@ -0,0 +1,69 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.List; + +import android.content.Context; +import android.os.Build.VERSION; +import android.view.View; + +import com.badlogic.androidgames.framework.Input; + +public class AndroidInput implements Input { + AccelerometerHandler accelHandler; + KeyboardHandler keyHandler; + TouchHandler touchHandler; + + public AndroidInput(Context context, View view, float scaleX, float scaleY) { + accelHandler = new AccelerometerHandler(context); + keyHandler = new KeyboardHandler(view); + if(Integer.parseInt(VERSION.SDK) < 5) + touchHandler = new SingleTouchHandler(view, scaleX, scaleY); + else + touchHandler = new MultiTouchHandler(view, scaleX, scaleY); + } + + @Override + public boolean isKeyPressed(int keyCode) { + return keyHandler.isKeyPressed(keyCode); + } + + @Override + public boolean isTouchDown(int pointer) { + return touchHandler.isTouchDown(pointer); + } + + @Override + public int getTouchX(int pointer) { + return touchHandler.getTouchX(pointer); + } + + @Override + public int getTouchY(int pointer) { + return touchHandler.getTouchY(pointer); + } + + @Override + public float getAccelX() { + return accelHandler.getAccelX(); + } + + @Override + public float getAccelY() { + return accelHandler.getAccelY(); + } + + @Override + public float getAccelZ() { + return accelHandler.getAccelZ(); + } + + @Override + public List getTouchEvents() { + return touchHandler.getTouchEvents(); + } + + @Override + public List getKeyEvents() { + return keyHandler.getKeyEvents(); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java new file mode 100644 index 0000000..9e9fd1c --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java @@ -0,0 +1,99 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; + +import android.content.res.AssetFileDescriptor; +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnCompletionListener; + +import com.badlogic.androidgames.framework.Music; + +public class AndroidMusic implements Music, OnCompletionListener { + MediaPlayer mediaPlayer; + boolean isPrepared = false; + + public AndroidMusic(AssetFileDescriptor assetDescriptor) { + mediaPlayer = new MediaPlayer(); + try { + mediaPlayer.setDataSource(assetDescriptor.getFileDescriptor(), + assetDescriptor.getStartOffset(), + assetDescriptor.getLength()); + mediaPlayer.prepare(); + isPrepared = true; + mediaPlayer.setOnCompletionListener(this); + } catch (Exception e) { + throw new RuntimeException("Couldn't load music"); + } + } + + @Override + public void dispose() { + if (mediaPlayer.isPlaying()) + mediaPlayer.stop(); + mediaPlayer.release(); + } + + @Override + public boolean isLooping() { + return mediaPlayer.isLooping(); + } + + @Override + public boolean isPlaying() { + return mediaPlayer.isPlaying(); + } + + @Override + public boolean isStopped() { + return !isPrepared; + } + + @Override + public void pause() { + if (mediaPlayer.isPlaying()) + mediaPlayer.pause(); + } + + @Override + public void play() { + if (mediaPlayer.isPlaying()) + return; + + try { + synchronized (this) { + if (!isPrepared) + mediaPlayer.prepare(); + mediaPlayer.start(); + } + } catch (IllegalStateException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void setLooping(boolean isLooping) { + mediaPlayer.setLooping(isLooping); + } + + @Override + public void setVolume(float volume) { + mediaPlayer.setVolume(volume, volume); + } + + @Override + public void stop() { + mediaPlayer.stop(); + synchronized (this) { + isPrepared = false; + } + } + + @Override + public void onCompletion(MediaPlayer arg0) { + synchronized (this) { + isPrepared = false; + } + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java new file mode 100644 index 0000000..9de5021 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework.impl; + +import android.graphics.Bitmap; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; +import com.badlogic.androidgames.framework.Pixmap; + +public class AndroidPixmap implements Pixmap { + Bitmap bitmap; + PixmapFormat format; + + public AndroidPixmap(Bitmap bitmap, PixmapFormat format) { + this.bitmap = bitmap; + this.format = format; + } + + @Override + public int getWidth() { + return bitmap.getWidth(); + } + + @Override + public int getHeight() { + return bitmap.getHeight(); + } + + @Override + public PixmapFormat getFormat() { + return format; + } + + @Override + public void dispose() { + bitmap.recycle(); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidSound.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidSound.java new file mode 100644 index 0000000..4a8fcd4 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/AndroidSound.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework.impl; + +import android.media.SoundPool; + +import com.badlogic.androidgames.framework.Sound; + +public class AndroidSound implements Sound { + int soundId; + SoundPool soundPool; + + public AndroidSound(SoundPool soundPool,int soundId) { + this.soundId = soundId; + this.soundPool = soundPool; + } + + @Override + public void play(float volume) { + soundPool.play(soundId, volume, volume, 0, 0, 1); + } + + @Override + public void dispose() { + soundPool.unload(soundId); + } + +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/GLGame.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/GLGame.java new file mode 100644 index 0000000..5822786 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/GLGame.java @@ -0,0 +1,178 @@ +package com.badlogic.androidgames.framework.impl; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import android.app.Activity; +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.opengl.GLSurfaceView.Renderer; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.view.Window; +import android.view.WindowManager; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input; +import com.badlogic.androidgames.framework.Screen; + +public abstract class GLGame extends Activity implements Game, Renderer { + enum GLGameState { + Initialized, + Running, + Paused, + Finished, + Idle + } + + GLSurfaceView glView; + GLGraphics glGraphics; + Audio audio; + Input input; + FileIO fileIO; + Screen screen; + GLGameState state = GLGameState.Initialized; + Object stateChanged = new Object(); + long startTime = System.nanoTime(); + WakeLock wakeLock; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + glView = new GLSurfaceView(this); + glView.setRenderer(this); + setContentView(glView); + + glGraphics = new GLGraphics(glView); + fileIO = new AndroidFileIO(getAssets()); + audio = new AndroidAudio(this); + input = new AndroidInput(this, glView, 1, 1); + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GLGame"); + } + + public void onResume() { + super.onResume(); + glView.onResume(); + wakeLock.acquire(); + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + glGraphics.setGL(gl); + + synchronized(stateChanged) { + if(state == GLGameState.Initialized) + screen = getStartScreen(); + state = GLGameState.Running; + screen.resume(); + startTime = System.nanoTime(); + } + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + } + + @Override + public void onDrawFrame(GL10 gl) { + GLGameState state = null; + + synchronized(stateChanged) { + state = this.state; + } + + if(state == GLGameState.Running) { + float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; + startTime = System.nanoTime(); + + screen.update(deltaTime); + screen.present(deltaTime); + } + + if(state == GLGameState.Paused) { + screen.pause(); + synchronized(stateChanged) { + this.state = GLGameState.Idle; + stateChanged.notifyAll(); + } + } + + if(state == GLGameState.Finished) { + screen.pause(); + screen.dispose(); + synchronized(stateChanged) { + this.state = GLGameState.Idle; + stateChanged.notifyAll(); + } + } + } + + @Override + public void onPause() { + synchronized(stateChanged) { + if(isFinishing()) + state = GLGameState.Finished; + else + state = GLGameState.Paused; + while(true) { + try { + stateChanged.wait(); + break; + } catch(InterruptedException e) { + } + } + } + wakeLock.release(); + glView.onPause(); + super.onPause(); + } + + public GLGraphics getGLGraphics() { + return glGraphics; + } + + @Override + public Input getInput() { + return input; + } + + @Override + public FileIO getFileIO() { + return fileIO; + } + + @Override + public Graphics getGraphics() { + throw new IllegalStateException("We are using OpenGL!"); + } + + @Override + public Audio getAudio() { + return audio; + } + + @Override + public void setScreen(Screen screen) { + if (screen == null) + throw new IllegalArgumentException("Screen must not be null"); + + this.screen.pause(); + this.screen.dispose(); + screen.resume(); + screen.update(0); + this.screen = screen; + } + + @Override + public Screen getCurrentScreen() { + return screen; + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/GLGraphics.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/GLGraphics.java new file mode 100644 index 0000000..2eb1b43 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/GLGraphics.java @@ -0,0 +1,30 @@ +package com.badlogic.androidgames.framework.impl; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLSurfaceView; + +public class GLGraphics { + GLSurfaceView glView; + GL10 gl; + + GLGraphics(GLSurfaceView glView) { + this.glView = glView; + } + + public GL10 getGL() { + return gl; + } + + void setGL(GL10 gl) { + this.gl = gl; + } + + public int getWidth() { + return glView.getWidth(); + } + + public int getHeight() { + return glView.getHeight(); + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/GLScreen.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/GLScreen.java new file mode 100644 index 0000000..466de8c --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/GLScreen.java @@ -0,0 +1,16 @@ +package com.badlogic.androidgames.framework.impl; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; + +public abstract class GLScreen extends Screen { + protected final GLGraphics glGraphics; + protected final GLGame glGame; + + public GLScreen(Game game) { + super(game); + glGame = (GLGame)game; + glGraphics = ((GLGame)game).getGLGraphics(); + } + +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java new file mode 100644 index 0000000..f70d3e9 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java @@ -0,0 +1,73 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.View; +import android.view.View.OnKeyListener; + +import com.badlogic.androidgames.framework.Input.KeyEvent; +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class KeyboardHandler implements OnKeyListener { + boolean[] pressedKeys = new boolean[128]; + Pool keyEventPool; + List keyEventsBuffer = new ArrayList(); + List keyEvents = new ArrayList(); + + public KeyboardHandler(View view) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public KeyEvent createObject() { + return new KeyEvent(); + } + }; + keyEventPool = new Pool(factory, 100); + view.setOnKeyListener(this); + view.setFocusableInTouchMode(true); + view.requestFocus(); + } + + @Override + public boolean onKey(View v, int keyCode, android.view.KeyEvent event) { + if (event.getAction() == android.view.KeyEvent.ACTION_MULTIPLE) + return false; + + synchronized (this) { + KeyEvent keyEvent = keyEventPool.newObject(); + keyEvent.keyCode = keyCode; + keyEvent.keyChar = (char) event.getUnicodeChar(); + if (event.getAction() == android.view.KeyEvent.ACTION_DOWN) { + keyEvent.type = KeyEvent.KEY_DOWN; + if(keyCode >= 0 && keyCode < 128) + pressedKeys[keyCode] = true; + } + if (event.getAction() == android.view.KeyEvent.ACTION_UP) { + keyEvent.type = KeyEvent.KEY_UP; + if(keyCode >= 0 && keyCode < 128) + pressedKeys[keyCode] = false; + } + keyEventsBuffer.add(keyEvent); + } + return false; + } + + public boolean isKeyPressed(int keyCode) { + if (keyCode < 0 || keyCode > 127) + return false; + return pressedKeys[keyCode]; + } + + public List getKeyEvents() { + synchronized (this) { + int len = keyEvents.size(); + for (int i = 0; i < len; i++) + keyEventPool.free(keyEvents.get(i)); + keyEvents.clear(); + keyEvents.addAll(keyEventsBuffer); + keyEventsBuffer.clear(); + return keyEvents; + } + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java new file mode 100644 index 0000000..4668c57 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java @@ -0,0 +1,137 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.MotionEvent; +import android.view.View; + +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class MultiTouchHandler implements TouchHandler { + boolean[] isTouched = new boolean[20]; + int[] touchX = new int[20]; + int[] touchY = new int[20]; + Pool touchEventPool; + List touchEvents = new ArrayList(); + List touchEventsBuffer = new ArrayList(); + float scaleX; + float scaleY; + + public MultiTouchHandler(View view, float scaleX, float scaleY) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }; + touchEventPool = new Pool(factory, 100); + view.setOnTouchListener(this); + + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + synchronized (this) { + int action = event.getAction() & MotionEvent.ACTION_MASK; + int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; + int pointerId = event.getPointerId(pointerIndex); + TouchEvent touchEvent; + + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_DOWN; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + isTouched[pointerId] = true; + touchEventsBuffer.add(touchEvent); + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_CANCEL: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_UP; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + isTouched[pointerId] = false; + touchEventsBuffer.add(touchEvent); + break; + + case MotionEvent.ACTION_MOVE: + int pointerCount = event.getPointerCount(); + for (int i = 0; i < pointerCount; i++) { + pointerIndex = i; + pointerId = event.getPointerId(pointerIndex); + + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_DRAGGED; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + touchEventsBuffer.add(touchEvent); + } + break; + } + + return true; + } + } + + @Override + public boolean isTouchDown(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return false; + else + return isTouched[pointer]; + } + } + + @Override + public int getTouchX(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return 0; + else + return touchX[pointer]; + } + } + + @Override + public int getTouchY(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return 0; + else + return touchY[pointer]; + } + } + + @Override + public List getTouchEvents() { + synchronized (this) { + int len = touchEvents.size(); + for (int i = 0; i < len; i++) + touchEventPool.free(touchEvents.get(i)); + touchEvents.clear(); + touchEvents.addAll(touchEventsBuffer); + touchEventsBuffer.clear(); + return touchEvents; + } + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java new file mode 100644 index 0000000..7ff1205 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java @@ -0,0 +1,101 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.MotionEvent; +import android.view.View; + +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class SingleTouchHandler implements TouchHandler { + boolean isTouched; + int touchX; + int touchY; + Pool touchEventPool; + List touchEvents = new ArrayList(); + List touchEventsBuffer = new ArrayList(); + float scaleX; + float scaleY; + + public SingleTouchHandler(View view, float scaleX, float scaleY) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }; + touchEventPool = new Pool(factory, 100); + view.setOnTouchListener(this); + + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + synchronized(this) { + TouchEvent touchEvent = touchEventPool.newObject(); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touchEvent.type = TouchEvent.TOUCH_DOWN; + isTouched = true; + break; + case MotionEvent.ACTION_MOVE: + touchEvent.type = TouchEvent.TOUCH_DRAGGED; + isTouched = true; + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + touchEvent.type = TouchEvent.TOUCH_UP; + isTouched = false; + break; + } + + touchEvent.x = touchX = (int)(event.getX() * scaleX); + touchEvent.y = touchY = (int)(event.getY() * scaleY); + touchEventsBuffer.add(touchEvent); + + return true; + } + } + + @Override + public boolean isTouchDown(int pointer) { + synchronized(this) { + if(pointer == 0) + return isTouched; + else + return false; + } + } + + @Override + public int getTouchX(int pointer) { + synchronized(this) { + return touchX; + } + } + + @Override + public int getTouchY(int pointer) { + synchronized(this) { + return touchY; + } + } + + @Override + public List getTouchEvents() { + synchronized(this) { + int len = touchEvents.size(); + for( int i = 0; i < len; i++ ) + touchEventPool.free(touchEvents.get(i)); + touchEvents.clear(); + touchEvents.addAll(touchEventsBuffer); + touchEventsBuffer.clear(); + return touchEvents; + } + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/TouchHandler.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/TouchHandler.java new file mode 100644 index 0000000..cc7e754 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/impl/TouchHandler.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.List; + +import android.view.View.OnTouchListener; + +import com.badlogic.androidgames.framework.Input.TouchEvent; + +public interface TouchHandler extends OnTouchListener { + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public List getTouchEvents(); +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Circle.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Circle.java new file mode 100644 index 0000000..2728f84 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Circle.java @@ -0,0 +1,11 @@ +package com.badlogic.androidgames.framework.math; + +public class Circle { + public final Vector2 center = new Vector2(); + public float radius; + + public Circle(float x, float y, float radius) { + this.center.set(x,y); + this.radius = radius; + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/OverlapTester.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/OverlapTester.java new file mode 100644 index 0000000..3650946 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/OverlapTester.java @@ -0,0 +1,72 @@ +package com.badlogic.androidgames.framework.math; + +public class OverlapTester { + public static boolean overlapCircles(Circle c1, Circle c2) { + float distance = c1.center.distSquared(c2.center); + float radiusSum = c1.radius + c2.radius; + return distance <= radiusSum * radiusSum; + } + + public static boolean overlapRectangles(Rectangle r1, Rectangle r2) { + if(r1.lowerLeft.x < r2.lowerLeft.x + r2.width && + r1.lowerLeft.x + r1.width > r2.lowerLeft.x && + r1.lowerLeft.y < r2.lowerLeft.y + r2.height && + r1.lowerLeft.y + r1.height > r2.lowerLeft.y) + return true; + else + return false; + } + + public static boolean overlapCircleRectangle(Circle c, Rectangle r) { + float closestX = c.center.x; + float closestY = c.center.y; + + if(c.center.x < r.lowerLeft.x) { + closestX = r.lowerLeft.x; + } + else if(c.center.x > r.lowerLeft.x + r.width) { + closestX = r.lowerLeft.x + r.width; + } + + if(c.center.y < r.lowerLeft.y) { + closestY = r.lowerLeft.y; + } + else if(c.center.y > r.lowerLeft.y + r.height) { + closestY = r.lowerLeft.y + r.height; + } + + return c.center.distSquared(closestX, closestY) < c.radius * c.radius; + } + + public static boolean overlapSpheres(Sphere s1, Sphere s2) { + float distance = s1.center.distSquared(s2.center); + float radiusSum = s1.radius + s2.radius; + return distance <= radiusSum * radiusSum; + } + + public static boolean pointInSphere(Sphere c, Vector3 p) { + return c.center.distSquared(p) < c.radius * c.radius; + } + + public static boolean pointInSphere(Sphere c, float x, float y, float z) { + return c.center.distSquared(x, y, z) < c.radius * c.radius; + } + + public static boolean pointInCircle(Circle c, Vector2 p) { + return c.center.distSquared(p) < c.radius * c.radius; + } + + public static boolean pointInCircle(Circle c, float x, float y) { + return c.center.distSquared(x, y) < c.radius * c.radius; + } + + public static boolean pointInRectangle(Rectangle r, Vector2 p) { + return r.lowerLeft.x <= p.x && r.lowerLeft.x + r.width >= p.x && + r.lowerLeft.y <= p.y && r.lowerLeft.y + r.height >= p.y; + } + + public static boolean pointInRectangle(Rectangle r, float x, float y) { + return r.lowerLeft.x <= x && r.lowerLeft.x + r.width >= x && + r.lowerLeft.y <= y && r.lowerLeft.y + r.height >= y; + } +} \ No newline at end of file diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Rectangle.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Rectangle.java new file mode 100644 index 0000000..9c2511f --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Rectangle.java @@ -0,0 +1,12 @@ +package com.badlogic.androidgames.framework.math; + +public class Rectangle { + public final Vector2 lowerLeft; + public float width, height; + + public Rectangle(float x, float y, float width, float height) { + this.lowerLeft = new Vector2(x,y); + this.width = width; + this.height = height; + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Sphere.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Sphere.java new file mode 100644 index 0000000..6f5c4a9 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Sphere.java @@ -0,0 +1,11 @@ +package com.badlogic.androidgames.framework.math; + +public class Sphere { + public final Vector3 center = new Vector3(); + public float radius; + + public Sphere(float x, float y, float z, float radius) { + this.center.set(x,y,z); + this.radius = radius; + } +} \ No newline at end of file diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Vector2.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Vector2.java new file mode 100644 index 0000000..56d79c2 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Vector2.java @@ -0,0 +1,126 @@ +package com.badlogic.androidgames.framework.math; + +import android.util.FloatMath; + +public class Vector2 { + public static float TO_RADIANS = (1 / 180.0f) * (float)Math.PI; + public static float TO_DEGREES = (1 / (float)Math.PI) * 180; + public float x, y; + + public Vector2() { + } + + public Vector2(float x, float y) { + this.x = x; + this.y = y; + } + + public Vector2(Vector2 other) { + this.x = other.x; + this.y = other.y; + } + + public Vector2 cpy() { + return new Vector2(x, y); + } + + public Vector2 set(float x, float y) { + this.x = x; + this.y = y; + return this; + } + + public Vector2 set(Vector2 other) { + this.x = other.x; + this.y = other.y; + return this; + } + + public Vector2 add(float x, float y) { + this.x += x; + this.y += y; + return this; + } + + public Vector2 add(Vector2 other) { + this.x += other.x; + this.y += other.y; + return this; + } + + public Vector2 sub(float x, float y) { + this.x -= x; + this.y -= y; + return this; + } + + public Vector2 sub(Vector2 other) { + this.x -= other.x; + this.y -= other.y; + return this; + } + + public Vector2 mul(float scalar) { + this.x *= scalar; + this.y *= scalar; + return this; + } + + public float len() { + return FloatMath.sqrt(x*x + y*y); + } + + public Vector2 nor() { + float len = len(); + if(len!=0) { + this.x /= len; + this.y /= len; + } + return this; + } + + public float angle() { + float angle = (float)Math.atan2(y, x) * TO_DEGREES; + if(angle < 0) + angle += 360; + return angle; + } + + public Vector2 rotate(float angle) { + float rad = angle * TO_RADIANS; + float cos = FloatMath.cos(rad); + float sin = FloatMath.sin(rad); + + float newX = this.x * cos - this.y * sin; + float newY = this.x * sin + this.y * cos; + + this.x = newX; + this.y = newY; + + return this; + } + + public float dist(Vector2 other) { + float distX = this.x - other.x; + float distY = this.y - other.y; + return FloatMath.sqrt(distX*distX + distY*distY); + } + + public float dist(float x, float y) { + float distX = this.x - x; + float distY = this.y - y; + return FloatMath.sqrt(distX*distX + distY*distY); + } + + public float distSquared(Vector2 other) { + float distX = this.x - other.x; + float distY = this.y - other.y; + return distX*distX + distY*distY; + } + + public float distSquared(float x, float y) { + float distX = this.x - x; + float distY = this.y - y; + return distX*distX + distY*distY; + } +} \ No newline at end of file diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Vector3.java b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Vector3.java new file mode 100644 index 0000000..f31dff5 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/framework/math/Vector3.java @@ -0,0 +1,135 @@ +package com.badlogic.androidgames.framework.math; + +import android.opengl.Matrix; +import android.util.FloatMath; + +public class Vector3 { + private static final float[] matrix = new float[16]; + private static final float[] inVec = new float[4]; + private static final float[] outVec = new float[4]; + public float x, y, z; + + public Vector3() { + } + + public Vector3(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Vector3(Vector3 other) { + this.x = other.x; + this.y = other.y; + this.z = other.z; + } + + public Vector3 cpy() { + return new Vector3(x, y, z); + } + + public Vector3 set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + public Vector3 set(Vector3 other) { + this.x = other.x; + this.y = other.y; + this.z = other.z; + return this; + } + + public Vector3 add(float x, float y, float z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + public Vector3 add(Vector3 other) { + this.x += other.x; + this.y += other.y; + this.z += other.z; + return this; + } + + public Vector3 sub(float x, float y, float z) { + this.x -= x; + this.y -= y; + this.z -= z; + return this; + } + + public Vector3 sub(Vector3 other) { + this.x -= other.x; + this.y -= other.y; + this.z -= other.z; + return this; + } + + public Vector3 mul(float scalar) { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + return this; + } + + public float len() { + return FloatMath.sqrt(x * x + y * y + z * z); + } + + public Vector3 nor() { + float len = len(); + if (len != 0) { + this.x /= len; + this.y /= len; + this.z /= len; + } + return this; + } + + public Vector3 rotate(float angle, float axisX, float axisY, float axisZ) { + inVec[0] = x; + inVec[1] = y; + inVec[2] = z; + inVec[3] = 1; + Matrix.setIdentityM(matrix, 0); + Matrix.rotateM(matrix, 0, angle, axisX, axisY, axisZ); + Matrix.multiplyMV(outVec, 0, matrix, 0, inVec, 0); + x = outVec[0]; + y = outVec[1]; + z = outVec[2]; + return this; + } + + public float dist(Vector3 other) { + float distX = this.x - other.x; + float distY = this.y - other.y; + float distZ = this.z - other.z; + return FloatMath.sqrt(distX * distX + distY * distY + distZ * distZ); + } + + public float dist(float x, float y, float z) { + float distX = this.x - x; + float distY = this.y - y; + float distZ = this.z - z; + return FloatMath.sqrt(distX * distX + distY * distY + distZ * distZ); + } + + public float distSquared(Vector3 other) { + float distX = this.x - other.x; + float distY = this.y - other.y; + float distZ = this.z - other.z; + return distX * distX + distY * distY + distZ * distZ; + } + + public float distSquared(float x, float y, float z) { + float distX = this.x - x; + float distY = this.y - y; + float distZ = this.z - z; + return distX * distX + distY * distY + distZ * distZ; + } +} \ No newline at end of file diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/EulerCameraTest.java b/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/EulerCameraTest.java new file mode 100644 index 0000000..c3832de --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/EulerCameraTest.java @@ -0,0 +1,183 @@ +package com.badlogic.androidgames.gladvanced; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.EulerCamera; +import com.badlogic.androidgames.framework.gl.PointLight; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.TextureRegion; +import com.badlogic.androidgames.framework.gl.Vertices3; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.Vector2; +import com.badlogic.androidgames.framework.math.Vector3; + +public class EulerCameraTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new EulerCameraScreen(this); + } + +class EulerCameraScreen extends GLScreen { + Texture crateTexture; + Vertices3 cube; + PointLight light; + EulerCamera camera; + Texture buttonTexture; + SpriteBatcher batcher; + Camera2D guiCamera; + TextureRegion buttonRegion; + Vector2 touchPos; + float lastX = -1; + float lastY = -1; + + public EulerCameraScreen(Game game) { + super(game); + + crateTexture = new Texture(glGame, "crate.png", true); + cube = createCube(); + light = new PointLight(); + light.setPosition(3, 3, -3); + camera = new EulerCamera(67, glGraphics.getWidth() / (float)glGraphics.getHeight(), 1, 100); + camera.getPosition().set(0, 1, 3); + + buttonTexture = new Texture(glGame, "button.png"); + batcher = new SpriteBatcher(glGraphics, 1); + guiCamera = new Camera2D(glGraphics, 480, 320); + buttonRegion = new TextureRegion(buttonTexture, 0, 0, 64, 64); + touchPos = new Vector2(); + } + + private Vertices3 createCube() { + float[] vertices = { -0.5f, -0.5f, 0.5f, 0, 1, 0, 0, 1, + 0.5f, -0.5f, 0.5f, 1, 1, 0, 0, 1, + 0.5f, 0.5f, 0.5f, 1, 0, 0, 0, 1, + -0.5f, 0.5f, 0.5f, 0, 0, 0, 0, 1, + + 0.5f, -0.5f, 0.5f, 0, 1, 1, 0, 0, + 0.5f, -0.5f, -0.5f, 1, 1, 1, 0, 0, + 0.5f, 0.5f, -0.5f, 1, 0, 1, 0, 0, + 0.5f, 0.5f, 0.5f, 0, 0, 1, 0, 0, + + 0.5f, -0.5f, -0.5f, 0, 1, 0, 0, -1, + -0.5f, -0.5f, -0.5f, 1, 1, 0, 0, -1, + -0.5f, 0.5f, -0.5f, 1, 0, 0, 0, -1, + 0.5f, 0.5f, -0.5f, 0, 0, 0, 0, -1, + + -0.5f, -0.5f, -0.5f, 0, 1, -1, 0, 0, + -0.5f, -0.5f, 0.5f, 1, 1, -1, 0, 0, + -0.5f, 0.5f, 0.5f, 1, 0, -1, 0, 0, + -0.5f, 0.5f, -0.5f, 0, 0, -1, 0, 0, + + -0.5f, 0.5f, 0.5f, 0, 1, 0, 1, 0, + 0.5f, 0.5f, 0.5f, 1, 1, 0, 1, 0, + 0.5f, 0.5f, -0.5f, 1, 0, 0, 1, 0, + -0.5f, 0.5f, -0.5f, 0, 0, 0, 1, 0, + + -0.5f, -0.5f, -0.5f, 0, 1, 0, -1, 0, + 0.5f, -0.5f, -0.5f, 1, 1, 0, -1, 0, + 0.5f, -0.5f, 0.5f, 1, 0, 0, -1, 0, + -0.5f, -0.5f, 0.5f, 0, 0, 0, -1, 0 }; + short[] indices = { 0, 1, 2, 2, 3, 0, + 4, 5, 6, 6, 7, 4, + 8, 9, 10, 10, 11, 8, + 12, 13, 14, 14, 15, 12, + 16, 17, 18, 18, 19, 16, + 20, 21, 22, 22, 23, 20, + 24, 25, 26, 26, 27, 24 }; + Vertices3 cube = new Vertices3(glGraphics, vertices.length / 8, indices.length, false, true, true); + cube.setVertices(vertices, 0, vertices.length); + cube.setIndices(indices, 0, indices.length); + return cube; + } + + @Override + public void resume() { + crateTexture.reload(); + } + + @Override + public void update(float deltaTime) { + game.getInput().getTouchEvents(); + float x = game.getInput().getTouchX(0); + float y = game.getInput().getTouchY(0); + guiCamera.touchToWorld(touchPos.set(x, y)); + + + if(game.getInput().isTouchDown(0)) { + if(touchPos.x < 64 && touchPos.y < 64) { + Vector3 direction = camera.getDirection(); + camera.getPosition().add(direction.mul(deltaTime)); + } else { + if(lastX == -1) { + lastX = x; + lastY = y; + } else { + camera.rotate((x - lastX) / 10, (y - lastY) / 10); + lastX = x; + lastY = y; + } + } + } else { + lastX = -1; + lastY = -1; + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + + camera.setMatrices(gl); + + gl.glEnable(GL10.GL_DEPTH_TEST); + gl.glEnable(GL10.GL_TEXTURE_2D); + gl.glEnable(GL10.GL_LIGHTING); + + crateTexture.bind(); + cube.bind(); + light.enable(gl, GL10.GL_LIGHT0); + + for(int z = 0; z >= -8; z-=2) { + for(int x = -4; x <=4; x+=2 ) { + gl.glPushMatrix(); + gl.glTranslatef(x, 0, z); + cube.draw(GL10.GL_TRIANGLES, 0, 6 * 2 * 3); + gl.glPopMatrix(); + } + } + + cube.unbind(); + + gl.glDisable(GL10.GL_LIGHTING); + gl.glDisable(GL10.GL_DEPTH_TEST); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + guiCamera.setViewportAndMatrices(); + batcher.beginBatch(buttonTexture); + batcher.drawSprite(32, 32, 64, 64, buttonRegion); + batcher.endBatch(); + + gl.glDisable(GL10.GL_BLEND); + gl.glDisable(GL10.GL_TEXTURE_2D); + } + + @Override + public void pause() { + + } + + @Override + public void dispose() { + } + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/GLAdvancedStarter.java b/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/GLAdvancedStarter.java new file mode 100644 index 0000000..457dd73 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/GLAdvancedStarter.java @@ -0,0 +1,33 @@ +package com.badlogic.androidgames.gladvanced; + +import android.app.ListActivity; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +public class GLAdvancedStarter extends ListActivity { + String tests[] = { "LightTest", "EulerCameraTest", "ObjTest"}; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setListAdapter(new ArrayAdapter(this, + android.R.layout.simple_list_item_1, tests)); + } + + @Override + protected void onListItemClick(ListView list, View view, int position, + long id) { + super.onListItemClick(list, view, position, id); + String testName = tests[position]; + try { + Class clazz = Class.forName("com.badlogic.androidgames.gladvanced." + + testName); + Intent intent = new Intent(this, clazz); + startActivity(intent); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/LightTest.java b/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/LightTest.java new file mode 100644 index 0000000..63a022f --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/LightTest.java @@ -0,0 +1,149 @@ +package com.badlogic.androidgames.gladvanced; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLU; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.AmbientLight; +import com.badlogic.androidgames.framework.gl.DirectionalLight; +import com.badlogic.androidgames.framework.gl.Material; +import com.badlogic.androidgames.framework.gl.Vertices3; +import com.badlogic.androidgames.framework.gl.PointLight; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLScreen; + +public class LightTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new LightScreen(this); + } + + class LightScreen extends GLScreen { + float angle; + Vertices3 cube; + Texture texture; + AmbientLight ambientLight; + PointLight pointLight; + DirectionalLight directionalLight; + Material material; + + public LightScreen(Game game) { + super(game); + + cube = createCube(); + texture = new Texture(glGame, "crate.png"); + ambientLight = new AmbientLight(); + ambientLight.setColor(0, 0.2f, 0, 1); + pointLight = new PointLight(); + pointLight.setDiffuse(1, 0, 0, 1); + pointLight.setPosition(3, 3, 0); + directionalLight = new DirectionalLight(); + directionalLight.setDiffuse(0, 0, 1, 1); + directionalLight.setDirection(1, 0, 0); + material = new Material(); + } + + @Override + public void resume() { + texture.reload(); + } + + private Vertices3 createCube() { + float[] vertices = { -0.5f, -0.5f, 0.5f, 0, 1, 0, 0, 1, + 0.5f, -0.5f, 0.5f, 1, 1, 0, 0, 1, + 0.5f, 0.5f, 0.5f, 1, 0, 0, 0, 1, + -0.5f, 0.5f, 0.5f, 0, 0, 0, 0, 1, + + 0.5f, -0.5f, 0.5f, 0, 1, 1, 0, 0, + 0.5f, -0.5f, -0.5f, 1, 1, 1, 0, 0, + 0.5f, 0.5f, -0.5f, 1, 0, 1, 0, 0, + 0.5f, 0.5f, 0.5f, 0, 0, 1, 0, 0, + + 0.5f, -0.5f, -0.5f, 0, 1, 0, 0, -1, + -0.5f, -0.5f, -0.5f, 1, 1, 0, 0, -1, + -0.5f, 0.5f, -0.5f, 1, 0, 0, 0, -1, + 0.5f, 0.5f, -0.5f, 0, 0, 0, 0, -1, + + -0.5f, -0.5f, -0.5f, 0, 1, -1, 0, 0, + -0.5f, -0.5f, 0.5f, 1, 1, -1, 0, 0, + -0.5f, 0.5f, 0.5f, 1, 0, -1, 0, 0, + -0.5f, 0.5f, -0.5f, 0, 0, -1, 0, 0, + + -0.5f, 0.5f, 0.5f, 0, 1, 0, 1, 0, + 0.5f, 0.5f, 0.5f, 1, 1, 0, 1, 0, + 0.5f, 0.5f, -0.5f, 1, 0, 0, 1, 0, + -0.5f, 0.5f, -0.5f, 0, 0, 0, 1, 0, + + -0.5f, -0.5f, -0.5f, 0, 1, 0, -1, 0, + 0.5f, -0.5f, -0.5f, 1, 1, 0, -1, 0, + 0.5f, -0.5f, 0.5f, 1, 0, 0, -1, 0, + -0.5f, -0.5f, 0.5f, 0, 0, 0, -1, 0 }; + short[] indices = { 0, 1, 2, 2, 3, 0, + 4, 5, 6, 6, 7, 4, + 8, 9, 10, 10, 11, 8, + 12, 13, 14, 14, 15, 12, + 16, 17, 18, 18, 19, 16, + 20, 21, 22, 22, 23, 20, + 24, 25, 26, 26, 27, 24 }; + Vertices3 cube = new Vertices3(glGraphics, vertices.length / 8, indices.length, false, true, true); + cube.setVertices(vertices, 0, vertices.length); + cube.setIndices(indices, 0, indices.length); + return cube; + } + + @Override + public void update(float deltaTime) { + angle += deltaTime * 20; + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClearColor(0.2f, 0.2f, 0.2f, 1.0f); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); + gl.glEnable(GL10.GL_DEPTH_TEST); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + GLU.gluPerspective(gl, 67, glGraphics.getWidth() + / (float) glGraphics.getHeight(), 0.1f, 10f); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + GLU.gluLookAt(gl, 0, 1, 3, 0, 0, 0, 0, 1, 0); + + gl.glEnable(GL10.GL_LIGHTING); + + ambientLight.enable(gl); + pointLight.enable(gl, GL10.GL_LIGHT0); + directionalLight.enable(gl, GL10.GL_LIGHT1); + material.enable(gl); + + gl.glEnable(GL10.GL_TEXTURE_2D); + texture.bind(); + + gl.glRotatef(angle, 0, 1, 0); + cube.bind(); + cube.draw(GL10.GL_TRIANGLES, 0, 6 * 2 * 3); + cube.unbind(); + + pointLight.disable(gl); + directionalLight.disable(gl); + + gl.glDisable(GL10.GL_TEXTURE_2D); + gl.glDisable(GL10.GL_DEPTH_TEST); + } + + @Override + public void pause() { + } + + @Override + public void dispose() { + } + } +} \ No newline at end of file diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/ObjTest.java b/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/ObjTest.java new file mode 100644 index 0000000..bad48b3 --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/ObjTest.java @@ -0,0 +1,142 @@ +package com.badlogic.androidgames.gladvanced; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.EulerCamera; +import com.badlogic.androidgames.framework.gl.ObjLoader; +import com.badlogic.androidgames.framework.gl.PointLight; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.TextureRegion; +import com.badlogic.androidgames.framework.gl.Vertices3; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.Vector2; +import com.badlogic.androidgames.framework.math.Vector3; + +public class ObjTest extends GLGame { + + @Override + public Screen getStartScreen() { + return new ObjScreen(this); + } + + class ObjScreen extends GLScreen { + + Texture crateTexture; + Vertices3 cube; + PointLight light; + EulerCamera camera; + Texture buttonTexture; + SpriteBatcher batcher; + Camera2D guiCamera; + TextureRegion buttonRegion; + Vector2 touchPos; + float lastX = -1; + float lastY = -1; + + public ObjScreen(Game game) { + super(game); + + crateTexture = new Texture(glGame, "crate.png", true); + cube = ObjLoader.load(glGame, "cube.obj"); + light = new PointLight(); + light.setPosition(3, 3, -3); + camera = new EulerCamera(67, glGraphics.getWidth() + / (float) glGraphics.getHeight(), 1, 100); + camera.getPosition().set(0, 1, 3); + + buttonTexture = new Texture(glGame, "button.png"); + batcher = new SpriteBatcher(glGraphics, 1); + guiCamera = new Camera2D(glGraphics, 480, 320); + buttonRegion = new TextureRegion(buttonTexture, 0, 0, 64, 64); + touchPos = new Vector2(); + } + + @Override + public void resume() { + crateTexture.reload(); + } + + @Override + public void update(float deltaTime) { + game.getInput().getTouchEvents(); + float x = game.getInput().getTouchX(0); + float y = game.getInput().getTouchY(0); + guiCamera.touchToWorld(touchPos.set(x, y)); + + if (game.getInput().isTouchDown(0)) { + if (touchPos.x < 64 && touchPos.y < 64) { + Vector3 direction = camera.getDirection(); + camera.getPosition().add(direction.mul(deltaTime)); + } else { + if (lastX == -1) { + lastX = x; + lastY = y; + } else { + camera.rotate((x - lastX) / 10, (y - lastY) / 10); + lastX = x; + lastY = y; + } + } + } else { + lastX = -1; + lastY = -1; + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + + camera.setMatrices(gl); + + gl.glEnable(GL10.GL_DEPTH_TEST); + gl.glEnable(GL10.GL_TEXTURE_2D); + gl.glEnable(GL10.GL_LIGHTING); + + crateTexture.bind(); + cube.bind(); + light.enable(gl, GL10.GL_LIGHT0); + + for (int z = 0; z >= -8; z -= 2) { + for (int x = -4; x <= 4; x += 2) { + gl.glPushMatrix(); + gl.glTranslatef(x, 0, z); + cube.draw(GL10.GL_TRIANGLES, 0, cube.getNumVertices()); + gl.glPopMatrix(); + } + } + + cube.unbind(); + + gl.glDisable(GL10.GL_LIGHTING); + gl.glDisable(GL10.GL_DEPTH_TEST); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + guiCamera.setViewportAndMatrices(); + batcher.beginBatch(buttonTexture); + batcher.drawSprite(32, 32, 64, 64, buttonRegion); + batcher.endBatch(); + + gl.glDisable(GL10.GL_BLEND); + gl.glDisable(GL10.GL_TEXTURE_2D); + } + + @Override + public void pause() { + + } + + @Override + public void dispose() { + } + } +} diff --git a/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/PhysicsTest.java b/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/PhysicsTest.java new file mode 100644 index 0000000..f03b69e --- /dev/null +++ b/ch11-gl-advanced/src/com/badlogic/androidgames/gladvanced/PhysicsTest.java @@ -0,0 +1,5 @@ +package com.badlogic.androidgames.gladvanced; + +public class PhysicsTest { + +} diff --git a/ch12-droidinvaders/.classpath b/ch12-droidinvaders/.classpath new file mode 100644 index 0000000..609aa00 --- /dev/null +++ b/ch12-droidinvaders/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ch12-droidinvaders/.project b/ch12-droidinvaders/.project new file mode 100644 index 0000000..428c0c6 --- /dev/null +++ b/ch12-droidinvaders/.project @@ -0,0 +1,33 @@ + + + ch12-droidinvaders + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/ch12-droidinvaders/AndroidManifest.xml b/ch12-droidinvaders/AndroidManifest.xml new file mode 100644 index 0000000..c7704e5 --- /dev/null +++ b/ch12-droidinvaders/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch12-droidinvaders/assets/background.jpg b/ch12-droidinvaders/assets/background.jpg new file mode 100644 index 0000000..c2ec817 Binary files /dev/null and b/ch12-droidinvaders/assets/background.jpg differ diff --git a/ch12-droidinvaders/assets/click.ogg b/ch12-droidinvaders/assets/click.ogg new file mode 100644 index 0000000..1dce73c Binary files /dev/null and b/ch12-droidinvaders/assets/click.ogg differ diff --git a/ch12-droidinvaders/assets/explode.png b/ch12-droidinvaders/assets/explode.png new file mode 100644 index 0000000..45af00c Binary files /dev/null and b/ch12-droidinvaders/assets/explode.png differ diff --git a/ch12-droidinvaders/assets/explosion.ogg b/ch12-droidinvaders/assets/explosion.ogg new file mode 100644 index 0000000..d7cc022 Binary files /dev/null and b/ch12-droidinvaders/assets/explosion.ogg differ diff --git a/ch12-droidinvaders/assets/invader.obj b/ch12-droidinvaders/assets/invader.obj new file mode 100644 index 0000000..bb9a93e --- /dev/null +++ b/ch12-droidinvaders/assets/invader.obj @@ -0,0 +1,258 @@ +# Exported from Wings 3D 1.0.1 +mtllib invader.mtl +o sphere2 +#26 vertices, 48 faces +v 0.48434630 -0.19891004 -4.6144371e-17 +v 0.34248455 -0.19891004 -0.34248455 +v 2.9657657e-17 -0.19891004 -0.48434630 +v -0.34248455 -0.19891004 -0.34248455 +v -0.48434630 -0.19891004 -1.0545969e-16 +v -0.34248455 -0.19891004 0.34248455 +v -8.8972972e-17 -0.19891004 0.48434630 +v 0.34248455 -0.19891004 0.34248455 +v 0.68496911 -7.9760847e-2 -7.8230687e-18 +v 0.48434630 -7.9760847e-2 -0.48434630 +v 4.1942261e-17 -7.9760847e-2 -0.68496911 +v -0.48434630 -7.9760847e-2 -0.48434630 +v -0.68496911 -7.9760847e-2 -9.1707591e-17 +v -0.48434630 -7.9760847e-2 0.48434630 +v -1.2582678e-16 -7.9760847e-2 0.68496911 +v 0.48434630 -7.9760847e-2 0.48434630 +v 0.48434630 3.9388343e-2 3.0498234e-17 +v 0.34248455 3.9388343e-2 -0.34248455 +v 2.9657657e-17 3.9388343e-2 -0.48434630 +v -0.34248455 3.9388343e-2 -0.34248455 +v -0.48434630 3.9388343e-2 -2.8817081e-17 +v -0.34248455 3.9388343e-2 0.34248455 +v -8.8972972e-17 3.9388343e-2 0.48434630 +v 0.34248455 3.9388343e-2 0.34248455 +v 0.0000000e+0 -0.24826325 -6.2017574e-17 +v 0.0000000e+0 0.24826325 9.7677527e-17 +vt 0.0000000e+0 0.25453378 +vt 0.0000000e+0 0.75191835 +vt 7.2037289e-2 0.92680319 +vt 7.2587157e-2 0.57667745 +vt 7.2884705e-2 7.5972196e-2 +vt 7.4350202e-2 0.43403105 +vt 8.1827405e-2 0.75206450 +vt 8.3701605e-2 0.25414466 +vt 0.13001938 0.86920479 +vt 0.13051225 0.63498840 +vt 0.13239424 0.13445791 +vt 0.13371519 0.37362684 +vt 0.24644001 1.00000000 +vt 0.24683713 0.91817343 +vt 0.24737306 0.75262768 +vt 0.24790900 0.58708193 +vt 0.24830612 0.50525536 +vt 0.25015469 0.0000000e+0 +vt 0.25121804 8.3695755e-2 +vt 0.25263264 0.25262768 +vt 0.25404724 0.42155960 +vt 0.25511060 0.50525536 +vt 0.36423388 0.87026696 +vt 0.36472674 0.63605057 +vt 0.37155009 0.13162851 +vt 0.37287104 0.37079745 +vt 0.41291872 0.75319086 +vt 0.42156368 0.25111070 +vt 0.42215897 0.92857791 +vt 0.42270884 0.57845216 +vt 0.43091508 7.1224306e-2 +vt 0.43238058 0.42928316 +vt 0.49474613 0.75333701 +vt 0.50526528 0.25072157 +vn 0.10128238 -0.99397277 -4.1952536e-2 +vn 0.10128238 -0.99397277 4.1952536e-2 +vn 0.49957938 -0.84118925 -0.20693256 +vn 0.49957938 -0.84118925 0.20693256 +vn 0.49957938 -0.84118925 -0.20693256 +vn 0.49957938 -0.84118925 0.20693256 +vn 0.10128238 -0.99397277 -4.1952536e-2 +vn 4.1952536e-2 -0.99397277 -0.10128238 +vn 0.49957938 -0.84118925 -0.20693256 +vn 0.20693256 -0.84118925 -0.49957938 +vn 4.1952536e-2 -0.99397277 -0.10128238 +vn -4.1952536e-2 -0.99397277 -0.10128238 +vn 0.20693256 -0.84118925 -0.49957938 +vn -0.20693256 -0.84118925 -0.49957938 +vn -0.20693256 -0.84118925 -0.49957938 +vn 0.20693256 -0.84118925 -0.49957938 +vn -4.1952536e-2 -0.99397277 -0.10128238 +vn -0.10128238 -0.99397277 -4.1952536e-2 +vn -0.49957938 -0.84118925 -0.20693256 +vn -0.20693256 -0.84118925 -0.49957938 +vn -0.10128238 -0.99397277 -4.1952536e-2 +vn -0.10128238 -0.99397277 4.1952536e-2 +vn -0.49957938 -0.84118925 -0.20693256 +vn -0.49957938 -0.84118925 0.20693256 +vn -0.49957938 -0.84118925 -0.20693256 +vn -0.49957938 -0.84118925 0.20693256 +vn -0.10128238 -0.99397277 4.1952536e-2 +vn -4.1952536e-2 -0.99397277 0.10128238 +vn -0.20693256 -0.84118925 0.49957938 +vn -0.49957938 -0.84118925 0.20693256 +vn -4.1952536e-2 -0.99397277 0.10128238 +vn 4.1952536e-2 -0.99397277 0.10128238 +vn -0.20693256 -0.84118925 0.49957938 +vn 0.20693256 -0.84118925 0.49957938 +vn -0.20693256 -0.84118925 0.49957938 +vn 0.20693256 -0.84118925 0.49957938 +vn 4.1952536e-2 -0.99397277 0.10128238 +vn 0.10128238 -0.99397277 4.1952536e-2 +vn 0.20693256 -0.84118925 0.49957938 +vn 0.49957938 -0.84118925 0.20693256 +vn 0.49957938 -0.84118925 -0.20693256 +vn 0.49957938 -0.84118925 0.20693256 +vn 0.49957938 0.84118925 -0.20693256 +vn 0.49957938 0.84118925 0.20693256 +vn 0.49957938 -0.84118925 -0.20693256 +vn 0.20693256 -0.84118925 -0.49957938 +vn 0.49957938 0.84118925 -0.20693256 +vn 0.20693256 0.84118925 -0.49957938 +vn 0.49957938 -0.84118925 -0.20693256 +vn 0.49957938 0.84118925 -0.20693256 +vn 0.20693256 0.84118925 -0.49957938 +vn 0.20693256 -0.84118925 -0.49957938 +vn 0.20693256 -0.84118925 -0.49957938 +vn -0.20693256 -0.84118925 -0.49957938 +vn -0.20693256 0.84118925 -0.49957938 +vn 0.20693256 0.84118925 -0.49957938 +vn -0.20693256 -0.84118925 -0.49957938 +vn -0.49957938 -0.84118925 -0.20693256 +vn -0.20693256 0.84118925 -0.49957938 +vn -0.49957938 0.84118925 -0.20693256 +vn -0.49957938 0.84118925 -0.20693256 +vn -0.20693256 0.84118925 -0.49957938 +vn -0.49957938 -0.84118925 -0.20693256 +vn -0.20693256 -0.84118925 -0.49957938 +vn -0.49957938 -0.84118925 -0.20693256 +vn -0.49957938 -0.84118925 0.20693256 +vn -0.49957938 0.84118925 0.20693256 +vn -0.49957938 0.84118925 -0.20693256 +vn -0.49957938 -0.84118925 0.20693256 +vn -0.20693256 -0.84118925 0.49957938 +vn -0.49957938 0.84118925 0.20693256 +vn -0.20693256 0.84118925 0.49957938 +vn -0.20693256 0.84118925 0.49957938 +vn -0.49957938 0.84118925 0.20693256 +vn -0.20693256 -0.84118925 0.49957938 +vn -0.49957938 -0.84118925 0.20693256 +vn -0.20693256 -0.84118925 0.49957938 +vn 0.20693256 -0.84118925 0.49957938 +vn 0.20693256 0.84118925 0.49957938 +vn -0.20693256 0.84118925 0.49957938 +vn 0.20693256 -0.84118925 0.49957938 +vn 0.49957938 -0.84118925 0.20693256 +vn 0.20693256 0.84118925 0.49957938 +vn 0.49957938 0.84118925 0.20693256 +vn 0.49957938 0.84118925 0.20693256 +vn 0.20693256 0.84118925 0.49957938 +vn 0.20693256 -0.84118925 0.49957938 +vn 0.49957938 -0.84118925 0.20693256 +vn 0.39077512 0.90614278 -0.16186436 +vn 0.39077512 0.90614278 0.16186436 +vn 0.49957938 0.84118925 -0.20693256 +vn 0.49957938 0.84118925 0.20693256 +vn 0.49957938 0.84118925 -0.20693256 +vn 0.49957938 0.84118925 0.20693256 +vn 0.39077512 0.90614278 -0.16186436 +vn 0.16186436 0.90614278 -0.39077512 +vn 0.49957938 0.84118925 -0.20693256 +vn 0.20693256 0.84118925 -0.49957938 +vn 0.16186436 0.90614278 -0.39077512 +vn -0.16186436 0.90614278 -0.39077512 +vn 0.20693256 0.84118925 -0.49957938 +vn -0.20693256 0.84118925 -0.49957938 +vn -0.20693256 0.84118925 -0.49957938 +vn 0.20693256 0.84118925 -0.49957938 +vn -0.16186436 0.90614278 -0.39077512 +vn -0.39077512 0.90614278 -0.16186436 +vn -0.20693256 0.84118925 -0.49957938 +vn -0.49957938 0.84118925 -0.20693256 +vn -0.39077512 0.90614278 -0.16186436 +vn -0.39077512 0.90614278 0.16186436 +vn -0.49957938 0.84118925 -0.20693256 +vn -0.49957938 0.84118925 0.20693256 +vn -0.49957938 0.84118925 0.20693256 +vn -0.49957938 0.84118925 -0.20693256 +vn -0.39077512 0.90614278 0.16186436 +vn -0.16186436 0.90614278 0.39077512 +vn -0.49957938 0.84118925 0.20693256 +vn -0.20693256 0.84118925 0.49957938 +vn -0.16186436 0.90614278 0.39077512 +vn 0.16186436 0.90614278 0.39077512 +vn -0.20693256 0.84118925 0.49957938 +vn 0.20693256 0.84118925 0.49957938 +vn 0.20693256 0.84118925 0.49957938 +vn -0.20693256 0.84118925 0.49957938 +vn 0.16186436 0.90614278 0.39077512 +vn 0.39077512 0.90614278 0.16186436 +vn 0.20693256 0.84118925 0.49957938 +vn 0.49957938 0.84118925 0.20693256 +vn 0.10128238 -0.99397277 -4.1952536e-2 +vn 4.1952536e-2 -0.99397277 -0.10128238 +vn -4.1952536e-2 -0.99397277 -0.10128238 +vn -0.10128238 -0.99397277 -4.1952536e-2 +vn -0.10128238 -0.99397277 4.1952536e-2 +vn -4.1952536e-2 -0.99397277 0.10128238 +vn 4.1952536e-2 -0.99397277 0.10128238 +vn 0.10128238 -0.99397277 4.1952536e-2 +vn 0.39077512 0.90614278 -0.16186436 +vn 0.16186436 0.90614278 -0.39077512 +vn -0.16186436 0.90614278 -0.39077512 +vn -0.39077512 0.90614278 -0.16186436 +vn -0.39077512 0.90614278 0.16186436 +vn -0.16186436 0.90614278 0.39077512 +vn 0.16186436 0.90614278 0.39077512 +vn 0.39077512 0.90614278 0.16186436 +g sphere2_sphere2_auv +usemtl sphere2_auv +f 1/27/3 10/30/45 9/33/41 +f 1/27/6 16/29/88 8/23/40 +f 1/27/1 25/15/129 2/24/7 +f 2/24/9 10/30/49 1/27/5 +f 2/24/8 25/15/130 3/16/11 +f 3/16/16 10/30/52 2/24/10 +f 3/16/14 12/4/57 11/17/54 +f 3/16/12 25/15/131 4/10/17 +f 4/10/20 12/4/64 3/16/15 +f 4/10/18 25/15/132 5/7/21 +f 5/7/25 12/4/63 4/10/19 +f 5/7/24 14/3/69 13/2/66 +f 5/7/22 25/15/133 6/9/27 +f 6/9/30 14/3/76 5/7/26 +f 6/9/28 25/15/134 7/14/31 +f 7/14/35 14/3/75 6/9/29 +f 7/14/34 16/29/81 15/13/78 +f 7/14/32 25/15/135 8/23/37 +f 8/23/39 16/29/87 7/14/36 +f 8/23/38 25/15/136 1/27/2 +f 9/33/42 16/29/82 1/27/4 +f 9/1/44 17/8/94 16/6/85 +f 10/5/50 17/8/93 9/1/43 +f 10/5/48 19/19/101 18/11/98 +f 11/17/53 10/30/46 3/16/13 +f 11/18/56 19/19/104 10/5/51 +f 12/31/62 19/19/103 11/18/55 +f 12/31/60 21/28/111 20/25/108 +f 13/2/65 12/4/58 5/7/23 +f 13/34/68 21/28/114 12/31/61 +f 14/32/74 21/28/113 13/34/67 +f 14/32/72 23/21/121 22/26/118 +f 15/13/77 14/3/70 7/14/33 +f 15/22/80 23/21/124 14/32/73 +f 16/6/84 17/8/92 24/12/128 +f 16/6/86 23/21/123 15/22/79 +f 17/8/90 26/20/144 24/12/126 +f 18/11/97 17/8/91 10/5/47 +f 18/11/95 26/20/137 17/8/89 +f 19/19/99 26/20/138 18/11/96 +f 20/25/107 19/19/102 12/31/59 +f 20/25/105 26/20/139 19/19/100 +f 21/28/109 26/20/140 20/25/106 +f 22/26/117 21/28/112 14/32/71 +f 22/26/115 26/20/141 21/28/110 +f 23/21/119 26/20/142 22/26/116 +f 24/12/127 23/21/122 16/6/83 +f 24/12/125 26/20/143 23/21/120 diff --git a/ch12-droidinvaders/assets/invader.png b/ch12-droidinvaders/assets/invader.png new file mode 100644 index 0000000..1378a40 Binary files /dev/null and b/ch12-droidinvaders/assets/invader.png differ diff --git a/ch12-droidinvaders/assets/items.png b/ch12-droidinvaders/assets/items.png new file mode 100644 index 0000000..35a368c Binary files /dev/null and b/ch12-droidinvaders/assets/items.png differ diff --git a/ch12-droidinvaders/assets/logo.png b/ch12-droidinvaders/assets/logo.png new file mode 100644 index 0000000..df135d4 Binary files /dev/null and b/ch12-droidinvaders/assets/logo.png differ diff --git a/ch12-droidinvaders/assets/music.mp3 b/ch12-droidinvaders/assets/music.mp3 new file mode 100644 index 0000000..b74a503 Binary files /dev/null and b/ch12-droidinvaders/assets/music.mp3 differ diff --git a/ch12-droidinvaders/assets/shield.obj b/ch12-droidinvaders/assets/shield.obj new file mode 100644 index 0000000..4d25356 --- /dev/null +++ b/ch12-droidinvaders/assets/shield.obj @@ -0,0 +1,58 @@ +# Exported from Wings 3D 1.2 +mtllib shield.mtl +o cube1 +#8 vertices, 12 faces +v -0.57735027 -0.14780167 0.57735027 +v -0.57735027 0.14780167 0.57735027 +v 0.57735027 0.14780167 0.57735027 +v 0.57735027 -0.14780167 0.57735027 +v -0.57735027 -0.14780167 -0.57735027 +v -0.57735027 0.14780167 -0.57735027 +v 0.57735027 0.14780167 -0.57735027 +v 0.57735027 -0.14780167 -0.57735027 +vt 0.0000000e+0 8.3266727e-17 +vt 0.0000000e+0 1.00000000 +vt 8.3266727e-17 0.50000000 +vt 0.50000000 0.0000000e+0 +vt 0.50000000 1.00000000 +vt 0.50000000 0.50000000 +vt 0.50000000 0.12800000 +vt 0.50000000 0.12800000 +vt 0.50000000 0.25600000 +vt 0.50000000 0.38400000 +vt 0.50000000 0.50000000 +vt 0.50000000 0.51200000 +vt 0.50000000 0.0000000e+0 +vt 0.50000000 0.25600000 +vt 0.50000000 0.38400000 +vt 1.00000000 0.12800000 +vt 1.00000000 0.25600000 +vt 1.00000000 0.25600000 +vt 1.00000000 0.38400000 +vt 1.00000000 0.38400000 +vt 1.00000000 0.51200000 +vt 1.00000000 2.9143354e-16 +vt 1.00000000 0.12800000 +vn -0.57735027 -0.57735027 0.57735027 +vn -0.57735027 0.57735027 0.57735027 +vn 0.57735027 0.57735027 0.57735027 +vn 0.57735027 -0.57735027 0.57735027 +vn -0.57735027 -0.57735027 -0.57735027 +vn -0.57735027 0.57735027 -0.57735027 +vn 0.57735027 0.57735027 -0.57735027 +vn 0.57735027 -0.57735027 -0.57735027 +g cube1_cube1_auv +usemtl cube1_auv +s 1 +f 1/9/1 4/18/4 2/10/2 +f 1/4/1 5/11/5 4/1/4 +f 2/10/2 4/18/4 3/19/3 +f 2/17/2 5/8/5 1/16/1 +f 2/17/2 6/14/6 5/8/5 +f 2/3/2 7/5/7 6/2/6 +f 3/6/3 7/5/7 2/3/2 +f 4/15/4 7/21/7 3/12/3 +f 4/15/4 8/20/8 7/21/7 +f 5/11/5 8/3/8 4/1/4 +f 6/23/6 7/7/7 5/22/5 +f 7/7/7 8/13/8 5/22/5 diff --git a/ch12-droidinvaders/assets/ship.mtl b/ch12-droidinvaders/assets/ship.mtl new file mode 100644 index 0000000..47a146c --- /dev/null +++ b/ch12-droidinvaders/assets/ship.mtl @@ -0,0 +1,10 @@ +# Exported from Wings 3D 1.2 +newmtl cube1_auv2 +Ns 100.0 +d 1.0 +illum 2 +Kd 1.0 1.0 1.0 +Ka 1.0 1.0 1.0 +Ks 1.0 1.0 1.0 +Ke 0.0 0.0 0.0 + diff --git a/ch12-droidinvaders/assets/ship.obj b/ch12-droidinvaders/assets/ship.obj new file mode 100644 index 0000000..a0ac4a7 --- /dev/null +++ b/ch12-droidinvaders/assets/ship.obj @@ -0,0 +1,239 @@ +# Exported from Wings 3D 1.2 +mtllib ship.mtl +o cube1 +#48 vertices, 92 faces +v 0.46604296 -4.6098739e-2 0.14070385 +v 0.46604296 4.5631895e-2 0.14070385 +v 0.28477197 -9.3266190e-2 0.61334395 +v 0.28477197 4.5631895e-2 0.61334395 +v 5.3436673e-2 -4.9075029e-2 -0.51874031 +v 5.3436673e-2 -0.10214269 -0.51874031 +v 1.8055049e-16 -4.9075029e-2 -0.54243393 +v 1.8055049e-16 -0.11873436 -0.54243393 +v 4.6464257e-17 0.11653721 0.55246322 +v 4.6464257e-17 -0.16403757 0.55246322 +v 9.6890236e-17 0.16403757 0.14070385 +v 9.6890236e-17 -0.16403757 0.14070385 +v 0.64756828 -3.8727536e-2 0.43975538 +v 0.64756828 7.1441743e-2 0.43975538 +v 0.49663244 7.1441743e-2 0.94584965 +v 0.49663244 -3.8727536e-2 0.94584965 +v 4.0833684e-2 -0.10214269 -0.53285566 +v 4.0833684e-2 -4.9075029e-2 -0.53285566 +v 0.16961230 0.11653721 0.58290359 +v 0.16961230 -0.14744589 0.58290359 +v 0.16961230 0.16403757 0.14070385 +v 0.16961230 -0.14744589 0.14070385 +v 9.1308654e-2 4.5778787e-2 -0.27071349 +v 9.1308654e-2 -0.10009243 -0.27071349 +v 1.4727433e-16 4.5778787e-2 -0.27071349 +v 1.4727433e-16 -0.11668411 -0.27071349 +v 0.18261731 -6.9341139e-2 -0.27071349 +v 0.18261731 -1.7215671e-3 -0.27071349 +v -0.46604296 -4.6098739e-2 0.14070385 +v -0.46604296 4.5631895e-2 0.14070385 +v -0.28477197 -9.3266190e-2 0.61334395 +v -0.28477197 4.5631895e-2 0.61334395 +v -5.3436673e-2 -4.9075029e-2 -0.51874031 +v -5.3436673e-2 -0.10214269 -0.51874031 +v -0.64756828 -3.8727536e-2 0.43975538 +v -0.64756828 7.1441743e-2 0.43975538 +v -0.49663244 7.1441743e-2 0.94584965 +v -0.49663244 -3.8727536e-2 0.94584965 +v -4.0833684e-2 -0.10214269 -0.53285566 +v -4.0833684e-2 -4.9075029e-2 -0.53285566 +v -0.16961230 0.11653721 0.58290359 +v -0.16961230 -0.14744589 0.58290359 +v -0.16961230 0.16403757 0.14070385 +v -0.16961230 -0.14744589 0.14070385 +v -9.1308654e-2 4.5778787e-2 -0.27071349 +v -9.1308654e-2 -0.10009243 -0.27071349 +v -0.18261731 -6.9341139e-2 -0.27071349 +v -0.18261731 -1.7215671e-3 -0.27071349 +vt 0.0000000e+0 0.69053970 +vt 4.2285593e-2 0.71525416 +vt 0.11847455 0.48783383 +vt 0.16076015 0.51254829 +vt 0.16331352 0.33807909 +vt 0.17536475 0.59545214 +vt 0.18715376 0.64617830 +vt 0.21512694 0.12420912 +vt 0.23028866 0.37309998 +vt 0.27933871 3.3287708e-2 +vt 0.29111566 1.8231410e-2 +vt 0.29780728 0.0000000e+0 +vt 0.30306274 0.16395866 +vt 0.32256180 5.8152293e-2 +vt 0.33503069 5.1982011e-2 +vt 0.34659842 0.15855392 +vt 0.34866807 4.3914835e-2 +vt 0.35966131 0.56514644 +vt 0.36786859 0.34315696 +vt 0.38304782 0.66577476 +vt 0.38362189 0.14708561 +vt 0.40944282 0.52981351 +vt 0.43912922 0.32433135 +vt 0.44897715 0.70072227 +vt 0.48119448 0.50585981 +vt 0.52771590 0.47388085 +vt 0.52771590 0.68855148 +vt 0.53171542 0.29271280 +vt 0.54359004 0.11090107 +vt 0.54696463 8.9466723e-4 +vt 0.56615771 0.0000000e+0 +vt 0.58393029 6.3125387e-4 +vt 0.58452235 0.11195282 +vt 0.60240334 0.48892951 +vt 0.60614015 0.29553098 +vt 0.62473227 0.11194349 +vt 0.66991430 0.50491521 +vt 0.75162596 0.29695078 +vt 0.77873865 0.65209135 +vt 0.84024960 0.13148316 +vt 0.84024960 0.42828199 +vt 0.90677601 0.35344281 +vt 0.92024753 0.0000000e+0 +vt 1.00000000 0.20728785 +vn 0.62859449 -0.71523972 -0.30545230 +vn 0.59902783 0.72378279 -0.34249691 +vn -0.17629092 -0.75512933 0.63142792 +vn -0.29009660 0.73118062 0.61742924 +vn 0.76765327 0.30765683 -0.56218833 +vn 0.76554130 -0.41706350 -0.48990260 +vn -4.3185093e-18 0.58584147 -0.81042567 +vn 2.0070644e-17 -0.68788097 -0.72582351 +vn -2.3463113e-17 0.67205458 0.74050161 +vn -1.9828865e-17 -0.71101641 0.70317542 +vn 2.4365004e-17 0.99659696 -8.2428758e-2 +vn -3.5025123e-18 -0.99809517 -6.1693117e-2 +vn 0.87371643 -0.47456448 -0.10680896 +vn 0.85906289 0.49502137 -0.13024899 +vn 2.5259282e-2 0.78005441 0.62520163 +vn 0.19363679 -0.75105370 0.63120768 +vn 0.46685034 -0.66233383 -0.58597326 +vn 0.43933667 0.53877727 -0.71882010 +vn 1.8637950e-2 0.65266085 0.75742091 +vn 1.1600201e-2 -0.69589117 0.71805356 +vn 0.21499120 0.97146430 -0.10017937 +vn 0.21108971 -0.97452954 -7.5718562e-2 +vn 0.22170706 0.91432784 -0.33889612 +vn 0.22537855 -0.97060991 -8.4385455e-2 +vn 9.4540281e-18 0.95212268 -0.30571620 +vn 7.0649495e-18 -0.99856757 -5.3505201e-2 +vn 0.72138833 -0.57908891 -0.37980904 +vn 0.72268963 0.47984194 -0.49746499 +vn -0.62859449 -0.71523972 -0.30545230 +vn -0.59902783 0.72378279 -0.34249691 +vn 0.17629092 -0.75512933 0.63142792 +vn 0.29009660 0.73118062 0.61742924 +vn -0.76765327 0.30765683 -0.56218833 +vn -0.76554130 -0.41706350 -0.48990260 +vn -0.87371643 -0.47456448 -0.10680896 +vn -0.85906289 0.49502137 -0.13024899 +vn -2.5259282e-2 0.78005441 0.62520163 +vn -0.19363679 -0.75105370 0.63120768 +vn -0.46685034 -0.66233383 -0.58597326 +vn -0.43933667 0.53877727 -0.71882010 +vn -1.8637950e-2 0.65266085 0.75742091 +vn -1.1600201e-2 -0.69589117 0.71805356 +vn -0.21499120 0.97146430 -0.10017937 +vn -0.21108971 -0.97452954 -7.5718562e-2 +vn -0.22170706 0.91432784 -0.33889612 +vn -0.22537855 -0.97060991 -8.4385455e-2 +vn -0.72138833 -0.57908891 -0.37980904 +vn -0.72268963 0.47984194 -0.49746499 +g cube1_cube1_auv2 +usemtl cube1_auv2 +s 1 +f 1/5/1 14/4/14 13/3/13 +f 1/38/1 16/39/16 3/37/3 +f 1/5/1 28/13/28 2/9/2 +f 2/9/2 14/4/14 1/5/1 +f 2/9/2 21/19/21 4/18/4 +f 2/9/2 28/13/28 21/19/21 +f 3/20/3 16/7/16 4/18/4 +f 3/37/3 20/34/20 1/38/1 +f 4/44/4 14/40/14 2/43/2 +f 4/44/4 15/42/15 14/40/14 +f 4/18/4 16/7/16 15/6/15 +f 4/18/4 20/24/20 3/20/3 +f 4/18/4 21/19/21 19/22/19 +f 5/14/5 28/13/28 6/10/6 +f 6/10/6 18/15/18 5/14/5 +f 6/32/6 24/33/24 17/31/17 +f 6/32/6 27/36/27 24/33/24 +f 6/10/6 28/13/28 27/8/27 +f 7/17/7 18/15/18 8/12/8 +f 7/17/7 25/21/25 18/15/18 +f 7/17/7 40/15/40 25/21/25 +f 8/12/8 18/15/18 17/11/17 +f 8/12/8 40/15/40 7/17/7 +f 8/30/8 46/33/46 39/31/39 +f 9/25/9 20/24/20 19/22/19 +f 9/25/9 21/19/21 11/23/11 +f 9/25/9 42/24/42 10/27/10 +f 9/25/9 43/19/43 41/22/41 +f 10/27/10 20/24/20 9/25/9 +f 10/26/10 42/34/42 12/28/12 +f 11/23/11 43/19/43 9/25/9 +f 12/28/12 20/34/20 10/26/10 +f 12/28/12 22/35/22 20/34/20 +f 12/28/12 24/33/24 22/35/22 +f 12/28/12 26/29/26 24/33/24 +f 12/28/12 46/33/46 26/29/26 +f 13/41/13 16/39/16 1/38/1 +f 14/4/14 16/1/16 13/3/13 +f 15/2/15 16/1/16 14/4/14 +f 17/11/17 18/15/18 6/10/6 +f 17/31/17 24/33/24 8/30/8 +f 18/15/18 25/21/25 23/16/23 +f 18/15/18 28/13/28 5/14/5 +f 19/22/19 20/24/20 4/18/4 +f 19/22/19 21/19/21 9/25/9 +f 20/34/20 22/35/22 1/38/1 +f 21/19/21 25/21/25 11/23/11 +f 21/19/21 28/13/28 23/16/23 +f 22/35/22 24/33/24 1/38/1 +f 23/16/23 25/21/25 21/19/21 +f 23/16/23 28/13/28 18/15/18 +f 24/33/24 26/29/26 8/30/8 +f 24/33/24 27/36/27 1/38/1 +f 25/21/25 43/19/43 11/23/11 +f 25/21/25 45/16/45 43/19/43 +f 26/29/26 46/33/46 8/30/8 +f 27/8/27 28/13/28 1/5/1 +f 29/5/29 36/4/36 30/9/30 +f 29/38/29 38/39/38 35/41/35 +f 29/38/29 42/34/42 31/37/31 +f 29/38/29 44/35/44 42/34/42 +f 29/38/29 46/33/46 44/35/44 +f 29/38/29 47/36/47 46/33/46 +f 29/5/29 48/13/48 47/8/47 +f 30/43/30 36/40/36 32/44/32 +f 30/9/30 48/13/48 29/5/29 +f 31/37/31 38/39/38 29/38/29 +f 31/20/31 42/24/42 32/18/32 +f 32/18/32 38/7/38 31/20/31 +f 32/18/32 42/24/42 41/22/41 +f 32/18/32 43/19/43 30/9/30 +f 33/14/33 40/15/40 34/10/34 +f 33/14/33 48/13/48 40/15/40 +f 34/10/34 40/15/40 39/11/39 +f 34/10/34 48/13/48 33/14/33 +f 35/3/35 36/4/36 29/5/29 +f 35/3/35 38/1/38 36/4/36 +f 36/40/36 37/42/37 32/44/32 +f 36/4/36 38/1/38 37/2/37 +f 37/6/37 38/7/38 32/18/32 +f 39/11/39 40/15/40 8/12/8 +f 39/31/39 46/33/46 34/32/34 +f 40/15/40 45/16/45 25/21/25 +f 40/15/40 48/13/48 45/16/45 +f 41/22/41 42/24/42 9/25/9 +f 41/22/41 43/19/43 32/18/32 +f 42/34/42 44/35/44 12/28/12 +f 43/19/43 48/13/48 30/9/30 +f 44/35/44 46/33/46 12/28/12 +f 45/16/45 48/13/48 43/19/43 +f 46/33/46 47/36/47 34/32/34 +f 47/8/47 48/13/48 34/10/34 diff --git a/ch12-droidinvaders/assets/ship.png b/ch12-droidinvaders/assets/ship.png new file mode 100644 index 0000000..fcaa50c Binary files /dev/null and b/ch12-droidinvaders/assets/ship.png differ diff --git a/ch12-droidinvaders/assets/shot.obj b/ch12-droidinvaders/assets/shot.obj new file mode 100644 index 0000000..f10a7e2 --- /dev/null +++ b/ch12-droidinvaders/assets/shot.obj @@ -0,0 +1,52 @@ +# Exported from Wings 3D 1.2 +mtllib shot.mtl +o cube1 +#8 vertices, 6 faces +v -5.7735027e-2 -5.7735027e-2 5.7735027e-2 +v -5.7735027e-2 5.7735027e-2 5.7735027e-2 +v 5.7735027e-2 5.7735027e-2 5.7735027e-2 +v 5.7735027e-2 -5.7735027e-2 5.7735027e-2 +v -5.7735027e-2 -5.7735027e-2 -5.7735027e-2 +v -5.7735027e-2 5.7735027e-2 -5.7735027e-2 +v 5.7735027e-2 5.7735027e-2 -5.7735027e-2 +v 5.7735027e-2 -5.7735027e-2 -5.7735027e-2 +vt 0.0000000e+0 0.33333333 +vt 0.0000000e+0 0.33333333 +vt 0.0000000e+0 1.00000000 +vt 5.5511151e-17 0.0000000e+0 +vt 5.5511151e-17 0.66666667 +vt 5.5511151e-17 0.66666667 +vt 0.33333333 0.33333333 +vt 0.33333333 0.33333333 +vt 0.33333333 0.66666667 +vt 0.33333333 0.66666667 +vt 0.33333333 1.00000000 +vt 0.33333333 5.5511151e-17 +vt 0.33333333 0.66666667 +vt 0.33333333 0.0000000e+0 +vt 0.33333333 0.33333333 +vt 0.33333333 0.33333333 +vt 0.33333333 0.66666667 +vt 0.66666667 0.66666667 +vt 0.66666667 1.00000000 +vt 0.66666667 5.5511151e-17 +vt 0.66666667 0.33333333 +vt 0.66666667 0.33333333 +vt 0.66666667 0.66666667 +vn -0.57735027 -0.57735027 0.57735027 +vn -0.57735027 0.57735027 0.57735027 +vn 0.57735027 0.57735027 0.57735027 +vn 0.57735027 -0.57735027 0.57735027 +vn -0.57735027 -0.57735027 -0.57735027 +vn -0.57735027 0.57735027 -0.57735027 +vn 0.57735027 0.57735027 -0.57735027 +vn 0.57735027 -0.57735027 -0.57735027 +g cube1_cube1_auv +usemtl cube1_auv +s 1 +f 1/2/1 4/8/4 3/10/3 2/6/2 +f 1/20/1 5/21/5 8/15/8 4/14/4 +f 2/23/2 6/17/6 5/16/5 1/22/1 +f 3/13/3 7/11/7 6/3/6 2/5/2 +f 4/4/4 8/12/8 7/7/7 3/1/3 +f 6/19/6 7/11/7 8/9/8 5/18/5 diff --git a/ch12-droidinvaders/assets/shot.ogg b/ch12-droidinvaders/assets/shot.ogg new file mode 100644 index 0000000..7e0c843 Binary files /dev/null and b/ch12-droidinvaders/assets/shot.ogg differ diff --git a/ch12-droidinvaders/default.properties b/ch12-droidinvaders/default.properties new file mode 100644 index 0000000..3ac2523 --- /dev/null +++ b/ch12-droidinvaders/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-12 diff --git a/ch12-droidinvaders/proguard.cfg b/ch12-droidinvaders/proguard.cfg new file mode 100644 index 0000000..8ad7d33 --- /dev/null +++ b/ch12-droidinvaders/proguard.cfg @@ -0,0 +1,34 @@ +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontpreverify +-verbose +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* + +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class com.android.vending.licensing.ILicensingService + +-keepclasseswithmembernames class * { + native ; +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/ch12-droidinvaders/res/drawable-hdpi/icon.png b/ch12-droidinvaders/res/drawable-hdpi/icon.png new file mode 100644 index 0000000..8074c4c Binary files /dev/null and b/ch12-droidinvaders/res/drawable-hdpi/icon.png differ diff --git a/ch12-droidinvaders/res/drawable-ldpi/icon.png b/ch12-droidinvaders/res/drawable-ldpi/icon.png new file mode 100644 index 0000000..1095584 Binary files /dev/null and b/ch12-droidinvaders/res/drawable-ldpi/icon.png differ diff --git a/ch12-droidinvaders/res/drawable-mdpi/icon.png b/ch12-droidinvaders/res/drawable-mdpi/icon.png new file mode 100644 index 0000000..a07c69f Binary files /dev/null and b/ch12-droidinvaders/res/drawable-mdpi/icon.png differ diff --git a/ch12-droidinvaders/res/layout/main.xml b/ch12-droidinvaders/res/layout/main.xml new file mode 100644 index 0000000..3a5f117 --- /dev/null +++ b/ch12-droidinvaders/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/ch12-droidinvaders/res/values/strings.xml b/ch12-droidinvaders/res/values/strings.xml new file mode 100644 index 0000000..150ee16 --- /dev/null +++ b/ch12-droidinvaders/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Hello World, DroidInvaders! + Droid Invaders + diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Assets.java b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Assets.java new file mode 100644 index 0000000..73056ec --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Assets.java @@ -0,0 +1,112 @@ +package com.badlogic.androidgames.droidinvaders; + +import com.badlogic.androidgames.framework.Music; +import com.badlogic.androidgames.framework.Sound; +import com.badlogic.androidgames.framework.gl.Animation; +import com.badlogic.androidgames.framework.gl.Font; +import com.badlogic.androidgames.framework.gl.ObjLoader; +import com.badlogic.androidgames.framework.gl.Texture; +import com.badlogic.androidgames.framework.gl.TextureRegion; +import com.badlogic.androidgames.framework.gl.Vertices3; +import com.badlogic.androidgames.framework.impl.GLGame; + +public class Assets { + public static Texture background; + public static TextureRegion backgroundRegion; + public static Texture items; + public static TextureRegion logoRegion; + public static TextureRegion menuRegion; + public static TextureRegion gameOverRegion; + public static TextureRegion pauseRegion; + public static TextureRegion settingsRegion; + public static TextureRegion touchRegion; + public static TextureRegion accelRegion; + public static TextureRegion touchEnabledRegion; + public static TextureRegion accelEnabledRegion; + public static TextureRegion soundRegion; + public static TextureRegion soundEnabledRegion; + public static TextureRegion leftRegion; + public static TextureRegion rightRegion; + public static TextureRegion fireRegion; + public static TextureRegion pauseButtonRegion; + public static Font font; + + public static Texture explosionTexture; + public static Animation explosionAnim; + public static Vertices3 shipModel; + public static Texture shipTexture; + public static Vertices3 invaderModel; + public static Texture invaderTexture; + public static Vertices3 shotModel; + public static Vertices3 shieldModel; + + public static Music music; + public static Sound clickSound; + public static Sound explosionSound; + public static Sound shotSound; + + public static void load(GLGame game) { + background = new Texture(game, "background.jpg", true); + backgroundRegion = new TextureRegion(background, 0, 0, 480, 320); + items = new Texture(game, "items.png", true); + logoRegion = new TextureRegion(items, 0, 256, 384, 128); + menuRegion = new TextureRegion(items, 0, 128, 224, 64); + gameOverRegion = new TextureRegion(items, 224, 128, 128, 64); + pauseRegion = new TextureRegion(items, 0, 192, 160, 64); + settingsRegion = new TextureRegion(items, 0, 160, 224, 32); + touchRegion = new TextureRegion(items, 0, 384, 64, 64); + accelRegion = new TextureRegion(items, 64, 384, 64, 64); + touchEnabledRegion = new TextureRegion(items, 0, 448, 64, 64); + accelEnabledRegion = new TextureRegion(items, 64, 448, 64, 64); + soundRegion = new TextureRegion(items, 128, 384, 64, 64); + soundEnabledRegion = new TextureRegion(items, 190, 384, 64, 64); + leftRegion = new TextureRegion(items, 0, 0, 64, 64); + rightRegion = new TextureRegion(items, 64, 0, 64, 64); + fireRegion = new TextureRegion(items, 128, 0, 64, 64); + pauseButtonRegion = new TextureRegion(items, 0, 64, 64, 64); + font = new Font(items, 224, 0, 16, 16, 20); + + explosionTexture = new Texture(game, "explode.png", true); + TextureRegion[] keyFrames = new TextureRegion[16]; + int frame = 0; + for (int y = 0; y < 256; y += 64) { + for (int x = 0; x < 256; x += 64) { + keyFrames[frame++] = new TextureRegion(explosionTexture, x, y, + 64, 64); + } + } + explosionAnim = new Animation(0.1f, keyFrames); + + shipTexture = new Texture(game, "ship.png", true); + shipModel = ObjLoader.load(game, "ship.obj"); + invaderTexture = new Texture(game, "invader.png", true); + invaderModel = ObjLoader.load(game, "invader.obj"); + shieldModel = ObjLoader.load(game, "shield.obj"); + shotModel = ObjLoader.load(game, "shot.obj"); + + music = game.getAudio().newMusic("music.mp3"); + music.setLooping(true); + music.setVolume(0.5f); + if (Settings.soundEnabled) + music.play(); + + clickSound = game.getAudio().newSound("click.ogg"); + explosionSound = game.getAudio().newSound("explosion.ogg"); + shotSound = game.getAudio().newSound("shot.ogg"); + } + + public static void reload() { + background.reload(); + items.reload(); + explosionTexture.reload(); + shipTexture.reload(); + invaderTexture.reload(); + if (Settings.soundEnabled) + music.play(); + } + + public static void playSound(Sound sound) { + if (Settings.soundEnabled) + sound.play(1); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/DroidInvaders.java b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/DroidInvaders.java new file mode 100644 index 0000000..dc10408 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/DroidInvaders.java @@ -0,0 +1,35 @@ +package com.badlogic.androidgames.droidinvaders; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Screen; +import com.badlogic.androidgames.framework.impl.GLGame; + +public class DroidInvaders extends GLGame { + boolean firstTimeCreate = true; + + @Override + public Screen getStartScreen() { + return new MainMenuScreen(this); + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + super.onSurfaceCreated(gl, config); + if (firstTimeCreate) { + Settings.load(getFileIO()); + Assets.load(this); + firstTimeCreate = false; + } else { + Assets.reload(); + } + } + + @Override + public void onPause() { + super.onPause(); + if (Settings.soundEnabled) + Assets.music.pause(); + } +} \ No newline at end of file diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/GameScreen.java b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/GameScreen.java new file mode 100644 index 0000000..399052b --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/GameScreen.java @@ -0,0 +1,274 @@ +package com.badlogic.androidgames.droidinvaders; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.droidinvaders.World.WorldListener; +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.FPSCounter; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class GameScreen extends GLScreen { + static final int GAME_RUNNING = 0; + static final int GAME_PAUSED = 1; + static final int GAME_OVER = 2; + + int state; + Camera2D guiCam; + Vector2 touchPoint; + SpriteBatcher batcher; + World world; + WorldListener worldListener; + WorldRenderer renderer; + Rectangle pauseBounds; + Rectangle resumeBounds; + Rectangle quitBounds; + Rectangle leftBounds; + Rectangle rightBounds; + Rectangle shotBounds; + int lastScore; + int lastLives; + int lastWaves; + String scoreString; + FPSCounter fpsCounter; + + public GameScreen(Game game) { + super(game); + + state = GAME_RUNNING; + guiCam = new Camera2D(glGraphics, 480, 320); + touchPoint = new Vector2(); + batcher = new SpriteBatcher(glGraphics, 100); + world = new World(); + worldListener = new WorldListener() { + @Override + public void shot() { + Assets.playSound(Assets.shotSound); + } + + @Override + public void explosion() { + Assets.playSound(Assets.explosionSound); + } + }; + world.setWorldListener(worldListener); + renderer = new WorldRenderer(glGraphics); + pauseBounds = new Rectangle(480 - 64, 320 - 64, 64, 64); + resumeBounds = new Rectangle(240 - 80, 160, 160, 32); + quitBounds = new Rectangle(240 - 80, 160 - 32, 160, 32); + shotBounds = new Rectangle(480 - 64, 0, 64, 64); + leftBounds = new Rectangle(0, 0, 64, 64); + rightBounds = new Rectangle(64, 0, 64, 64); + lastScore = 0; + lastLives = world.ship.lives; + lastWaves = world.waves; + scoreString = "lives:" + lastLives + " waves:" + lastWaves + " score:" + + lastScore; + fpsCounter = new FPSCounter(); + } + + @Override + public void update(float deltaTime) { + switch (state) { + case GAME_PAUSED: + updatePaused(); + break; + case GAME_RUNNING: + updateRunning(deltaTime); + break; + case GAME_OVER: + updateGameOver(); + break; + } + } + + private void updatePaused() { + List events = game.getInput().getTouchEvents(); + int len = events.size(); + for (int i = 0; i < len; i++) { + TouchEvent event = events.get(i); + if (event.type != TouchEvent.TOUCH_UP) + continue; + + guiCam.touchToWorld(touchPoint.set(event.x, event.y)); + if (OverlapTester.pointInRectangle(resumeBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + state = GAME_RUNNING; + } + + if (OverlapTester.pointInRectangle(quitBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + game.setScreen(new MainMenuScreen(game)); + } + } + } + + private void updateRunning(float deltaTime) { + List events = game.getInput().getTouchEvents(); + int len = events.size(); + for (int i = 0; i < len; i++) { + TouchEvent event = events.get(i); + if (event.type != TouchEvent.TOUCH_DOWN) + continue; + + guiCam.touchToWorld(touchPoint.set(event.x, event.y)); + + if (OverlapTester.pointInRectangle(pauseBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + state = GAME_PAUSED; + } + if (OverlapTester.pointInRectangle(shotBounds, touchPoint)) { + world.shot(); + } + } + + world.update(deltaTime, calculateInputAcceleration()); + if (world.ship.lives != lastLives || world.score != lastScore + || world.waves != lastWaves) { + lastLives = world.ship.lives; + lastScore = world.score; + lastWaves = world.waves; + scoreString = "lives:" + lastLives + " waves:" + lastWaves + + " score:" + lastScore; + } + if (world.isGameOver()) { + state = GAME_OVER; + } + } + + private float calculateInputAcceleration() { + float accelX = 0; + if (Settings.touchEnabled) { + for (int i = 0; i < 2; i++) { + if (game.getInput().isTouchDown(i)) { + guiCam.touchToWorld(touchPoint.set(game.getInput() + .getTouchX(i), game.getInput().getTouchY(i))); + if (OverlapTester.pointInRectangle(leftBounds, touchPoint)) { + accelX = -Ship.SHIP_VELOCITY / 5; + } + if (OverlapTester.pointInRectangle(rightBounds, touchPoint)) { + accelX = Ship.SHIP_VELOCITY / 5; + } + } + } + } else { + accelX = game.getInput().getAccelY(); + } + return accelX; + } + + private void updateGameOver() { + List events = game.getInput().getTouchEvents(); + int len = events.size(); + for (int i = 0; i < len; i++) { + TouchEvent event = events.get(i); + if (event.type == TouchEvent.TOUCH_UP) { + Assets.playSound(Assets.clickSound); + game.setScreen(new MainMenuScreen(game)); + } + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); + guiCam.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_TEXTURE_2D); + batcher.beginBatch(Assets.background); + batcher.drawSprite(240, 160, 480, 320, Assets.backgroundRegion); + batcher.endBatch(); + gl.glDisable(GL10.GL_TEXTURE_2D); + + renderer.render(world, deltaTime); + + switch (state) { + case GAME_RUNNING: + presentRunning(); + break; + case GAME_PAUSED: + presentPaused(); + break; + case GAME_OVER: + presentGameOver(); + } + + fpsCounter.logFrame(); + } + + private void presentPaused() { + GL10 gl = glGraphics.getGL(); + guiCam.setViewportAndMatrices(); + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(Assets.items); + Assets.font.drawText(batcher, scoreString, 10, 320 - 20); + batcher.drawSprite(240, 160, 160, 64, Assets.pauseRegion); + batcher.endBatch(); + + gl.glDisable(GL10.GL_TEXTURE_2D); + gl.glDisable(GL10.GL_BLEND); + } + + private void presentRunning() { + GL10 gl = glGraphics.getGL(); + guiCam.setViewportAndMatrices(); + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(Assets.items); + batcher.drawSprite(480 - 32, 320 - 32, 64, 64, Assets.pauseButtonRegion); + Assets.font.drawText(batcher, scoreString, 10, 320 - 20); + if (Settings.touchEnabled) { + batcher.drawSprite(32, 32, 64, 64, Assets.leftRegion); + batcher.drawSprite(96, 32, 64, 64, Assets.rightRegion); + } + batcher.drawSprite(480 - 40, 32, 64, 64, Assets.fireRegion); + batcher.endBatch(); + + gl.glDisable(GL10.GL_TEXTURE_2D); + gl.glDisable(GL10.GL_BLEND); + } + + private void presentGameOver() { + GL10 gl = glGraphics.getGL(); + guiCam.setViewportAndMatrices(); + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(Assets.items); + batcher.drawSprite(240, 160, 128, 64, Assets.gameOverRegion); + Assets.font.drawText(batcher, scoreString, 10, 320 - 20); + batcher.endBatch(); + + gl.glDisable(GL10.GL_TEXTURE_2D); + gl.glDisable(GL10.GL_BLEND); + } + + @Override + public void pause() { + state = GAME_PAUSED; + } + + @Override + public void resume() { + + } + + @Override + public void dispose() { + + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Invader.java b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Invader.java new file mode 100644 index 0000000..8413b71 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Invader.java @@ -0,0 +1,65 @@ +package com.badlogic.androidgames.droidinvaders; + +import com.badlogic.androidgames.framework.DynamicGameObject3D; + +public class Invader extends DynamicGameObject3D { + static final int INVADER_ALIVE = 0; + static final int INVADER_DEAD = 1; + static final float INVADER_EXPLOSION_TIME = 1.6f; + static final float INVADER_RADIUS = 0.75f; + static final float INVADER_VELOCITY = 1; + static final int MOVE_LEFT = 0; + static final int MOVE_DOWN = 1; + static final int MOVE_RIGHT = 2; + + int state = INVADER_ALIVE; + float stateTime = 0; + int move = MOVE_LEFT; + boolean wasLastStateLeft = true; + float movedDistance = World.WORLD_MAX_X / 2; + + public Invader(float x, float y, float z) { + super(x, y, z, INVADER_RADIUS); + } + + public void update(float deltaTime, float speedMultiplier) { + if (state == INVADER_ALIVE) { + movedDistance += deltaTime * INVADER_VELOCITY * speedMultiplier; + if (move == MOVE_LEFT) { + position.x -= deltaTime * INVADER_VELOCITY * speedMultiplier; + if (movedDistance > World.WORLD_MAX_X) { + move = MOVE_DOWN; + movedDistance = 0; + wasLastStateLeft = true; + } + } + if (move == MOVE_RIGHT) { + position.x += deltaTime * INVADER_VELOCITY * speedMultiplier; + if (movedDistance > World.WORLD_MAX_X) { + move = MOVE_DOWN; + movedDistance = 0; + wasLastStateLeft = false; + } + } + if (move == MOVE_DOWN) { + position.z += deltaTime * INVADER_VELOCITY * speedMultiplier; + if (movedDistance > 1) { + if (wasLastStateLeft) + move = MOVE_RIGHT; + else + move = MOVE_LEFT; + movedDistance = 0; + } + } + + bounds.center.set(position); + } + + stateTime += deltaTime; + } + + public void kill() { + state = INVADER_DEAD; + stateTime = 0; + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/MainMenuScreen.java b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/MainMenuScreen.java new file mode 100644 index 0000000..6faee71 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/MainMenuScreen.java @@ -0,0 +1,89 @@ +package com.badlogic.androidgames.droidinvaders; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class MainMenuScreen extends GLScreen { + Camera2D guiCam; + SpriteBatcher batcher; + Vector2 touchPoint; + Rectangle playBounds; + Rectangle settingsBounds; + + public MainMenuScreen(Game game) { + super(game); + + guiCam = new Camera2D(glGraphics, 480, 320); + batcher = new SpriteBatcher(glGraphics, 10); + touchPoint = new Vector2(); + playBounds = new Rectangle(240 - 112, 100, 224, 32); + settingsBounds = new Rectangle(240 - 112, 100 - 32, 224, 32); + } + + @Override + public void update(float deltaTime) { + List events = game.getInput().getTouchEvents(); + int len = events.size(); + for (int i = 0; i < len; i++) { + TouchEvent event = events.get(i); + if (event.type != TouchEvent.TOUCH_UP) + continue; + + guiCam.touchToWorld(touchPoint.set(event.x, event.y)); + if (OverlapTester.pointInRectangle(playBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + game.setScreen(new GameScreen(game)); + } + if (OverlapTester.pointInRectangle(settingsBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + game.setScreen(new SettingsScreen(game)); + } + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + guiCam.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(Assets.background); + batcher.drawSprite(240, 160, 480, 320, Assets.backgroundRegion); + batcher.endBatch(); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + batcher.beginBatch(Assets.items); + batcher.drawSprite(240, 240, 384, 128, Assets.logoRegion); + batcher.drawSprite(240, 100, 224, 64, Assets.menuRegion); + batcher.endBatch(); + + gl.glDisable(GL10.GL_BLEND); + gl.glDisable(GL10.GL_TEXTURE_2D); + } + + @Override + public void pause() { + } + + @Override + public void resume() { + } + + @Override + public void dispose() { + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Settings.java b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Settings.java new file mode 100644 index 0000000..c7edce4 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Settings.java @@ -0,0 +1,52 @@ +package com.badlogic.androidgames.droidinvaders; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; + +import com.badlogic.androidgames.framework.FileIO; + +public class Settings { + public static boolean soundEnabled = true; + public static boolean touchEnabled = true; + public final static String file = ".droidinvaders"; + + public static void load(FileIO files) { + BufferedReader in = null; + try { + in = new BufferedReader(new InputStreamReader(files.readFile(file))); + soundEnabled = Boolean.parseBoolean(in.readLine()); + touchEnabled = Boolean.parseBoolean(in.readLine()); + } catch (IOException e) { + // :( It's ok we have defaults + } catch (NumberFormatException e) { + // :/ It's ok, defaults save our day + } finally { + try { + if (in != null) + in.close(); + } catch (IOException e) { + } + } + } + + public static void save(FileIO files) { + BufferedWriter out = null; + try { + out = new BufferedWriter(new OutputStreamWriter( + files.writeFile(file))); + out.write(Boolean.toString(soundEnabled)); + out.write("\n"); + out.write(Boolean.toString(touchEnabled)); + } catch (IOException e) { + } finally { + try { + if (out != null) + out.close(); + } catch (IOException e) { + } + } + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/SettingsScreen.java b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/SettingsScreen.java new file mode 100644 index 0000000..968a488 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/SettingsScreen.java @@ -0,0 +1,117 @@ +package com.badlogic.androidgames.droidinvaders; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.gl.Camera2D; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.impl.GLScreen; +import com.badlogic.androidgames.framework.math.OverlapTester; +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class SettingsScreen extends GLScreen { + Camera2D guiCam; + SpriteBatcher batcher; + Vector2 touchPoint; + Rectangle touchBounds; + Rectangle accelBounds; + Rectangle soundBounds; + Rectangle backBounds; + + public SettingsScreen(Game game) { + super(game); + guiCam = new Camera2D(glGraphics, 480, 320); + batcher = new SpriteBatcher(glGraphics, 10); + touchPoint = new Vector2(); + + touchBounds = new Rectangle(120 - 32, 160 - 32, 64, 64); + accelBounds = new Rectangle(240 - 32, 160 - 32, 64, 64); + soundBounds = new Rectangle(360 - 32, 160 - 32, 64, 64); + backBounds = new Rectangle(32, 32, 64, 64); + } + + @Override + public void update(float deltaTime) { + List events = game.getInput().getTouchEvents(); + int len = events.size(); + for (int i = 0; i < len; i++) { + TouchEvent event = events.get(i); + if (event.type != TouchEvent.TOUCH_UP) + continue; + + guiCam.touchToWorld(touchPoint.set(event.x, event.y)); + if (OverlapTester.pointInRectangle(touchBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + Settings.touchEnabled = true; + Settings.save(game.getFileIO()); + } + if (OverlapTester.pointInRectangle(accelBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + Settings.touchEnabled = false; + Settings.save(game.getFileIO()); + } + if (OverlapTester.pointInRectangle(soundBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + Settings.soundEnabled = !Settings.soundEnabled; + if (Settings.soundEnabled) { + Assets.music.play(); + } else { + Assets.music.pause(); + } + Settings.save(game.getFileIO()); + } + if (OverlapTester.pointInRectangle(backBounds, touchPoint)) { + Assets.playSound(Assets.clickSound); + game.setScreen(new MainMenuScreen(game)); + } + } + } + + @Override + public void present(float deltaTime) { + GL10 gl = glGraphics.getGL(); + gl.glClear(GL10.GL_COLOR_BUFFER_BIT); + guiCam.setViewportAndMatrices(); + + gl.glEnable(GL10.GL_TEXTURE_2D); + + batcher.beginBatch(Assets.background); + batcher.drawSprite(240, 160, 480, 320, Assets.backgroundRegion); + batcher.endBatch(); + + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + + batcher.beginBatch(Assets.items); + batcher.drawSprite(240, 280, 224, 32, Assets.settingsRegion); + batcher.drawSprite(120, 160, 64, 64, + Settings.touchEnabled ? Assets.touchEnabledRegion + : Assets.touchRegion); + batcher.drawSprite(240, 160, 64, 64, + Settings.touchEnabled ? Assets.accelRegion + : Assets.accelEnabledRegion); + batcher.drawSprite(360, 160, 64, 64, + Settings.soundEnabled ? Assets.soundEnabledRegion + : Assets.soundRegion); + batcher.drawSprite(32, 32, 64, 64, Assets.leftRegion); + batcher.endBatch(); + + gl.glDisable(GL10.GL_BLEND); + } + + @Override + public void pause() { + } + + @Override + public void resume() { + } + + @Override + public void dispose() { + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Shield.java b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Shield.java new file mode 100644 index 0000000..0f459b2 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Shield.java @@ -0,0 +1,11 @@ +package com.badlogic.androidgames.droidinvaders; + +import com.badlogic.androidgames.framework.GameObject3D; + +public class Shield extends GameObject3D { + static float SHIELD_RADIUS = 0.5f; + + public Shield(float x, float y, float z) { + super(x, y, z, SHIELD_RADIUS); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Ship.java b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Ship.java new file mode 100644 index 0000000..7ba8f1a --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Ship.java @@ -0,0 +1,46 @@ +package com.badlogic.androidgames.droidinvaders; + +import com.badlogic.androidgames.framework.DynamicGameObject3D; + +public class Ship extends DynamicGameObject3D { + static float SHIP_VELOCITY = 20f; + static int SHIP_ALIVE = 0; + static int SHIP_EXPLODING = 1; + static float SHIP_EXPLOSION_TIME = 1.6f; + static float SHIP_RADIUS = 0.5f; + + int lives; + int state; + float stateTime = 0; + + public Ship(float x, float y, float z) { + super(x, y, z, SHIP_RADIUS); + lives = 3; + state = SHIP_ALIVE; + } + + public void update(float deltaTime, float accelY) { + if (state == SHIP_ALIVE) { + velocity.set(accelY / 10 * SHIP_VELOCITY, 0, 0); + position.add(velocity.x * deltaTime, 0, 0); + if (position.x < World.WORLD_MIN_X) + position.x = World.WORLD_MIN_X; + if (position.x > World.WORLD_MAX_X) + position.x = World.WORLD_MAX_X; + bounds.center.set(position); + } else { + if (stateTime >= SHIP_EXPLOSION_TIME) { + lives--; + stateTime = 0; + state = SHIP_ALIVE; + } + } + stateTime += deltaTime; + } + + public void kill() { + state = SHIP_EXPLODING; + stateTime = 0; + velocity.x = 0; + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Shot.java b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Shot.java new file mode 100644 index 0000000..534d470 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/Shot.java @@ -0,0 +1,18 @@ +package com.badlogic.androidgames.droidinvaders; + +import com.badlogic.androidgames.framework.DynamicGameObject3D; + +public class Shot extends DynamicGameObject3D { + static float SHOT_VELOCITY = 10f; + static float SHOT_RADIUS = 0.1f; + + public Shot(float x, float y, float z, float velocityZ) { + super(x, y, z, SHOT_RADIUS); + velocity.z = velocityZ; + } + + public void update(float deltaTime) { + position.z += velocity.z * deltaTime; + bounds.center.set(position); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/World.java b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/World.java new file mode 100644 index 0000000..ea2bc05 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/World.java @@ -0,0 +1,204 @@ +package com.badlogic.androidgames.droidinvaders; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import com.badlogic.androidgames.framework.math.OverlapTester; + +public class World { + public interface WorldListener { + public void explosion(); + + public void shot(); + } + + final static float WORLD_MIN_X = -14; + final static float WORLD_MAX_X = 14; + final static float WORLD_MIN_Z = -15; + + WorldListener listener; + int waves = 1; + int score = 0; + float speedMultiplier = 1; + final List shots = new ArrayList(); + final List invaders = new ArrayList(); + final List shields = new ArrayList(); + final Ship ship; + long lastShotTime; + Random random; + + public World() { + ship = new Ship(0, 0, 0); + generateInvaders(); + generateShields(); + lastShotTime = System.nanoTime(); + random = new Random(); + } + + private void generateInvaders() { + for (int row = 0; row < 4; row++) { + for (int column = 0; column < 8; column++) { + Invader invader = new Invader(-WORLD_MAX_X / 2 + column * 2f, + 0, WORLD_MIN_Z + row * 2f); + invaders.add(invader); + } + } + } + + private void generateShields() { + for (int shield = 0; shield < 3; shield++) { + shields.add(new Shield(-10 + shield * 10 - 1, 0, -3)); + shields.add(new Shield(-10 + shield * 10 + 0, 0, -3)); + shields.add(new Shield(-10 + shield * 10 + 1, 0, -3)); + shields.add(new Shield(-10 + shield * 10 - 1, 0, -2)); + shields.add(new Shield(-10 + shield * 10 + 1, 0, -2)); + } + } + + public void setWorldListener(WorldListener worldListener) { + this.listener = worldListener; + } + + public void update(float deltaTime, float accelX) { + ship.update(deltaTime, accelX); + updateInvaders(deltaTime); + updateShots(deltaTime); + + checkShotCollisions(); + checkInvaderCollisions(); + + if (invaders.size() == 0) { + generateInvaders(); + waves++; + speedMultiplier += 0.5f; + } + } + + private void updateInvaders(float deltaTime) { + int len = invaders.size(); + for (int i = 0; i < len; i++) { + Invader invader = invaders.get(i); + invader.update(deltaTime, speedMultiplier); + + if (invader.state == Invader.INVADER_ALIVE) { + if (random.nextFloat() < 0.001f) { + Shot shot = new Shot(invader.position.x, + invader.position.y, invader.position.z, + Shot.SHOT_VELOCITY); + shots.add(shot); + listener.shot(); + } + } + + if (invader.state == Invader.INVADER_DEAD + && invader.stateTime > Invader.INVADER_EXPLOSION_TIME) { + invaders.remove(i); + i--; + len--; + } + } + } + + private void updateShots(float deltaTime) { + int len = shots.size(); + for (int i = 0; i < len; i++) { + Shot shot = shots.get(i); + shot.update(deltaTime); + if (shot.position.z < WORLD_MIN_Z || + shot.position.z > 0) { + shots.remove(i); + i--; + len--; + } + } + } + + private void checkInvaderCollisions() { + if (ship.state == Ship.SHIP_EXPLODING) + return; + + int len = invaders.size(); + for (int i = 0; i < len; i++) { + Invader invader = invaders.get(i); + if (OverlapTester.overlapSpheres(ship.bounds, invader.bounds)) { + ship.lives = 1; + ship.kill(); + return; + } + } + } + + private void checkShotCollisions() { + int len = shots.size(); + for (int i = 0; i < len; i++) { + Shot shot = shots.get(i); + boolean shotRemoved = false; + + int len2 = shields.size(); + for (int j = 0; j < len2; j++) { + Shield shield = shields.get(j); + if (OverlapTester.overlapSpheres(shield.bounds, shot.bounds)) { + shields.remove(j); + shots.remove(i); + i--; + len--; + shotRemoved = true; + break; + } + } + if (shotRemoved) + continue; + + if (shot.velocity.z < 0) { + len2 = invaders.size(); + for (int j = 0; j < len2; j++) { + Invader invader = invaders.get(j); + if (OverlapTester.overlapSpheres(invader.bounds, + shot.bounds) + && invader.state == Invader.INVADER_ALIVE) { + invader.kill(); + listener.explosion(); + score += 10; + shots.remove(i); + i--; + len--; + break; + } + } + } else { + if (OverlapTester.overlapSpheres(shot.bounds, ship.bounds) + && ship.state == Ship.SHIP_ALIVE) { + ship.kill(); + listener.explosion(); + shots.remove(i); + i--; + len--; + } + } + } + } + + public boolean isGameOver() { + return ship.lives == 0; + } + + public void shot() { + if (ship.state == Ship.SHIP_EXPLODING) + return; + + int friendlyShots = 0; + int len = shots.size(); + for (int i = 0; i < len; i++) { + if (shots.get(i).velocity.z < 0) + friendlyShots++; + } + + if (System.nanoTime() - lastShotTime > 1000000000 || friendlyShots == 0) { + shots.add(new Shot(ship.position.x, ship.position.y, + ship.position.z, -Shot.SHOT_VELOCITY)); + lastShotTime = System.nanoTime(); + listener.shot(); + } + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/WorldRenderer.java b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/WorldRenderer.java new file mode 100644 index 0000000..1544341 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/droidinvaders/WorldRenderer.java @@ -0,0 +1,158 @@ +package com.badlogic.androidgames.droidinvaders; + +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.gl.AmbientLight; +import com.badlogic.androidgames.framework.gl.Animation; +import com.badlogic.androidgames.framework.gl.DirectionalLight; +import com.badlogic.androidgames.framework.gl.LookAtCamera; +import com.badlogic.androidgames.framework.gl.SpriteBatcher; +import com.badlogic.androidgames.framework.gl.TextureRegion; +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.Vector3; + +public class WorldRenderer { + GLGraphics glGraphics; + LookAtCamera camera; + AmbientLight ambientLight; + DirectionalLight directionalLight; + SpriteBatcher batcher; + float invaderAngle = 0; + + public WorldRenderer(GLGraphics glGraphics) { + this.glGraphics = glGraphics; + camera = new LookAtCamera(67, glGraphics.getWidth() + / (float) glGraphics.getHeight(), 0.1f, 100); + camera.getPosition().set(0, 6, 2); + camera.getLookAt().set(0, 0, -4); + ambientLight = new AmbientLight(); + ambientLight.setColor(0.2f, 0.2f, 0.2f, 1.0f); + directionalLight = new DirectionalLight(); + directionalLight.setDirection(-1, -0.5f, 0); + batcher = new SpriteBatcher(glGraphics, 10); + } + + public void render(World world, float deltaTime) { + GL10 gl = glGraphics.getGL(); + camera.getPosition().x = world.ship.position.x; + camera.getLookAt().x = world.ship.position.x; + camera.setMatrices(gl); + + gl.glEnable(GL10.GL_DEPTH_TEST); + gl.glEnable(GL10.GL_TEXTURE_2D); + gl.glEnable(GL10.GL_LIGHTING); + gl.glEnable(GL10.GL_COLOR_MATERIAL); +// ambientLight.enable(gl); + directionalLight.enable(gl, GL10.GL_LIGHT0); + + renderShip(gl, world.ship); + renderInvaders(gl, world.invaders, deltaTime); + + gl.glDisable(GL10.GL_TEXTURE_2D); + + renderShields(gl, world.shields); + renderShots(gl, world.shots); + + gl.glDisable(GL10.GL_COLOR_MATERIAL); + gl.glDisable(GL10.GL_LIGHTING); + gl.glDisable(GL10.GL_DEPTH_TEST); + } + + private void renderShip(GL10 gl, Ship ship) { + if (ship.state == Ship.SHIP_EXPLODING) { + gl.glDisable(GL10.GL_LIGHTING); + renderExplosion(gl, ship.position, ship.stateTime); + gl.glEnable(GL10.GL_LIGHTING); + } else { + Assets.shipTexture.bind(); + Assets.shipModel.bind(); + gl.glPushMatrix(); + gl.glTranslatef(ship.position.x, ship.position.y, ship.position.z); + gl.glRotatef(ship.velocity.x / Ship.SHIP_VELOCITY * 90, 0, 0, -1); + Assets.shipModel.draw(GL10.GL_TRIANGLES, 0, + Assets.shipModel.getNumVertices()); + gl.glPopMatrix(); + Assets.shipModel.unbind(); + } + } + + private void renderInvaders(GL10 gl, List invaders, float deltaTime) { + invaderAngle += 45 * deltaTime; + + Assets.invaderTexture.bind(); + Assets.invaderModel.bind(); + int len = invaders.size(); + for (int i = 0; i < len; i++) { + Invader invader = invaders.get(i); + if (invader.state == Invader.INVADER_DEAD) { + gl.glDisable(GL10.GL_LIGHTING); + Assets.invaderModel.unbind(); + renderExplosion(gl, invader.position, invader.stateTime); + Assets.invaderTexture.bind(); + Assets.invaderModel.bind(); + gl.glEnable(GL10.GL_LIGHTING); + } else { + gl.glPushMatrix(); + gl.glTranslatef(invader.position.x, invader.position.y, + invader.position.z); + gl.glRotatef(invaderAngle, 0, 1, 0); + Assets.invaderModel.draw(GL10.GL_TRIANGLES, 0, + Assets.invaderModel.getNumVertices()); + gl.glPopMatrix(); + } + } + Assets.invaderModel.unbind(); + } + + private void renderShields(GL10 gl, List shields) { + gl.glEnable(GL10.GL_BLEND); + gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + gl.glColor4f(0, 0, 1, 0.4f); + Assets.shieldModel.bind(); + int len = shields.size(); + for (int i = 0; i < len; i++) { + Shield shield = shields.get(i); + gl.glPushMatrix(); + gl.glTranslatef(shield.position.x, shield.position.y, + shield.position.z); + Assets.shieldModel.draw(GL10.GL_TRIANGLES, 0, + Assets.shieldModel.getNumVertices()); + gl.glPopMatrix(); + } + Assets.shieldModel.unbind(); + gl.glColor4f(1, 1, 1, 1f); + gl.glDisable(GL10.GL_BLEND); + } + + private void renderShots(GL10 gl, List shots) { + gl.glColor4f(1, 1, 0, 1); + Assets.shotModel.bind(); + int len = shots.size(); + for (int i = 0; i < len; i++) { + Shot shot = shots.get(i); + gl.glPushMatrix(); + gl.glTranslatef(shot.position.x, shot.position.y, shot.position.z); + Assets.shotModel.draw(GL10.GL_TRIANGLES, 0, + Assets.shotModel.getNumVertices()); + gl.glPopMatrix(); + } + Assets.shotModel.unbind(); + gl.glColor4f(1, 1, 1, 1); + } + + private void renderExplosion(GL10 gl, Vector3 position, float stateTime) { + TextureRegion frame = Assets.explosionAnim.getKeyFrame(stateTime, + Animation.ANIMATION_NONLOOPING); + + gl.glEnable(GL10.GL_BLEND); + gl.glPushMatrix(); + gl.glTranslatef(position.x, position.y, position.z); + batcher.beginBatch(Assets.explosionTexture); + batcher.drawSprite(0, 0, 2, 2, frame); + batcher.endBatch(); + gl.glPopMatrix(); + gl.glDisable(GL10.GL_BLEND); + } +} \ No newline at end of file diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Audio.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Audio.java new file mode 100644 index 0000000..d30d722 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Audio.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Audio { + public Music newMusic(String filename); + + public Sound newSound(String filename); +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Color.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Color.java new file mode 100644 index 0000000..57d68e5 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Color.java @@ -0,0 +1,10 @@ +package com.badlogic.androidgames.framework; + +public class Color { + public static int convert (int r, int g, int b, int a) { + return ((a & 0xff) << 24) | + ((r & 0xff) << 16) | + ((g & 0xff) << 8) | + ((b & 0xff)); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/DynamicGameObject.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/DynamicGameObject.java new file mode 100644 index 0000000..00afabb --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/DynamicGameObject.java @@ -0,0 +1,14 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.math.Vector2; + +public class DynamicGameObject extends GameObject { + public final Vector2 velocity; + public final Vector2 accel; + + public DynamicGameObject(float x, float y, float width, float height) { + super(x, y, width, height); + velocity = new Vector2(); + accel = new Vector2(); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/DynamicGameObject3D.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/DynamicGameObject3D.java new file mode 100644 index 0000000..734a94a --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/DynamicGameObject3D.java @@ -0,0 +1,14 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.math.Vector3; + +public class DynamicGameObject3D extends GameObject3D { + public final Vector3 velocity; + public final Vector3 accel; + + public DynamicGameObject3D(float x, float y, float z, float radius) { + super(x, y, z, radius); + velocity = new Vector3(); + accel = new Vector3(); + } +} \ No newline at end of file diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/FileIO.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/FileIO.java new file mode 100644 index 0000000..d555b7d --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/FileIO.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.framework; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public interface FileIO { + public InputStream readAsset(String fileName) throws IOException; + + public InputStream readFile(String fileName) throws IOException; + + public OutputStream writeFile(String fileName) throws IOException; +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Game.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Game.java new file mode 100644 index 0000000..fb5c5aa --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Game.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework; + +public interface Game { + public Input getInput(); + + public FileIO getFileIO(); + + public Graphics getGraphics(); + + public Audio getAudio(); + + public void setScreen(Screen screen); + + public Screen getCurrentScreen(); + + public Screen getStartScreen(); +} \ No newline at end of file diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/GameObject.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/GameObject.java new file mode 100644 index 0000000..54aaa09 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/GameObject.java @@ -0,0 +1,14 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.math.Rectangle; +import com.badlogic.androidgames.framework.math.Vector2; + +public class GameObject { + public final Vector2 position; + public final Rectangle bounds; + + public GameObject(float x, float y, float width, float height) { + this.position = new Vector2(x,y); + this.bounds = new Rectangle(x-width/2, y-height/2, width, height); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/GameObject3D.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/GameObject3D.java new file mode 100644 index 0000000..1cc5f93 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/GameObject3D.java @@ -0,0 +1,14 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.math.Sphere; +import com.badlogic.androidgames.framework.math.Vector3; + +public class GameObject3D { + public final Vector3 position; + public final Sphere bounds; + + public GameObject3D(float x, float y, float z, float radius) { + this.position = new Vector3(x,y,z); + this.bounds = new Sphere(x, y, z, radius); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Graphics.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Graphics.java new file mode 100644 index 0000000..c7b8f45 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Graphics.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework; + +public interface Graphics { + public static enum PixmapFormat { + ARGB8888, ARGB4444, RGB565 + } + + public Pixmap newPixmap(String fileName, PixmapFormat format); + + public void clear(int color); + + public void drawPixel(int x, int y, int color); + + public void drawLine(int x, int y, int x2, int y2, int color); + + public void drawRect(int x, int y, int width, int height, int color); + + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight); + + public void drawPixmap(Pixmap pixmap, int x, int y); + + public int getWidth(); + + public int getHeight(); +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Input.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Input.java new file mode 100644 index 0000000..0893d62 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Input.java @@ -0,0 +1,70 @@ +package com.badlogic.androidgames.framework; + +import java.util.List; + +public interface Input { + public static class KeyEvent { + public static final int KEY_DOWN = 0; + public static final int KEY_UP = 1; + + public int type; + public int keyCode; + public char keyChar; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == KEY_DOWN) + builder.append("key down, "); + else + builder.append("key up, "); + builder.append(keyCode); + builder.append(","); + builder.append(keyChar); + return builder.toString(); + } + } + + public static class TouchEvent { + public static final int TOUCH_DOWN = 0; + public static final int TOUCH_UP = 1; + public static final int TOUCH_DRAGGED = 2; + + public int type; + public int x, y; + public int pointer; + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == TOUCH_DOWN) + builder.append("touch down, "); + else if (type == TOUCH_DRAGGED) + builder.append("touch dragged, "); + else + builder.append("touch up, "); + builder.append(pointer); + builder.append(","); + builder.append(x); + builder.append(","); + builder.append(y); + return builder.toString(); + } + } + + public boolean isKeyPressed(int keyCode); + + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public float getAccelX(); + + public float getAccelY(); + + public float getAccelZ(); + + public List getKeyEvents(); + + public List getTouchEvents(); +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Music.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Music.java new file mode 100644 index 0000000..1b95a1e --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Music.java @@ -0,0 +1,21 @@ +package com.badlogic.androidgames.framework; + +public interface Music { + public void play(); + + public void stop(); + + public void pause(); + + public void setLooping(boolean looping); + + public void setVolume(float volume); + + public boolean isPlaying(); + + public boolean isStopped(); + + public boolean isLooping(); + + public void dispose(); +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Pixmap.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Pixmap.java new file mode 100644 index 0000000..4b1eec9 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Pixmap.java @@ -0,0 +1,13 @@ +package com.badlogic.androidgames.framework; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; + +public interface Pixmap { + public int getWidth(); + + public int getHeight(); + + public PixmapFormat getFormat(); + + public void dispose(); +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Pool.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Pool.java new file mode 100644 index 0000000..d0b7cec --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Pool.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework; + +import java.util.ArrayList; +import java.util.List; + +public class Pool { + public interface PoolObjectFactory { + public T createObject(); + } + + private final List freeObjects; + private final PoolObjectFactory factory; + private final int maxSize; + + public Pool(PoolObjectFactory factory, int maxSize) { + this.factory = factory; + this.maxSize = maxSize; + this.freeObjects = new ArrayList(maxSize); + } + + public T newObject() { + T object = null; + + if (freeObjects.size() == 0) + object = factory.createObject(); + else + object = freeObjects.remove(freeObjects.size() - 1); + + return object; + } + + public void free(T object) { + if (freeObjects.size() < maxSize) + freeObjects.add(object); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Screen.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Screen.java new file mode 100644 index 0000000..40a98b9 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Screen.java @@ -0,0 +1,19 @@ +package com.badlogic.androidgames.framework; + +public abstract class Screen { + protected final Game game; + + public Screen(Game game) { + this.game = game; + } + + public abstract void update(float deltaTime); + + public abstract void present(float deltaTime); + + public abstract void pause(); + + public abstract void resume(); + + public abstract void dispose(); +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Sound.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Sound.java new file mode 100644 index 0000000..3f49835 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/Sound.java @@ -0,0 +1,7 @@ +package com.badlogic.androidgames.framework; + +public interface Sound { + public void play(float volume); + + public void dispose(); +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/TestScreen.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/TestScreen.java new file mode 100644 index 0000000..5a0bbcf --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/TestScreen.java @@ -0,0 +1,112 @@ +package com.badlogic.androidgames.framework; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.List; + +import android.graphics.Color; +import android.util.Log; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; +import com.badlogic.androidgames.framework.Input.KeyEvent; +import com.badlogic.androidgames.framework.Input.TouchEvent; + +class TestScreen extends Screen { + long startTime = System.nanoTime(); + int frames; + Pixmap bob; + Pixmap bobAlpha; + Sound sound; + Music music; + + public TestScreen(Game game) { + super(game); + bob = game.getGraphics().newPixmap("bobrgb888.png", PixmapFormat.RGB565); + bobAlpha = game.getGraphics().newPixmap("bobargb8888.png", PixmapFormat.ARGB4444); + music = game.getAudio().newMusic("music.ogg"); + music.setLooping(true); + music.setVolume(0.5f); + music.play(); + sound = game.getAudio().newSound("music.ogg"); + + try { + BufferedReader in = new BufferedReader(new InputStreamReader(game.getFileIO().readAsset("test.txt"))); + String text = in.readLine(); + in.close(); + + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(game.getFileIO().writeFile("test.txt"))); + out.write("This is a freaking test"); + out.close(); + + in = new BufferedReader(new InputStreamReader(game.getFileIO().readFile("test.txt"))); + String text2 = in.readLine(); + in.close(); + + Log.d("MrNom", text + ", " + text2 ); + } catch(Exception ex) { + ex.printStackTrace(); + } + } + + @Override + public void update(float deltaTime) { + } + + @Override + public void present(float deltaTime) { + Graphics g = game.getGraphics(); + Input inp = game.getInput(); + g.clear(Color.RED); + g.drawLine(0,0,320, 480, Color.BLUE); + g.drawRect(20,20,100,100, Color.GREEN); + g.drawPixmap(bob, 100, 100); + g.drawPixmap(bobAlpha, 100, 200); + g.drawPixmap(bob, 200, 200, 0, 0, 64, 64); + for(int i=0; i < 2; i++) { + if(inp.isTouchDown(i)) { + g.drawPixmap(bob, inp.getTouchX(i), inp.getTouchY(i), 0, 0, 64, 64); + } + } + + g.drawPixmap(bob, (int)(inp.getAccelX() * 10) + 160 - 16, (int)(inp.getAccelY() * 10) + 240 - 16, 0, 0, 32, 32 ); + + List keyEvents = inp.getKeyEvents(); + int len = keyEvents.size(); + for(int i = 0; i < len; i++) { + Log.d("MrNom", keyEvents.get(i).toString()); + } + + List touchEvents = inp.getTouchEvents(); + len = touchEvents.size(); + for(int i = 0; i < len; i++) { + Log.d("MrNom", touchEvents.get(i).toString()); + if(touchEvents.get(i).type == TouchEvent.TOUCH_UP) + sound.play(1); + } + + frames++; + if(System.nanoTime() - startTime > 1000000000l) { + Log.d("MrNom", "fps: " + frames + ", delta: " + deltaTime); + frames = 0; + startTime = System.nanoTime(); + } + } + + @Override + public void pause() { + Log.d("MrNom", "pause"); + } + + @Override + public void resume() { + Log.d("MrNom", "resume"); + } + + @Override + public void dispose() { + Log.d("MrNom", "dispose"); + music.dispose(); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/AmbientLight.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/AmbientLight.java new file mode 100644 index 0000000..4e2b41a --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/AmbientLight.java @@ -0,0 +1,18 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +public class AmbientLight { + float[] color = {0.2f, 0.2f, 0.2f, 1}; + + public void setColor(float r, float g, float b, float a) { + color[0] = r; + color[1] = g; + color[2] = b; + color[3] = a; + } + + public void enable(GL10 gl) { + gl.glLightModelfv(GL10.GL_LIGHT_MODEL_AMBIENT, color, 0); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Animation.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Animation.java new file mode 100644 index 0000000..d826e39 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Animation.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework.gl; + + +public class Animation { + public static final int ANIMATION_LOOPING = 0; + public static final int ANIMATION_NONLOOPING = 1; + + final TextureRegion[] keyFrames; + final float frameDuration; + + public Animation(float frameDuration, TextureRegion ... keyFrames) { + this.frameDuration = frameDuration; + this.keyFrames = keyFrames; + } + + public TextureRegion getKeyFrame(float stateTime, int mode) { + int frameNumber = (int)(stateTime / frameDuration); + + if(mode == ANIMATION_NONLOOPING) { + frameNumber = Math.min(keyFrames.length-1, frameNumber); + } else { + frameNumber = frameNumber % keyFrames.length; + } + return keyFrames[frameNumber]; + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Camera2D.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Camera2D.java new file mode 100644 index 0000000..2a58199 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Camera2D.java @@ -0,0 +1,42 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.Vector2; + +public class Camera2D { + public final Vector2 position; + public float zoom; + public final float frustumWidth; + public final float frustumHeight; + final GLGraphics glGraphics; + + public Camera2D(GLGraphics glGraphics, float frustumWidth, float frustumHeight) { + this.glGraphics = glGraphics; + this.frustumWidth = frustumWidth; + this.frustumHeight = frustumHeight; + this.position = new Vector2(frustumWidth / 2, frustumHeight / 2); + this.zoom = 1.0f; + } + + public void setViewportAndMatrices() { + GL10 gl = glGraphics.getGL(); + gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight()); + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrthof(position.x - frustumWidth * zoom / 2, + position.x + frustumWidth * zoom/ 2, + position.y - frustumHeight * zoom / 2, + position.y + frustumHeight * zoom/ 2, + 1, -1); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + } + + public void touchToWorld(Vector2 touch) { + touch.x = (touch.x / (float) glGraphics.getWidth()) * frustumWidth * zoom; + touch.y = (1 - touch.y / (float) glGraphics.getHeight()) * frustumHeight * zoom; + touch.add(position).sub(frustumWidth * zoom / 2, frustumHeight * zoom / 2); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/DirectionalLight.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/DirectionalLight.java new file mode 100644 index 0000000..d87e71c --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/DirectionalLight.java @@ -0,0 +1,51 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +public class DirectionalLight { + float[] ambient = { 0.2f, 0.2f, 0.2f, 1.0f }; + float[] diffuse = { 1.0f, 1.0f, 1.0f, 1.0f }; + float[] specular = { 0.0f, 0.0f, 0.0f, 0.0f }; + float[] direction = { 0, 0, -1, 0 }; + int lastLightId = 0; + + public void setAmbient(float r, float g, float b, float a) { + ambient[0] = r; + ambient[1] = g; + ambient[2] = b; + ambient[3] = a; + } + + public void setDiffuse(float r, float g, float b, float a) { + diffuse[0] = r; + diffuse[1] = g; + diffuse[2] = b; + diffuse[3] = a; + } + + public void setSpecular(float r, float g, float b, float a) { + specular[0] = r; + specular[1] = g; + specular[2] = b; + specular[3] = a; + } + + public void setDirection(float x, float y, float z) { + direction[0] = -x; + direction[1] = -y; + direction[2] = -z; + } + + public void enable(GL10 gl, int lightId) { + gl.glEnable(lightId); + gl.glLightfv(lightId, GL10.GL_AMBIENT, ambient, 0); + gl.glLightfv(lightId, GL10.GL_DIFFUSE, diffuse, 0); + gl.glLightfv(lightId, GL10.GL_SPECULAR, specular, 0); + gl.glLightfv(lightId, GL10.GL_POSITION, direction, 0); + lastLightId = lightId; + } + + public void disable(GL10 gl) { + gl.glDisable(lastLightId); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/EulerCamera.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/EulerCamera.java new file mode 100644 index 0000000..7dc41b4 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/EulerCamera.java @@ -0,0 +1,80 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLU; +import android.opengl.Matrix; + +import com.badlogic.androidgames.framework.math.Vector3; + +public class EulerCamera { + final Vector3 position = new Vector3(); + float yaw; + float pitch; + float fieldOfView; + float aspectRatio; + float near; + float far; + + public EulerCamera(float fieldOfView, float aspectRatio, float near, float far) { + this.fieldOfView = fieldOfView; + this.aspectRatio = aspectRatio; + this.near = near; + this.far = far; + } + + public Vector3 getPosition() { + return position; + } + + public float getYaw() { + return yaw; + } + + public float getPitch() { + return pitch; + } + + public void setAngles(float yaw, float pitch) { + if (pitch < -90) + pitch = -90; + if (pitch > 90) + pitch = 90; + this.yaw = yaw; + this.pitch = pitch; + } + + public void rotate(float yawInc, float pitchInc) { + this.yaw += yawInc; + this.pitch += pitchInc; + if (pitch < -90) + pitch = -90; + if (pitch > 90) + pitch = 90; + } + + public void setMatrices(GL10 gl) { + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + GLU.gluPerspective(gl, fieldOfView, aspectRatio, near, far); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + gl.glRotatef(-pitch, 1, 0, 0); + gl.glRotatef(-yaw, 0, 1, 0); + gl.glTranslatef(-position.x, -position.y, -position.z); + } + + final float[] matrix = new float[16]; + final float[] inVec = { 0, 0, -1, 1 }; + final float[] outVec = new float[4]; + final Vector3 direction = new Vector3(); + + public Vector3 getDirection() { + Matrix.setIdentityM(matrix, 0); + Matrix.rotateM(matrix, 0, yaw, 0, 1, 0); + Matrix.rotateM(matrix, 0, pitch, 1, 0, 0); + Matrix.multiplyMV(outVec, 0, matrix, 0, inVec, 0); + direction.set(outVec[0], outVec[1], outVec[2]); + return direction; + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/FPSCounter.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/FPSCounter.java new file mode 100644 index 0000000..cd816ca --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/FPSCounter.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework.gl; + +import android.util.Log; + +public class FPSCounter { + long startTime = System.nanoTime(); + int frames = 0; + + public void logFrame() { + frames++; + if(System.nanoTime() - startTime >= 1000000000) { + Log.d("FPSCounter", "fps: " + frames); + frames = 0; + startTime = System.nanoTime(); + } + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Font.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Font.java new file mode 100644 index 0000000..ace1452 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Font.java @@ -0,0 +1,39 @@ +package com.badlogic.androidgames.framework.gl; + +public class Font { + public final Texture texture; + public final int glyphWidth; + public final int glyphHeight; + public final TextureRegion[] glyphs = new TextureRegion[96]; + + public Font(Texture texture, + int offsetX, int offsetY, + int glyphsPerRow, int glyphWidth, int glyphHeight) { + this.texture = texture; + this.glyphWidth = glyphWidth; + this.glyphHeight = glyphHeight; + int x = offsetX; + int y = offsetY; + for(int i = 0; i < 96; i++) { + glyphs[i] = new TextureRegion(texture, x, y, glyphWidth, glyphHeight); + x += glyphWidth; + if(x == offsetX + glyphsPerRow * glyphWidth) { + x = offsetX; + y += glyphHeight; + } + } + } + + public void drawText(SpriteBatcher batcher, String text, float x, float y) { + int len = text.length(); + for(int i = 0; i < len; i++) { + int c = text.charAt(i) - ' '; + if(c < 0 || c > glyphs.length - 1) + continue; + + TextureRegion glyph = glyphs[c]; + batcher.drawSprite(x, y, glyphWidth, glyphHeight, glyph); + x += glyphWidth; + } + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Light.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Light.java new file mode 100644 index 0000000..5c0d095 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Light.java @@ -0,0 +1,49 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Light { + final float[] ambient; + final float[] diffuse; + final float[] position; + final GLGraphics glGraphics; + + public Light(GLGraphics glGraphics, boolean isDirectional) { + this.glGraphics = glGraphics; + ambient = new float[] {0.2f, 0.2f, 0.2f, 1.0f}; + diffuse = new float[] {1.0f, 1.0f, 1.0f, 1.0f}; + position = new float[] {0, 0, 0, isDirectional?0:1}; + } + + public boolean isDirectional() { + return position[3] == 0; + } + + public void setAmbient(float r, float g, float b) { + ambient[0] = r; + ambient[1] = g; + ambient[2] = b; + } + + public void setDiffuse(float r, float g, float b) { + diffuse[0] = r; + diffuse[1] = g; + diffuse[2] = b; + } + + public void setPosition(float x, float y, float z) { + position[0] = x; + position[1] = y; + position[2] = z; + } + + public void enable(int lightNum) { + GL10 gl = glGraphics.getGL(); + gl.glEnable(lightNum); + gl.glLightfv(lightNum, GL10.GL_AMBIENT, ambient, 0); + gl.glLightfv(lightNum, GL10.GL_DIFFUSE, diffuse, 0); + gl.glLightfv(lightNum, GL10.GL_POSITION, position, 0); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/LookAtCamera.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/LookAtCamera.java new file mode 100644 index 0000000..addcbf4 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/LookAtCamera.java @@ -0,0 +1,49 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLU; + +import com.badlogic.androidgames.framework.math.Vector3; + +public class LookAtCamera { + final Vector3 position; + final Vector3 up; + final Vector3 lookAt; + float fieldOfView; + float aspectRatio; + float near; + float far; + + public LookAtCamera(float fieldOfView, float aspectRatio, float near, float far) { + this.fieldOfView = fieldOfView; + this.aspectRatio = aspectRatio; + this.near = near; + this.far = far; + + position = new Vector3(); + up = new Vector3(0, 1, 0); + lookAt = new Vector3(0,0,-1); + } + + public Vector3 getPosition() { + return position; + } + + public Vector3 getUp() { + return up; + } + + public Vector3 getLookAt() { + return lookAt; + } + + public void setMatrices(GL10 gl) { + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + GLU.gluPerspective(gl, fieldOfView, aspectRatio, near, far); + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + GLU.gluLookAt(gl, position.x, position.y, position.z, lookAt.x, lookAt.y, lookAt.z, up.x, up.y, up.z); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Material.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Material.java new file mode 100644 index 0000000..5f6d8e0 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Material.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +public class Material { + float[] ambient = { 0.2f, 0.2f, 0.2f, 1.0f }; + float[] diffuse = { 1.0f, 1.0f, 1.0f, 1.0f }; + float[] specular = { 0.0f, 0.0f, 0.0f, 1.0f }; + + public void setAmbient(float r, float g, float b, float a) { + ambient[0] = r; + ambient[1] = g; + ambient[2] = b; + ambient[3] = a; + } + + public void setDiffuse(float r, float g, float b, float a) { + diffuse[0] = r; + diffuse[1] = g; + diffuse[2] = b; + diffuse[3] = a; + } + + public void setSpecular(float r, float g, float b, float a) { + specular[0] = r; + specular[1] = g; + specular[2] = b; + specular[3] = a; + } + + public void enable(GL10 gl) { + gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, ambient, 0); + gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, diffuse, 0); + gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, specular, 0); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/MipMappedTexture.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/MipMappedTexture.java new file mode 100644 index 0000000..59ed667 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/MipMappedTexture.java @@ -0,0 +1,122 @@ +package com.badlogic.androidgames.framework.gl; + +import java.io.IOException; +import java.io.InputStream; + +import javax.microedition.khronos.opengles.GL10; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.opengl.GLUtils; + +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class MipMappedTexture { + GLGraphics glGraphics; + FileIO fileIO; + String fileName; + int textureId; + int minFilter; + int magFilter; + public int width; + public int height; + boolean mipmapped; + + public MipMappedTexture(GLGame glGame, String fileName, boolean mipmapped) { + this.glGraphics = glGame.getGLGraphics(); + this.fileIO = glGame.getFileIO(); + this.fileName = fileName; + this.mipmapped = mipmapped; + load(); + } + + private void load() { + GL10 gl = glGraphics.getGL(); + int[] textureIds = new int[1]; + gl.glGenTextures(1, textureIds, 0); + textureId = textureIds[0]; + + InputStream in = null; + try { + in = fileIO.readAsset(fileName); + Bitmap bitmap = BitmapFactory.decodeStream(in); + if(mipmapped) { + createMipmaps(gl, bitmap); + } else { + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); + setFilters(GL10.GL_NEAREST, GL10.GL_NEAREST); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + width = bitmap.getWidth(); + height = bitmap.getHeight(); + bitmap.recycle(); + } + } catch(IOException e) { + throw new RuntimeException("Couldn't load texture '" + fileName +"'", e); + } finally { + if(in != null) + try { in.close(); } catch (IOException e) { } + } + } + + private void createMipmaps(GL10 gl, Bitmap bitmap) { + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + width = bitmap.getWidth(); + height = bitmap.getHeight(); + setFilters(GL10.GL_LINEAR_MIPMAP_NEAREST, GL10.GL_LINEAR); + + int level = 0; + int newWidth = width; + int newHeight = height; + while(true) { + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0); + newWidth = newWidth / 2; + newHeight = newHeight / 2; + if(newWidth <= 0) + break; + Bitmap newBitmap = Bitmap.createBitmap(newWidth, newHeight, bitmap.getConfig()); + Canvas canvas = new Canvas(newBitmap); + canvas.drawBitmap(bitmap, + new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), + new Rect(0, 0, newWidth, newHeight), + null); + bitmap.recycle(); + bitmap = newBitmap; + level++; + } + + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + bitmap.recycle(); + } + + public void reload() { + load(); + bind(); + setFilters(minFilter, magFilter); + glGraphics.getGL().glBindTexture(GL10.GL_TEXTURE_2D, 0); + } + + public void setFilters(int minFilter, int magFilter) { + this.minFilter = minFilter; + this.magFilter = magFilter; + GL10 gl = glGraphics.getGL(); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, minFilter); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, magFilter); + } + + public void bind() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + } + + public void dispose() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + int[] textureIds = { textureId }; + gl.glDeleteTextures(1, textureIds, 0); + } +} \ No newline at end of file diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/ObjLoader.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/ObjLoader.java new file mode 100644 index 0000000..a2d2658 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/ObjLoader.java @@ -0,0 +1,154 @@ +package com.badlogic.androidgames.framework.gl; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +import com.badlogic.androidgames.framework.impl.GLGame; + +public class ObjLoader { + public static Vertices3 load(GLGame game, String file) { + InputStream in = null; + try { + in = game.getFileIO().readAsset(file); + List lines = readLines(in); + + float[] vertices = new float[lines.size() * 3]; + float[] normals = new float[lines.size() * 3]; + float[] uv = new float[lines.size() * 2]; + + int numVertices = 0; + int numNormals = 0; + int numUV = 0; + int numFaces = 0; + + int[] facesVerts = new int[lines.size() * 3]; + int[] facesNormals = new int[lines.size() * 3]; + int[] facesUV = new int[lines.size() * 3]; + int vertexIndex = 0; + int normalIndex = 0; + int uvIndex = 0; + int faceIndex = 0; + + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i); + if (line.startsWith("v ")) { + String[] tokens = line.split("[ ]+"); + vertices[vertexIndex] = Float.parseFloat(tokens[1]); + vertices[vertexIndex + 1] = Float.parseFloat(tokens[2]); + vertices[vertexIndex + 2] = Float.parseFloat(tokens[3]); + vertexIndex += 3; + numVertices++; + continue; + } + + if (line.startsWith("vn ")) { + String[] tokens = line.split("[ ]+"); + normals[normalIndex] = Float.parseFloat(tokens[1]); + normals[normalIndex + 1] = Float.parseFloat(tokens[2]); + normals[normalIndex + 2] = Float.parseFloat(tokens[3]); + normalIndex += 3; + numNormals++; + continue; + } + + if (line.startsWith("vt")) { + String[] tokens = line.split("[ ]+"); + uv[uvIndex] = Float.parseFloat(tokens[1]); + uv[uvIndex + 1] = Float.parseFloat(tokens[2]); + uvIndex += 2; + numUV++; + continue; + } + + if (line.startsWith("f ")) { + String[] tokens = line.split("[ ]+"); + + String[] parts = tokens[1].split("/"); + facesVerts[faceIndex] = getIndex(parts[0], numVertices); + if (parts.length > 2) + facesNormals[faceIndex] = getIndex(parts[2], numNormals); + if (parts.length > 1) + facesUV[faceIndex] = getIndex(parts[1], numUV); + faceIndex++; + + parts = tokens[2].split("/"); + facesVerts[faceIndex] = getIndex(parts[0], numVertices); + if (parts.length > 2) + facesNormals[faceIndex] = getIndex(parts[2], numNormals); + if (parts.length > 1) + facesUV[faceIndex] = getIndex(parts[1], numUV); + faceIndex++; + + parts = tokens[3].split("/"); + facesVerts[faceIndex] = getIndex(parts[0], numVertices); + if (parts.length > 2) + facesNormals[faceIndex] = getIndex(parts[2], numNormals); + if (parts.length > 1) + facesUV[faceIndex] = getIndex(parts[1], numUV); + faceIndex++; + numFaces++; + continue; + } + } + + float[] verts = new float[(numFaces * 3) + * (3 + (numNormals > 0 ? 3 : 0) + (numUV > 0 ? 2 : 0))]; + + for (int i = 0, vi = 0; i < numFaces * 3; i++) { + int vertexIdx = facesVerts[i] * 3; + verts[vi++] = vertices[vertexIdx]; + verts[vi++] = vertices[vertexIdx + 1]; + verts[vi++] = vertices[vertexIdx + 2]; + + if (numUV > 0) { + int uvIdx = facesUV[i] * 2; + verts[vi++] = uv[uvIdx]; + verts[vi++] = 1 - uv[uvIdx + 1]; + } + + if (numNormals > 0) { + int normalIdx = facesNormals[i] * 3; + verts[vi++] = normals[normalIdx]; + verts[vi++] = normals[normalIdx + 1]; + verts[vi++] = normals[normalIdx + 2]; + } + } + + Vertices3 model = new Vertices3(game.getGLGraphics(), numFaces * 3, + 0, false, numUV > 0, numNormals > 0); + model.setVertices(verts, 0, verts.length); + return model; + } catch (Exception ex) { + throw new RuntimeException("couldn't load '" + file + "'", ex); + } finally { + if (in != null) + try { + in.close(); + } catch (Exception ex) { + + } + } + } + + static int getIndex(String index, int size) { + int idx = Integer.parseInt(index); + if (idx < 0) + return size + idx; + else + return idx - 1; + } + + static List readLines(InputStream in) throws IOException { + List lines = new ArrayList(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + String line = null; + while ((line = reader.readLine()) != null) + lines.add(line); + return lines; + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/PointLight.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/PointLight.java new file mode 100644 index 0000000..de55943 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/PointLight.java @@ -0,0 +1,51 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +public class PointLight { + float[] ambient = { 0.2f, 0.2f, 0.2f, 1.0f }; + float[] diffuse = { 1.0f, 1.0f, 1.0f, 1.0f }; + float[] specular = { 0.0f, 0.0f, 0.0f, 1.0f }; + float[] position = { 0, 0, 0, 1 }; + int lastLightId = 0; + + public void setAmbient(float r, float g, float b, float a) { + ambient[0] = r; + ambient[1] = g; + ambient[2] = b; + ambient[3] = a; + } + + public void setDiffuse(float r, float g, float b, float a) { + diffuse[0] = r; + diffuse[1] = g; + diffuse[2] = b; + diffuse[3] = a; + } + + public void setSpecular(float r, float g, float b, float a) { + specular[0] = r; + specular[1] = g; + specular[2] = b; + specular[3] = a; + } + + public void setPosition(float x, float y, float z) { + position[0] = x; + position[1] = y; + position[2] = z; + } + + public void enable(GL10 gl, int lightId) { + gl.glEnable(lightId); + gl.glLightfv(lightId, GL10.GL_AMBIENT, ambient, 0); + gl.glLightfv(lightId, GL10.GL_DIFFUSE, diffuse, 0); + gl.glLightfv(lightId, GL10.GL_SPECULAR, specular, 0); + gl.glLightfv(lightId, GL10.GL_POSITION, position, 0); + lastLightId = lightId; + } + + public void disable(GL10 gl) { + gl.glDisable(lastLightId); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java new file mode 100644 index 0000000..da3cb4f --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/SpatialHashGrid.java @@ -0,0 +1,143 @@ +package com.badlogic.androidgames.framework.gl; + +import java.util.ArrayList; +import java.util.List; + +import com.badlogic.androidgames.framework.GameObject; + +import android.util.FloatMath; + +public class SpatialHashGrid { + List[] dynamicCells; + List[] staticCells; + int cellsPerRow; + int cellsPerCol; + float cellSize; + int[] cellIds = new int[4]; + List foundObjects; + + @SuppressWarnings("unchecked") + public SpatialHashGrid(float worldWidth, float worldHeight, float cellSize) { + this.cellSize = cellSize; + this.cellsPerRow = (int)FloatMath.ceil(worldWidth/cellSize); + this.cellsPerCol = (int)FloatMath.ceil(worldHeight/cellSize); + int numCells = cellsPerRow * cellsPerCol; + dynamicCells = new List[numCells]; + staticCells = new List[numCells]; + for(int i = 0; i < numCells; i++) { + dynamicCells[i] = new ArrayList(10); + staticCells[i] = new ArrayList(10); + } + foundObjects = new ArrayList(10); + } + + public void insertStaticObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + staticCells[cellId].add(obj); + } + } + + public void insertDynamicObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + dynamicCells[cellId].add(obj); + } + } + + public void removeObject(GameObject obj) { + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + dynamicCells[cellId].remove(obj); + staticCells[cellId].remove(obj); + } + } + + public void clearDynamicCells(GameObject obj) { + int len = dynamicCells.length; + for(int i = 0; i < len; i++) { + dynamicCells[i].clear(); + } + } + + public List getPotentialColliders(GameObject obj) { + foundObjects.clear(); + int[] cellIds = getCellIds(obj); + int i = 0; + int cellId = -1; + while(i <= 3 && (cellId = cellIds[i++]) != -1) { + int len = dynamicCells[cellId].size(); + for(int j = 0; j < len; j++) { + GameObject collider = dynamicCells[cellId].get(j); + if(!foundObjects.contains(collider)) + foundObjects.add(collider); + } + + len = staticCells[cellId].size(); + for(int j = 0; j < len; j++) { + GameObject collider = staticCells[cellId].get(j); + if(!foundObjects.contains(collider)) + foundObjects.add(collider); + } + } + return foundObjects; + } + + public int[] getCellIds(GameObject obj) { + int x1 = (int)FloatMath.floor(obj.bounds.lowerLeft.x / cellSize); + int y1 = (int)FloatMath.floor(obj.bounds.lowerLeft.y / cellSize); + int x2 = (int)FloatMath.floor((obj.bounds.lowerLeft.x + obj.bounds.width) / cellSize); + int y2 = (int)FloatMath.floor((obj.bounds.lowerLeft.y + obj.bounds.height) / cellSize); + + if(x1 == x2 && y1 == y2) { + if(x1 >= 0 && x1 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[0] = x1 + y1 * cellsPerRow; + else + cellIds[0] = -1; + cellIds[1] = -1; + cellIds[2] = -1; + cellIds[3] = -1; + } + else if(x1 == x2) { + int i = 0; + if(x1 >= 0 && x1 < cellsPerRow) { + if(y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x1 + y1 * cellsPerRow; + if(y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x1 + y2 * cellsPerRow; + } + while(i <= 3) cellIds[i++] = -1; + } + else if(y1 == y2) { + int i = 0; + if(y1 >= 0 && y1 < cellsPerCol) { + if(x1 >= 0 && x1 < cellsPerRow) + cellIds[i++] = x1 + y1 * cellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow) + cellIds[i++] = x2 + y1 * cellsPerRow; + } + while(i <= 3) cellIds[i++] = -1; + } + else { + int i = 0; + int y1CellsPerRow = y1 * cellsPerRow; + int y2CellsPerRow = y2 * cellsPerRow; + if(x1 >= 0 && x1 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x1 + y1CellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow && y1 >= 0 && y1 < cellsPerCol) + cellIds[i++] = x2 + y1CellsPerRow; + if(x2 >= 0 && x2 < cellsPerRow && y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x2 + y2CellsPerRow; + if(x1 >= 0 && x1 < cellsPerRow && y2 >= 0 && y2 < cellsPerCol) + cellIds[i++] = x1 + y2CellsPerRow; + while(i <= 3) cellIds[i++] = -1; + } + return cellIds; + } +} \ No newline at end of file diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java new file mode 100644 index 0000000..8b9eee2 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/SpriteBatcher.java @@ -0,0 +1,128 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; + +import android.util.FloatMath; + +import com.badlogic.androidgames.framework.impl.GLGraphics; +import com.badlogic.androidgames.framework.math.Vector2; + +public class SpriteBatcher { + final float[] verticesBuffer; + int bufferIndex; + final Vertices vertices; + int numSprites; + + public SpriteBatcher(GLGraphics glGraphics, int maxSprites) { + this.verticesBuffer = new float[maxSprites*4*4]; + this.vertices = new Vertices(glGraphics, maxSprites*4, maxSprites*6, false, true); + this.bufferIndex = 0; + this.numSprites = 0; + + short[] indices = new short[maxSprites*6]; + int len = indices.length; + short j = 0; + for (int i = 0; i < len; i += 6, j += 4) { + indices[i + 0] = (short)(j + 0); + indices[i + 1] = (short)(j + 1); + indices[i + 2] = (short)(j + 2); + indices[i + 3] = (short)(j + 2); + indices[i + 4] = (short)(j + 3); + indices[i + 5] = (short)(j + 0); + } + vertices.setIndices(indices, 0, indices.length); + } + + public void beginBatch(Texture texture) { + texture.bind(); + numSprites = 0; + bufferIndex = 0; + } + + public void endBatch() { + vertices.setVertices(verticesBuffer, 0, bufferIndex); + vertices.bind(); + vertices.draw(GL10.GL_TRIANGLES, 0, numSprites * 6); + vertices.unbind(); + } + + public void drawSprite(float x, float y, float width, float height, TextureRegion region) { + float halfWidth = width / 2; + float halfHeight = height / 2; + float x1 = x - halfWidth; + float y1 = y - halfHeight; + float x2 = x + halfWidth; + float y2 = y + halfHeight; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v1; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v1; + + numSprites++; + } + + public void drawSprite(float x, float y, float width, float height, float angle, TextureRegion region) { + float halfWidth = width / 2; + float halfHeight = height / 2; + + float rad = angle * Vector2.TO_RADIANS; + float cos = FloatMath.cos(rad); + float sin = FloatMath.sin(rad); + + float x1 = -halfWidth * cos - (-halfHeight) * sin; + float y1 = -halfWidth * sin + (-halfHeight) * cos; + float x2 = halfWidth * cos - (-halfHeight) * sin; + float y2 = halfWidth * sin + (-halfHeight) * cos; + float x3 = halfWidth * cos - halfHeight * sin; + float y3 = halfWidth * sin + halfHeight * cos; + float x4 = -halfWidth * cos - halfHeight * sin; + float y4 = -halfWidth * sin + halfHeight * cos; + + x1 += x; + y1 += y; + x2 += x; + y2 += y; + x3 += x; + y3 += y; + x4 += x; + y4 += y; + + verticesBuffer[bufferIndex++] = x1; + verticesBuffer[bufferIndex++] = y1; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x2; + verticesBuffer[bufferIndex++] = y2; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v2; + + verticesBuffer[bufferIndex++] = x3; + verticesBuffer[bufferIndex++] = y3; + verticesBuffer[bufferIndex++] = region.u2; + verticesBuffer[bufferIndex++] = region.v1; + + verticesBuffer[bufferIndex++] = x4; + verticesBuffer[bufferIndex++] = y4; + verticesBuffer[bufferIndex++] = region.u1; + verticesBuffer[bufferIndex++] = region.v1; + + numSprites++; + } +} \ No newline at end of file diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Texture.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Texture.java new file mode 100644 index 0000000..da82e50 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Texture.java @@ -0,0 +1,132 @@ +package com.badlogic.androidgames.framework.gl; + +import java.io.IOException; +import java.io.InputStream; + +import javax.microedition.khronos.opengles.GL10; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.opengl.GLUtils; + +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.impl.GLGame; +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Texture { + GLGraphics glGraphics; + FileIO fileIO; + String fileName; + int textureId; + int minFilter; + int magFilter; + public int width; + public int height; + boolean mipmapped; + + public Texture(GLGame glGame, String fileName) { + this(glGame, fileName, false); + } + + public Texture(GLGame glGame, String fileName, boolean mipmapped) { + this.glGraphics = glGame.getGLGraphics(); + this.fileIO = glGame.getFileIO(); + this.fileName = fileName; + this.mipmapped = mipmapped; + load(); + } + + private void load() { + GL10 gl = glGraphics.getGL(); + int[] textureIds = new int[1]; + gl.glGenTextures(1, textureIds, 0); + textureId = textureIds[0]; + + InputStream in = null; + try { + in = fileIO.readAsset(fileName); + Bitmap bitmap = BitmapFactory.decodeStream(in); + if (mipmapped) { + createMipmaps(gl, bitmap); + } else { + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); + setFilters(GL10.GL_NEAREST, GL10.GL_NEAREST); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + width = bitmap.getWidth(); + height = bitmap.getHeight(); + bitmap.recycle(); + } + } catch (IOException e) { + throw new RuntimeException("Couldn't load texture '" + fileName + + "'", e); + } finally { + if (in != null) + try { + in.close(); + } catch (IOException e) { + } + } + } + + private void createMipmaps(GL10 gl, Bitmap bitmap) { + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + width = bitmap.getWidth(); + height = bitmap.getHeight(); + setFilters(GL10.GL_LINEAR_MIPMAP_NEAREST, GL10.GL_LINEAR); + + int level = 0; + int newWidth = width; + int newHeight = height; + while (true) { + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0); + newWidth = newWidth / 2; + newHeight = newHeight / 2; + if (newWidth <= 0) + break; + Bitmap newBitmap = Bitmap.createBitmap(newWidth, newHeight, + bitmap.getConfig()); + Canvas canvas = new Canvas(newBitmap); + canvas.drawBitmap(bitmap, + new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), + new Rect(0, 0, newWidth, newHeight), null); + bitmap.recycle(); + bitmap = newBitmap; + level++; + } + + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + bitmap.recycle(); + } + + public void reload() { + load(); + bind(); + setFilters(minFilter, magFilter); + glGraphics.getGL().glBindTexture(GL10.GL_TEXTURE_2D, 0); + } + + public void setFilters(int minFilter, int magFilter) { + this.minFilter = minFilter; + this.magFilter = magFilter; + GL10 gl = glGraphics.getGL(); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, + minFilter); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, + magFilter); + } + + public void bind() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); + } + + public void dispose() { + GL10 gl = glGraphics.getGL(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); + int[] textureIds = { textureId }; + gl.glDeleteTextures(1, textureIds, 0); + } +} \ No newline at end of file diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/TextureRegion.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/TextureRegion.java new file mode 100644 index 0000000..cb91a07 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/TextureRegion.java @@ -0,0 +1,15 @@ +package com.badlogic.androidgames.framework.gl; + +public class TextureRegion { + public final float u1, v1; + public final float u2, v2; + public final Texture texture; + + public TextureRegion(Texture texture, float x, float y, float width, float height) { + this.u1 = x / texture.width; + this.v1 = y / texture.height; + this.u2 = this.u1 + width / texture.width; + this.v2 = this.v1 + height / texture.height; + this.texture = texture; + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/VBOVertices3.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/VBOVertices3.java new file mode 100644 index 0000000..14c2845 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/VBOVertices3.java @@ -0,0 +1,95 @@ +package com.badlogic.androidgames.framework.gl; + +import javax.microedition.khronos.opengles.GL10; +import javax.microedition.khronos.opengles.GL11; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class VBOVertices3 extends Vertices3{ + int vboHandle; + + public VBOVertices3(GLGraphics glGraphics, int maxVertices, int maxIndices, + boolean hasColor, boolean hasTexCoords, boolean hasNormals) { + super(glGraphics, maxVertices, maxIndices, hasColor, hasTexCoords, hasNormals); + createHandle(); + } + + public void createHandle() { + int[] tmp = new int[1]; + GL11 gl = (GL11)glGraphics.getGL(); + gl.glGenBuffers(1, tmp, 0); + vboHandle = tmp[0]; + } + + public void setVertices(float[] vertices, int offset, int length) { + super.setVertices(vertices, offset, length); + + GL11 gl = (GL11)glGraphics.getGL(); + gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vboHandle); + gl.glBufferData(GL11.GL_ARRAY_BUFFER, this.vertices.limit() * 4, this.vertices, GL11.GL_STATIC_DRAW); + gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0); + } + + public void bind() { + GL11 gl = (GL11)glGraphics.getGL(); + + gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vboHandle); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + gl.glVertexPointer(3, GL10.GL_FLOAT, vertexSize, 0); + + if (hasColor) { + gl.glEnableClientState(GL10.GL_COLOR_ARRAY); + gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, 3*4); + } + + if (hasTexCoords) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, (hasColor ? 7 : 3) * 4); + } + + if (hasNormals) { + gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); + int offset = 3; + if (hasColor) + offset += 4; + if (hasTexCoords) + offset += 2; + gl.glNormalPointer(GL10.GL_FLOAT, vertexSize, offset * 4); + } + } + + public void draw(int primitiveType, int offset, int numVertices) { + GL10 gl = glGraphics.getGL(); + + if (indices != null) { + indices.position(offset); + gl.glDrawElements(primitiveType, numVertices, + GL10.GL_UNSIGNED_SHORT, indices); + } else { + gl.glDrawArrays(primitiveType, offset, numVertices); + } + } + + public void unbind() { + GL11 gl = (GL11)glGraphics.getGL(); + if (hasTexCoords) + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + if (hasColor) + gl.glDisableClientState(GL10.GL_COLOR_ARRAY); + + if (hasNormals) + gl.glDisableClientState(GL10.GL_NORMAL_ARRAY); + + gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0); + } + + public int getNumIndices() { + return indices.limit(); + } + + public int getNumVertices() { + return vertices.limit() / (vertexSize / 4); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Vertices.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Vertices.java new file mode 100644 index 0000000..d8bbee3 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Vertices.java @@ -0,0 +1,95 @@ +package com.badlogic.androidgames.framework.gl; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Vertices { + final GLGraphics glGraphics; + final boolean hasColor; + final boolean hasTexCoords; + final int vertexSize; + final IntBuffer vertices; + final int[] tmpBuffer; + final ShortBuffer indices; + + public Vertices(GLGraphics glGraphics, int maxVertices, int maxIndices, boolean hasColor, boolean hasTexCoords) { + this.glGraphics = glGraphics; + this.hasColor = hasColor; + this.hasTexCoords = hasTexCoords; + this.vertexSize = (2 + (hasColor?4:0) + (hasTexCoords?2:0)) * 4; + this.tmpBuffer = new int[maxVertices * vertexSize / 4]; + + ByteBuffer buffer = ByteBuffer.allocateDirect(maxVertices * vertexSize); + buffer.order(ByteOrder.nativeOrder()); + vertices = buffer.asIntBuffer(); + + if(maxIndices > 0) { + buffer = ByteBuffer.allocateDirect(maxIndices * Short.SIZE / 8); + buffer.order(ByteOrder.nativeOrder()); + indices = buffer.asShortBuffer(); + } else { + indices = null; + } + } + + public void setVertices(float[] vertices, int offset, int length) { + this.vertices.clear(); + int len = offset + length; + for(int i=offset, j=0; i < len; i++, j++) + tmpBuffer[j] = Float.floatToRawIntBits(vertices[i]); + this.vertices.put(tmpBuffer, 0, length); + this.vertices.flip(); + } + + public void setIndices(short[] indices, int offset, int length) { + this.indices.clear(); + this.indices.put(indices, offset, length); + this.indices.flip(); + } + +public void bind() { + GL10 gl = glGraphics.getGL(); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + vertices.position(0); + gl.glVertexPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + + if(hasColor) { + gl.glEnableClientState(GL10.GL_COLOR_ARRAY); + vertices.position(2); + gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices); + } + + if(hasTexCoords) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + vertices.position(hasColor?6:2); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + } +} + +public void draw(int primitiveType, int offset, int numVertices) { + GL10 gl = glGraphics.getGL(); + + if(indices!=null) { + indices.position(offset); + gl.glDrawElements(primitiveType, numVertices, GL10.GL_UNSIGNED_SHORT, indices); + } else { + gl.glDrawArrays(primitiveType, offset, numVertices); + } +} + +public void unbind() { + GL10 gl = glGraphics.getGL(); + if(hasTexCoords) + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + if(hasColor) + gl.glDisableClientState(GL10.GL_COLOR_ARRAY); +} +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Vertices3.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Vertices3.java new file mode 100644 index 0000000..81c321d --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/gl/Vertices3.java @@ -0,0 +1,122 @@ +package com.badlogic.androidgames.framework.gl; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import com.badlogic.androidgames.framework.impl.GLGraphics; + +public class Vertices3 { + final GLGraphics glGraphics; + final boolean hasColor; + final boolean hasTexCoords; + final boolean hasNormals; + final int vertexSize; + final IntBuffer vertices; + final int[] tmpBuffer; + final ShortBuffer indices; + + public Vertices3(GLGraphics glGraphics, int maxVertices, int maxIndices, + boolean hasColor, boolean hasTexCoords, boolean hasNormals) { + this.glGraphics = glGraphics; + this.hasColor = hasColor; + this.hasTexCoords = hasTexCoords; + this.hasNormals = hasNormals; + this.vertexSize = (3 + (hasColor ? 4 : 0) + (hasTexCoords ? 2 : 0) + (hasNormals ? 3 + : 0)) * 4; + this.tmpBuffer = new int[maxVertices * vertexSize / 4]; + + ByteBuffer buffer = ByteBuffer.allocateDirect(maxVertices * vertexSize); + buffer.order(ByteOrder.nativeOrder()); + vertices = buffer.asIntBuffer(); + + if (maxIndices > 0) { + buffer = ByteBuffer.allocateDirect(maxIndices * Short.SIZE / 8); + buffer.order(ByteOrder.nativeOrder()); + indices = buffer.asShortBuffer(); + } else { + indices = null; + } + } + + public void setVertices(float[] vertices, int offset, int length) { + this.vertices.clear(); + int len = offset + length; + for (int i = offset, j = 0; i < len; i++, j++) + tmpBuffer[j] = Float.floatToRawIntBits(vertices[i]); + this.vertices.put(tmpBuffer, 0, length); + this.vertices.flip(); + } + + public void setIndices(short[] indices, int offset, int length) { + this.indices.clear(); + this.indices.put(indices, offset, length); + this.indices.flip(); + } + + public void bind() { + GL10 gl = glGraphics.getGL(); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + vertices.position(0); + gl.glVertexPointer(3, GL10.GL_FLOAT, vertexSize, vertices); + + if (hasColor) { + gl.glEnableClientState(GL10.GL_COLOR_ARRAY); + vertices.position(3); + gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices); + } + + if (hasTexCoords) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + vertices.position(hasColor ? 7 : 3); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices); + } + + if (hasNormals) { + gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); + int offset = 3; + if (hasColor) + offset += 4; + if (hasTexCoords) + offset += 2; + vertices.position(offset); + gl.glNormalPointer(GL10.GL_FLOAT, vertexSize, vertices); + } + } + + public void draw(int primitiveType, int offset, int numVertices) { + GL10 gl = glGraphics.getGL(); + + if (indices != null) { + indices.position(offset); + gl.glDrawElements(primitiveType, numVertices, + GL10.GL_UNSIGNED_SHORT, indices); + } else { + gl.glDrawArrays(primitiveType, offset, numVertices); + } + } + + public void unbind() { + GL10 gl = glGraphics.getGL(); + if (hasTexCoords) + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + if (hasColor) + gl.glDisableClientState(GL10.GL_COLOR_ARRAY); + + if (hasNormals) + gl.glDisableClientState(GL10.GL_NORMAL_ARRAY); + } + + public int getNumIndices() { + return indices.limit(); + } + + public int getNumVertices() { + return vertices.limit() / (vertexSize / 4); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java new file mode 100644 index 0000000..4820c5a --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AccelerometerHandler.java @@ -0,0 +1,48 @@ +package com.badlogic.androidgames.framework.impl; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + +public class AccelerometerHandler implements SensorEventListener { + float accelX; + float accelY; + float accelZ; + + public AccelerometerHandler(Context context) { + SensorManager manager = (SensorManager) context + .getSystemService(Context.SENSOR_SERVICE); + if (manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() != 0) { + Sensor accelerometer = manager.getSensorList( + Sensor.TYPE_ACCELEROMETER).get(0); + manager.registerListener(this, accelerometer, + SensorManager.SENSOR_DELAY_GAME); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // nothing to do here + } + + @Override + public void onSensorChanged(SensorEvent event) { + accelX = event.values[0]; + accelY = event.values[1]; + accelZ = event.values[2]; + } + + public float getAccelX() { + return accelX; + } + + public float getAccelY() { + return accelY; + } + + public float getAccelZ() { + return accelZ; + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java new file mode 100644 index 0000000..42f8c1f --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidAudio.java @@ -0,0 +1,45 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; + +import android.app.Activity; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.media.AudioManager; +import android.media.SoundPool; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.Music; +import com.badlogic.androidgames.framework.Sound; + +public class AndroidAudio implements Audio { + AssetManager assets; + SoundPool soundPool; + + public AndroidAudio(Activity activity) { + activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); + this.assets = activity.getAssets(); + this.soundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0); + } + + @Override + public Music newMusic(String filename) { + try { + AssetFileDescriptor assetDescriptor = assets.openFd(filename); + return new AndroidMusic(assetDescriptor); + } catch (IOException e) { + throw new RuntimeException("Couldn't load music '" + filename + "'"); + } + } + + @Override + public Sound newSound(String filename) { + try { + AssetFileDescriptor assetDescriptor = assets.openFd(filename); + int soundId = soundPool.load(assetDescriptor, 0); + return new AndroidSound(soundPool, soundId); + } catch (IOException e) { + throw new RuntimeException("Couldn't load sound '" + filename + "'"); + } + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java new file mode 100644 index 0000000..6171d90 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidFastRenderView.java @@ -0,0 +1,60 @@ +package com.badlogic.androidgames.framework.impl; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class AndroidFastRenderView extends SurfaceView implements Runnable { + AndroidGame game; + Bitmap framebuffer; + Thread renderThread = null; + SurfaceHolder holder; + volatile boolean running = false; + + public AndroidFastRenderView(AndroidGame game, Bitmap framebuffer) { + super(game); + this.game = game; + this.framebuffer = framebuffer; + this.holder = getHolder(); + } + + public void resume() { + running = true; + renderThread = new Thread(this); + renderThread.start(); + } + + public void run() { + Rect dstRect = new Rect(); + long startTime = System.nanoTime(); + while(running) { + if(!holder.getSurface().isValid()) + continue; + + float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; + startTime = System.nanoTime(); + + game.getCurrentScreen().update(deltaTime); + game.getCurrentScreen().present(deltaTime); + + Canvas canvas = holder.lockCanvas(); + canvas.getClipBounds(dstRect); + canvas.drawBitmap(framebuffer, null, dstRect, null); + holder.unlockCanvasAndPost(canvas); + } + } + + public void pause() { + running = false; + while(true) { + try { + renderThread.join(); + break; + } catch (InterruptedException e) { + // retry + } + } + } +} \ No newline at end of file diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java new file mode 100644 index 0000000..b13b115 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidFileIO.java @@ -0,0 +1,39 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import android.content.res.AssetManager; +import android.os.Environment; + +import com.badlogic.androidgames.framework.FileIO; + +public class AndroidFileIO implements FileIO { + AssetManager assets; + String externalStoragePath; + + public AndroidFileIO(AssetManager assets) { + this.assets = assets; + this.externalStoragePath = Environment.getExternalStorageDirectory() + .getAbsolutePath() + File.separator; + } + + @Override + public InputStream readAsset(String fileName) throws IOException { + return assets.open(fileName); + } + + @Override + public InputStream readFile(String fileName) throws IOException { + return new FileInputStream(externalStoragePath + fileName); + } + + @Override + public OutputStream writeFile(String fileName) throws IOException { + return new FileOutputStream(externalStoragePath + fileName); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidGame.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidGame.java new file mode 100644 index 0000000..d1fd0b3 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidGame.java @@ -0,0 +1,107 @@ +package com.badlogic.androidgames.framework.impl; + +import android.app.Activity; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.os.Bundle; +import android.view.Window; +import android.view.WindowManager; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input; +import com.badlogic.androidgames.framework.Screen; + +public abstract class AndroidGame extends Activity implements Game { + AndroidFastRenderView renderView; + Graphics graphics; + Audio audio; + Input input; + FileIO fileIO; + Screen screen; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + + boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + int frameBufferWidth = isLandscape ? 480 : 320; + int frameBufferHeight = isLandscape ? 320 : 480; + Bitmap frameBuffer = Bitmap.createBitmap(frameBufferWidth, + frameBufferHeight, Config.RGB_565); + + float scaleX = (float) frameBufferWidth + / getWindowManager().getDefaultDisplay().getWidth(); + float scaleY = (float) frameBufferHeight + / getWindowManager().getDefaultDisplay().getHeight(); + + renderView = new AndroidFastRenderView(this, frameBuffer); + graphics = new AndroidGraphics(getAssets(), frameBuffer); + fileIO = new AndroidFileIO(getAssets()); + audio = new AndroidAudio(this); + input = new AndroidInput(this, renderView, scaleX, scaleY); + screen = getStartScreen(); + setContentView(renderView); + } + + @Override + public void onResume() { + super.onResume(); + screen.resume(); + renderView.resume(); + } + + @Override + public void onPause() { + super.onPause(); + renderView.pause(); + screen.pause(); + + if (isFinishing()) { + screen.dispose(); + } + } + + @Override + public Input getInput() { + return input; + } + + @Override + public FileIO getFileIO() { + return fileIO; + } + + @Override + public Graphics getGraphics() { + return graphics; + } + + @Override + public Audio getAudio() { + return audio; + } + + @Override + public void setScreen(Screen screen) { + if (screen == null) + throw new IllegalArgumentException("Screen must not be null"); + + this.screen.pause(); + this.screen.dispose(); + screen.resume(); + screen.update(0); + this.screen = screen; + } + + public Screen getCurrentScreen() { + return screen; + } +} \ No newline at end of file diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java new file mode 100644 index 0000000..ade52b5 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidGraphics.java @@ -0,0 +1,133 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; +import java.io.InputStream; + +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.BitmapFactory.Options; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Rect; + +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Pixmap; + +public class AndroidGraphics implements Graphics { + AssetManager assets; + Bitmap frameBuffer; + Canvas canvas; + Paint paint; + Rect srcRect = new Rect(); + Rect dstRect = new Rect(); + + public AndroidGraphics(AssetManager assets, Bitmap frameBuffer) { + this.assets = assets; + this.frameBuffer = frameBuffer; + this.canvas = new Canvas(frameBuffer); + this.paint = new Paint(); + } + + @Override + public Pixmap newPixmap(String fileName, PixmapFormat format) { + Config config = null; + if (format == PixmapFormat.RGB565) + config = Config.RGB_565; + else if (format == PixmapFormat.ARGB4444) + config = Config.ARGB_4444; + else + config = Config.ARGB_8888; + + Options options = new Options(); + options.inPreferredConfig = config; + + InputStream in = null; + Bitmap bitmap = null; + try { + in = assets.open(fileName); + bitmap = BitmapFactory.decodeStream(in, null, options); + if (bitmap == null) + throw new RuntimeException("Couldn't load bitmap from asset '" + + fileName + "'"); + } catch (IOException e) { + throw new RuntimeException("Couldn't load bitmap from asset '" + + fileName + "'"); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + + if (bitmap.getConfig() == Config.RGB_565) + format = PixmapFormat.RGB565; + else if (bitmap.getConfig() == Config.ARGB_4444) + format = PixmapFormat.ARGB4444; + else + format = PixmapFormat.ARGB8888; + + return new AndroidPixmap(bitmap, format); + } + + @Override + public void clear(int color) { + canvas.drawRGB((color & 0xff0000) >> 16, (color & 0xff00) >> 8, + (color & 0xff)); + } + + @Override + public void drawPixel(int x, int y, int color) { + paint.setColor(color); + canvas.drawPoint(x, y, paint); + } + + @Override + public void drawLine(int x, int y, int x2, int y2, int color) { + paint.setColor(color); + canvas.drawLine(x, y, x2, y2, paint); + } + + @Override + public void drawRect(int x, int y, int width, int height, int color) { + paint.setColor(color); + paint.setStyle(Style.FILL); + canvas.drawRect(x, y, x + width - 1, y + height - 1, paint); + } + + @Override + public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight) { + srcRect.left = srcX; + srcRect.top = srcY; + srcRect.right = srcX + srcWidth - 1; + srcRect.bottom = srcY + srcHeight - 1; + + dstRect.left = x; + dstRect.top = y; + dstRect.right = x + srcWidth - 1; + dstRect.bottom = y + srcHeight - 1; + + canvas.drawBitmap(((AndroidPixmap) pixmap).bitmap, srcRect, dstRect, + null); + } + + @Override + public void drawPixmap(Pixmap pixmap, int x, int y) { + canvas.drawBitmap(((AndroidPixmap)pixmap).bitmap, x, y, null); + } + + @Override + public int getWidth() { + return frameBuffer.getWidth(); + } + + @Override + public int getHeight() { + return frameBuffer.getHeight(); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidInput.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidInput.java new file mode 100644 index 0000000..11f5863 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidInput.java @@ -0,0 +1,69 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.List; + +import android.content.Context; +import android.os.Build.VERSION; +import android.view.View; + +import com.badlogic.androidgames.framework.Input; + +public class AndroidInput implements Input { + AccelerometerHandler accelHandler; + KeyboardHandler keyHandler; + TouchHandler touchHandler; + + public AndroidInput(Context context, View view, float scaleX, float scaleY) { + accelHandler = new AccelerometerHandler(context); + keyHandler = new KeyboardHandler(view); + if(Integer.parseInt(VERSION.SDK) < 5) + touchHandler = new SingleTouchHandler(view, scaleX, scaleY); + else + touchHandler = new MultiTouchHandler(view, scaleX, scaleY); + } + + @Override + public boolean isKeyPressed(int keyCode) { + return keyHandler.isKeyPressed(keyCode); + } + + @Override + public boolean isTouchDown(int pointer) { + return touchHandler.isTouchDown(pointer); + } + + @Override + public int getTouchX(int pointer) { + return touchHandler.getTouchX(pointer); + } + + @Override + public int getTouchY(int pointer) { + return touchHandler.getTouchY(pointer); + } + + @Override + public float getAccelX() { + return accelHandler.getAccelX(); + } + + @Override + public float getAccelY() { + return accelHandler.getAccelY(); + } + + @Override + public float getAccelZ() { + return accelHandler.getAccelZ(); + } + + @Override + public List getTouchEvents() { + return touchHandler.getTouchEvents(); + } + + @Override + public List getKeyEvents() { + return keyHandler.getKeyEvents(); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java new file mode 100644 index 0000000..9e9fd1c --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidMusic.java @@ -0,0 +1,99 @@ +package com.badlogic.androidgames.framework.impl; + +import java.io.IOException; + +import android.content.res.AssetFileDescriptor; +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnCompletionListener; + +import com.badlogic.androidgames.framework.Music; + +public class AndroidMusic implements Music, OnCompletionListener { + MediaPlayer mediaPlayer; + boolean isPrepared = false; + + public AndroidMusic(AssetFileDescriptor assetDescriptor) { + mediaPlayer = new MediaPlayer(); + try { + mediaPlayer.setDataSource(assetDescriptor.getFileDescriptor(), + assetDescriptor.getStartOffset(), + assetDescriptor.getLength()); + mediaPlayer.prepare(); + isPrepared = true; + mediaPlayer.setOnCompletionListener(this); + } catch (Exception e) { + throw new RuntimeException("Couldn't load music"); + } + } + + @Override + public void dispose() { + if (mediaPlayer.isPlaying()) + mediaPlayer.stop(); + mediaPlayer.release(); + } + + @Override + public boolean isLooping() { + return mediaPlayer.isLooping(); + } + + @Override + public boolean isPlaying() { + return mediaPlayer.isPlaying(); + } + + @Override + public boolean isStopped() { + return !isPrepared; + } + + @Override + public void pause() { + if (mediaPlayer.isPlaying()) + mediaPlayer.pause(); + } + + @Override + public void play() { + if (mediaPlayer.isPlaying()) + return; + + try { + synchronized (this) { + if (!isPrepared) + mediaPlayer.prepare(); + mediaPlayer.start(); + } + } catch (IllegalStateException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void setLooping(boolean isLooping) { + mediaPlayer.setLooping(isLooping); + } + + @Override + public void setVolume(float volume) { + mediaPlayer.setVolume(volume, volume); + } + + @Override + public void stop() { + mediaPlayer.stop(); + synchronized (this) { + isPrepared = false; + } + } + + @Override + public void onCompletion(MediaPlayer arg0) { + synchronized (this) { + isPrepared = false; + } + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java new file mode 100644 index 0000000..9de5021 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidPixmap.java @@ -0,0 +1,36 @@ +package com.badlogic.androidgames.framework.impl; + +import android.graphics.Bitmap; + +import com.badlogic.androidgames.framework.Graphics.PixmapFormat; +import com.badlogic.androidgames.framework.Pixmap; + +public class AndroidPixmap implements Pixmap { + Bitmap bitmap; + PixmapFormat format; + + public AndroidPixmap(Bitmap bitmap, PixmapFormat format) { + this.bitmap = bitmap; + this.format = format; + } + + @Override + public int getWidth() { + return bitmap.getWidth(); + } + + @Override + public int getHeight() { + return bitmap.getHeight(); + } + + @Override + public PixmapFormat getFormat() { + return format; + } + + @Override + public void dispose() { + bitmap.recycle(); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidSound.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidSound.java new file mode 100644 index 0000000..4a8fcd4 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/AndroidSound.java @@ -0,0 +1,26 @@ +package com.badlogic.androidgames.framework.impl; + +import android.media.SoundPool; + +import com.badlogic.androidgames.framework.Sound; + +public class AndroidSound implements Sound { + int soundId; + SoundPool soundPool; + + public AndroidSound(SoundPool soundPool,int soundId) { + this.soundId = soundId; + this.soundPool = soundPool; + } + + @Override + public void play(float volume) { + soundPool.play(soundId, volume, volume, 0, 0, 1); + } + + @Override + public void dispose() { + soundPool.unload(soundId); + } + +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/GLGame.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/GLGame.java new file mode 100644 index 0000000..5822786 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/GLGame.java @@ -0,0 +1,178 @@ +package com.badlogic.androidgames.framework.impl; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import android.app.Activity; +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.opengl.GLSurfaceView.Renderer; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.view.Window; +import android.view.WindowManager; + +import com.badlogic.androidgames.framework.Audio; +import com.badlogic.androidgames.framework.FileIO; +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Graphics; +import com.badlogic.androidgames.framework.Input; +import com.badlogic.androidgames.framework.Screen; + +public abstract class GLGame extends Activity implements Game, Renderer { + enum GLGameState { + Initialized, + Running, + Paused, + Finished, + Idle + } + + GLSurfaceView glView; + GLGraphics glGraphics; + Audio audio; + Input input; + FileIO fileIO; + Screen screen; + GLGameState state = GLGameState.Initialized; + Object stateChanged = new Object(); + long startTime = System.nanoTime(); + WakeLock wakeLock; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + glView = new GLSurfaceView(this); + glView.setRenderer(this); + setContentView(glView); + + glGraphics = new GLGraphics(glView); + fileIO = new AndroidFileIO(getAssets()); + audio = new AndroidAudio(this); + input = new AndroidInput(this, glView, 1, 1); + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GLGame"); + } + + public void onResume() { + super.onResume(); + glView.onResume(); + wakeLock.acquire(); + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + glGraphics.setGL(gl); + + synchronized(stateChanged) { + if(state == GLGameState.Initialized) + screen = getStartScreen(); + state = GLGameState.Running; + screen.resume(); + startTime = System.nanoTime(); + } + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + } + + @Override + public void onDrawFrame(GL10 gl) { + GLGameState state = null; + + synchronized(stateChanged) { + state = this.state; + } + + if(state == GLGameState.Running) { + float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; + startTime = System.nanoTime(); + + screen.update(deltaTime); + screen.present(deltaTime); + } + + if(state == GLGameState.Paused) { + screen.pause(); + synchronized(stateChanged) { + this.state = GLGameState.Idle; + stateChanged.notifyAll(); + } + } + + if(state == GLGameState.Finished) { + screen.pause(); + screen.dispose(); + synchronized(stateChanged) { + this.state = GLGameState.Idle; + stateChanged.notifyAll(); + } + } + } + + @Override + public void onPause() { + synchronized(stateChanged) { + if(isFinishing()) + state = GLGameState.Finished; + else + state = GLGameState.Paused; + while(true) { + try { + stateChanged.wait(); + break; + } catch(InterruptedException e) { + } + } + } + wakeLock.release(); + glView.onPause(); + super.onPause(); + } + + public GLGraphics getGLGraphics() { + return glGraphics; + } + + @Override + public Input getInput() { + return input; + } + + @Override + public FileIO getFileIO() { + return fileIO; + } + + @Override + public Graphics getGraphics() { + throw new IllegalStateException("We are using OpenGL!"); + } + + @Override + public Audio getAudio() { + return audio; + } + + @Override + public void setScreen(Screen screen) { + if (screen == null) + throw new IllegalArgumentException("Screen must not be null"); + + this.screen.pause(); + this.screen.dispose(); + screen.resume(); + screen.update(0); + this.screen = screen; + } + + @Override + public Screen getCurrentScreen() { + return screen; + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/GLGraphics.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/GLGraphics.java new file mode 100644 index 0000000..2eb1b43 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/GLGraphics.java @@ -0,0 +1,30 @@ +package com.badlogic.androidgames.framework.impl; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLSurfaceView; + +public class GLGraphics { + GLSurfaceView glView; + GL10 gl; + + GLGraphics(GLSurfaceView glView) { + this.glView = glView; + } + + public GL10 getGL() { + return gl; + } + + void setGL(GL10 gl) { + this.gl = gl; + } + + public int getWidth() { + return glView.getWidth(); + } + + public int getHeight() { + return glView.getHeight(); + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/GLScreen.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/GLScreen.java new file mode 100644 index 0000000..466de8c --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/GLScreen.java @@ -0,0 +1,16 @@ +package com.badlogic.androidgames.framework.impl; + +import com.badlogic.androidgames.framework.Game; +import com.badlogic.androidgames.framework.Screen; + +public abstract class GLScreen extends Screen { + protected final GLGraphics glGraphics; + protected final GLGame glGame; + + public GLScreen(Game game) { + super(game); + glGame = (GLGame)game; + glGraphics = ((GLGame)game).getGLGraphics(); + } + +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java new file mode 100644 index 0000000..f70d3e9 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/KeyboardHandler.java @@ -0,0 +1,73 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.View; +import android.view.View.OnKeyListener; + +import com.badlogic.androidgames.framework.Input.KeyEvent; +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class KeyboardHandler implements OnKeyListener { + boolean[] pressedKeys = new boolean[128]; + Pool keyEventPool; + List keyEventsBuffer = new ArrayList(); + List keyEvents = new ArrayList(); + + public KeyboardHandler(View view) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public KeyEvent createObject() { + return new KeyEvent(); + } + }; + keyEventPool = new Pool(factory, 100); + view.setOnKeyListener(this); + view.setFocusableInTouchMode(true); + view.requestFocus(); + } + + @Override + public boolean onKey(View v, int keyCode, android.view.KeyEvent event) { + if (event.getAction() == android.view.KeyEvent.ACTION_MULTIPLE) + return false; + + synchronized (this) { + KeyEvent keyEvent = keyEventPool.newObject(); + keyEvent.keyCode = keyCode; + keyEvent.keyChar = (char) event.getUnicodeChar(); + if (event.getAction() == android.view.KeyEvent.ACTION_DOWN) { + keyEvent.type = KeyEvent.KEY_DOWN; + if(keyCode >= 0 && keyCode < 128) + pressedKeys[keyCode] = true; + } + if (event.getAction() == android.view.KeyEvent.ACTION_UP) { + keyEvent.type = KeyEvent.KEY_UP; + if(keyCode >= 0 && keyCode < 128) + pressedKeys[keyCode] = false; + } + keyEventsBuffer.add(keyEvent); + } + return false; + } + + public boolean isKeyPressed(int keyCode) { + if (keyCode < 0 || keyCode > 127) + return false; + return pressedKeys[keyCode]; + } + + public List getKeyEvents() { + synchronized (this) { + int len = keyEvents.size(); + for (int i = 0; i < len; i++) + keyEventPool.free(keyEvents.get(i)); + keyEvents.clear(); + keyEvents.addAll(keyEventsBuffer); + keyEventsBuffer.clear(); + return keyEvents; + } + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java new file mode 100644 index 0000000..4668c57 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/MultiTouchHandler.java @@ -0,0 +1,137 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.MotionEvent; +import android.view.View; + +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class MultiTouchHandler implements TouchHandler { + boolean[] isTouched = new boolean[20]; + int[] touchX = new int[20]; + int[] touchY = new int[20]; + Pool touchEventPool; + List touchEvents = new ArrayList(); + List touchEventsBuffer = new ArrayList(); + float scaleX; + float scaleY; + + public MultiTouchHandler(View view, float scaleX, float scaleY) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }; + touchEventPool = new Pool(factory, 100); + view.setOnTouchListener(this); + + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + synchronized (this) { + int action = event.getAction() & MotionEvent.ACTION_MASK; + int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; + int pointerId = event.getPointerId(pointerIndex); + TouchEvent touchEvent; + + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_DOWN; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + isTouched[pointerId] = true; + touchEventsBuffer.add(touchEvent); + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_CANCEL: + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_UP; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + isTouched[pointerId] = false; + touchEventsBuffer.add(touchEvent); + break; + + case MotionEvent.ACTION_MOVE: + int pointerCount = event.getPointerCount(); + for (int i = 0; i < pointerCount; i++) { + pointerIndex = i; + pointerId = event.getPointerId(pointerIndex); + + touchEvent = touchEventPool.newObject(); + touchEvent.type = TouchEvent.TOUCH_DRAGGED; + touchEvent.pointer = pointerId; + touchEvent.x = touchX[pointerId] = (int) (event + .getX(pointerIndex) * scaleX); + touchEvent.y = touchY[pointerId] = (int) (event + .getY(pointerIndex) * scaleY); + touchEventsBuffer.add(touchEvent); + } + break; + } + + return true; + } + } + + @Override + public boolean isTouchDown(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return false; + else + return isTouched[pointer]; + } + } + + @Override + public int getTouchX(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return 0; + else + return touchX[pointer]; + } + } + + @Override + public int getTouchY(int pointer) { + synchronized (this) { + if (pointer < 0 || pointer >= 20) + return 0; + else + return touchY[pointer]; + } + } + + @Override + public List getTouchEvents() { + synchronized (this) { + int len = touchEvents.size(); + for (int i = 0; i < len; i++) + touchEventPool.free(touchEvents.get(i)); + touchEvents.clear(); + touchEvents.addAll(touchEventsBuffer); + touchEventsBuffer.clear(); + return touchEvents; + } + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java new file mode 100644 index 0000000..7ff1205 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/SingleTouchHandler.java @@ -0,0 +1,101 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.ArrayList; +import java.util.List; + +import android.view.MotionEvent; +import android.view.View; + +import com.badlogic.androidgames.framework.Pool; +import com.badlogic.androidgames.framework.Input.TouchEvent; +import com.badlogic.androidgames.framework.Pool.PoolObjectFactory; + +public class SingleTouchHandler implements TouchHandler { + boolean isTouched; + int touchX; + int touchY; + Pool touchEventPool; + List touchEvents = new ArrayList(); + List touchEventsBuffer = new ArrayList(); + float scaleX; + float scaleY; + + public SingleTouchHandler(View view, float scaleX, float scaleY) { + PoolObjectFactory factory = new PoolObjectFactory() { + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }; + touchEventPool = new Pool(factory, 100); + view.setOnTouchListener(this); + + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + synchronized(this) { + TouchEvent touchEvent = touchEventPool.newObject(); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touchEvent.type = TouchEvent.TOUCH_DOWN; + isTouched = true; + break; + case MotionEvent.ACTION_MOVE: + touchEvent.type = TouchEvent.TOUCH_DRAGGED; + isTouched = true; + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + touchEvent.type = TouchEvent.TOUCH_UP; + isTouched = false; + break; + } + + touchEvent.x = touchX = (int)(event.getX() * scaleX); + touchEvent.y = touchY = (int)(event.getY() * scaleY); + touchEventsBuffer.add(touchEvent); + + return true; + } + } + + @Override + public boolean isTouchDown(int pointer) { + synchronized(this) { + if(pointer == 0) + return isTouched; + else + return false; + } + } + + @Override + public int getTouchX(int pointer) { + synchronized(this) { + return touchX; + } + } + + @Override + public int getTouchY(int pointer) { + synchronized(this) { + return touchY; + } + } + + @Override + public List getTouchEvents() { + synchronized(this) { + int len = touchEvents.size(); + for( int i = 0; i < len; i++ ) + touchEventPool.free(touchEvents.get(i)); + touchEvents.clear(); + touchEvents.addAll(touchEventsBuffer); + touchEventsBuffer.clear(); + return touchEvents; + } + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/TouchHandler.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/TouchHandler.java new file mode 100644 index 0000000..cc7e754 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/impl/TouchHandler.java @@ -0,0 +1,17 @@ +package com.badlogic.androidgames.framework.impl; + +import java.util.List; + +import android.view.View.OnTouchListener; + +import com.badlogic.androidgames.framework.Input.TouchEvent; + +public interface TouchHandler extends OnTouchListener { + public boolean isTouchDown(int pointer); + + public int getTouchX(int pointer); + + public int getTouchY(int pointer); + + public List getTouchEvents(); +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Circle.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Circle.java new file mode 100644 index 0000000..2728f84 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Circle.java @@ -0,0 +1,11 @@ +package com.badlogic.androidgames.framework.math; + +public class Circle { + public final Vector2 center = new Vector2(); + public float radius; + + public Circle(float x, float y, float radius) { + this.center.set(x,y); + this.radius = radius; + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/OverlapTester.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/OverlapTester.java new file mode 100644 index 0000000..3650946 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/OverlapTester.java @@ -0,0 +1,72 @@ +package com.badlogic.androidgames.framework.math; + +public class OverlapTester { + public static boolean overlapCircles(Circle c1, Circle c2) { + float distance = c1.center.distSquared(c2.center); + float radiusSum = c1.radius + c2.radius; + return distance <= radiusSum * radiusSum; + } + + public static boolean overlapRectangles(Rectangle r1, Rectangle r2) { + if(r1.lowerLeft.x < r2.lowerLeft.x + r2.width && + r1.lowerLeft.x + r1.width > r2.lowerLeft.x && + r1.lowerLeft.y < r2.lowerLeft.y + r2.height && + r1.lowerLeft.y + r1.height > r2.lowerLeft.y) + return true; + else + return false; + } + + public static boolean overlapCircleRectangle(Circle c, Rectangle r) { + float closestX = c.center.x; + float closestY = c.center.y; + + if(c.center.x < r.lowerLeft.x) { + closestX = r.lowerLeft.x; + } + else if(c.center.x > r.lowerLeft.x + r.width) { + closestX = r.lowerLeft.x + r.width; + } + + if(c.center.y < r.lowerLeft.y) { + closestY = r.lowerLeft.y; + } + else if(c.center.y > r.lowerLeft.y + r.height) { + closestY = r.lowerLeft.y + r.height; + } + + return c.center.distSquared(closestX, closestY) < c.radius * c.radius; + } + + public static boolean overlapSpheres(Sphere s1, Sphere s2) { + float distance = s1.center.distSquared(s2.center); + float radiusSum = s1.radius + s2.radius; + return distance <= radiusSum * radiusSum; + } + + public static boolean pointInSphere(Sphere c, Vector3 p) { + return c.center.distSquared(p) < c.radius * c.radius; + } + + public static boolean pointInSphere(Sphere c, float x, float y, float z) { + return c.center.distSquared(x, y, z) < c.radius * c.radius; + } + + public static boolean pointInCircle(Circle c, Vector2 p) { + return c.center.distSquared(p) < c.radius * c.radius; + } + + public static boolean pointInCircle(Circle c, float x, float y) { + return c.center.distSquared(x, y) < c.radius * c.radius; + } + + public static boolean pointInRectangle(Rectangle r, Vector2 p) { + return r.lowerLeft.x <= p.x && r.lowerLeft.x + r.width >= p.x && + r.lowerLeft.y <= p.y && r.lowerLeft.y + r.height >= p.y; + } + + public static boolean pointInRectangle(Rectangle r, float x, float y) { + return r.lowerLeft.x <= x && r.lowerLeft.x + r.width >= x && + r.lowerLeft.y <= y && r.lowerLeft.y + r.height >= y; + } +} \ No newline at end of file diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Rectangle.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Rectangle.java new file mode 100644 index 0000000..9c2511f --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Rectangle.java @@ -0,0 +1,12 @@ +package com.badlogic.androidgames.framework.math; + +public class Rectangle { + public final Vector2 lowerLeft; + public float width, height; + + public Rectangle(float x, float y, float width, float height) { + this.lowerLeft = new Vector2(x,y); + this.width = width; + this.height = height; + } +} diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Sphere.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Sphere.java new file mode 100644 index 0000000..6f5c4a9 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Sphere.java @@ -0,0 +1,11 @@ +package com.badlogic.androidgames.framework.math; + +public class Sphere { + public final Vector3 center = new Vector3(); + public float radius; + + public Sphere(float x, float y, float z, float radius) { + this.center.set(x,y,z); + this.radius = radius; + } +} \ No newline at end of file diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Vector2.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Vector2.java new file mode 100644 index 0000000..56d79c2 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Vector2.java @@ -0,0 +1,126 @@ +package com.badlogic.androidgames.framework.math; + +import android.util.FloatMath; + +public class Vector2 { + public static float TO_RADIANS = (1 / 180.0f) * (float)Math.PI; + public static float TO_DEGREES = (1 / (float)Math.PI) * 180; + public float x, y; + + public Vector2() { + } + + public Vector2(float x, float y) { + this.x = x; + this.y = y; + } + + public Vector2(Vector2 other) { + this.x = other.x; + this.y = other.y; + } + + public Vector2 cpy() { + return new Vector2(x, y); + } + + public Vector2 set(float x, float y) { + this.x = x; + this.y = y; + return this; + } + + public Vector2 set(Vector2 other) { + this.x = other.x; + this.y = other.y; + return this; + } + + public Vector2 add(float x, float y) { + this.x += x; + this.y += y; + return this; + } + + public Vector2 add(Vector2 other) { + this.x += other.x; + this.y += other.y; + return this; + } + + public Vector2 sub(float x, float y) { + this.x -= x; + this.y -= y; + return this; + } + + public Vector2 sub(Vector2 other) { + this.x -= other.x; + this.y -= other.y; + return this; + } + + public Vector2 mul(float scalar) { + this.x *= scalar; + this.y *= scalar; + return this; + } + + public float len() { + return FloatMath.sqrt(x*x + y*y); + } + + public Vector2 nor() { + float len = len(); + if(len!=0) { + this.x /= len; + this.y /= len; + } + return this; + } + + public float angle() { + float angle = (float)Math.atan2(y, x) * TO_DEGREES; + if(angle < 0) + angle += 360; + return angle; + } + + public Vector2 rotate(float angle) { + float rad = angle * TO_RADIANS; + float cos = FloatMath.cos(rad); + float sin = FloatMath.sin(rad); + + float newX = this.x * cos - this.y * sin; + float newY = this.x * sin + this.y * cos; + + this.x = newX; + this.y = newY; + + return this; + } + + public float dist(Vector2 other) { + float distX = this.x - other.x; + float distY = this.y - other.y; + return FloatMath.sqrt(distX*distX + distY*distY); + } + + public float dist(float x, float y) { + float distX = this.x - x; + float distY = this.y - y; + return FloatMath.sqrt(distX*distX + distY*distY); + } + + public float distSquared(Vector2 other) { + float distX = this.x - other.x; + float distY = this.y - other.y; + return distX*distX + distY*distY; + } + + public float distSquared(float x, float y) { + float distX = this.x - x; + float distY = this.y - y; + return distX*distX + distY*distY; + } +} \ No newline at end of file diff --git a/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Vector3.java b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Vector3.java new file mode 100644 index 0000000..f31dff5 --- /dev/null +++ b/ch12-droidinvaders/src/com/badlogic/androidgames/framework/math/Vector3.java @@ -0,0 +1,135 @@ +package com.badlogic.androidgames.framework.math; + +import android.opengl.Matrix; +import android.util.FloatMath; + +public class Vector3 { + private static final float[] matrix = new float[16]; + private static final float[] inVec = new float[4]; + private static final float[] outVec = new float[4]; + public float x, y, z; + + public Vector3() { + } + + public Vector3(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Vector3(Vector3 other) { + this.x = other.x; + this.y = other.y; + this.z = other.z; + } + + public Vector3 cpy() { + return new Vector3(x, y, z); + } + + public Vector3 set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + public Vector3 set(Vector3 other) { + this.x = other.x; + this.y = other.y; + this.z = other.z; + return this; + } + + public Vector3 add(float x, float y, float z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + public Vector3 add(Vector3 other) { + this.x += other.x; + this.y += other.y; + this.z += other.z; + return this; + } + + public Vector3 sub(float x, float y, float z) { + this.x -= x; + this.y -= y; + this.z -= z; + return this; + } + + public Vector3 sub(Vector3 other) { + this.x -= other.x; + this.y -= other.y; + this.z -= other.z; + return this; + } + + public Vector3 mul(float scalar) { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + return this; + } + + public float len() { + return FloatMath.sqrt(x * x + y * y + z * z); + } + + public Vector3 nor() { + float len = len(); + if (len != 0) { + this.x /= len; + this.y /= len; + this.z /= len; + } + return this; + } + + public Vector3 rotate(float angle, float axisX, float axisY, float axisZ) { + inVec[0] = x; + inVec[1] = y; + inVec[2] = z; + inVec[3] = 1; + Matrix.setIdentityM(matrix, 0); + Matrix.rotateM(matrix, 0, angle, axisX, axisY, axisZ); + Matrix.multiplyMV(outVec, 0, matrix, 0, inVec, 0); + x = outVec[0]; + y = outVec[1]; + z = outVec[2]; + return this; + } + + public float dist(Vector3 other) { + float distX = this.x - other.x; + float distY = this.y - other.y; + float distZ = this.z - other.z; + return FloatMath.sqrt(distX * distX + distY * distY + distZ * distZ); + } + + public float dist(float x, float y, float z) { + float distX = this.x - x; + float distY = this.y - y; + float distZ = this.z - z; + return FloatMath.sqrt(distX * distX + distY * distY + distZ * distZ); + } + + public float distSquared(Vector3 other) { + float distX = this.x - other.x; + float distY = this.y - other.y; + float distZ = this.z - other.z; + return distX * distX + distY * distY + distZ * distZ; + } + + public float distSquared(float x, float y, float z) { + float distX = this.x - x; + float distY = this.y - y; + float distZ = this.z - z; + return distX * distX + distY * distY + distZ * distZ; + } +} \ No newline at end of file diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..f6005ad --- /dev/null +++ b/contributing.md @@ -0,0 +1,14 @@ +# Contributing to Apress Source Code + +Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. + +## How to Contribute + +1. Make sure you have a GitHub account. +2. Fork the repository for the relevant book. +3. Create a new branch on which to make your change, e.g. +`git checkout -b my_code_contribution` +4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. +5. Submit a pull request. + +Thank you for your contribution! \ No newline at end of file