分享

J2ME vs. Android: Game Trap - Hari's Blog

 ShangShujie 2010-05-08

J2ME vs. Android: Game Trap

| | Comments (0)
[Note] Updated: Adopted code to Android SDK 1.0 R2

To become familiar with mobile programming plattforms I've implemented a little game (a minesweeper clone) in J2ME. After having finished this I ported this game to Android.



While porting to Android I explored the following features:

  • Rendering images via View.onDraw()
  • Working with multiple Activities (Main Activity, Options Activity)
  • Passing data from one Activity to another
  • Handling Activity states (freeze, restore)
  • Handling custom menues (onCreateOptionsMenu)
  • etc.
It turned out that - as expected ;-) - separating program logic is a good thing. I used the same classes for the game logic.

Disclaimer: Please note, that my intention was to explore some features of the plattforms. There is still room for improvement as I've not implemented a timer, a highscore table etc..
Attached you can find the Eclipse (3.4) projects of both implementations: The following sources are extracts from the Android implementation:

TrapMain.java

package at.lacherstorfer.trap.android;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
public class TrapMain extends Activity {
private TrapView trapView;
private static int EDIT_OPTIONS = 1;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.trap_main_layout);
trapView = (TrapView) findViewById(R.id.trap);
if (icicle != null) {
// We are being restored
Bundle map = icicle.getBundle("trapView");
if (map != null) {
trapView.restoreState(map);
}
}
trapView.doStart();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// store game state
outState.putBundle("trapView", trapView.saveState());
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuItem menuItem = menu.add(0, 0, 0, "Start");
menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
trapView.doStart();
return true;
}
});
menuItem = menu.add(0, 0, 0, "Options");
menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
Intent optionsIntent = new Intent(TrapMain.this, TrapOptions.class);
Bundle extras = new Bundle();
extras.putInt("numberRows", trapView.getNumberRows());
extras.putInt("numberCols", trapView.getNumberCols());
extras.putInt("numberTraps", trapView.getNumberTraps());
optionsIntent.putExtras(extras);
startActivityForResult(optionsIntent, EDIT_OPTIONS);
return true;
}
});
return true;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
trapView.doStart(data.getIntExtra("numberRows", 8),
data.getIntExtra("numberCols", 8), data.getIntExtra("numberTraps", 8));
}
}
}


TrapView.java

package at.lacherstorfer.trap.android;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import at.lacherstorfer.trap.shared.Cell;
import at.lacherstorfer.trap.shared.Field;
public class TrapView extends View {
private Field field;
private int numberRows = 8;
private int numberCols = 8;
private int numberTraps = 8;
private int cursorX;
private int cursorY;
private Drawable cellClosedImage;
private Drawable cell0Image;
private Drawable cell1Image;
private Drawable cell2Image;
private Drawable cell3Image;
private Drawable cell4Image;
private Drawable cellBombImage;
private Drawable cellExplodedImage;
private Drawable cellFlaggedImage;
private Drawable cursorImage;
public TrapView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
loadImages(context);
}
public TrapView(Context context, AttributeSet attrs) {
super(context, attrs);
loadImages(context);
}
public TrapView(Context context) {
super(context);
loadImages(context);
}
public void loadImages(Context context) {
cellClosedImage = context.getResources().getDrawable(
R.drawable.cellclosed);
cell0Image = context.getResources().getDrawable(R.drawable.cell0);
cell1Image = context.getResources().getDrawable(R.drawable.cell1);
cell2Image = context.getResources().getDrawable(R.drawable.cell2);
cell3Image = context.getResources().getDrawable(R.drawable.cell3);
cell4Image = context.getResources().getDrawable(R.drawable.cell4);
cellBombImage = context.getResources().getDrawable(R.drawable.cellbomb);
cellExplodedImage = context.getResources().getDrawable(
R.drawable.cellexploded);
cellFlaggedImage = context.getResources().getDrawable(
R.drawable.cellflagged);
cursorImage = context.getResources().getDrawable(R.drawable.cursor);
setFocusable(true);
}
public Bundle saveState() {
Bundle map = new Bundle();
map.putInt("numberRows", Integer.valueOf(numberRows));
map.putInt("numberCols", Integer.valueOf(numberCols));
map.putInt("numberTraps", Integer.valueOf(numberTraps));
map.putInt("cursorX", Integer.valueOf(cursorX));
map.putInt("cursorY", Integer.valueOf(cursorY));
int[] fieldState = field.getState();
for (int i = 0; i < fieldState.length; i++) {
map.putInt("field-" + i, fieldState[i]);
}
return map;
}
public void restoreState(Bundle icicle) {
numberRows = icicle.getInt("numberRows");
numberCols = icicle.getInt("numberCols");
numberTraps = icicle.getInt("numberTraps");
cursorX = icicle.getInt("cursorX");
cursorY = icicle.getInt("cursorY");
int[] fieldState = new int[icicle.size() - 5];
for (int i = 0; i < fieldState.length; i++) {
fieldState[i] = icicle.getInt("field-" + i);
}
field = new Field(fieldState);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
Log.i("TrapView", "pressed key=" + keyCode);
boolean handled = false;
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
cursorX -= cursorX > 0 ? 1 : 0;
handled = true;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
cursorX += cursorX < numberCols - 1 ? 1 : 0;
handled = true;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
cursorY -= cursorY > 0 ? 1 : 0;
handled = true;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
cursorY += cursorY < numberRows - 1 ? 1 : 0;
handled = true;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
|| keyCode == KeyEvent.KEYCODE_ENTER) {
fire();
handled = true;
} else if (keyCode == KeyEvent.KEYCODE_SPACE) {
flag();
handled = true;
}
if (handled) {
postInvalidate();
}
return handled;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean handled = false;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
cursorX = (int) (event.getX() / 15);
cursorX = cursorX > numberCols ? numberCols - 1 : cursorX;
cursorY = (int) (event.getY() / 15);
cursorY = cursorY > numberRows ? numberRows - 1 : cursorY;
handled = true;
}
postInvalidate();
return handled;
}
private void fire() {
if (field == null)
return; // game not started yet
int cellValue = field.fireAt(cursorY, cursorX);
if (cellValue == Cell.TRAP) {
field.reveal();
Builder b = new AlertDialog.Builder(this.getContext());
b.setTitle("Trap");
b.setIcon(0);
b.setMessage("Game Over");
b.show();
} else if (field.isSolved()) {
Builder b = new AlertDialog.Builder(this.getContext());
b.setTitle("Trap");
b.setIcon(0);
b.setMessage("Congratulation, You Win!");
b.show();
}
}
private void flag() {
if (field == null)
return; // game not started yet
field.flagAt(cursorY, cursorX);
if (field.isSolved()) {
Builder b = new AlertDialog.Builder(this.getContext());
b.setTitle("Trap");
b.setIcon(0);
b.setMessage("Congratulation, You Win!");
b.show();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (field == null)
return; // game not started yet
// draw the field
for (int row = 0; row < numberRows; row++) {
for (int col = 0; col < numberCols; col++) {
Cell cell = field.getCellAt(row, col);
Drawable cellImage = getDrawable(cell);
cellImage.setBounds(/* left */col * 15, /* top */row * 15, /* right */
col * 15 + 15, /* bottom */row * 15 + 15);
cellImage.draw(canvas);
}
}
// draw the cursor
cursorImage.setBounds(cursorX * 15, cursorY * 15, cursorX * 15 + 15,
cursorY * 15 + 15);
cursorImage.draw(canvas);
}
public void doStart(int numberRows, int numberCols, int numberTraps) {
this.numberRows = numberRows;
this.numberCols = numberCols;
this.numberTraps = numberTraps;
field = new Field(numberRows, numberCols, numberTraps);
postInvalidate();
}
public void doStart() {
if (field == null) {
field = new Field(numberRows, numberCols, numberTraps);
} else {
field.restart();
}
postInvalidate();
}
private Drawable getDrawable(Cell cell) {
if (cell.isOpen()) {
switch (cell.getValue()) {
case Cell.TRAP:
return cellBombImage;
case 0:
return cell0Image;
case 1:
return cell1Image;
case 2:
return cell2Image;
case 3:
return cell3Image;
case 4:
return cell4Image;
}
} else if (cell.isClosed()) {
return cellClosedImage;
} else if (cell.isFlagged()) {
return cellFlaggedImage;
} else if (cell.isExploded()) {
return cellExplodedImage;
} else {
throw new RuntimeException("Internal Error");
}
return null;
}
public int getNumberRows() {
return numberRows;
}
public int getNumberCols() {
return numberCols;
}
public int getNumberTraps() {
return numberTraps;
}
}


TrapOptions.java

package at.lacherstorfer.trap.android;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
public class TrapOptions extends Activity {
private EditText etNumberCols;
private EditText etNumberRows;
private EditText etNumberTraps;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.trap_options_layout);
etNumberRows = (EditText) findViewById(R.id.number_rows);
etNumberCols = (EditText) findViewById(R.id.number_cols);
etNumberTraps = (EditText) findViewById(R.id.number_traps);
Bundle extras = getIntent().getExtras();
etNumberRows.setText(""+extras.getInt("numberRows"));
etNumberCols.setText(""+extras.getInt("numberCols"));
etNumberTraps.setText(""+extras.getInt("numberTraps"));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuItem menuItem = menu.add(0, 0, 0, "Ok");
menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
int numberRows = Integer.parseInt(etNumberRows.getText().toString());
int numberCols = Integer.parseInt(etNumberCols.getText().toString());
int numberTraps = Integer.parseInt(etNumberTraps.getText().toString());
Bundle b = new Bundle();
b.putInt("numberRows", numberRows);
b.putInt("numberCols", numberCols);
b.putInt("numberTraps", numberTraps);
Intent resultIntent = new Intent();
resultIntent.putExtras(b);
setResult(RESULT_OK, resultIntent);
finish();
return true;
}
});
menuItem = menu.add(0, 0, 0, "Cancel");
menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
setResult(RESULT_CANCELED);
finish();
return true;
}
});
return true;
}
}


trap_main_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas./apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<at.lacherstorfer.trap.android.TrapView
android:id="@+id/trap"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</FrameLayout>


trap_options_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas./apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TableRow>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Number Columns"/>
<EditText android:id="@+id/number_cols"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:maxLength="2" />
</TableRow>
<TableRow>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Number Rows"/>
<EditText android:id="@+id/number_rows"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLength="2" />
</TableRow>
<TableRow>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Number Traps"/>
<EditText android:id="@+id/number_traps"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:maxLength="2"/>
</TableRow>
</TableLayout>


AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas./apk/res/android"
package="at.lacherstorfer.trap.android">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name="TrapMain" android:label="@string/app_name">
<intent-filter>
<action android:value="android.intent.action.MAIN"
android:name="android.intent.action.MAIN"/>
<category android:value="android.intent.category.LAUNCHER"
android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name="TrapOptions"
android:label="@string/app_name"/>
</application>
</manifest>

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多