先看看运行的效果
最顶层的是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>