Android组件学习笔记(贪吃蛇小demo)
2017-11-11 14:38
465 查看
自学Android也有点时间了,做了个贪吃蛇小demo,花了我大概四天时间,期间遇到很多问题,不过好在一一解决了,对Android体系以及运用也更加的熟练了,总之很有收获,写下文章与大家一起进步加油。
(注意:手机版本不同会导致运行效果不好,我会继续调整的)
密码:2RBxnL
(注意:手机版本不同会导致运行效果不好,我会继续调整的)
这个问题已经解决,请点击前往最新版本查看:贪吃蛇2.0
成品下载:
链接:https://share.weiyun.com/88442b473ddd658b1b7eb7aab159b6a6密码:2RBxnL
简要功能:
1.随机生成食物 2.蛇移动 3.蛇吃食物 4.蛇长身体 5.蛇死亡重生 6.暂停与开始游戏 7.组件显示关键数据
遇到问题:
1.自定义View的设计与使用不熟练(经过这个demo熟悉了很多) 2.根据不同的手机运行环境不同,导致游戏效果不同(通过获取屏幕数据来调整,但实际效果仍然未达到我的标准,大家如果下载的的代码的话可能会因为版本导致一些问题) 3.信号的传递效率问题,有些信号为触发的,不需要放置与循环中(新建判断信号来控制是否调用就好) 4.改代码问题(尽量用函数) 5.不知道哪有问题的问题(调试,Android的调试我还不太熟练) 6.信号传递机制(不会使用,上网找资料学习) 7.界面设计问题,自定义的不好再加组件(权重以及布局,在布局中添加,并且一些细节代码会使得其更加美观) 8.存储蛇的数据问题数组十分麻烦(用ArrayList十分方便) 9.触摸信号根据不同状态来调整具体状态
图片:
源代码:
mainactivity代码:package com.example.hp.likeeatsnake; import android.content.Intent; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutCompat; import android.util.DisplayMetrics; import android.view.Display; import android.view.KeyEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.Button; import android.widget 4000 .LinearLayout; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends AppCompatActivity { SnakeView game;//游戏View Button butstart;//按钮可控制暂停,开始、、重生 TextView textgrade,textjudge;//两个组件显示关键数据 private boolean ifstop=false;//判断是否暂停 int rembut=1;//调节按钮背景图 int remgrade;//显示吃的食物 String stringgrade;//显示吃的食物数//字符串类型才可以显示 private Handler mHandler = new Handler() { //thread handler 消息处理 @Override public void handleMessage(Message msg) {//消息传递机制 // TODO Auto-generated method stub super.handleMessage(msg); if(msg.arg1 == 1) { if(game != null) { game.invalidate();//刷新界面 } } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); butstart=(Button)findViewById(R.id.butstart); textgrade=(TextView)findViewById(R.id.grade); textjudge=(TextView)findViewById(R.id.judge); game=(SnakeView)findViewById(R.id.game); WindowManager manager = getWindowManager(); DisplayMetrics metrics = new DisplayMetrics(); manager.getDefaultDisplay().getMetrics(metrics);//获取屏幕高宽 int width = metrics.widthPixels; //以要素为单位 int height = metrics.heightPixels; butstart.setBackgroundResource(R.drawable.stop); butstart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(game.ifhasdead()==true)//点击一下转变状态和背景图、、如果蛇死了连续点击两次则重新开始 { game.setsnake(); remgrade=0; textjudge.setText("活"); } if(rembut==1) { ifstop=true; butstart.setBackgroundResource(R.drawable.start1); rembut=2; } else { ifstop=false; butstart.setBackgroundResource(R.drawable.stop); rembut=1; } } }); game.getnum(height,width);//得到屏幕数据 game.creatsnake();//创造蛇 game.setdirect(1);//设置蛇的移动方向 Thread t=new Thread(new Runnable() { @Override public void run() { while(true) { Message msg = mHandler.obtainMessage();//消息处理机制 msg.arg1 = 1; mHandler.sendMessage(msg); try {//必须要用try catch 不知道为啥,否则就有错 if(ifstop==false) { game.engine();//未暂停时游戏引擎运行 } if(game.ifeat()==true)//如果蛇吃到了食物则得到数据,并刷新显示他的组件 { remgrade=game.givegrade(); stringgrade=String.valueOf(remgrade); try {//为啥这里要用try catch我不知道 textgrade.setText(stringgrade); } catch (Exception e) { e.printStackTrace(); } game.setifeatfood();//恢复原来的状态,进行下一次捕食 } if(game.ifhasdead()==true)//蛇死了就刷新显示的组件 { try {//为啥这里要用try catch我不知道 textjudge.setText("死"); } catch (Exception e) { e.printStackTrace(); } } if(remgrade<=5)//根据得分调整蛇的移动速度 { Thread.sleep(300); } else if(remgrade>=5&&remgrade<=10) { Thread.sleep(200); } else if(remgrade>10&&remgrade<=20) { Thread.sleep(100); } else { Thread.sleep(50); } } catch (InterruptedException e) { e.printStackTrace(); } } } }); t.start();//开启线程 } }自定义View代码:
package com.example.hp.likeeatsnake; import android.view.View; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; import java.util.Random; import java.util.Vector; public class SnakeView extends View { private Paint edge,snake;//画边界的画笔与画蛇与食物的画笔 private int height,width,direct=1,speed=30;//记录屏幕高度宽度与蛇的方向1,2,3,4和蛇移动步伐大小 private float foodpos_x=800,foodpos_y=800;//记录食物的位置 Random random;//随机数使用前必须刷新 private boolean ifcreatfood=true;//判断条件是否允许产生食物 private boolean ifdead=false;//判断蛇是否死亡 private boolean ifstop=false;//游戏是否暂停//可能为暂停,也可能为蛇死亡 private boolean ifeatfood=false;//判断是否吃到了食物 private ArrayList<Point> mSnakeList; //蛇体可以看做是很多食物组成的 private int grade=0;//记录吃的食物个数 private int ifbenew=0;//它是信号,显示是否刷新画布 private float jx,jy,jx2,jy2;//记录手指的位置,以此调节蛇的方向 public SnakeView(Context context)//画笔初始化不在这里,我也不知道为啥 { super(context); } public SnakeView(Context context, AttributeSet set)//画笔初始化在这里,我还是不知道为啥,另外少了这段代码无法运行成功 { super(context,set); mSnakeList = new ArrayList<Point>(); edge=new Paint();//画笔初始化 edge.setColor(Color.BLACK); edge.setStrokeWidth(20); edge.setStyle(Paint.Style.STROKE);//设置画笔风格,空心或实心 snake=new Paint();//画笔初始化 snake.setColor(Color.RED); snake.setStrokeWidth(30); snake.setStyle(Paint.Style.FILL); } public void creatsnake()//创造蛇 { mSnakeList.add(new Point(500,500)); ifcreatfood=true;//初始化蛇的数据 ifdead=false; } public void ifsnakedead()//判断蛇是否死亡 { int test=1; if(mSnakeList.get(0).x<=30||mSnakeList.get(0).x>=width-30||mSnakeList.get(0).y<=30||mSnakeList.get(0).y>=1280)//判断是否越过边界 { ifdead= true; } else { ifdead=false; } for(int i=4;i<mSnakeList.size();i++) { if(mSnakeList.get(0).x==mSnakeList.get(i).x&&mSnakeList.get(0).y==mSnakeList.get(i).y) { ifdead= true; } } } //给外界信号作为判断依据,并有函数从外界来调节信号 public boolean ifhasdead() { return ifdead; } public void setsnake()//复活蛇 { ifdead=false; mSnakeList=new ArrayList<>();// mSnakeList.add(new Point(500,500)); ifcreatfood=true;//初始化蛇的数据 grade=0; ifdead=false; } public boolean ifeat() { return ifeatfood; } public void setifeatfood() { ifeatfood=false; } public void snakeeat() { Point oldhead=mSnakeList.get(0); if (oldhead.x - foodpos_x <= 30&&oldhead.x-foodpos_x>=0 || foodpos_x - oldhead.x <= 30&&foodpos_x - oldhead.x >=0) { if (oldhead.y - foodpos_y <= 30&&oldhead.y-foodpos_y>=0 || foodpos_y-oldhead.y>=0&&foodpos_y - oldhead.y <= 30) { Point newhead=new Point(); switch (direct)//上下左右、 头动 { case 1: newhead.x = oldhead.x; newhead.y = oldhead.y - speed ; break; case 2: newhead.x = oldhead.x; newhead.y = oldhead.y + speed ; break; case 3: newhead.x = oldhead.x - speed; newhead.y = oldhead.y; break; case 4: newhead.x = oldhead.x + speed ; newhead.y = oldhead.y; break; default: break; } mSnakeList.add(0,newhead);//吃东西头部加一,尾部不管 grade+=1;ifeatfood=true; ifcreatfood=true;//准备生产新的食物 } } } public int givegrade() { return grade; } public boolean ifcancreatfood()//告诉系统是否生成食物 { return ifcreatfood; } public void snakemove()//控制蛇的移动 { if(ifdead==false) { Point oldhead = mSnakeList.get(0); Point newhead = new Point(); //身体除了头外全部移动 switch (direct)//上下左右、 头动 { case 1: newhead.x = oldhead.x; newhead.y = oldhead.y - speed ; break; case 2: newhead.x = oldhead.x; newhead.y = oldhead.y + speed ; break; case 3: newhead.x = oldhead.x - speed; newhead.y = oldhead.y; break; case 4: newhead.x = oldhead.x + speed ; newhead.y = oldhead.y; break; default: break; } mSnakeList.add(0,newhead);//头部加一,尾部减一实现移动 mSnakeList.remove(mSnakeList.size() - 1); } } public void creatfood()//状态允许的话使用随机数生成下一个事物的位置 { Point head=mSnakeList.get(0); if(ifcreatfood==true) { int ok; while(true)//食物的位置不能与蛇的身体重合 { ok=0; random=new Random();foodpos_x = random.nextInt(970) + 50; random=new Random();foodpos_y = random.nextInt(1200) + 50; for(int i=0;i<mSnakeList.size();i++) { if(foodpos_x!=head.x||foodpos_y!=head.y) { ok++; } } if(ok==mSnakeList.size())break; } ifcreatfood=false; } } public void getnum(int height,int width)//从外界得到屏幕的数据 { this.height=height; this.width=width; } public void setdirect(int direct)//从外界得到一个数字表示方向 { this.direct=direct; } public void engine()//游戏引擎 { ifsnakedead(); snakeeat(); creatfood(); snakemove(); ifbenew=1; } public void drawedge(Canvas canvas)//画出边界 { canvas.drawRect(10,10,width-10,1300,edge); } public void drawfood(Canvas canvas)//画食物 { snake.setColor(Color.BLUE); canvas.drawCircle(foodpos_x,foodpos_y,30,snake);//坐标,半径,使用的画笔 } public void drawsnake(Canvas canvas)//画蛇 { snake.setColor(Color.RED); for(int i=0;i<mSnakeList.size();i++)//画蛇 { Point point = mSnakeList.get(i); canvas.drawCircle(point.x,point.y,30,snake); } } @Override protected void onDraw(Canvas canvas) //画画 { super.onDraw(canvas); drawedge(canvas); drawfood(canvas); drawsnake(canvas); } @Override public boolean onTouchEvent(MotionEvent event) //根据触屏滑动改变方向 { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: jx=event.getX(); jy=event.getY(); break; case MotionEvent.ACTION_UP: jx2=event.getX(); jy2=event.getY(); float endx=jx-jx2,endy=jy-jy2; if(endx>0) { if(endy>0) { if(endx>endy) direct=3; else direct=1;// } else if(endy<0) { endy*=-1; if(endx>endy) direct=3; else direct=2; } } else if(endx<0) { endx*=-1; if(endy>0) { if(endx<endy) direct=1; else direct=4; } else if(endy<0) { endy*=-1; if(endx<endy) direct=2; else direct=4; } } break; } return true; } }布局代码:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.hp.likeeatsnake.MainActivity"> <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" app:layout_constraintVertical_weight="12" tools:layout_editor_absoluteY="8dp" tools:layout_editor_absoluteX="8dp"> <com.example.hp.likeeatsnake.SnakeView android:id="@+id/game" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="12" tools:layout_editor_absoluteX="0dp" tools:layout_editor_absoluteY="0dp" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="1" android:orientation="horizontal"> <TextView android:id="@+id/grade" android:layout_width="80dp" android:layout_height="80dp" android:layout_gravity="left" android:gravity="center" android:text="0" android:textAlignment="center" android:textAllCaps="false" android:textSize="30sp" a05d android:textStyle="bold" /> <Button android:id="@+id/butstart" android:layout_width="80dp" android:layout_height="80dp" android:layout_gravity="center" tools:layout_editor_absoluteX="0dp" tools:layout_editor_absoluteY="0dp" /> <TextView android:id="@+id/judge" android:layout_width="80dp" android:layout_height="80dp" android:layout_gravity="right" android:gravity="center" android:text="活" android:textAlignment="center" android:textSize="30sp" android:textStyle="bold" /> </LinearLayout> </LinearLayout> </android.support.constraint.ConstraintLayout>
相关文章推荐
- Android Animation学习笔记【原帖没有Demo,自己写了一个,链接在最后】
- 【Android】学习笔记(14)——BroadcastReceiver组件使用
- android菜鸟学习笔记25----与服务器端交互(二)解析服务端返回的json数据及使用一个开源组件请求服务端数据
- 黑马程序员------学习笔记(10)Android四大组件
- Android 学习笔记 —— Android 四大组件 与 MVC 架构模式
- APIDemo学习笔记——Android上几种简单的Animation使用方法(一)
- APIDemo学习笔记——Android上几种简单的Animation使用方法(三)——3D Transition
- 学习笔记:Android基本组件之Service
- Android系统相关组件解析(Android第一行代码学习笔记2)
- Android(java)学习笔记186:对ListView等列表组件中数据进行增、删、改操作
- Android四大组件之Service服务学习笔记(一)
- APIDemo学习笔记——Android上几种简单的Animation使用方法(二)
- React Native for Android 学习笔记(一) 操蛋的环境搭建与demo运行
- Android疯狂讲义学习笔记——XML布局和java代码共同控制用户界面组件
- 【Android平台】 Alljoyn学习笔记二 编译自带的demo的步骤
- android四大组件学习笔记--Activity
- 学习笔记1:Android 的应用程序组件
- APIDemo学习笔记——Android上几种简单的Animation使用方法
- Android 学习笔记 - 【Android应用程序四大组件】
- Android应用组件学习笔记