闲来无事就想写个游戏来耍耍。想来想去,就计划写个贪吃蛇。开始的时候不知道怎么下手,自己也不想上网用别人的代码,后来不断地翻书。终于让我找到了一个好方法,那就是自己绘画贪吃蛇和食物。
下面就是我具体代码的分析。
主要代码有分布在三个类中:
1、Point类:记录蛇身位置(左上角和右下角坐标)。
2、Snake类:负责画蛇身和食物。
3、MySnakeView类:继承于View,并且处理逻辑。
主要思维:
1、游戏蛇身和食物位置的产生和判断:首先初始化的的蛇身方块放到另一个一个list表snake中,然后将屏幕上蛇身能够经过的方块位置放在一个表中(去除蛇身自方块,也就是snake表中的蛇身方块),以便于在不是蛇身位置产生食物。
2、蛇身的移动:在新线程中设置移动速度,不断地在画布上画相关内容,移动逻辑是上一个蛇身方块不断替换下一个蛇身方块,从而达到移动效果。
3、碰撞检测:检测是否触碰到屏幕边缘,和吃到自己。
好了,下面就是代码了。
一、Point类:
package com.example.mysnakegame;
public class Point {//方块左上角坐标,右下角坐标
private float snakeX,snakeY,snakeBottomX,snakeBottomY;
public Point(float snakeX,float snakeY,float snakeBottomX,float snakeBottomY,
boolean isSnake,boolean isFood){
this.snakeX = snakeX;//左上角X
this.snakeY = snakeY;//左上角Y
this.snakeBottomX = snakeBottomX;//右下角X
this.snakeBottomY = snakeBottomY;//右下角Y
}
public float getSnakeX() {
return snakeX;
}
public float getSnakeY() {
return snakeY;
}
public float getSnakeBottomX() {
return snakeBottomX;
}
public float getSnakeBottomY() {
return snakeBottomY;
}
public boolean isEqualSnake(Snake snake){//判断两个方块是否相同
boolean flag = false;
if (Math.abs(snake.getSnakeX()-this.snakeX)<1e-4&&
Math.abs(snake.getSnakeY()-this.snakeY)<1e-4&&
Math.abs(snake.getSnakeBottomX()-this.snakeBottomX)<1e-4&&
Math.abs(snake.getSnakeBottomY()-this.snakeBottomY)<1e-4
) {
flag = true;
}
return flag;
}
}
二、Snake类:
package com.example.mysnakegame;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
public class Snake {//画蛇身方块
float snakeX,snakeY,snakeBottomX,snakeBottomY,moveDistance;//蛇身方块的左上角坐标
private Paint paint;
private boolean isHead;
public Snake(){
snakeX = 0;
snakeY = 0;
snakeBottomX = 0;
snakeBottomY = 0;
moveDistance = 0;
isHead = false;
paint = new Paint();
paint.setColor(Color.GRAY);
}
public void setParameter(float snakeX,float snakeY,float snakeBottomX,
float snakeBottomY,float moveDistance){//设置蛇身方块的位置参数
this.snakeX = snakeX;
this.snakeY = snakeY;
this.snakeBottomX = snakeBottomX;
this.snakeBottomY = snakeBottomY;
this.moveDistance = moveDistance;
}
public void setHead(boolean b){//设置蛇头
isHead = b;
}
public boolean isSnakeHead(){//判断蛇头
return isHead;
}
public void setHeadPaintColor(int color){//设置画笔的颜色
paint.setColor(color);
}
public void draw(Canvas canvas){
canvas.drawRect(snakeX+1, snakeY+1, snakeBottomX-1, snakeBottomY-1, paint);
}
public float getSnakeX(){
return this.snakeX;
}
public float getSnakeY(){
return this.snakeY;
}
public float getSnakeBottomX(){
return this.snakeBottomX;
}
public float getSnakeBottomY(){
return this.snakeBottomY;
}
public void addSnakeX(){
snakeX+=moveDistance;
}
public void addSnakeY(){
snakeY+=moveDistance;
}
public void addSnakeBottomX(){
snakeBottomX+=moveDistance;
}
public void addSnakeBottomY(){
snakeBottomY+=moveDistance;
}
public void reduceSnakeX(){
snakeX-=moveDistance;
}
public void reduceSnakeY(){
snakeY-=moveDistance;
}
public void reduceSnakeBottomX(){
snakeBottomX-=moveDistance;
}
public void reduceSnakeBottomY(){
snakeBottomY-=moveDistance;
}
public boolean isEqualSnake(Snake snake){
boolean flag = false;
if (Math.abs(snake.getSnakeX()-this.snakeX)<1e-4&&
Math.abs(snake.getSnakeY()-this.snakeY)<1e-4&&
Math.abs(snake.getSnakeBottomX()-this.snakeBottomX)<1e-4&&
Math.abs(snake.getSnakeBottomY()-this.snakeBottomY)<1e-4
) {
flag = true;
}
return flag;
}
}
三、MySnakeView类:
package com.example.mysnakegame;
import java.util.ArrayList;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
public class MySnakeView extends View implements Runnable{
private float snakeX,snakeY;//贪吃蛇方块位置
private float snakeBottomX,snakeBottomY;//贪吃蛇方块右下标位置
private float moveDistance;//每次移动的距离
private float screenWidth;
private final int LEFT = 1;//向左运动
private final int RIGHT = 2;//向右运动
private final int UP = 3;//向上运动
private final int DOWN = 4;//向下运动
private int state;//蛇头的状态
private List<Snake> snake;//存放蛇身方块
private Paint paint;//蛇身活动区域画笔
private List<Point> snakeAddFoodArea;//食物产生区域
private Point food;//食物
public MySnakeView(Context context) {
super(context);
init();
}
public MySnakeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public MySnakeView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
paint = new Paint();
paint.setColor(Color.WHITE);
canvas.drawRect(0, 0, screenWidth, screenWidth, paint);
drawFood(canvas);//画食物
for (int i = 0; i < snake.size(); i++) {
Snake s = snake.get(i);
s.draw(canvas);
}
super.onDraw(canvas);
}
@SuppressWarnings("deprecation")
private void init(){//初始化View
WindowManager wm = (WindowManager) getContext()
.getSystemService(Context.WINDOW_SERVICE);
screenWidth = wm.getDefaultDisplay().getWidth();
setMeasuredDimension((int)screenWidth, (int)screenWidth);
moveDistance = screenWidth/10;//获取每次移动的距离
snake = new ArrayList<Snake>();//储存蛇身方块
snakeAddFoodArea = new ArrayList<Point>();//储存未被蛇身占有的方块以供产生食物
snakeX = 0;
snakeY = 0;
snakeBottomX = moveDistance;
snakeBottomY = moveDistance;
initSnakeGame();
Thread thread = new Thread(this);
thread.start();
}
@Override
public void run() {//新线程不断刷屏
while(true){
try {
Thread.sleep(500);//设置移动速度
} catch (InterruptedException e) {
e.printStackTrace();
}
moveSnake();
}
}
private void initSnakeGame(){//初始化蛇身
state = RIGHT;
if(!snake.isEmpty()){
snake.removeAll(snake);
}
int i;
for (i = 0; i < 3; i++) {
Snake body = new Snake();
body.setParameter(snakeX+moveDistance*i, snakeY, snakeBottomX+moveDistance*i,snakeBottomY,moveDistance);
if(i == 2){
body.setHeadPaintColor(Color.DKGRAY);
body.setHead(true);
}
snake.add(body);
}
addFood();
}
private void addFood(){//添加食物
initFoodArea();
System.out.println("snake.size "+snake.size());
for(int i = 0 ; i < snake.size(); i++){
for(int j = 0 ; j < snakeAddFoodArea.size(); j++){
if(snakeAddFoodArea.get(j).isEqualSnake(snake.get(i))){
snakeAddFoodArea.remove(j);
System.out.println("移除移除");
break;
}
}
}
food = snakeAddFoodArea.get((int)(Math.random()*(snakeAddFoodArea.size())));
}
private boolean isEatenFood(){//判断蛇头是否吃了食物
return food.isEqualSnake(snake.get(snake.size()-1));
}
private void drawFood(Canvas canvas){//画食物
Paint paintFood = new Paint();
paintFood.setColor(Color.GREEN);
canvas.drawRect(food.getSnakeX(), food.getSnakeY(),
food.getSnakeBottomX(), food.getSnakeBottomY(), paintFood);
}
private void initFoodArea(){//初始化食物产生区域
if(snakeAddFoodArea.size()>1){
snakeAddFoodArea.removeAll(snakeAddFoodArea);
}
for(int j = 0; j < 10; j++){//存入所有蛇身活动区域的点
for(int i = 0 ; i < 10; i++){
snakeAddFoodArea.add(new Point(snakeX+moveDistance*i,
snakeY+moveDistance*j,
snakeBottomX+moveDistance*i,
snakeBottomY+moveDistance*j,
false, false));
}
}
}
private void moveSnake(){//移动蛇身,PS:上一个蛇身不断替换上一个蛇身的位置,达到蛇移动的效果
postInvalidate();
Snake headSnake = headPosition();
if (headSnake!=null) {
snake.get(snake.size()-1).setHeadPaintColor(Color.GRAY);
snake.add(headSnake);
snake.get(snake.size()-1).setHeadPaintColor(Color.DKGRAY);
}
if (!isEatenFood()) {
snake.remove(0);
}else{
addFood();
}
if(checkGameOver()){
initSnakeGame();
}
}
private Snake headPosition() {//设置蛇头的位置
Snake head = new Snake();
head.setParameter(snake.get(snake.size()-1).getSnakeX(),
snake.get(snake.size()-1).getSnakeY(),
snake.get(snake.size()-1).getSnakeBottomX(),
snake.get(snake.size()-1).getSnakeBottomY(), moveDistance);
switch (state) {
case LEFT:
head.reduceSnakeX();
head.reduceSnakeBottomX();
break;
case RIGHT:
head.addSnakeX();
head.addSnakeBottomX();
break;
case UP:
head.reduceSnakeY();
head.reduceSnakeBottomY();
break;
case DOWN:
head.addSnakeY();
head.addSnakeBottomY();
break;
default:
return null;
}
System.err.println("snakeX"+snake.get(snake.size()-1).getSnakeX()
+",snakeY"+snake.get(snake.size()-1).getSnakeX()
+",snakeBottomX"+snake.get(snake.size()-1).getSnakeBottomX()+
",snakeBottomY"+snake.get(snake.size()-1).getSnakeBottomX()+"\n\n");
return head;
}
private boolean checkGameOver(){//检验游戏是否结束
boolean flag = false;
if(isEatSelf()||isKnockWall()){
flag = true;
}
return flag;
}
private boolean isEatSelf(){//吃到自己
boolean flag = false;
Snake head = snake.get(snake.size()-1);
for(int i = 0; i < snake.size()-1; i++){
if(head.isEqualSnake(snake.get(i))){
flag = true;
break;
}
}
return flag;
}
private boolean isKnockWall(){//边缘碰撞检测
boolean flag = false;
Snake head = snake.get(snake.size()-1);
switch (state) {
case LEFT:
if(head.getSnakeX() < 0){
flag = true;
}
break;
case RIGHT:
if(head.getSnakeBottomX() > screenWidth){
flag = true;
}
break;
case DOWN:
if(head.getSnakeBottomY() > screenWidth){
flag = true;
}
break;
case UP:
if(head.getSnakeY() < 0){
flag = true;
}
break;
default:
break;
}
return flag;
}
@Override
public boolean onTouchEvent(MotionEvent event) {//移动状态获取
Snake s = snake.get(snake.size()-1);
float x,y;
x = event.getX();
y = event.getY();
switch (state) {
case LEFT:
if(s.getSnakeY() > y){
state = UP;
}
if(s.getSnakeBottomY() < y){
state = DOWN;
}
break;
case RIGHT:
if(s.getSnakeY() > y){
state = UP;
}
if(s.getSnakeBottomY() < y){
state = DOWN;
}
break;
case UP:
if(s.getSnakeX() > x){
state = LEFT;
}
if(s.getSnakeBottomX() < x){
state = RIGHT;
}
break;
case DOWN:
if(s.getSnakeX() > x){
state = LEFT;
}
if(s.getSnakeBottomX() < x){
state = RIGHT;
}
break;
default:
break;
}
return super.onTouchEvent(event);
}
}
好了,主要逻辑代码就这些,运行界面如下:
更多博主源码分享:
2048增强版(可以自己设置方块数,还方块上面出现的数字,方块出现的几率)