commit bc9dba9c065844ffe756a7b766f90895606819ee Author: Victor Date: Fri Jan 18 13:59:52 2013 +0200 Galaxy In Pixels 0.1 Competition version diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..0ca188f --- /dev/null +++ b/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..35e065e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +assets/ +bin/ +gen/ +proguard/ \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 0000000..9c999f1 --- /dev/null +++ b/.project @@ -0,0 +1,33 @@ + + + GalaxyInPixels + + + + + + 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/AndroidManifest.xml b/AndroidManifest.xml new file mode 100644 index 0000000..73300de --- /dev/null +++ b/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/proguard-project.txt b/proguard-project.txt new file mode 100644 index 0000000..31ac955 --- /dev/null +++ b/proguard-project.txt @@ -0,0 +1,5 @@ +@E:\\SETUPS\\Disk\\Programming\\Java\\android.pro +-obfuscationdictionary E:\\SETUPS\\Disk\\Programming\\Java\\compact.txt +-optimizationpasses 9 +-allowaccessmodification +-overloadaggressively \ No newline at end of file diff --git a/project.properties b/project.properties new file mode 100644 index 0000000..0b83e1b --- /dev/null +++ b/project.properties @@ -0,0 +1,14 @@ +# 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 edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +proguard.config=${sdk.dir}\\tools\\proguard\\proguard-android.txt:proguard-project.txt + +# Project target. +target=android-17 diff --git a/res/drawable-hdpi/ic_launcher.png b/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..d8cc20b Binary files /dev/null and b/res/drawable-hdpi/ic_launcher.png differ diff --git a/res/drawable-ldpi/ic_launcher.png b/res/drawable-ldpi/ic_launcher.png new file mode 100644 index 0000000..9c0e5b0 Binary files /dev/null and b/res/drawable-ldpi/ic_launcher.png differ diff --git a/res/drawable-mdpi/ic_launcher.png b/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..69598c1 Binary files /dev/null and b/res/drawable-mdpi/ic_launcher.png differ diff --git a/res/drawable-xhdpi/ic_launcher.png b/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..dfa6006 Binary files /dev/null and b/res/drawable-xhdpi/ic_launcher.png differ diff --git a/res/values/strings.xml b/res/values/strings.xml new file mode 100644 index 0000000..1380460 --- /dev/null +++ b/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + Galaxy In Pixels + + \ No newline at end of file diff --git a/src/com/annimon/gipgame/Background.java b/src/com/annimon/gipgame/Background.java new file mode 100644 index 0000000..afa63cc --- /dev/null +++ b/src/com/annimon/gipgame/Background.java @@ -0,0 +1,60 @@ +package com.annimon.gipgame; + +import android.graphics.Canvas; +import android.graphics.Paint; + +public class Background { + + private int yBackground, squareSize; + private BackgroundCreator creator; + + + public Background() { + yBackground = 0; + squareSize = 20; + creator = new BackgroundCreator(); + } + + public void draw(Canvas canvas, Paint paint) { + int[][] array = creator.getBackground(); + final int rowWidth = array[0].length; + final int width = canvas.getWidth(); + final int height = canvas.getHeight(); + squareSize = width / rowWidth + 1; + creator.setHeight(height / squareSize + 4); + + // Fill rectangles. + paint.setStyle(Paint.Style.FILL); + for(int y = -1; y < array.length - 2; y++) { + int[] row = array[2 + y]; + + int top = y * squareSize + yBackground; + if (top > height) break; + + for (int x = 0; x < rowWidth; x++) { + int left = x * squareSize; + int right = left + squareSize; + int bottom = top + squareSize; + + paint.setColor(row[x]); + canvas.drawRect(left, top, right, bottom, paint); + } + + } + } + + public void update(int addValue) { + if (addValue > 200) { + // When game is finished, speed up background. + yBackground += 2; + addValue /= 2; + } + yBackground++; + + // Remove last and add new row in background. + if (yBackground >= squareSize) { + creator.updateBackground(addValue); + yBackground = 0; + } + } +} diff --git a/src/com/annimon/gipgame/BackgroundCreator.java b/src/com/annimon/gipgame/BackgroundCreator.java new file mode 100644 index 0000000..7181ae1 --- /dev/null +++ b/src/com/annimon/gipgame/BackgroundCreator.java @@ -0,0 +1,93 @@ +package com.annimon.gipgame; + +import java.util.ArrayList; + + +public class BackgroundCreator { + + private static final boolean ANTI_ALIAS = true; + + private static final int WIDTH = 5; + private int height = 12; + + private ArrayList background; + + public BackgroundCreator() { + background = new ArrayList(); + + createBackground(); + } + + public void setHeight(int height) { + this.height = height; + } + + public int[][] getBackground() { + int[][] array = background.toArray(new int[0][]); + if (!ANTI_ALIAS) return array; + + // Smooth background colors. + int[][] antialias = new int[height][WIDTH]; + for(int y = 0; y < height; y++) { + for (int x = 0; x < WIDTH; x++) { + int top = (y > 0) ? array[y - 1][x] : 0; + int bottom = (y < (height - 1)) ? array[y + 1][x] : 0; + int left = (x > 0) ? array[y][x - 1] : 0; + int right = (x < (WIDTH - 1)) ? array[y][x + 1] : 0; + int center = array[y][x]; + + antialias[y][x] = getAntiAliasColor(new int[] {top, bottom, left, right, center}); + } + } + return antialias; + } + + private int getAntiAliasColor(int[] colors) { + int red = 0; + int green = 0; + int blue = 0; + + for (int i = 0; i < 4; i++) { + int color = colors[i]; + red += (color >> 16) & 0xFF; + green += (color >> 8) & 0xFF; + blue += color & 0xFF; + } + + red /= 4; + green /= 4; + blue /= 4; + + red += (colors[4] >> 16) & 0xFF; + green += (colors[4] >> 8) & 0xFF; + blue += colors[4] & 0xFF; + + return 0xFF000000 | (red << 16) | (green << 8) | blue; + } + + public void updateBackground(int addValue) { + background.remove(height - 1); + background.add(0, createRow(addValue)); + } + + + private void createBackground() { + for (int i = 0; i < height; i++) { + background.add(createRow(0)); + } + } + + private int[] createRow(int addValue) { + int[] row = new int[WIDTH]; + for (int i = 0; i < WIDTH; i++) { + row[i] = Util.randomSpaceColor(15 + (addValue / 2)); + } + if (Util.rand(8) == 5) { + // Create star. + int x = Util.rand(WIDTH); + int add = (addValue / 2); + row[x] = Util.randomColor(10 + add, 60 + add); + } + return row; + } +} diff --git a/src/com/annimon/gipgame/Bonus.java b/src/com/annimon/gipgame/Bonus.java new file mode 100644 index 0000000..110c8fe --- /dev/null +++ b/src/com/annimon/gipgame/Bonus.java @@ -0,0 +1,66 @@ +package com.annimon.gipgame; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.DisplayMetrics; +import android.view.View; + +public class Bonus { + + private static int BONUS_SIZE = 16; + private static int HORIZ_RANGE = 320; + private static int HEIGHT = 480; + + public static void initBonusSize(View view, DisplayMetrics metrics) { + final int width = view.getWidth(); + HEIGHT = view.getHeight(); + + BONUS_SIZE = (int) Util.convertDpToPixel(width / 30, metrics); + HORIZ_RANGE = width - BONUS_SIZE; + } + + + private int color; + private float speed; + private RectF bonusRect; + + public Bonus() { + bonusRect = new RectF(0, 0, BONUS_SIZE, BONUS_SIZE); + initNewPosition(); + } + + public void draw(Canvas canvas, Paint paint) { + paint.setColor(color); + paint.setAlpha(200); + canvas.drawRect(bonusRect, paint); + paint.setAlpha(255); + } + + public void update(PlayerShip player) { + bonusRect.offset(0, speed); + if (bonusRect.top > HEIGHT) { + initNewPosition(); + player.changeScore(-1); + } else { + if (Util.rand(50) == 5) changeSpeed(); + } + if (player.isCollide(bonusRect)) { + initNewPosition(); + player.changeScore(1); + } + } + + private void initNewPosition() { + color = Util.randomColor(128, 255); + changeSpeed(); + + int x = Util.rand(HORIZ_RANGE); + int y = - BONUS_SIZE - Util.rand(HEIGHT); + bonusRect.offsetTo(x, y); + } + + private void changeSpeed() { + speed = Util.rand(0.2f, 4f); + } +} diff --git a/src/com/annimon/gipgame/Fps.java b/src/com/annimon/gipgame/Fps.java new file mode 100644 index 0000000..9b0d140 --- /dev/null +++ b/src/com/annimon/gipgame/Fps.java @@ -0,0 +1,33 @@ +package com.annimon.gipgame; + +public class Fps { + + private static final int MAX_FPS = 30; + private static final int MAX_DELAY = 1000 / MAX_FPS; + + private static long currentFps; + private static long counter = 0, startTime = 0; + private static long startTimeForMeasureDelay = 0; + + public static String getFpsAsString() { + counter++; + if (startTime == 0) { + startTime = System.currentTimeMillis(); + } + if ( (System.currentTimeMillis() - startTime) >= 1000) { + currentFps = counter; + counter = 0; + startTime = System.currentTimeMillis(); + } + return Long.toString(currentFps); + } + + public static void startMeasuringDelay() { + startTimeForMeasureDelay = System.currentTimeMillis(); + } + + public static long getDelay() { + long delay = System.currentTimeMillis() - startTimeForMeasureDelay; + return (delay > MAX_DELAY ? 0 : MAX_DELAY - delay); + } +} diff --git a/src/com/annimon/gipgame/GameView.java b/src/com/annimon/gipgame/GameView.java new file mode 100644 index 0000000..7626314 --- /dev/null +++ b/src/com/annimon/gipgame/GameView.java @@ -0,0 +1,142 @@ +package com.annimon.gipgame; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.View; + +public class GameView extends View implements SensorEventListener, Runnable { + + private static final int BONUS_COUNT = 10; + + private Background background; + private PlayerShip player; + private Bonus[] bonus; + + private Paint paint; + private SensorManager sensorManager; + + private Thread thread; + + public GameView(Context context) { + super(context); + initView(context); + } + + public GameView(Context context, AttributeSet attributes) { + super(context, attributes); + initView(context); + } + + public GameView(Context context, AttributeSet attributes, int defStyle) { + super(context, attributes, defStyle); + initView(context); + } + + private void initView(Context context) { + background = new Background(); + player = new PlayerShip(this); + bonus = new Bonus[BONUS_COUNT]; + for (int i = 0; i < BONUS_COUNT; i++) { + bonus[i] = new Bonus(); + } + + paint = new Paint(); + sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + + thread = new Thread(this); + thread.start(); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + if (player == null) player = new PlayerShip(this); + player.init(getResources().getDisplayMetrics()); + + Bonus.initBonusSize(this, getResources().getDisplayMetrics()); + for (int i = 0; i < BONUS_COUNT; i++) { + bonus[i] = new Bonus(); + } + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.drawColor(Color.BLACK); + + background.draw(canvas, paint); + player.draw(canvas, paint); + if (!player.gameIsFinished()) { + for (int i = 0; i < BONUS_COUNT; i++) { + bonus[i].draw(canvas, paint); + } + } + // Draw FPS. + //paint.setColor(Color.WHITE); + //canvas.drawText(Fps.getFpsAsString(), 10, 10, paint); + } + + private void update() { + background.update(player.getScore()); + + if (player.gameIsFinished()) return; + + for (int i = 0; i < BONUS_COUNT; i++) { + bonus[i].update(player); + } + } + + @Override + public void run() { + while(Thread.currentThread() == thread) { + Fps.startMeasuringDelay(); + update(); + postInvalidate(); + try { + Thread.sleep(Fps.getDelay()); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + // Quit application if user press the back key. + if (keyCode == KeyEvent.KEYCODE_BACK) { + background = null; + player = null; + bonus = null; + unregisterSensorListener(); + } + + return true; + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { } + + @Override + public void onSensorChanged(SensorEvent event) { + float delta = 0.8f; + float xChange = delta * event.values[0]; + // Control player ship only by x-axis. + player.updateX((int)xChange); + } + + public void registerSensorListener() { + Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME); + } + + public void unregisterSensorListener() { + sensorManager.unregisterListener(this); + } + +} diff --git a/src/com/annimon/gipgame/GipGameActivity.java b/src/com/annimon/gipgame/GipGameActivity.java new file mode 100644 index 0000000..343df85 --- /dev/null +++ b/src/com/annimon/gipgame/GipGameActivity.java @@ -0,0 +1,37 @@ +package com.annimon.gipgame; + +import android.os.Bundle; +import android.view.WindowManager; +import android.app.Activity; + +public class GipGameActivity extends Activity { + + private GameView gameView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Set backlight always on. + getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, + WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + gameView = new GameView(this); + gameView.setFocusable(true); + + setContentView(gameView); + } + + @Override + protected void onResume() { + super.onResume(); + gameView.registerSensorListener(); + } + + @Override + public void onSaveInstanceState(Bundle icicle) { + super.onSaveInstanceState(icicle); + gameView.unregisterSensorListener(); + } + +} diff --git a/src/com/annimon/gipgame/PlayerShip.java b/src/com/annimon/gipgame/PlayerShip.java new file mode 100644 index 0000000..fcf4e49 --- /dev/null +++ b/src/com/annimon/gipgame/PlayerShip.java @@ -0,0 +1,123 @@ +package com.annimon.gipgame; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.DisplayMetrics; +import android.view.View; + + +public class PlayerShip { + + private static final int WIDTH = 5, HEIGHT = 4; + private static int[] SHIP_COLORS = { + Color.WHITE, + Color.GREEN, + Color.BLUE, + Color.CYAN, + Color.YELLOW, + Color.DKGRAY, + Color.MAGENTA, + Color.RED, + Color.BLACK, + Color.TRANSPARENT + }; + + private int SHIP_WIDTH, SHIP_HEIGHT; + private int TEXT_SIZE; + + private int x, y; + private int score; + private Bitmap shipBitmap; + + private View view; + + public PlayerShip(View view) { + this.view = view; + score = 0; + } + + public void init(DisplayMetrics metrics) { + final int w = view.getWidth(); + final int h = view.getHeight(); + + SHIP_WIDTH = (int) Util.convertDpToPixel(w / 16, metrics); + SHIP_HEIGHT = (int) Util.convertDpToPixel(h / 20, metrics); + TEXT_SIZE = (int) Util.convertDpToPixel(12, metrics); + + createPlayerShip(SHIP_COLORS[0]); + + x = (w - SHIP_WIDTH) / 2; + y = h - SHIP_HEIGHT * 2; + } + + public int getScore() { + return score; + } + + public boolean gameIsFinished() { + return ( score > (SHIP_COLORS.length * 20) ); + } + + public void draw(Canvas canvas, Paint paint) { + // When game is finished we don't need to draw ship. + if (gameIsFinished()) { + drawText("Game is finished", canvas, paint, true); + return; + } + // Draw ship. + canvas.drawBitmap(shipBitmap, x, y, paint); + // Draw score. + drawText(String.valueOf(score), canvas, paint, false); + } + + public void updateX(int value) { + x -= value; + + if (x < 0) x = 0; + else if (x > ( view.getWidth() - SHIP_WIDTH )) { + x = view.getWidth() - SHIP_WIDTH; + } + } + + public boolean isCollide(RectF rectangle) { + return (rectangle.intersects(x, y, x + SHIP_WIDTH, y + SHIP_HEIGHT)); + } + + public void changeScore(int value) { + if (gameIsFinished()) return; + + score += value; + if (score < 0) score = 0; + else if (score % 20 == 0) { + // Change color of the ship by score level. + int shipLevel = Math.min(score / 20, SHIP_COLORS.length - 1); + createPlayerShip(SHIP_COLORS[shipLevel]); + } + } + + private void createPlayerShip(int color) { + Bitmap bmp = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888); + bmp.setPixel(2, 0, color); + bmp.setPixel(1, 1, color); bmp.setPixel(3, 1, color); + bmp.setPixel(1, 2, color); bmp.setPixel(3, 2, color); + bmp.setPixel(0, 3, color); bmp.setPixel(4, 3, color); + // Stretch bitmap to calculated size. + shipBitmap = Bitmap.createScaledBitmap(bmp, SHIP_WIDTH, SHIP_HEIGHT, false); + } + + private void drawText(String text, Canvas canvas, Paint paint, boolean vertCenter) { + paint.setTextSize(TEXT_SIZE); + paint.setTextScaleX(2); + paint.setColor(Color.YELLOW); + paint.setTextAlign(Paint.Align.CENTER); + Rect textBounds = new Rect(); + paint.getTextBounds(text, 0, text.length(), textBounds); + int y = textBounds.height() * 2; + if (vertCenter) y += view.getHeight() / 2; + canvas.drawText(text, view.getWidth() / 2, y, paint); + } +} diff --git a/src/com/annimon/gipgame/Util.java b/src/com/annimon/gipgame/Util.java new file mode 100644 index 0000000..7e05a23 --- /dev/null +++ b/src/com/annimon/gipgame/Util.java @@ -0,0 +1,48 @@ +package com.annimon.gipgame; + +import java.util.Random; +import android.util.DisplayMetrics; + +public class Util { + + private static Random rnd = new Random(); + + public static int rand(int to) { + return rnd.nextInt(to); + } + + public static int rand(int from, int to) { + return rnd.nextInt(to - from) + from; + } + + public static float rand(float from, float to) { + return rnd.nextFloat() * (to - from) + from; + } + + public static int randomColor(int from, int to) { + if (from > 230) from = 230; + if (to > 255) to = 255; + + int red = rand(from, to); + int green = rand(from, to); + int blue = rand(from, to); + + return 0xFF000000 | (red << 16) | (green << 8) | blue; + } + + public static int randomSpaceColor(int to) { + if (to > 240) to = 240; + + int red = rand(to / 2); + int green = rand(red / 2, to / 2); + int blue = rand(red, to); + + return 0xFF000000 | (red << 16) | (green << 8) | blue; + } + + public static float convertDpToPixel(float dp, DisplayMetrics metrics) { + float px = dp * (metrics.densityDpi / 160f); + return px; + } + +}