Android植物大战僵尸教程学习总结(一)
转眼间就2015年了,时间过得真快,基本在找资料的时候很多都在CSDN的博客里找到。现在,也自己开始写了,希望自己能在这条路上坚持下去吧。
一开始是看着老师的视频一点点打的,整个看完了以后,整理了一下思路,重新自己打了一遍,也是遇到了很多的问题,看似是理解了,但是实际操作起来还是有很多的问题,最后还是完成了。老师写的只算是 个半成品,还有许多地方可以完善,这篇文章先写下来,趁热打铁嘛,这样自己以后也方便温习。然后一些知识点,都是我学到的,都加了淡色背景,可能排版还是有点糟,就先这样吧。
首先,先把工程下的几个包列一下
XXX为工程包名
XXX.entity 这个包用来放所有的实体集合
XXX.global 这个包用来放所有的静态全局变量
XXX.model这个包用来放父类(用于实体的extends)和一些接口
XXX.tools 这个包用来放图像处理类的工具
XXX.view 这个包用来放视图类的文件
先在.view包下新建GameView,让其extends SurfaceView,实现接口SurfaceHolder.Callbak和Runnable
关于SurfaceView
一、什么是SurfaceView?
可以直接从内存或者DMA等硬件接口取得图像数据,是个非常重要的绘图容器。它的特性是:可以在主线程之外的线程中向屏幕绘图上。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。在游戏开发中多用到SurfaceView,游戏中的背景、人物、动画等等尽量在画布canvas中画出。
二、SurfaceView和其他View的区别?
SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面而View必须在UI的主线程中更新画面。可以理解为Surfaceview的更新不需要在主线程中进行,这样就可以避免主线程来做大量的界面更新操作,引起ANR(主线程的界面更新的消息队列当中存在大量的更新界面请求)。
三、SurfaceView的使用
首先继承SurfaceView并实现SurfaceHolder.Callback接口使用接口的原因:因为使用SurfaceView 有一个原则,所有的绘图工作必须得在Surface 被创建之后才能开始,而在Surface 被销毁之前必须结束。所以Callback 中的surfaceCreated 和surfaceDestroyed 就成了绘图处理代码的边界。
上面提到了SurfaceHolder这个类
关于SurfaceHolder
可以把SurfaceHolder当成surface的控制器,用来操纵surface。处理它的Canvas上画的效果和动画,控制表面,大小,像素等。几个需要注意的方法:
(1)、abstract void addCallback(SurfaceHolder.Callback callback);
// 给SurfaceView当前的持有者一个回调对象。
(2)、abstract Canvas lockCanvas();
// 锁定画布,一般在锁定后就可以通过其返回的画布对象Canvas,在其上面画图等操作了
(3)、abstract Canvas lockCanvas(Rect dirty);
// 锁定画布的某个区域进行画图等..因为画完图后,会调用下面的unlockCanvasAndPost来改变显示内容。
// 相对部分内存要求比较高的游戏来说,可以不用重画dirty外的其它区域的像素,可以提高速度。
(4)、abstract void unlockCanvasAndPost(Canvas canvas);
// 结束锁定画图,并提交改变。
四、Surfaceview 的生命周期
在SurfaceView的派生类中,使用getHolder方法来获取SurfaceHolder对象。同时还需要addCallback方法来添加回 调函数。
surfaceChanged :在surfaceview的大小发生改变的时候调用。
surfaceCreated : 在创建Surface时激发。
surfacedestroyed:在销毁Surface时激发。
以上,是我找到的一些帮助理解的资料,因为之前没有接触过SurfaceView,不过我觉得吧。根据Android里的消息队列的处理机制,还是能找相似处的。其实,SurfaceView就可以看成是一个绘图类的线程(因为有run()方法),但是他是VIP级别的,所以不需要遵循Handler Message的机制,即队列机制,就是不需要排队。(纯属个人理解)
这边给个链接,是之前看的关于handler的处理机制的一篇文章,写的挺好的,网址如下:
http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html
先定义画笔,画布,以及surfaceHolder还有上下文环境context
再定义一个boolean型的游戏运行标志
<span style="font-size:18px;"><span style="white-space:pre"> </span>private Canvas canvas;private Paint paint;private SurfaceHolder surfaceHolder;private Context mContext;private boolean gameRunFlag;</span>
在构造方法里,配置好
<span style="font-size:18px;">public GameView(Context context) {super(context);this.mContext = context;paint = new Paint();surfaceHolder = getHolder();surfaceHolder.addCallback(this);gameView = this;gameRunFlag = true;}</span>
在run()方法里,先判断游戏是否运行,加上同步锁synchronized
用一个try catch finally结构,记得之前提到的,先给画布上锁,画完了,再解锁
还有就是每次执行,给个休眠
<span style="font-size:18px;">public void run() {while (gameRunFlag) {synchronized (surfaceHolder) {try {canvas = surfaceHolder.lockCanvas();} catch (Exception e) {} finally {surfaceHolder.unlockCanvasAndPost(canvas);}}try {Thread.sleep(50);} catch (Exception e) {}}}</span>
用finally的理由是,不管是否出现异常,你必须给画布解锁(总不能因为你出问题了就不解锁了吧)
关于Synchronized
synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法,有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,直接运行。它包括两种用法:synchronized 方法和 synchronized 块。
//方法体
}
synchronized(a1){
//一次只能有一个线程进入
}
<span style="font-size:18px;">private GameView gameview;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);gameview = new GameView(this);init();setContentView(gameview);}private void init() {}
</span>
然后,在Mainfest文件里,修改MainActivity的主题以及屏幕方向为
<span style="font-size:18px;"><span style="font-size:18px;">android:screenOrientation="landscape"android:theme="@android:style/Theme.NoTitleBar.Fullscreen"</span></span>
接下来,在XXX.tools包下新建DeviceTools类,该类主要用来获得屏幕的信息,以及变换图片
<span style="font-size:18px;"><span style="font-size:18px;">public static Bitmap resizeBitmap(Bitmap bitmap)public static Bitmap resizeBitmap(Bitmap bitmap, int w, int h)public static int[] getDeviceInfo(Context context)</span></span>
涉及到方法的重载,一个根据已定好的缩放比来处理图片,一个是按自定义的缩放比来处理图片,还有一个是获取设备的屏幕参数。一个个说吧,先定义一个int[2]的数组,分别获得宽,高放入数组中。
<span style="font-size:18px;">public static int[] getDeviceInfo(Context context) {if (DeviceWidthHeight[0] == 0 && DeviceWidthHeight[1] == 0) {DisplayMetrics metrics = new DisplayMetrics();((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metrics);DeviceWidthHeight[0] = metrics.widthPixels;DeviceWidthHeight[1] = metrics.heightPixels;}return DeviceWidthHeight;}</span>
关于DisplayMetrics
<span style="font-size:18px;"><span style="background-color: rgb(255, 204, 255);">DisplayMetrics metrics = new DisplayMetrics();((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metrics);</span></span>
通过这几句话就可以获得绝对像素(pixels)
<span style="font-size:18px;">public static Bitmap resizeBitmap(Bitmap bitmap) {if (bitmap != null) {int width = bitmap.getWidth();int height = bitmap.getHeight();Matrix matrix = new Matrix();matrix.preScale(Config.scaleWidth, Config.scaleHeight);Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width,height, matrix, true);return resizedBitmap;} else {return null;}}</span>
首先,记得在XXX.global包下新建Config类,里面用来存放所有的静态全局变量(这之后提到定义Config.XXX即是先定义再用) 这个方法就是先获得传入 图片的宽,高。通过矩阵Matrix变换,通过定义好的缩放比(scale)来完成变换。最后新建一个bitmap,通过createBitmap方法将其变换好的导进去。这边有个要注意的
后调用的pre操作先执行,而后调用的post操作则后执行。set方法一旦调用即会清空之前matrix中的所有变换。
这里不作过多深入研究,了解一下就好。
另一个resizeBitmap类似,只不过,这个变换的参数不是在Config里配置的,而是自己传入,width和height来计算出的,方法具体如下:
<span style="font-size:18px;">public static Bitmap resizeBitmap(Bitmap bitmap, int w, int h) {if (bitmap != null) {int width = bitmap.getWidth();int height = bitmap.getHeight();int newWidth = w;int newHeight = h;float scaleWidth = (float) newWidth / width;float scaleHeight = (float) newHeight / height;Matrix matrix = new Matrix();matrix.preScale(scaleWidth, scaleHeight);Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width,height, matrix, true);return resizedBitmap;} else {return null;}}</span>
接下来,回到MainActivity,在init()方法里,通过之前获得的屏幕宽,高和背景图片比对,来计算缩放比(记得在Config里配置好需要的全局静态参量)
例:public static int DeviceWidth;
<span style="font-size:18px;">Config.DeviceWidth = DeviceTools.getDeviceInfo(this)[0];Config.DeviceHeight = DeviceTools.getDeviceInfo(this)[1];Config.bkGround = BitmapFactory.decodeResource(getResources(),R.drawable.bk);Config.scaleWidth = Config.DeviceWidth/ (float) Config.bkGround.getWidth();Config.scaleHeight = Config.DeviceHeight/ (float) Config.bkGround.getHeight();</span>
计算完毕了,接下来就可以配置图片了(这边相当于配置相当于肉腌好,等会儿画相当于炒菜,原谅喜欢烹饪的我拿这个作比方)
<span style="font-size:18px;">Config.bkGround = DeviceTools.resizeBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.bk));</span>
这边获得Bitmap对象,一定要用BitmapFactory的decodeResource来解析图像资源,才能得到
肉腌好了,可以炒了,哈哈
回到GameView,在run()里,写上canvas.drawBitmap(Config.bkGround, 0, 0, paint);
这里0,0是因为背景图片嘛。再定义两个方法 ondraw(用于添加具体的有生命的实体) updataData(用于判定有生命的实体的死亡)再以同样的方法,画上seedbank(那个放植物卡片的棕色栏)
canvas.drawBitmap(Config.seedBank, Config.sunDisapperX, 0,paint);
最后的run()如下:
<span style="font-size:18px;">public void run() {while (gameRunFlag) {synchronized (surfaceHolder) {try {canvas = surfaceHolder.lockCanvas();canvas.drawBitmap(Config.bkGround, 0, 0, paint);canvas.drawBitmap(Config.seedBank, Config.sunDisapperX, 0,paint);updataData();ondraw(canvas);} catch (Exception e) {// TODO: handle exception} finally {surfaceHolder.unlockCanvasAndPost(canvas);}}try {Thread.sleep(50);} catch (Exception e) {// TODO: handle exception}}}</span>
这里的Config.sunDisappearX需要计算一下,方便之后写阳光的运动。新建一个createElement方法(该方法主要是用来创建有生命的实体的) 在里面计算好Config.sunDisappearX
Config.sunDisapperX = (Config.DeviceWidth - Config.seedBank.getWidth()) / 2;
到此为止,所有的没有生命的背景都写好了,我休息一下,继续打字。
Android植物大战僵尸教程学习总结(一)相关推荐
- Android植物大战僵尸教程学习总结(三)
植物都处理好了,那接下来先是阳光. 阳光,是由向日葵产生的.所以,我们回到Flower类.这边的话,跟之前的思考方式是一样的,不过加了一个时间的限定,毕竟,阳光产生要冷却的. private long ...
- Android植物大战僵尸附源码
本文介绍cocos2d-android实现的Android植物大战僵尸,最后附源码 内容介绍: 一.游戏最原始的开发框架. 主要会介绍 a) SurfaceView+SurfaceHolder.Ca ...
- 植物大战僵尸经典android,植物大战僵尸经典版
植物大战僵尸经典版即植物大战僵尸最原始最经典的版本,也是玩家们最开始接触到的版本,在这个植物大战僵尸经典版中,所有的植物僵尸都是最开始的模样,一点都没有变,也不会像现在有着各种各样奇奇怪怪的植物和僵尸 ...
- C语言实现植物大战僵尸----学习过程
大一下学期c语言课程设计要我们用c语言制作一款游戏,之前网上冲浪时候发现了c语言实现植物大战僵尸的教程,就想来跟着教程做一遍,并记录下自己的学习过程与经验. 前排分享我所学习的视频和文章: [可能是B ...
- 远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例
远程代码注入及DLL注入教程 说明 本人刚开始学习逆向,不知道有没有动力学下深去,这一块也没有详细的实战教学,学多少就上传多少,希望能给想学的朋友一点帮助吧,本教程想通过植物大战僵尸这一经典游戏来 ...
- 部分 CM11 系统 Android 平板执行植物大战僵尸 2 黑屏的解决的方法
原文 http://forum.xda-developers.com/showthread.php?t=2755197 部分 CM11 系统的 Android 平板(比如三星 GT-P5110 )执行 ...
- 【转】Android Studio安装配置学习教程指南 Gradle基础--不错
原文网址:http://www.linuxidc.com/Linux/2015-02/113890p4.htm 其实很早之前也写了一篇Gradle的基础博客,但是时间很久了,现在Gradle已经更新了 ...
- 学习逆向知识之用于游戏外挂的实现.第二讲,快速寻找植物大战僵尸阳光基址.以及动态基址跟静态基址的区别...
通过游戏外挂,学习逆向技术之快速寻找植物大战僵尸阳光基址.以及动态基址跟静态基址的区别 一丶静态基址. 动态基址. 基址的区别 通过上一讲超级马里奥的游戏外挂技术制作.我们学习到了静态基址.以及观看内 ...
- 使用Python对植物大战僵尸学习研究
根据上一篇 使用使用Python学习win32库进行内存读写 中,使用Python win32库,对一款游戏进行了读内存 操作. 今天来写一下对内存进行写的操作 正文 要进行32位的读写,首先了解一下 ...
最新文章
- Fiddler 获取、安装与浏览器代理设置,Fiddler的第一次使用
- 政策定价风控审批策略
- keytool安装tls证书_TLS使用指南(一):如何在Rancher 2.x中进行TLS终止?
- Nodejs学习事件模块
- 这次是真香了!iPhone 11一个月卖出1200万部 苹果加大产量
- ZH奶酪:自然语言处理工具LTP语言云调用方法
- 小程序入门学习19--springboot之HelloWorld
- FlashDevelop 3.0.0 Beta2 released
- VC双缓冲画图技术介绍
- 互联网支付系统整体架构详解
- windows 搭建kms服务器激活_搭建kms服务器,自建KMS激活服务器的两种方法
- 小型项目的微服务架构指南
- java研发手机归属地批量查询
- 小学听力测试英语软件,小学英语听力测试
- 使用ISO镜像制作适用于OpenStack的云镜像
- python调用谷歌地图api_python显示地图与谷歌地图
- 中国科学院计算机研究所上级单位,陈援非(中国科学院计算技术研究所高工)_百度百科...
- excel 2010 删除重复行(按某一列重复)
- Kafka KSQL安装
- ArduinoIDE + STM32Link烧录调试
热门文章
- 安卓修改大师v3.1.0.0:傻瓜式安卓反编译逆向修改工具
- 网吧无盘服务器游戏盘,网吧有盘无盘轻松自由切换
- 电脑编程用的是什么c语言吗,学电脑编程里的C语言什么意思
- 五轴数控转台_有人说先学会三轴,再去搞四轴、五轴加工中心,这几种机床有何区别呢?...
- 如何提取处理网页视频中的字幕
- HTML文件保存时关闭自动换行,如何禁止html自动换行
- 大厂对话系统实践文章链接
- JS逆向之Webpack(二)
- web前端-特殊背景自适应结构(上中下)
- 清理优化mac苹果磁盘空间的方法