贪吃蛇是一款经典的小游戏,游戏比较简单,实现也比较简单。
本篇博客将详细介绍我自己写的贪吃蛇的思路以及实现方式。
首先我们需要在游戏界面将游戏区域划分成无数个小方格,类似下图
画布(游戏区域)的宽为 width 高为 height ,列数 sizex=width/pointSize ,行数 sizey=height/pointSize. 由于画布的长宽不一定是pointSize的整数倍,所以为了所有的数据居中需要设置 x轴 y轴的偏移量(当前貌似偏移量可有可无,本人强迫症一定要加上),offsetX=(width - sizex*pointSize)/ 2,offsetY=(height - sizey*pointSize) / 2.
新建数组 spoints=new int[ sizex ][ sizey ] 用于保存方格矩阵 其中 spoints[x][y]==1说明该点属于当前小蛇身体的一部分, spoints[x][y]==0则表示小蛇当前未运动到此点。
新建 ArrayList<Point> points=new ArrayList<Point>(); 用于保存小蛇的身体的所有节点(points 中的 所有point的x,y对应的是 spoints数组的所有值为1的点的下标),points.get(0) 为头部坐标点,最后一个为尾部坐标点
上面两幅图片代表的意思相同,都表示小蛇的当前状态(小蛇画的比较丑,请见谅)
下面开始考虑的小蛇的移动,小蛇的移动可以看成是将小蛇的尾巴的移动到小蛇的头部(根据方向判断移动的上下左右位置),其他的节点保持不动,见图示
得到下图运动后的状态
然后修改spoints数组对应的坐标的值,新增的头部对应的值修改为 1,移除的尾部的值修改为 0. 并将对应的坐标点加入points链表的对应位置,移除尾部的坐标点
解决了小蛇的移动问题,再来解决小蛇吃食物点的问题。
当小蛇吃到食物的时候,相当于将食物的点当成新的头部,而尾部不变,这样就相当于吃了之后变长了
这样就完成了小蛇的增长。
没有代码那就是耍流氓!!下面开始贴代码
git代码下载: 贪吃蛇 https://gitee.com/wuchang0213/TCS.git
csdn下载:https://download.csdn.net/download/weixin_41191134/10832869
再提供一个数独小游戏的下载:https://download.csdn.net/download/weixin_41191134/10832903
只贴了要的游戏界面的代码
public class TcsView extends View {
private Paint paint;
//小蛇的组成点
private ArrayList<Point> points=new ArrayList<Point>();
private Point pointFood=new Point();//食物点
//游戏界面的 方格划分
private int[][] spoints;
//方格数组的大小
private int sizex;
private int sizey;
//小蛇的头部对应在数组中的位置
private int headerX;
private int headerY;
//食物的坐标点
private int foodX;
private int foodY;
//偏移量
private int offsetX=0;
private int offsetY=0;
//随机数
private Random random=new Random();
private boolean isStart=true;
//屏幕宽高
private int width;
private int height;
//小蛇方块的大小
private int pointSize=20;
//方向
private int direction=0;// 0 上 -1 下 1 左 2 右
//触摸点坐标
private float tdx;
private float tdy;
private boolean isStop=false;
private TcsScoreListener tcsScoreListener;
private Handler handler=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
switch (message.what){
case 1:
headerNext();
break;
}
return false;
}
});
private void headerNext(){
if(points.size()==0||isStop||foodX==-1) return;
//小蛇的移动控制,可以看成是小蛇的首尾发生了移动,
// 头部根据下一次移动的方向移动到下一格,然后去掉结尾
switch (direction){
case 0:
headerY--;
break;
case -1:
headerY++;
break;
case 1:
headerX--;
break;
case 2:
headerX++;
break;
}
//判断是否到达边界
if(headerX<0||headerY<0){
isStop=true;
}else {
//判断小蛇是否吃到自己
if(spoints[headerX][headerY]==1){
isStop=true;
}
points.add(0,new Point(headerX,headerY));
spoints[headerX][headerY]=1;
if(tcsScoreListener!=null){
tcsScoreListener.onTCSScore(points.size());
}
if(headerX==foodX && headerY==foodY){
foodX=-1;
foodY=-1;
initPointFood();
}else {
Point move=points.get(points.size()-1);
spoints[move.x][move.y]=0;
points.remove(points.size() - 1);
}
if (!isStop){
invalidate();
}
}
}
public TcsView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
paint=new Paint();
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(1);
paint.setAntiAlias(true);
//100为时间的发生间隔,即小蛇多久移动一次,此处修改可调节小蛇的速度
new Timer().schedule(new TimerTask() {
@Override
public void run() {
handler.sendEmptyMessage(1);
}
},0,100);
}
public void setTcsScoreListener(TcsScoreListener tcsScoreListener) {
this.tcsScoreListener = tcsScoreListener;
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
width=getWidth();
height=getHeight();
if(isStart){
//根据宽高以及 pointSize 将游戏区域划分成不同的矩形区块,
sizex=width/pointSize;
sizey=height/pointSize;
spoints=new int[sizex][sizey];
//计算偏移量
offsetX=(width-(sizex*pointSize))/2;
offsetY=(height-(sizey*pointSize))/2;
//头部起始坐标默认在屏幕中央
headerX=sizex/2;
headerY=sizey/2;
spoints[headerX][headerY]=1;
points.add(0,new Point(headerX,headerY));
initPointFood();
isStart=false;
}
for(int i=0;i<points.size();i++){
Point point=points.get(i);
drawPoint(canvas,paint,point);
}
drawPoint(canvas,paint,pointFood);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
if(!isStop) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
tdx = event.getX();
tdy = event.getY();
break;
case MotionEvent.ACTION_UP:
float dx = Math.abs(tdx - event.getX());
float dy = Math.abs(tdy - event.getY());
if (dx > 40 || dy > 40) {
//判断为滑动,不是点击
Log.d("*****onTouchEvent****", "/**/*/*//**98*8/*9*/89*898565/*** ");
} else {
if (direction == 0 || direction == -1) {
if (tdx > points.get(0).x) {
direction = 2;
} else {
direction = 1;
}
} else if (direction == 1 || direction == 2) {
if (tdy > points.get(0).y) {
direction = -1;
} else {
direction = 0;
}
}
handler.sendEmptyMessage(1);
}
break;
}
}
return super.onTouchEvent(event);
}
//根据点的左边画出对应的点
private void drawPoint(Canvas canvas,Paint paint,Point point){
RectF rectF=new RectF(
offsetX+point.x*pointSize,
offsetY+point.y*pointSize,
offsetX+(point.x+1)*pointSize,
offsetY+(point.y+1)*pointSize);
canvas.drawRect(rectF,paint);
}
/**
* 确定食物点的位置
*/
private void initPointFood(){
foodX=random.nextInt(sizex);//获取随机的坐标点
foodY=random.nextInt(sizey);
//判断当前坐标点是否在小蛇的轨迹上
if(spoints[foodX][foodY]==1){
initPointFood();
}else {
//标准化坐标位置
pointFood.set(foodX,foodY);
}
}
/**
* 根据传入的参数 修改小蛇的运动方向
* @param dire
*/
public void changeDirection(int dire) {
switch (dire){
case 0:// 小蛇上下运动时 可修改为左右运动
case -1:
if(direction==1||direction==2){
direction=dire;
}
break;
case 1://小蛇左右运动时 可修改方向为上下运动
case 2:
if(direction==0||direction==-1){
direction=dire;
}
break;
}
}
/**
* 设置重新开始
*/
public void restart(){
isStop=false;
isStart=true;
points.clear();
invalidate();
}
}