安卓手机游戏制作教程入门

Ta最近发表(1)
Android手机游戏开发从入门到精通
74708 次浏览
654 位用户参与讨论
本帖最后由 lipiao123 于
15:13 编辑
Android手机游戏开发从入门到精通手机应用目前来说,50%是各种游戏,所以,游戏的开发流程,具体设计技巧,值得开发者和爱好者学习和探讨。
android手机开发从入门到精通封面.jpg (92.6 KB, 下载次数: 852)
15:09 上传
精彩评论654
& &&&刘剑卓编著的《Android手机游戏开发从入门到精通》为众多游戏开发者及对Android平台的游戏有兴趣的读者而写。《Android手机游戏开发从入门到精通》分为3篇共18章,按照章节的先后顺序,由浅入深地讲解了Android平台上游戏应用的制作方法,全面详尽地为读者介绍了Android平台上2D与3D游戏产品的开发技术。读者阅读之后,可轻松掌握游戏开发的基本技巧和制作流程,具备独立制作一款游戏的能力。本书通过两个游戏项目样例,让读者掌握游戏应用产品从创意之初直至上市发行过程中每一步的具体内容。读者在学习Android平台开发技巧的同时,还能够获得一些成功游戏开发者的经验和教训。如果读者想成为一名优秀的Android游戏开发者,本书将会是一个不可或缺的辅助工具。
第一篇&&入门篇
第1章&&欢迎来到Android世界
1.1&&智能手机的历史
1.1.1&&Linux
1.1.2&&Symbian
1.1.3&&Palm OS
1.1.4&&Windows Mobile
1.1.5&&BlackBerry OS
1.1.6&&iOS
1.1.7&&Android
1.2&&Android概述
1.2&&Android的发展历史
1.3&&Android的系统架构
1.4&&Android的未来
第2章&&开始之前的准备
2.1&&Android系统基础知识
2.1.1&&Android应用程序的运行机制
2.1.2&&Android应用程序组件
2.2& & & && &如何获得帮助?
2.2.1&&查阅帮助文档
2.2.2&&官方技术论坛
2.3&&那些必不可少的工具
2.3.1&&需要的操作系统
2.3.2&&需要的软件环境
2.3.3&&如何下载Android SDK
2.3.4&&用什么来写代码?
2.3.5&&与游戏有关的工具
2.4& & & & 小结
第3章&&建立Android开发环境
3.1&&如何设置Eclipse
3.2&&配置开发环境
3.3&&如何更新SDK?
3.4&&创建一个新项目
第4章&&从HelloWorld开始
4.1&&项目的布局
4.2&&项目工程的组成
4.2.1&&各个目录的含义
4.2.2&&Android Manifest文件
4.2.3&&Android运行的核心组件
4.3&&选择不同的SDK
4.4&&编译运行
4.4.1&&基本编译方法
4.4.2&&Ant脚本编译
4.4.3&&运行项目
第5章&&如何调试程序
5.1&&调试之前的准备
5.1.1&&建立AVD
5.1.2&&AVD的属性配置与操作
5.1.3&&AVD与真机的区别
5.1.4&&DDMS的介绍
5.1.5&&DDMS的界面
5.1.6&&DDMS工作原理
5.2&&调试方法
5.2.1&&断点调试
5.2.2&&日志调试
5.2.3&&控制台调试
5.3&&手机特性调试
5.3.1&&来电调试
5.3.2&&其他调试
5.4&&模拟器的文件结构
第二篇&&提高篇
第6章&&游戏开发之前的准备
6.1&&如何确立项目
6.1.1&&游戏创意
6.1.2&&哪些人是你的客户?
6.1.3&&确定游戏的类型
6.1.4&&确定人员的组成
6.1.5&&其他辅助人员
6.1.6&&确定预期的效果
6.2&&何为游戏性?
6.2.1&&用户黏性
6.2.2&&游戏的节奏
6.3&&独立游戏制作人
6.4&&如何制定开发计划
第7章&&引擎-游戏的核心
7.1&&游戏的基本框架
7.1.1&&游戏的组成内容
7.2&&引擎的历史
7.2.1&&何为优秀的引擎
7.2.2&&知名的引擎介绍
7.2.3&&Android平台的游戏引擎
7.3&&引擎中的模块
7.3.1&&没有标准的引擎
7.3.2&&按需配置模块
7.3.3&&运行引擎的结构
7.4&&引擎的层次
7.4.1&&基本内核层
7.4.2&&功能层
7.4.3&&表现层
7.5&&代码的规范
第8章&&从内核构建开始
& & & & 8.1&&建立游戏框架
8.1.1&&为什么用SurfaceView
8.1.2&&使用SurfaceView
8.1.3&&Activity的生命周期
8.1.4&&脏矩形技术
8.2&&游戏的生命周期
8.2.1&&从哪里开始
8.2.2&&游戏的循环
8.2.3&&有限状态机制
8.2.4&&从哪里结束
8.3&&绘制,内核中的关键
8.3.1&&刷新率的决定因素
8.3.2&&几何图形绘制
8.3.3&&图片绘制
& & & & 第9章&&游戏需要具备的功能
9.1&&游戏中的逻辑处理
9.1.1&&线程中的逻辑循环
9.1.2&&逻辑轮询
9.1.3&&个体与群体
9.2&&用户输入
9.2.1&&接口设计
9.2.2&&事件类的实现
9.3&&音乐和音效处理
9.3.1&&音乐和音效的区别
9.3.2&&声音的状态机制
9.3.3&&音乐处理类的实现
9.3.4&&音效处理类的实现
9.3.5&&声音处理类的实现
9.4&&升级绘制模块
9.4.1&&Android API中绘图类的封装
9.4.2&&绘制接口的实现
9.4.3&&文字绘制
9.5&&文件操作模块
9.5.1&&SharedPreferences处理方式
9.5.2&&资源的整理
9.5.3&&文件读取与存储
9.6&&多语言版本
第10章&&展现给玩家的世界
10.1&&游戏画面的层次
10.1.1&&世界
10.1.2&&场景
10.1.3&&层次
10.1.4&&精灵
10.2&&实现游戏内容
10.2.1&&游戏菜单界面
10.2.2&&游戏画面的构成
10.2.3&&有生命力的物体
10.2.4&&动画效果
10.3&&游戏的规则
10.3.1&&什么是规则?
10.3.2&&如何制订规则
10.4&&小结
第11章&&更精彩的游戏
11.1&&为游戏添加声音
11.1.1&&背景音乐
11.1.2&&游戏音效
11.2&&积分排行榜
11.2.1&&显示界面
11.2.2&&数据保存
11.3&&多国语言版本
11.4&&智能的UFO
11.5&&工具的使用
11.5.1&&工具的产生
11.5.2&&如何制作工具?
11.5.3&&工具的使用
11.6&&Xml文件
11.6.1&&XML数据文件
11.6.2&&解析XMl文件
11.7&&展现游戏地图背景
11.8&&小结
第12章&&精益求精
12.1&&手持平台特有的功能
12.1.1&&加速度传感器
12.1.2&&需要定位服务吗?
12.1.3&&拍下你所看到的
12.2&&性能与优化
12.2.1&&是什么拖慢了游戏?
12.2.2&&不得不做出抉择?
12.2.3&&优化的方向在哪里?
12.3&&如何防止游戏崩溃
12.3.1&&小心内存泄露
12.3.1&&如何避免内存泄漏
12.4&&异常处理
12.5&&小结
第13章&&将游戏推向市场
13.1&&上市之前准备
13.1.1&&包从哪里来?
13.1.2&&属于你的签名
13.1.3&&签名应用程序
13.1.4&&用命令语句来打包
13.2&&版本的选择
13.3.1&&免费版与收费版
13.3.2&&是否加入广告
13.3.3&&广告代理选择
13.3.4&&为游戏加入广告
13.3&&准备上市
13.3.1&&市场的选择
13.3.2&&提交标准
13.4 上市之后,要做些什么?
13.4.1&&如何宣传游戏?
13.4.2&&接纳用户反馈
13.4.3&&及时地更新
13.4.4&&价格决定销量
13.5&&小结
第三篇&&精通篇
第14章&&重新开始
14.1&&为什么重新开始?
14.2&&从哪里开始?
14.2.1&&什么JNI?
14.2.2&&NDK的开发方式
14.3&&配置开发环境
14.3.1&&Cygwin安装
14.3.2&&测试Cygwin环境
14.3.3&&NDK安装与配置
14.4&&掌握JNI和NDK
14.4.1&&JNI的使用方法
14.4.2&&NDK编译运行
14.4.3&&如何调试
14.5&&小结
第15章&&崭新的3D世界
15.1&&建立3D世界
15.1.1&&真实的世界是怎样的?
15.1.2&&虚拟的世界
15.1.3&&什么是渲染器?
15.2&&OpenGL ES介绍
15.2.1&&OpenGL的历史
15.2.2&&技术支持
15.3&&Open GL在Android中的应用
15.3.1&&Native与Java开发区别
15.3.2&&Activity的选择
15.3.3&&如何使用GLSurfaceView
15.3.4&&OpenGL 渲染器
15.4&&进入3D世界
15.4.1&&OpenGL基础
15.4.2&&OpenGL基本功能
15.4.3&&OpenGL程序库
15.4.4&&3D世界的投影
15.4.5&&OpenGL初始化
15.4.6&&绘制盒子图形
15.5&&小结
第16章&&制作3D游戏
16.1&&建立工程
16.2&&Android平台的游戏表现
16.2.1&&游戏生命周期
16.2.2&&链接本地化的原生库
16.3&&OpenGL的内核
16.3.1&&初始化
16.3.2&&三维世界投影
16.3.3&&球体的模型
16.3.4&&三维空间里的光照
16.3.5&&物体模型的纹理贴图
16.4&&用户交互
16.5&&球体封装类
16.6&&如何写编译文件
16.7&&小结
第17章&&人工智能
17.1&&人工智能的历史
17.2&&你追我跑的游戏世界
17.2.1&&它不是木头
17.2.2&&别那么傻
17.2.3&&一定要追上
17.3&&制定规则
17.3.1&&有限状态机
17.3.2&&自动巡逻
17.4&&模糊逻辑技术
17.4.1&&就是测不准?
17.4.2&&模糊状态机
17.5&&智能寻路
17.5.1&&为什么寻路?
17.5.2&&一定要用A*吗?
17.6&&小结
第18章&&物理碰撞
18.1&&真实世界的物理
18.2&&基本牛顿力学
18.3&&游戏中的物理
18.3.1&&重力世界
18.3.2&&刚体与软体
18.4&&游戏中的碰撞检测
18.4.1&&点与线组成的画面
18.4.2&&包容体的概念
18.4.3&&AABB
18.4.4&&OBB
18.4.4&&栅格化检测
18.5&&物理规律与碰撞检测的结合
18.6&&小结
经典电影网
美女图片 &
第9章&&游戏需要具备的功能
第九章我们将要介绍游戏中需要的功能。在前一章的基础上,我们已经搭建好了“游戏大厦”的底层框架。接下来的工作是去完善这座“游戏大厦”。为了让玩家能舒服“住”进来,则需要添加一些必备的设备和设施。从建筑行业来说,这些设施的内容就是电梯、排水、电缆等;在游戏制作中它们就是提供游戏的功能模块。
本章节提及的游戏功能是广泛和普遍的。大多数游戏都会需要和已经具备了这些功能,而不是针对某个游戏界的奇葩等特殊例子。不妨读者先回想一下曾经体验过的游戏,提取一下他们之间的共同点。试着总结出一些通用的功能?比如绘制功能,是我们在前一章节已经介绍过的。在本章会时它更加丰富,更加具体。针对游戏的需求,来提供特定的功能。还有什么通用的功能?声音模块,估计你没有玩过没有音乐效果的游戏吧?文字模块,游戏中的人物对白、帮助说明都是它提供的服务。数据存储,还记得前一章节中提到的游戏存档是多么重要吗?这些都是游戏通常具备的功能。
前面提到功能都是显而易见,因为你作为玩家可以直接体验到这些功能带来的效果。但还有一些虽然在玩家体验游戏的过程中提供了服务,但它们总是默默无闻,不易被察觉。只有游戏开发者才知道它们的存在,才清楚它们发挥的作用。至于是什么功能?就请你保持着好奇心,继续下面的章节吧!
9.1&&游戏中的逻辑处理
在游戏的生命周期中逻辑处理部分,可以说是游戏运算中心。大约百分之八十的数学运算工作都是在这里完成的。所以正如人类的思考都是来自大脑一样,逻辑处理模块就是游戏的大脑。大脑对于我们的作用是不言而喻的,失去它人们将失去生命。因为它是人体的总指挥,控制的我们的动作,思维。一个游戏的逻辑模块,同样控制了游戏的所有内容。它将用户输入转为游戏操作,然后将游戏操作进行运算,最后将运算的结果交付给绘制模块来显示给用户。
真实的大脑机构是极其复杂的,就算在科技发达的今天,还有许多区域作用是未知。人类大脑的工作原理和流程依然是生物学家不断摸索的领域。好在游戏中的“大脑”是由聪明的开发者创造的,其构造和运行机制是我们可以掌握的知识。
/钢铁侠www.77bd.net/0/35864/ 钢铁侠4&
/钢铁侠www.77bd.net/0/35864/ 钢铁侠4&
9.1.1&&线程中的逻辑循环
我们知道游戏的生命周期是一个不断循环的线程。Android平台中Activity的机制保证了循环线程的运行。因此,在线程中的逻辑模块也是不断循环的。每一天太阳都会照常升起,可你知道吗?每天太阳运行的轨道都是不一样的,因为每天太阳升起的位置是存在细微差异的。同样的道理,线程中的逻辑循环虽然是一个不断反复的过程,但也存在很多不同的情况。首先,请你回到前一章节,回顾下代码中逻辑模块在游戏循环中的位置。它前面是处理用户输入的模块,后面是绘制模块。从代码位置上就能看出,三个部分的工作衔接是按照流水线方式作业的。在线程中环环相扣,后者依靠前者的工作才能完成。数据流就是三个模块之间传递的信息。
我们先从逻辑模块接收的数据流开始,这个数据流中包含了玩家的输入数据,主要的内容为按键、触碰,还可能是感应器传来的手持设备速度和方位。逻辑模块要能接收所有的输入数据吗?答案是否定的,并不是所有的游戏都需要通过摇摆手机来游玩的。更何况越来越多的Android手持设备都已经去掉键盘设备,剩下的仅仅是几个功能键。所以首先考虑游戏内容,再来设计逻辑模块需要处理的输入数据流。在获得了用户输入数据流之后,将转化为游戏内的事件。转化的过程是要按照游戏规则来进行的。比如玩家点击了屏幕左下角的位置,按照游戏的规则左下角是暂停按钮。逻辑模块首先要处理点击的坐标是否在按钮范围之内,之后发出暂停的事件。接下来,可能就是进入游戏暂停状态。这是一个最简单的例子。多数情况下玩家的操作会需要很多逻辑运算。玩家一次触碰可能是足球游戏中射门,也可能是射击游戏中开枪,也可能是格斗游戏中出招。这些形形色色的情况,逻辑模块要如何处理呢?
要知道计算机本身有强大的运算能力,但它并不具备智能或者自我思考的能力。我们可不是要讨论科幻电影中出现得机器人哦!游戏中的逻辑模块是按照开发者的设计来执行的。这种设计是偏平化的,并不是线形的。开发者事先将玩家可能的输入都考虑在内,在Android手持设备上这并不是一件难事。然后针对玩家输入的情况,将所有处理的方法罗列出来进行条件选择。所谓的线形思维方式,是指根据事物的发展变化来进行处理。只说概念可能你不太清楚。我们来举一个例子,就拿之前足球游戏中的射门时间来说。按照扁平化处理的方式,开发者已将射门事件产生的结果进行了安排。无非是这几种情况:球碰到球员(守门员)、球进入球门、球碰到了门柱、球飞出界外。按照线形思维的方式呢?球的发展结果并不存在预期的答案,而是按照事物的发展来推演结果的。假设球被踢到了观众席上,线形思维的结果就是要再拿一个新球来踢了。
逻辑模块输出的数据流就是为绘制模块提供游戏中事物以及它们的坐标。需要注意的是逻辑模块提供的数据流中包含了游戏当前状态中的所有事物。并不是只提供显示在游戏画面上的事物。对于事物是否需要绘制的判断,是在绘制模块中完成的。
9.1.2&&逻辑轮询
我们已经知道了逻辑模块的作用和它处理的方式。为了使逻辑模块的条理清晰,开发者会使用轮询方式,来依次调用每个处理环节。在代码中则就体现为不同的方法。根据不同的游戏类型,轮询的内容也是不同的,比如棋牌类的游戏就不需要物理碰撞。估计你已经意识到了游戏制作是一项自由度和创造性很高的工作。因此多数的内容都没有特定标准或者格式,也没有国际化标准组织(ISO)。这主要是因为游戏的种类、平台、方式太多,对于游戏的评定标准是来自玩家的喜好度。这是一个不能准确定义的感觉数值。因此游戏的内容中存在很多的随意性。一款游戏只要能受到玩家的喜爱就可以了,并不需要它准从某种标准。不过,法律和伦理的标准游戏是不能逾越的。总的来说,轮询过程也没有一个特定的模式或者流程。
代码9-1 逻辑循环中轮询方式
private void logic() {
& & updateState();
& & updateInput();
& & updateAI();
& & updatePhysics();
& & updateAnimations();
& & updateSound();
在代码9-1中,列出了通常轮询的调用次序。它门为状态处理,输入处理,人工智能,物理碰撞,动画,声音和视频。
状态处理是一个游戏状态管理和运转机制。我们曾在第七章中提及游戏状态,例如:结束状态、暂停状态、主菜单状态。状态处理提供了维持每一个状态更新的功能。它还可以进行状态之间的转换操作。在状态转换前后调用初始化方法和结束方法。在状态转换时,控制延时或者播放界面切换的效果。
输入模块就是我们之前多次提及的,处理玩家输入内容的工作。输入内容包括玩家的按键、点击和滑动操作。输入模块的处理最好是在人工智能和物理碰撞之前。因为玩家的操作当中,有些是对游戏中人工智能和物理碰撞会产生影响的。例如当一个玩家操作主角前进时,那其它人物就不能再占用主角前进位置。输入模块处理的信息是由Android平台中UI线程来获取的。因为UI线程能够持续的监听到玩家的操作,而逻辑循环中处理输入信息是在单个时间点的操作。所以通常开发者会将玩家的输入暂时保存,然后再每一次逻辑循环中进行处理。这样的设计,是为了保证游戏状态、人工智能和物理碰撞的稳定性和准确性。
人工智能的工作是分析玩家的行为,以此来控制游戏中其他事物。在高级篇章中,我们会更详细的介绍这个模块。这里需要强调的是人工智能包含了游戏中是事物的行为,所以它的位置也要在物理碰撞模块之前。
物理碰撞是处理游戏中的事物运动规则和检测他们之间是否发生碰撞。并不是所有的游戏都用真实的物理模型在进行运算。比如《魂斗罗》游戏中飞行的子弹,在现实世界里你是很难看到它们的。这一部分内容,我们将会在高级章节中再次介绍的。
动画模块并不只是播放一段动态图片那么简单。开发者需要保证在每一个瞬间都能绘制出正确的图片。例如主角奔跑的动作,就是一个连贯的动画。我们就需要在每一个时间点将图片的位置计算出来,交给绘制模块。通常开发者会使用有限状态机制来处理动画系统。比如一个游戏中的人物,他可能存在站立,行走,打击,摔倒等动作,每一个动作都对应着一个动画。在有了动画模块后,开发者只需要准确的设定人物状态,然后对应动画中的每一帧就会在特定时间中被绘制出来了。
声音模块主要是对声音的操作。这里操作的是游戏中所有的声音资源。声音资源处理包括播放、暂停声音、调整音量大小和修改音调。在Android平台上提供了三种处理声音的接口。SoundPool 和MediaPlayer是以二进制流的方式创建声音资源。另一种OGGs是更低层次的使用二进制数组的方式来进行声音处理。后面的章节会详细介绍Android平台提供的三种声音处理方式优之间的优劣的。
9.1.3&&个体与群体在游戏中会存在许多的事物。每个事物都会有自己逻辑。因此在进行逻辑更新时,需要对每个事物进行运算。这就会带来一个问题是整体的更新,还是单独更新?我们通过一个典型的例子来说明。《吃豆人》(PAC-MAN)这款游戏是日本NAMCO公司的经典游戏。
9-1.png (37.02 KB, 下载次数: 53)
15:24 上传
图9-1 《吃豆人》
这款游戏当中,有一个玩家操作的主角和四个由人工智能控制的鬼怪,总共五个角色。游戏当中需要检测的物理碰撞有两种主角和鬼怪之间的碰撞,群体与迷宫墙壁之间的碰撞。如果你作为这个游戏的开发者,在逻辑模块中会怎么处理这两种碰撞呢?这就是个体与群体的问题?从类型上来看,主角和鬼怪的碰撞应该单独处理,因为这是个体的碰撞。群体和迷宫墙壁的碰撞,应该整体处理。因为是需要群体检测的。实际情况却正好相反。在游戏逻辑当中,处理了玩家输入和人工智能之后。首先,我们要让每一个角色去检测自己是否碰到了墙壁。其次,再把主角与鬼怪们进行碰撞检测。仔细思考一下,你会发现后面这种碰撞检测的方式,更为高效和简洁。这就是因为开发者对个体和整体的选择不同。前一种方式,将墙壁看作了个体与整体角色进行碰撞检测,将鬼怪看作了群体与主角进行碰撞检测。后一种则将整体角色看作个体与墙壁进行检测,将主角看作个体与鬼怪们进行检测。从代码结构的角度来分析这两种情况你就会更加清楚了。假设主角和鬼怪都是继承自角色类的对象,那么他们与墙壁的检测方法只需写在角色类中就可以了,而不需要创建墙壁这个“奇怪”的类来进行检测。主角与鬼怪们的检测也只需写在主角类中,而不用写在每一个鬼怪类当中。
将要成为开发者的你,需要一些实际经验才能区分个体和群体的关系。在开发过程当中,虽然每种方式都可以实现效果,但你要尽量选择合理高效的方式。最好的方式,就是用现实生活的例子来思考。假设自己就是游戏中的角色,会怎么样处理遇到的情况?这样很快你就会掌握这个技巧的。
9.2&&用户输入
游戏并不是视频电影类的应用,而是要与用户进行交互的应用。玩家也很期望能够融入游戏当中。因此游戏中就需要用户输入模块。它接受用户的操作(按键或者触屏),然后将操作转换为事件。在Android平台,我们所提到的用户输入操作都是针对当前显示窗口的。当前显示的窗口切换是由Activity中UI线程来完成的。UI线程会捕获到用户的输入,将获得信息通过回调方法传递给开发者。所以游戏开发者只需要关注当前显示窗口中的用户输入,将它们转换为游戏中事件。这样我们就会很容易的处理用户输入。
按照时效,我们可以将用户输入的方式分为两种。
& & & & 及时性:就是每当获得用户输入,就进行相应的处理。不存在按键保留和延时。这种方式的好处在于玩家操作会直接被游戏执行,没有过多的延迟。但它的缺点也十分明显。及时性的方式意味着开发者只能获得用户当前的按键。之前的操作没有任何记录。对于一些需要组合或者连续按键的游戏,这将是致命性的缺陷。另一个缺点就是,时效性的机制设计会使游戏引擎将处理用户按键设为最高执行权限。如果按键时间过于频繁,将会阻塞游戏循环线程。考虑到上面两点,及时性的输入处理方式已经在游戏项目中很少出现了。
& & & & 事件性:是指将玩家每一次操作转化为游戏引擎中事件(Event)并暂时保留起来。在每次游戏循环周期当中,将当前存在事件进行统一的处理。这种方式非常适合连续性或持续性输入的游戏。
上面提到的两种方式区别是十分明显的。举两个例子,你就会清楚为什么我极力推荐事件性的处理方式。比如在游戏开始时,经常会需要玩家输入主角的名字。为了体现中国特色,玩家想起一个中文的名字。拼音输入法就是典型的连续性用户输入。如果采用及时方式,估计玩家一个汉字也拼不出来了。还有一种情况也是只有事件性方式才能解决的。假设玩家在屏幕上进行滑动操作。当玩家的手指是在屏幕上移动时,及时性的方式获得是每一次点击,而事件方式则是连续的滑动操作。可见事件性处理方式对于游戏产品是多么的契合。
接下来,我们将会采用事件性的处理方式,来解决Android平台的用户输入。首先,我们分析一下Android平台用户输入方式有哪些?这些输入方式都有哪些状态和信息。在Android平台,存在三种输入方式:按键、触摸和重力加速度计。重力加速度计不是本章节介绍的重点,在后面的章节你会再次遇到它的。
9.2.1&&接口设计Android平台提供的按键存在两个状态:按下和抬起。开发者可以获得按键状态,同时包含了按键的信息。在按键信息中包含了两个数值,一个是按键的编码,一个是按键的字符。编码是指系统为每一个按键设置的唯一编号。比如上箭头的编码是5。按键的字符是指被按下的按钮所代表的含义。比如按下“A”这个按钮时,其字符就是A。Android提供了两个信息是为了区分组合键。比如玩家同时按下了“A”和“Shift”,虽然按键的编码没有改变,但字符却变为了“a”。代码 9-2 按键事件类(input.java)/** 按键事件类 */public static class KeyEvent {& && && &// 按键状态& && && &publicstatic final int KEY_DOWN = 0;& && && &publicstatic final int KEY_UP = 1;& && && &// 按键类型& && && && && && &//按键编码& && && &publicint keyC& && && &//按键字符& && && &publicchar keyC & && && &publicString toString() {& && && && && && & StringBuilderbuilder = new StringBuilder();& && && && && && & if(type == KEY_DOWN)& && && && && && && && && & builder.append(&key down, &);& && && && && && & else& && && && && && && && && & builder.append(&key up, &);& && && && && && & builder.append(keyCode);& && && && && && & builder.append(&,&);& && && && && && & builder.append(keyChar);& && && && && && & returnbuilder.toString();& && && &}}触屏操作在Android平台上存在三个状态:触碰、滑动和抬起。这三个状态都是由玩家来触发的。每次触屏操作中都会包含触碰点的数目和他们的坐标。代码9-3 触屏事件类(Input.java)/** 触屏时间类 */public static class TouchEvent {& && && &// 触屏状态& && && &publicstatic final int TOUCH_DOWN = 0;& && && &publicstatic final int TOUCH_UP = 1;& && && &publicstatic final int TOUCH_DRAGGED = 2;& && && &//触屏类型& && && && && && &//触点坐标& && && &publicint x,& && && &//触点数目& && && & & && && &publicString toString() {& && && && && && & StringBuilderbuilder = new StringBuilder();& && && && && && & if(type == TOUCH_DOWN)& && && && && && && && && & builder.append(&touchdown, &);& && && && && && & elseif (type == TOUCH_DRAGGED)& && && && && && && && && & builder.append(&touchdragged, &);& && && && && && & else& && && && && && && && && & builder.append(&touchup, &);& && && && && && & builder.append(pointer);& && && && && && & builder.append(&,&);& && && && && && & builder.append(x);& && && && && && & builder.append(&,&);& && && && && && & builder.append(y);& && && && && && & returnbuilder.toString();& && && &}}接下来我们将进行程序中用户输入的接口设计。在开始之前我想提醒你一下,还记得我们在第七章提到的游戏引擎的结构组成吗?本章节提到的模块都是出于引擎的功能层。为了表现出游戏引擎的结构和将来引擎能够支持多平台运行,这一层的功能模块都是可以独立存在,并且在接口设计上不会只依附于Android平台。当你自己制作游戏引擎时,也需要考虑以上两点。这样的设计体现了面向对象的思想,降低了代码耦合度,增加了代码内聚度。为将来引擎的扩展和优化奠定了良好的基础。代码9-4 输入处理接口(input.java)package ch09. import java.util.L/**用户输入处理模块的接口*/public interface Input {/**按键编码为KeyCode的按钮是否被按下? */& && && &publicboolean isKeyPressed(int keyCode);& && && &/**触点为pointer的触碰是否发生? */& && && &publicboolean isTouchDown(int pointer);& && && &/**得到触点的X坐标 */& && && &publicint getTouchX(int pointer);& && && &/**得到触点的X坐标 */& && && &publicint getTouchY(int pointer);& && && &/**得到当前按键事件的列表 */& && && &publicList&KeyEvent& getKeyEvents();& && && &/**得到当前触屏事件的列表 */& && && &publicList&TouchEvent& getTouchEvents();}从代码9-4中,可以看出我们设计了获得用户输入的两个方法getKeyEvents()和getTouchEvents()。它们会返回存储的用户输入操作事件。这些接口设计出于游戏功能来考虑的,与Android平台并没有关系。所以只要开发者遵从统一接口的规范,就不用考虑平台针对性。同一套游戏代码只需更换底层实现,就可以做到跨平台运行了。前面我们定义了用户输入的接口,接下来就是实现接口的功能。别急,我们要先完成事件性的用户输入机制。由于需要将用户输入的事件暂时存储,所以我们需要一个容器。因为至少存在两种用户输入的事件类型:按键事件和触屏事件。所以这个容器不仅能够存储事件,还要提供创造事件的统一接口。在游戏引擎循环周期中,每一次逻辑循环都需要处理当前所有输入事件。容器最好能够支持频繁的增加和清空操作。如果每次用户输入操作都创造一个与之对应事件。那么一次游戏运行下来,估计我们要创建成百上千的输入事件。这样的负担是内存承受不起的。尤其是在Android手持设备内存有限的情况下,更不会允许我们这样做。综上所述,“池”技术就是最适合用来管理输入事件的工具了。“池”技术并没有听起来那么复杂。我们只需将已经被游戏逻辑处理完的事件放入释放池。释放池中的事件是处于自由状态的,它们是可以被再次激活的。当发生新的输入操作时,首先去释放池寻找是否存在自由状态的事件。如果有则将其再次激活,如果没有则创建新的输入事件。如果你还不明白,那我再举个日常生活中的例子。我们知道为了保护环境,超市都在实行“限塑令”。所以每次去超市购物前,我都会去抽屉里找找看有没有旧的塑料袋,如果没有就只好去超市买了。在这个例子中,抽屉就相当于“释放池”,哪些被我从超市买回来的塑料袋就存放在这里。它们都处于“自由状态”是可以被再次利用的。为了让游戏程序更“环保”,我们就要用“池”技术了。9-5 事件池(pool.java)package ch09. import java.util.ArrayLimport java.util.L/**输入事件容器池类*/public class Pool&T& {& && && &/**事件创造的统一接口*/& & publicinterface PoolObjectFactory&T& {& && & public T createObject();& & }& & /**释放池*/& & privatefinal List&T& freeO& & /**事件创造接口*/& & privatefinal PoolObjectFactory&T&& & /**池存储事件的上限*/& & privatefinal int maxS & & publicPool(PoolObjectFactory&T& factory, int maxSize) {& && & this.factory =& && & this.maxSize = maxS& && & this.freeObjects = new ArrayList&T&(maxSize);& & }& && && &/**创建事件或者再次激活旧事件*/& & public TnewObject() {& && &&&Tobject = & && &&&if(freeObjects.size() == 0)& && && &&&object = factory.createObject();& && &&&else& && && &&&object = freeObjects.remove(freeObjects.size() - 1); & && && & }& && && &/**释放已处理的事件*/& & publicvoid free(T object) {& && &&&if(freeObjects.size() & maxSize)& && && &&&freeObjects.add(object);& & }}上面这段代码实现了事件池的功能。有了它的帮助,游戏中输入处理模块运作将会更加顺畅。另外一点,不要被专业的词汇吓到你。最好利用生活中的例子来模拟它,这样会使你理解起来更容易些。
9.2.2&&事件类的实现实现类的代码就是与Android平台相关的内容。因此在游戏引擎的结构当中,我们将这些类放在底层框架当中。接下来,我们来实现按键的事件处理。看下面的代码:代码9-6 按键事件处理类(KeyHandler.java)package ch09. import java.util.ArrayLimport java.util.L import android.view.Vimport android.view.View.OnKeyL import ch09.core.Input.KeyEimport ch09.core.Pimport ch09.core.Pool.PoolObjectF/**按键事件实现类*/public class KeyHandler implements OnKeyListener {& && && &//按键状态数组& &boolean[] pressedKeys = new boolean[128];& & //按键事件池& &Pool&KeyEvent& keyEventP& & //按键事件缓冲列表& & List&KeyEvent&keyEventsBuffer = new ArrayList&KeyEvent&(); & & //按键事件列表& &List&KeyEvent& keyEvents = new ArrayList&KeyEvent&(); & & publicKeyHandler(View view) {& && & PoolObjectFactory&KeyEvent& factory = newPoolObjectFactory&KeyEvent&() {& && && && &@Override& && && &&&public KeyEvent createObject() {& && && && && &return new KeyEvent();& && && &&&}& && &&&};& && & keyEventPool = new Pool&KeyEvent&(factory, 100);& && & view.setOnKeyListener(this);& && & view.setFocusableInTouchMode(true);& && & view.requestFocus();& & } & &@Overridepublic booleanonKey(View v, int keyCode, android.view.KeyEvent event) {& & //不支持同时按下多个按键& && &&&if(event.getAction() == android.view.KeyEvent.ACTION_MULTIPLE)& && && &&& & && & synchronized (this) {& && && &&&KeyEvent keyEvent = keyEventPool.newObject();& && && &&&keyEvent.keyCode = keyC& && && &&&keyEvent.keyChar = (char) event.getUnicodeChar();& && && &&&if (event.getAction() == android.view.KeyEvent.ACTION_DOWN) {& && && && && &keyEvent.type = KeyEvent.KEY_DOWN;& && && && && &if(keyCode & 0 && keyCode & 127)& && && && && && & pressedKeys[keyCode] =& && && &&&}& && && &&&if (event.getAction() == android.view.KeyEvent.ACTION_UP) {& && && && && &keyEvent.type = KeyEvent.KEY_UP;& && && && && &if(keyCode & 0 && keyCode & 127)& && && && && && & pressedKeys[keyCode] =& && && && &}& && && &&&keyEventsBuffer.add(keyEvent);& && &&&}& && && & } & & publicboolean isKeyPressed(int keyCode) {& && &&&if(keyCode & 0 || keyCode & 127)& && && &&&& && & return pressedKeys[keyCode];& & } & & public List&KeyEvent&getKeyEvents() {& && & synchronized (this) {& && && &&&int len = keyEvents.size();& && && &&&for (int i = 0; i & i++)& && && && && &keyEventPool.free(keyEvents.get(i));& && && &&&keyEvents.clear();& && && &&&keyEvents.addAll(keyEventsBuffer);& && && &&&keyEventsBuffer.clear();& && && &&&return keyE& && &&&}& & }}可以看到按键事件实现类中继承了OnKeyListener,这是Android平台提供的按键监听类。它能够捕获到当前界面中用户的按键操作,然后调用onKey(View v, int keyCode, android.view.KeyEvent event)方法,将按键信息传递给我们。我们在onKey()方法中,利用池技术将按键保存在缓冲列表(keyEventsBuffer)中。然后当游戏逻辑循环调用getKeyEvents()方法时,将缓冲列表中的按键事件放入按键列表(keyEvents)传递出去。最后清空缓冲列表,用Free()方法将已经处理过的事件放入释放池以备再次利用。上面的代码片段还有一点需要注意,就在对所有列表(List)对象进行操作时,我们都加入了异步锁定。这是因为Java语言中的列表对象(List)是不具备线程安全的,这就意味着它只能同时被一个线程操作,否则会导致数据错乱。所以我们在每次操作它的过程都加入了异步锁。触屏事件的实现类与按键事件类是一样的功能,唯一的区别在于游戏引擎不支持多个按键但支持多点触碰。由于篇幅限制,我们就不再次介绍了。在本书的范例代码中,你可以找到完整的触屏事件实现类。&&之前的铺垫都是为了实现输入处理类。我们将此类定义为AndroidInput。它整合了按键和触屏事件类,是游戏引擎在Android平台上用户输入模块。见下面的代码:代码9-7 用户输入类(AndroidInput.java)package ch09. import java.util.L import android.content.Cimport android.os.Build.VERSION;import android.view.V import ch09.core.I/**输入处理类*/public class AndroidInput implements Input {& & & && && &//按键事件对象 & &KeyHandler keyH& & //触屏事件对象& &TouchHandler touchH & & publicAndroidInput(Context context, View view, float scaleX, float scaleY) {& && & keyHandler = new KeyHandler(view);& && &&&//判断是否支持多点触碰& && & if(Integer.parseInt(VERSION.SDK) & 5) & && && &&&touchHandler = new SingleTouchHandler(view, scaleX, scaleY);& && &&&else& && && &&&touchHandler = new MultiTouchHandler(view, scaleX, scaleY);& && &&&& & } & &@Override& & publicboolean isKeyPressed(int keyCode) {& && & return keyHandler.isKeyPressed(keyCode);& & } & &@Override& & public boolean isTouchDown(int pointer) {& && & return touchHandler.isTouchDown(pointer);& & } & &@Override& & publicint getTouchX(int pointer) {& && & return touchHandler.getTouchX(pointer);& & } & &@Override& & publicint getTouchY(int pointer) {& && & return touchHandler.getTouchY(pointer);& & } & &@Override& & publicList&TouchEvent& getTouchEvents() {& && & return touchHandler.getTouchEvents();& & }& & & &@Override& & publicList&KeyEvent& getKeyEvents() {& && & return keyHandler.getKeyEvents();& & }}因为我们之前定义了良好的接口,所以这个类看起来就非常简单。它继承了Input接口并实现了接口中的方法。如果你不清楚某个方法的意思,最好退回上一章节看看接口中说明。这段代码中的构造函数才是我们要详细介绍的。AndroidInput(Context context, View view, floatscaleX, float scaleY)先看构造函数的参数:l& && &&&Context是Android提供的上下文关联类,在代码中我们并没有使用它。l& && &&&View是当前设备屏幕上正在显示的界面。为了能够获得用户按键和触屏操作,我们需要它来设置监听器。l& && &&&ScaleX和ScaleY是屏幕的缩放比例,当界面缩放时能够返回准确的触点坐标。在构造方法内,我们创建了两个实例对象:按键事件对象和触屏事件对象。还有一点需要说明的就是Android SDK自从版本5之后才开始支持多点触碰,所以我们在代码中做出判断。我们已经将游戏引擎中的用户输入模块编写完成了。这是我们掌握的第一个功能模块。之后的模块同样会这种“接口到实现“的结构风格。请你回顾一下,整个的过程。从接口设计到编写实现类。我们是如何将游戏引擎与android平台衔接到一起的同时又保证了引擎的跨平台和可扩展。输入模块是功能层比较复杂一个。在你理解它的结构和作用之后,接下来的内容就很简单许多了。

我要回帖

更多关于 橙光游戏制作教程入门 的文章

 

随机推荐