android游戏---贪吃蛇
先看看运行的效果
最顶层的是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>