先看看運行的效果
最頂層的是wall類,生成和繪制外牆 代碼如下:
package com.example.worm; import android.content.Context; import android.util.DisplayMetrics; import android.view.View; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Bitmap; public class wall extends View{ private Context mContext; private int width; private int height; private int widthNum; // 方格20 * 20 保存方格行列數; private int heightNum; private Bitmap bm; public wall(Context context) { super(context); mContext = context; DisplayMetrics dm = new DisplayMetrics(); dm = context.getResources().getDisplayMetrics(); width = dm.widthPixels; height = dm.heightPixels; widthNum = width / 20; heightNum = height / 20; bm = BitmapFactory.decodeResource(context.getResources(), R.drawable.wall); } private void drawTableForm (Canvas canvas) //繪制表格白線 { Paint paint = new Paint (); paint.setStrokeWidth(1); paint.setStyle(Paint.Style.STROKE); paint.setColor(Color.WHITE); for (int i = 0; i <= widthNum; i++) { canvas.drawLine(20 * i, 0, 20 * i, height, paint); } for (int i = 0; i <= heightNum; i++) { canvas.drawLine(0, 20 * i, width, 20 * i, paint); } } private void drawWall(Canvas canvas) // 在最外層繪制棕色牆體; { for (int i = 0; i < widthNum; i++) { canvas.drawBitmap(bm, 20 * i, 0, null); canvas.drawBitmap(bm, 20 * i, height - 20, null); } for (int i = 1; i < heightNum - 1; i++) { canvas.drawBitmap(bm, 0, 20 * i, null); canvas.drawBitmap(bm, width - 20, 20 * i, null); } } @Override protected void onDraw (Canvas canvas) //覆蓋onDraw { super.onDraw(canvas); drawTableForm (canvas); drawWall(canvas); } }
往上是worm類,此類包含了control層,連帶左右上下控制
package com.example.worm; import android.view.View; import android.content.Context; import android.util.DisplayMetrics; import android.graphics.Canvas; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.Matrix; import android.view.MotionEvent; import android.graphics.Paint; public class Worm extends View{ private Context mContext; private int widthNum; //行列數,方便起見,此處以減去牆體,所以 widthNum = dm.widthPixels / 20 - 2
private int heightNum; private int[] wormX = new int[35]; //保存蛇的 X Y坐標, wormX[0], wormY[0] 為蛇頭 private int[] wormY = new int[35]; private float[] wormRotate = new float[35]; //因為蛇頭,蛇身在旋轉時需要圖片相應轉動 此處保存蛇個部位圖片的旋轉角度 private int length; //蛇長 private Bitmap bmHead; private Bitmap bmBody; private Bitmap bmFood; public int direction; //運動方向 private float startX; //此為判斷控制的參數 private float startY; private float stopX; private float stopY; private long startTime; private long stopTime; private int food[] = new int[2]; //food[0] 為food的X坐標, food[1] 為food的Y坐標; private int score = 0; public long speed = 500; private int level = 1; private final static int LEFT = 0; private final static int RIGHT = 1; private final static int UP = 2; private final static int DOWN = 3; private OnGameOverListener listener; public Worm (Context context) { super(context); mContext = context; DisplayMetrics dm = new DisplayMetrics(); dm = mContext.getResources().getDisplayMetrics(); widthNum = dm.widthPixels / 20 - 2; heightNum = dm.heightPixels / 20 - 2; bmHead = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.head); bmBody = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.body); bmFood = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.food); } public void reSet() //重置貪吃蛇,點擊重新開始游戲時需要調用 { length = 3; wormX[0] = 2; wormX[1] = 1; wormX[2] = 0; wormY[0] = 10; wormY[1] = 10; wormY[2] = 10; wormRotate[0] = 0.0f; wormRotate[1] = 0.0f; wormRotate[2] = 0.0f; direction = RIGHT; updateFood(); } @Override protected void onDraw(Canvas canvas) { drawFood(canvas); drawWorm(canvas); drawText(canvas); } private void drawWorm(Canvas canvas) //繪制蛇 { Matrix m = new Matrix(); Matrix rotateM = new Matrix(); Bitmap bm; m.setTranslate(20 * (1 + wormX[0]), 20 * (1 + wormY[0])); rotateM.setRotate(wormRotate[0]); bm = Bitmap.createBitmap(bmHead, 0, 0, //先得到旋轉后的圖片 bmHead.getWidth(), bmHead.getHeight(), rotateM, true); canvas.drawBitmap(bm, m, null); //繪制蛇頭 for (int i = 1; i < length; i++) //繪制蛇身 { m.setTranslate( 20 * (1 + wormX[i]), 20 * (1 + wormY[i])); rotateM.setRotate(wormRotate[i]); bm = Bitmap.createBitmap(bmBody, 0, 0, //同樣先得到旋轉后的圖片 bmBody.getWidth(), bmBody.getHeight(), rotateM, true); canvas.drawBitmap(bm, m, null); } } private void drawFood(Canvas canvas) //繪制food { canvas.drawBitmap(bmFood, 20 * (1 + food[0]), 20 * (1 + food[1]), null); } private void drawText (Canvas canvas) // 繪制 score和 level { Paint paint = new Paint(); paint.setTextSize(40); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(5); paint.setAlpha(50); paint.setColor(Color.GREEN); canvas.drawText("Score: ", 100, 40, paint); canvas.drawText("Level: ", 300, 40, paint); paint.setTextSize(50); paint.setColor(Color.RED); canvas.drawText(String.valueOf(score), 210, 40, paint); canvas.drawText(String.valueOf(level), 400, 40, paint); } private void upDateBody() //更新蛇身 位置 { for (int i = length - 1; i > 0; i--) { wormX[i] = wormX[i - 1]; wormY[i] = wormY[i - 1]; wormRotate[i] = wormRotate[i - 1]; } } private boolean isAble(int dir) //判斷蛇頭的下一步是否可行 { switch(dir) { case LEFT: if (wormX[0] - 1 < 0) return false; break; case RIGHT: if (wormX[0] + 1 == widthNum) return false; break; case UP: if (wormY[0] - 1 < 0) return false; break; case DOWN: if (wormY[0] + 1 == heightNum) return false; break; } return true; } private void eatFood() //判斷是否遲到food { if (wormX[0] == food[0] && wormY[0] == food[1]) { score ++; length ++; if (score > 30) { score = 0; level ++; speed = (long) (speed * 0.8f); length = 3; wormX[0] = 2; wormX[1] = 1; wormX[2] = 0; wormY[0] = 10; wormY[1] = 10; wormY[2] = 10; } updateFood(); } } public void turnLeft() //左轉 { if (isBody(wormX[0] - 1, wormY[0]) || !isAble(LEFT)) //下一步不可行, 這調用onGameOver() { listener.onGameOver(); return ; } upDateBody(); //可行時,先更新Body位置 在更新蛇頭 wormX[0]--; wormRotate[0] = 180.0f; direction = LEFT; eatFood(); this.invalidate(); } public void turnRight() { if (isBody(wormX[0] + 1, wormY[0]) || !isAble(RIGHT)) { listener.onGameOver(); return ; } upDateBody(); wormX[0]++; wormRotate[0] = 0.0f; direction = RIGHT; eatFood(); this.invalidate(); } public void turnUp() { if (isBody(wormX[0], wormY[0] - 1) || !isAble(UP)) { listener.onGameOver(); return ; } upDateBody(); wormY[0]--; wormRotate[0] = -90.0f; direction = UP; eatFood(); this.invalidate(); } public void turnDown() { if (direction == UP || !isAble(DOWN)) { listener.onGameOver(); return ; } upDateBody(); wormY[0]++; wormRotate[0] = 90.0f; direction = DOWN; eatFood(); this.invalidate(); } public void setOnGameOverListener(OnGameOverListener listener) //自定義的一個listener { this.listener = listener; } private boolean isBody(int x, int y) //判斷該位置是否是蛇身 { for (int i = 1; i < length - 1; i++) { if (x == wormX[i] && y == wormY[i]) return true; } return false; } private boolean isLegitFood(int x, int y) //判斷food的位置是否合法 { for (int i = 0; i < length; i++) { if (x == wormX[i] && y == wormY[i]) return false; } return true; } private void updateFood() //隨機生成一個food位置 { food[0] = (int) (Math.random() * (widthNum - 1)); food[1] = (int) (Math.random() * (heightNum - 1)); while (!isLegitFood(food[0], food[1])) { food[0] = (int) (Math.random() * (widthNum - 1)); food[1] = (int) (Math.random() * (heightNum - 1)); } } @Override public boolean onTouchEvent(MotionEvent event) // 控制操作 { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startX = event.getX(); startY = event.getY(); startTime = event.getEventTime(); break; case MotionEvent.ACTION_UP: stopX = event.getX(); stopY = event.getY(); stopTime = event.getDownTime(); if (stopTime - startTime > 1000) //當按壓時間超過1000ms是無效 break; if ((stopX - startX) > 100) //左右上下滑動超過100有效 { if (direction != LEFT) direction = RIGHT; } else if ((stopX - startX) < -100) { if (direction != RIGHT) direction = LEFT; } else if ((stopY - startY) > 100) { if (direction != UP) direction = DOWN; } else if ((stopY - startY) < -100) { if (direction != DOWN) direction = UP; } else if (stopTime - startTime < 150) //滑動不夠100並且時間小於150ms調用onGamePause()暫停游戲 { listener.onGamePause(); } break; } return true; } }
其中使用了自定義的一個listener 用於Main類可以相應GameOver和GamePause事件 如下:
package com.example.worm; public interface OnGameOverListener { public void onGameOver(); public void onGamePause(); }
下面是main類
package com.example.worm; import java.util.Timer; import java.util.TimerTask; import android.os.Bundle; import android.os.Message; import android.app.Activity; import android.view.Menu; import android.view.ViewGroup.LayoutParams; import android.os.Handler; import android.content.Intent; public class MainActivity extends Activity { private wall mWall; private Worm mWorm; private Timer timer = new Timer(); private TimerTask timerTask; private int message = 1; //發送messge控制游戲的進行和暫停 1為進行 2為暫停 private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch(msg.what) { case 1: //當收到1時 進行游戲 switch(mWorm.direction) //direction是個int, 0, 1, 2, 3, 分別代表4個方向 { case 0: mWorm.turnLeft(); break; case 1: mWorm.turnRight(); break; case 2: mWorm.turnUp(); break; case 3: mWorm.turnDown(); break; } break; case 2: //收到2 時 直接退出 break; } sendMsg(); //不斷的發送消息,保證游戲連續進行 super.handleMessage(msg); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mWall = new wall(this); mWorm = new Worm(this); setContentView(mWall); addContentView(mWorm, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); mWorm.setOnGameOverListener(new OnGameOverListener() // 定義一個OnGameListener 當發生GameOver 和 GamePause是main類知道怎么做 { public void onGameOver() { gameStop(); } public void onGamePause() { gamePause(); } }); sendMsg(); //發送第一條消息 } @Override public void onStart() { mWorm.reSet(); //之所以要將reSet()放在onStart()中方便暫停重開 super.onStart(); } @Override public void onResume() //繼續游戲時 將message改回 1; { message = 1; super.onResume(); } @Override public void onActivityResult (int requestCode, int resultCode, Intent data) //暫停和結束都是調用了一個Acitivity 這個函數可以回收信息 { switch(requestCode) { case 1: //1 為調用了pauseActivity switch (resultCode) { case 0: //0 為繼續游戲 this.onResume(); break; case 1: //1 為重新開始 this.onStart(); break; case 2: //2 為結束游戲 finish(); break; } break; case 2: //2 為調用stopActivity switch (resultCode) { case 0: // 重新開始 this.onStart(); break; case 1: // 結束游戲 finish(); break; } break; } super.onActivityResult(requestCode, resultCode, data); } @Override public void onPause() { message = 2; //暫停時修改messge為 2; super.onPause(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } private void gamePause() //gamePause()調用reStartActivity { message = 2; Intent intent = new Intent(this, reStartActivity.class); startActivityForResult (intent, 1); } private void gameStop() //gameStop()調用onStopActivity { message = 2; Intent intent = new Intent(this, onStopActivity.class); startActivityForResult (intent, 2); } private void sendMsg() //發送消息 { timerTask = new TimerTask() { @Override public void run() { Message msg = new Message(); msg.what = message; handler.sendMessage(msg); } }; timer.schedule(timerTask, mWorm.speed); //消息發送延遲 mWorm.speed; } }
就下來是兩個輔助Activity和他們的layout
package com.example.worm; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class reStartActivity extends Activity{ private Button resume; private Button reStart; private Button stop; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_restart); resume = (Button) findViewById (R.id.resume); reStart = (Button) findViewById (R.id.reStart); stop = (Button) findViewById (R.id.stop); resume.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub setResult(0); finish(); } }); reStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub setResult(1); finish(); } }); stop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub setResult(2); finish(); } }); } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/resume" android:text="繼續游戲" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/reStart" android:text="重新開始" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/stop" android:text="結束游戲" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
package com.example.worm; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class onStopActivity extends Activity{ private Button reStart; private Button stop; @Override public void onCreate (Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_onstop); reStart = (Button) findViewById (R.id.reStart); stop = (Button) findViewById (R.id.stop); reStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { // TODO Auto-generated method stub setResult(0); finish(); } }); stop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub setResult(1); finish(); } }); } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/reStart" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="重新開始"/> <Button android:id="@+id/stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="結束游戲"/> </LinearLayout>
最后要注意的是配置文件的設置,以上兩個Activity的theme要定義為theme.Dialog;
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.worm" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" android:label="@string/title_activity_main" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".reStartActivity" android:theme="@android:style/Theme.Dialog"> </activity> <activity android:name=".onStopActivity" android:theme="@android:style/Theme.Dialog"> </activity> </application> </manifest>