初学Android,想做一个小游戏来练练手,于是选择了贪吃蛇这个既能练手,做完了自己又爱玩的游戏。
代码如下:
MainActivity.java
package com.example.simplesnack;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.app.Activity;
import android.graphics.Color;
public class MainActivity extends Activity {
private GamePanel mv;
private Button left,up,right,down;
private Button start;
private Button gameContinue;
private Button gameStop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DisplayMetrics metric = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metric);
int width = metric.widthPixels; // 屏幕宽度(像素)
int height = metric.heightPixels; // 屏幕高度(像素)
mv = (GamePanel)findViewById(R.id.mv);
mv.setBackgroundColor(Color.LTGRAY);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)mv.getLayoutParams();
params.width = width;
params.height = width;
mv.setLayoutParams(params);
left=(Button)findViewById(R.id.left);
up=(Button)findViewById(R.id.up);
right=(Button)findViewById(R.id.right);
down=(Button)findViewById(R.id.down);
left.setWidth(width/4);
left.setHeight(width/5);
up.setWidth(width/4);
up.setHeight(width/5);
right.setWidth(width/4);
right.setHeight(width/5);
down.setWidth(width/4);
down.setHeight(width/5);
left.setX(width/2-width/8-width/5);
left.setY(width+(height-width)/3+width/10);
up.setX(width/2-width/8);
up.setY(width+(height-width)/3-width/20);
right.setX(width/2-width/8+width/5);
right.setY(width+(height-width)/3+width/10);
down.setX(width/2-width/8);
down.setY(width+(height-width)/3+width/5+width/20);
start=(Button)findViewById(R.id.start);
start.setOnTouchListener(mv);
gameContinue=(Button)findViewById(R.id.gameContinue);
gameContinue.setOnTouchListener(mv);
gameStop=(Button)findViewById(R.id.gameStop);
gameStop.setOnTouchListener(mv);
left.setOnTouchListener(mv);
up.setOnTouchListener(mv);
right.setOnTouchListener(mv);
down.setOnTouchListener(mv);
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onPause() {
super.onPause();
mv.start=false;
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onRestart() {
super.onRestart();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
activity_main.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.example.simplesnack.GamePanel
android:id="@+id/mv"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<Button
android:id="@+id/left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="←"
android:textStyle="bold"
/>
<Button
android:id="@+id/up"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="↑"
android:textStyle="bold"
/>
<Button
android:id="@+id/right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="→"
android:textStyle="bold"
/>
<Button
android:id="@+id/down"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="↓"
android:textStyle="bold"
/>
<Button
android:id="@+id/start"
android:text="放蛇"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/mv"
android:layout_marginLeft="0dp"
/>
<Button
android:id="@+id/gameStop"
android:text="暂停"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/mv"
android:layout_alignRight="@id/mv"
/>
<Button
android:id="@+id/gameContinue"
android:text="继续"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/mv"
android:layout_toRightOf="@id/start"
/>
</RelativeLayout>
MyThread.java
package com.example.simplesnack;
public class MyThread extends Thread{
private GamePanel mv;
public MyThread(GamePanel mv){
this.mv=mv;
}
@Override
public void run() {
while (mv.start) {
try {
long before=System.currentTimeMillis();
mv.postInvalidate();
long after=System.currentTimeMillis();
Thread.sleep((int)((10000/mv.speed)-(after-before)));
} catch (InterruptedException e) {
e.printStackTrace();
}
switch (mv.getDirection()) {
case 1:
mv.move(-1, 0);
mv.lastDirection=1;
break;
case 2:
mv.move(0, -1);
mv.lastDirection=2;
break;
case 3:
mv.move(1, 0);
mv.lastDirection=3;
break;
case 4:
mv.move(0, 1);
mv.lastDirection=4;
break;
default:
break;
}
if(mv.hitOrBite()){
mv.start=false;
mv.dead();
continue;
}
}
}
}
SnackBody.java
package com.example.simplesnack;
public class SnackBody {
private int x,y;
public void setX(int a){
x=a;
}
public void setY(int a){
y=a;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
}
DirectionThread.java
package com.example.simplesnack;
public class DirectionThread extends Thread{
private GamePanel mv;
private int direction;
public boolean canRun;
public DirectionThread(GamePanel mv,int direction){
this.mv=mv;
this.direction=direction;
}
@Override
public void run() {
while (canRun) {
mv.setDirection(direction);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
GamePanel.java
package com.example.simplesnack;
import java.util.ArrayList;
import java.util.Random;
import android.view.MotionEvent;
import android.view.View.OnTouchListener;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class GamePanel extends View implements OnTouchListener{
public int wid, hei;//获得此view的宽高
public ArrayList<SnackBody> body = new ArrayList<SnackBody>();//蛇身
public int score;//分数
public double speed;//速度
public boolean start;//开关标志
private static final int MAX_Height = 20;
private static final int MAX_Width = 20;//将活动区域分为20格
private int rectSize;//每一格宽度
private int snackLength;//蛇长(暂时无用)
private int foodX, foodY;//食物坐标
private Random random;
private Paint paint;
private int direction;//方向
private int eatSign;//用于刷出下一个食物时判断是否和蛇身重叠
private MyThread mThread;
private DirectionThread lt = new DirectionThread(this, 1);
private DirectionThread ut = new DirectionThread(this, 2);
private DirectionThread rt = new DirectionThread(this, 3);
private DirectionThread dt = new DirectionThread(this, 4);
private int itemX, itemY; // 预判蛇头位置
public int lastDirection; // 用于限制快速按键时出现反向吃自己的BUG
public GamePanel(Context context) {
super(context);
}
public GamePanel(Context context, AttributeSet attr) {
super(context, attr);
}
public void init() {
rectSize = wid / MAX_Width;
score = 0;
speed = 100;
itemX = -1;
itemY = -1;
body.clear();
snackLength = 0;
random = new Random();
paint = new Paint();
paint.setAntiAlias(true);
direction = 3;
lastDirection = 3;
this.setWillNotDraw(false);
}
@Override
protected void onDraw(Canvas canvas) {
if (start) {
paint.setColor(Color.RED);
canvas.drawRect(foodX * rectSize, foodY * rectSize, foodX
* rectSize + rectSize, foodY * rectSize + rectSize, paint);
if (body.size() > 0) {
paint.setColor(Color.YELLOW);
canvas.drawRect(body.get(0).getX() * rectSize, body.get(0)
.getY() * rectSize, body.get(0).getX() * rectSize
+ rectSize, body.get(0).getY() * rectSize + rectSize,
paint);
paint.setColor(Color.rgb(255, 215, 0));
for (int i = 1; i < body.size(); i++) {
canvas.drawRect(body.get(i).getX() * rectSize, body.get(i)
.getY() * rectSize, body.get(i).getX() * rectSize
+ rectSize, body.get(i).getY() * rectSize
+ rectSize, paint);
}
paint.setColor(Color.BLACK);
paint.setTextSize(20);
canvas.drawText("得分:" + score, wid - 120, wid - 20, paint);
}
}
}
public void startGame() {
init();
start = true;
SnackBody newBody = new SnackBody();
newBody.setX(MAX_Width / 2);
newBody.setY(MAX_Height / 2);
SnackBody newBody2 = new SnackBody();
newBody2.setX(MAX_Width / 2 - 1);
newBody2.setY(MAX_Width / 2);
body.add(newBody);
body.add(newBody2);
do {
foodX = random.nextInt(MAX_Width - 1);
foodY = random.nextInt(MAX_Height - 1);
} while (foodX == newBody.getX() && foodY == newBody.getY());
mThread = new MyThread(this);
mThread.start();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
wid = w;
hei = h;
}
public void move(int x, int y) {
itemX = body.get(0).getX() + x;
itemY = body.get(0).getY() + y;
if (itemX == foodX && itemY == foodY) {
eat();
for (int i = body.size() - 2; i > 0; i--) {
body.get(i).setX(body.get(i - 1).getX());
body.get(i).setY(body.get(i - 1).getY());
}
} else {
for (int i = body.size() - 1; i > 0; i--) {
body.get(i).setX(body.get(i - 1).getX());
body.get(i).setY(body.get(i - 1).getY());
}
}
body.get(0).setX(body.get(0).getX() + x);
body.get(0).setY(body.get(0).getY() + y);
}
public void eat() {
score = score + 10;
snackLength++;
do {
eatSign = 0;
foodX = random.nextInt(MAX_Height - 1);
foodY = random.nextInt(MAX_Width - 1);
for (int i = 0; i < body.size(); i++) {
if (foodX == body.get(i).getX() && foodY == body.get(i).getY()) {
eatSign++;
}
}
if (foodX == itemX && foodY == itemY)
eatSign++;
} while (eatSign > 0);
SnackBody growBody = new SnackBody();
growBody.setX(body.get(body.size() - 1).getX());
growBody.setY(body.get(body.size() - 1).getY());
body.add(growBody);
}
public boolean hitOrBite() {
if (body.get(0).getX() < 0 || body.get(0).getX() >= 20
|| body.get(0).getY() < 0 || body.get(0).getY() >= 20)
return true;
else {
for (int i = body.size() - 1; i > 2; i--) {
if (body.get(0).getX() == body.get(i).getX()
&& body.get(0).getY() == body.get(i).getY())
return true;
}
}
return false;
}
public void dead() {
}
public int getDirection() {
return direction;
}
public void setDirection(int direction) {
this.direction = direction;
}
public int getScore() {
return score;
}
@Override
public boolean onTouch(View v, MotionEvent e) {
if (v.getId() == R.id.left && e.getAction() == MotionEvent.ACTION_DOWN) {
if (lastDirection != 3) {
lt = new DirectionThread(this, 1);
ut.canRun = false;
rt.canRun = false;
dt.canRun = false;
lt.canRun = true;
lt.start();
}
} else if (v.getId() == R.id.left
&& e.getAction() == MotionEvent.ACTION_UP) {
lt.canRun = false;
}
if (v.getId() == R.id.up && e.getAction() == MotionEvent.ACTION_DOWN) {
if (lastDirection != 4) {
ut = new DirectionThread(this, 2);
lt.canRun = false;
ut.canRun = true;
rt.canRun = false;
dt.canRun = false;
ut.start();
}
} else if (v.getId() == R.id.up
&& e.getAction() == MotionEvent.ACTION_UP) {
ut.canRun = false;
}
if (v.getId() == R.id.right && e.getAction() == MotionEvent.ACTION_DOWN) {
if (lastDirection != 1) {
rt = new DirectionThread(this, 3);
lt.canRun = false;
ut.canRun = false;
rt.canRun = true;
dt.canRun = false;
rt.start();
}
} else if (v.getId() == R.id.right
&& e.getAction() == MotionEvent.ACTION_UP) {
rt.canRun = false;
}
if (v.getId() == R.id.down && e.getAction() == MotionEvent.ACTION_DOWN) {
if (lastDirection != 2) {
dt = new DirectionThread(this, 4);
lt.canRun = false;
ut.canRun = false;
rt.canRun = false;
dt.canRun = true;
dt.start();
}
} else if (v.getId() == R.id.down
&& e.getAction() == MotionEvent.ACTION_UP) {
dt.canRun = false;
}
if (v.getId() == R.id.start && e.getAction() == MotionEvent.ACTION_UP&&start==false) {
this.startGame();
}
if (v.getId() == R.id.gameContinue
&& e.getAction() == MotionEvent.ACTION_UP&&start==false) {
start = true;
mThread = new MyThread(this);
mThread.start();
}
if (v.getId() == R.id.gameStop
&& e.getAction() == MotionEvent.ACTION_UP) {
start = false;
}
return false;
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.simplesnack"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="18" />
<application
android:allowBackup="true"
android:icon="@drawable/snake"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.simplesnack.MainActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.Light.NoTitleBar.Fullscreen"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
代码就这些差不多了,主要是用来给自己看,总结一些经验的,所以就不多描述了。
总结:
1.在处理多方向按键的时候,可以创建4个子线程分别对应4个方向,按下其中一个方向键的时候,开启对应子线程,并用while(标志)的循环方式不断使线程将方向设置为改按键对应方向,以达到优化手感的作用。同时,声明一个变量判断最后一次移动时的方向,用于锁定,避免回头吃自己。
2.采用预判的方式,判断蛇下一步的位置所会触发的事件,将事件处理完再绘制界面。
3.在activity的oncreate方法中使用下列语句
DisplayMetrics metric = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metric);
int width = metric.widthPixels;
int height = metric.heightPixels;
获得屏幕的宽高(像素 px),同时可以使用获得的宽高来按比例设置各组件的位置和大小。有以下两点需要注意:
(1)好像自定义的view在activity的oncreate方法里不能直接设置width和height,需要用到以下代码:
RelativeLayout.LayoutParams params =(RelativeLayout.LayoutParams)mv.getLayoutParams();
params.width = width;
params.height = width;
mv.setLayoutParams(params);
(mv是自定义view的引用)
(2)在activity的oncreate方法里用setX,setY方法对各个Button设置位置,其实际位置是xml布局里的原位置坐标加上此X和Y。