首页 热点资讯 义务教育 高等教育 出国留学 考研考公
您的当前位置:首页正文

游戏设计与开发

2020-02-15 来源:华拓网


中国矿业大学计算机学院

2013 级本科生课程报告

课程名称 《软件测试》

报告时间 2016年7月 学生姓名 李龙 学 号 ******** 专 业 计算机科学与技术

任课教师评语

任课教师评语

(①对课程基础理论的掌握;②对课程知识应用能力的评价;③对课程报告相关实验、作品、软件等成果的评价;④课程学习态度和上课纪律;⑤课程成果和报告工作量;⑥总体评价和成绩;⑦存在问题等):

成 绩:

任课教师签字: 2016 年 6 月25

摘 要

本课题是设计开发一款小游戏,由于本人知识的有限,以及客观条件的限制,本人打算开发一个单机版的游戏。本人在手机上玩过贪吃蛇的游戏,曾经为了和别人比赛,苦苦的玩了好多次,追求高分!后来得知这个小小的游戏是nokia当年很成功的一款手机游戏,许多人都玩过,也很喜欢。现在这款游戏的版本已经发展到第三版了,手机生产厂商继续开发这个游戏,看来这个游戏还是有很大的市场的。Google公司2007年11月5日发布的开源的Android平台 —— 一款包括操作系统(基于Linux内核)、中间件和关键应用的手机平台,并组建了开放手机联盟(Open Handset Alliance),包括Google、中国移动、T-Mobile、宏达电、高通、摩托罗拉等领军企业。于是,我决定利用自己大学所学的知识,独立开发这个小游戏。重首先说明了这个贪吃蛇程序所用到的一些类和控件,包括Drawable,Canvas, Thread,等等。介绍了这些类的一般的使用方法,以及本程序是如何使用这些类来进行游戏的开发的。本程序将老少皆宜的经典作品移植到手机上来,为更流行的硬件平台提供应用软件。这些都将能很好的满足未来人们对手机游戏的需求。吞吃蛇游戏基于Android平台编写,满足一般手机用户的娱乐需求。

关键词:Android系统; 贪食蛇游戏; 手机游戏

ABSTRACT

.This topic is using java language on the Android platform mobile phone game development, I limited knowledge, and objective constraints, I intend to develop a stand-alone game. I played the game of Snake, on the phone once in order to match with others, struggling to play many times, the pursuit of high scores! Later learned that this little game nokia was very successful mobile phone games, many people have played, and also enjoyed. Version of the game has been developed to the third edition, mobile phone manufacturers continue to develop this game, it seems that this game is a great market. Open-source Android platform, Google's released on November 5, 2007 - an operating system (Linux kernel), middleware and key applications-based mobile phone platform, and the formation of the Open Handset Alliance (Open Handset Alliance), includingGoogle, China Mobile, T-Mobile, HTC, Qualcomm, Motorola and other leading enterprisesSo, I decided to use the knowledge learned in his college, in the instructor's help, the independent development of this game.This article first details some of the basic java knowledge, on this basis leads to Android, the basis of the Android and its characteristics, and then introduces the Android future prospects and development. Highlights this Snake procedures used and controls, including the Drawable Canvas, Thread, and so on. Describes the general use, as well as the program is how to use these classes to the development of the game. The ages of the classic works transplantation in this program to the phone up, to provide application software for the more popular hardware platforms. These will be well positioned to meet future demand for mobile games. Devour the snake game based on the Android platform, prepared to meet the entertainment needs of the general mobile phone users.

Key words: Android system; Gluttony snake game; Mobile game

目录

1 背景

1.1研究背景

随着社会的发展,人们生活的步调日益加快,越来越多的人加入了全球化的世界。人们不再拘泥于一块天地,加班,出差成了现代人不可避免的公务。而此时一款可以随时随地娱乐的游戏成了必需品。贪吃蛇这一游戏简单易行,操作方便,娱乐性较强,吸引了不少人。这一款游戏紧紧地抓住了人们的心理,虽然简单,却其乐无穷,在人们不断追求更多的欲望下,该游戏给人们带来了追逐的快感,以及成功后的满足感,对于一直处于高压下的现代人是很好的放松工具[1]。

当前科学技术飞速发展,特别是微电子技术,计算机软件与应用技术的发展,使得人们的日常生活丰富多彩。单片微型计算机(简称单片机)作为微型计算机家族的一员,以其独特的结构,良好的稳定性,便宜的价格在嵌入式领域广泛应用。与传统的PC上设计的贪吃蛇游戏不同,本次作者利用Android平台,java语言编程,实现便携地贪吃蛇游戏

1.2 项目目的与意义

1.2.1开发目的

自2007年Google发布Android系统1.0之后,各大手机生产厂商纷纷将目光投向了最具发展潜力的Android系统,并陆续推出了各种品牌的搭载Android系统的智能手机。 时隔四年,Android手机在手机市场中占的市场份额正以非常迅猛的速度上升:2010年市场份额:Symbian 36.6%、Android 25.5%、iPhone 16.7、windows Mobile 2.8%、linux 2.1% 。Nokia的Symbian系统在Android系统的冲击下,正在从手机市场中的王者慢慢被挤下来。在未来的几年内,Android手机必定会占据手机市场的主要份额,所以在这种形式下,Android应用的前景也必定是前景一片大好。 1.2.2开发意义

现如今,电子商品的硬件支持越来越高端。人们对手机的功能要求不再仅仅局限于接电话和打电话了,在这些基本功能的基础上,手机更成了人们日常生活中非常重要的娱乐和休闲工具。许多时候人们都会处于无聊的状态,比如等车的时候、坐长途车的时候...这时若有一款有意思的游戏来打发时间是再好不过了。由于Android手机具有多任务功能,可以一边听音乐一边玩游戏,是让人们在等车的时候玩上一玩消磨时间的好东东。

1.3 手机游戏国内外现状和发展趋势

1.3.1 手机游戏国内外现状

近年来,网路尤其是无线通信网络在国内迅速普及,网络游戏也得到了高速发展。同样手机的普及,使得手机游戏市场空前发展。国内外巨大的手机使用量,也让商家看到了新的商机,手机游戏市场利润将会十分的丰厚。面对潜力巨大的手机游戏市场,各大游戏开发商、运营商、服务商以及手机终端厂商等都开始准备应对措施,以便抢占更多的手机市场分额。Nokia、索爱、三星等各大手机厂商开始不断的提升手机的游戏性能,同时采用开放式的手机操作系统,方便用户

能够享受到不同的游戏业务[4]。而国内游戏开发商,如盛大、腾讯、网易等公司,纷纷跻身手机游戏开发商行列。新浪、搜狐等著名的门户网站,也纷纷涉足手机游戏领域,相继开辟了各自的手机游戏相关栏目,其他一些游戏网站也都加快了手机游戏开发的脚步。现阶段国内的手机游戏还局限于小型单机游戏和简单的卡牌类网络游戏,而国外已经开始致力于开发能够依靠GPRS定位,或者用户佩戴辅助器械的大型手机游戏。 1.3.2 手机游戏的发展趋势

每个人都可以随时随地玩手机游戏,虽然手机游戏的价格不贵,但它的使用量巨大,也因此带来巨大的商业价值。手机游戏具有娱乐性和趣味性,而且它的操作简单,易与掌握,是人们日常生活中不可或缺的娱乐方式。伴随着3G、4G时代的到来,手机上网逐渐普及,上网资费进一步下调,使用手机进行娱乐互动的消费者越来越多。随着移动快捷支付系统日趋成熟和盈利模式的逐渐明朗,随着资本对于手机游戏市场的关切,随着业界内各个环节逐渐完善与壮大,手机游戏必将会迎来一个新的发展高峰[6]。

高科技游戏曾经是任天堂、索尼和微软三足鼎立的天下,由于智能手机的兴起,手机游戏的发展在短短2年里就超越了掌上机及电脑游戏。Pop Cap Games公司针对2500名英美成年人所做的调查显示,有一半以上的受访者至少玩过一次手机游戏。

大量平价游戏、更为简化的游戏技术,大大降低了用户玩手机游戏的门槛,在中国手机游戏几乎都是免费下载的。撇开平价手机游戏所带来的商业影响不谈,游戏开发商认为手机游戏其实蕴藏着无限发展机遇。智能手机用户是移动行业中数量增长最快的用户,手机游戏公司们最近的表现也同样势不可挡。

操作简易,内容简单的游戏是小型手机游戏的主打产品,但随着电子技术的发展,手机硬件性能的不断提升,手机电池容量更大续航时间更久,以及手机外设的开发,手机游戏也向着流畅舒服的操作,内容丰富的大型游戏制作方向发展。开发大型手机游戏不仅能开拓新的市场,新的商机和利润,也能给用户带来操作、视觉的双重享受。

贪吃蛇作为一款操作简单的单机类游戏,在竞争激烈的手机游戏领域中,近些年来也得到了充分的发展。Nokia在其WP7手机上推出了疯狂贪吃蛇(Crazy Snake),虽然操作简单,画面简单,但游戏体验早就不简单了,是一款不能错过的游戏。Snake的开发商XIMAD又推出了宝石贪吃蛇游戏,这款名为Snake的游戏将贪吃蛇的画面水平推向了一个新的高度。Snake无论是游戏画面还是游戏可玩度都非一般的贪吃蛇游戏所能比拟的。在这款游戏中,你的贪吃蛇将会在地下收集文物,但是要及时的避开各种障碍物,以通过各种关卡,获取积分。不仅如此,贪吃蛇游戏还发展出了3D贪吃蛇,现在正在开发的GPRS定位通过用户走动控制的贪吃蛇游戏,将会把贪吃蛇推向新的高度。

[7]

2 方案论证

2.1经济可行性分析

本系统开发经费并不是很多。个人或者团队都可以按相应时间完成基本功能。若在基本功能上进而实现界面的美化,则还需要更好的美术人员协助实现。

投入:除软件开发成本外,需要一台安装Windows操作系统和Android虚拟机的中等以上电脑,一部中等配置的装有Android2.3或以上的Android手机。

2.2技术可行性分析

2.2.1Android平台特性

(1) 支持组件的重用与替换。意味着我们可以把系统中不喜欢的应用程序换掉,安装我们自己喜欢的程序。

(2) Dalvik虚拟机专门为移动设备做了优化。将class文件通过DX工具转换成后缀为.dex的文件来执。Dalvik虚拟机基于寄存器,比java虚拟机快。

(3) 内部集成浏览器基于开源的WebKit引擎。

(4) 优化的2D和3D图形库。

(5) SQLite数据库,用于结构化的数据存储。 (6) 支持各种多媒体格式:MPEG4、H.264、MP3、AAC、AMR、JPG、PNG、GIF。 (7) GSM电话—全球通。 (8) 蓝牙、Wifi(无线网络)、EDGE(GSM到3G的过渡)。 (9) 照相机、GPS、指南针和加速度计。

(10) 丰富的开发环境包括设备模拟器,调式工具,内存及性能分析表和Eclipse集成开发环境插件。Google提供了Android开发包SDK,其中包含大量的类库和开发工具。

2.2.2Android应用程序组件

如前所述,Android运行在Linux 内核上。Android应用程序是用Java 编程语言编写的,它们在一个虚拟机(VM)中运行。需要注意的是,这个VM并非您想象中的JVM,而是Dalvik Virtual Machine,这是一种开源技术。每个Android应用程序都在Dalvik VM 的一个实例中运行,这个实例驻留在一个由Linux内核管理的进程中,如下图2-1所示。

如图2-1

Android 应用程序由一个或多个组件组成。

1. 活动(Activity)

具有可视 UI 的应用程序是用活动实现的。当用户从主屏幕或应用程序启动器选择一个应用程序时,就会开始一个动作。

2. 服务(Service)

服务应该用于任何需要持续较长时间的应用程序,例如网络监视器或更新检查应用程序。

3. 内容提供程序(Content Provider) 可以将内容提供程序看作数据库服务器。内容提供程序的任务是管理对持久数据的访问,例如 SQLite数据库。如果应用程序非常简单,那么可能不需要创建内容提供程序。如果要构建一个较大的应用程序,或者构建需要为多个活动或应用程序提供数据的应用程序,那么可以使用内容提供程序实现数据访问。 4. 广播接收器(Broadcast Receiver)

Android 应用程序可用于处理一个数据元素,或者对一个事件(例如接收文本消息)做出响应。

5. 视图(View)

View是Android中图形用户界面的基类,提供了可视化界面展示。Android的图形界面分为三层:底层是Activity;Activity上面是Window; Window上面是Views。View又可以分为View和ViewGroup。View是基本控件,ViewGroup是布局控件。

6. 信使(Intent) Intent是不同组件之间相互导航的纽带,封装了不同组件导航查找的条件。 Android应用程序是连同一个AndroidManifest.xml文件一起部署到设备的。AndroidManifest.xml包含必要的配置信息,以便将它适当地安装到设备。它包括必需的类名和应用程序能够处理的事件类型,以及运行应用程序所需的许可。例如,如果应用程序需要访问网络—例如为了下载一个文件—那么manifest文件中必须显式地列出该许可。很多应用程序可能启用了这个特定的许可。这种声明式安全性有助于减少恶意应用程序损害设备的可能性。

2.4 开发工具及环境简介 2.4.1开发工具Eclipse简介

Eclipse是一个开放源代码的、与NetBeans、Sun ONE Studio

Borland Jbuilder类似的一种基于Java的整合型可扩展开发平台,也是目前最著名的开源项目之一,Eclipse 附带了一个标准的插件集,包括 Java 开发工具(Java Development Tools,JDT)。其未来的目标不仅仅是成为专门开发Java程序的IDE环境,根据Eclipse的体系结构,通过开发插件,它能扩展到任何语言的开发,甚至能成为图片绘制的工具。

目前,Eclipse已经开始提供C语言开发的功能插件。更难能可贵的是,Eclipse是一个开放源代码的项目,任何人都可以下载Eclipse的源代码,并且在此基础上开发自己的功能插件。也就是说未来只要有人需要,就会有在Eclipse

之上COBOL,Perl,Python等语言的开发插件出现。同时可以通过开发新的插件扩展现有插件的功能,可以无限扩展,而且有着统一的外观,操作和系统资源管理,这也正是Eclipse的潜力所在。

Eclipse的主要组成。

Eclipse是一个开放源代码的软件开发项目,专注于为高度集成的工具开发提供一个全功能的、具有商业品质的工业平台。它主要由Eclipse项目、Eclipse工具项目和Eclipse技术项目三个项目组成,具体包括四个部分组成—— Eclipse Platform、JDT、CDT和PDE.JDT支持Java开发、CDT支持C开发、PDE用来支持插件开发,Eclipse Platform则是一个开放的可扩展IDE,提供了一个通用的开发平台。它提供建造块和构造并运行集成软件开发工具的基础。

Eclipse SDK(软件开发者包)是Eclipse Platform、JDT和PDE所生产的组件合并,它们可以一次下载。这些部分在一起提供了一个具有丰富特性的开发环境,允许开发者有效地建造可以无缝集成到Eclipse Platform中的工具。Eclipse SDK由Eclipse项目生产的工具和来自其它开放源代码的第三方软件组合而成。Eclipse项目生产的软件以 CPL发布,第三方组件有各自自身的许可协议。

2.4.2开发环境简介

本项目在windowsXP操作系统上进行开发,Google为开发者提供了 SDK(Software Development Kit)。Android SDK的下载地址为

http://development.Android.com/index.html。因为本项目的开发工具是Eclipse,所以还需要安装ADT(Android Development Tools)插件。具体安装步骤就不在本文中介绍了。

第二章 游戏开发 2.1需求分析

需求分析工作是软件生存周期中重要的一步,也是起决定性的一步。通过它可以全面了解整个系统的功能和性能方面的要求,为软件设计打下坚实的基础。该部分的目标主要有:获得目标系统的物理模型,了解目标系统是如何运行的;抽象出目标系统的逻辑模型,对物理模型进行筛选,得到与软件系统有关的部分;建立目标系统的逻辑模型,得出数据流图和数据字典;补充目标系统的逻辑模型,对目标系统的用户界面,至今尚未考虑的细节进行说明。

本系统主要是完成贪吃蛇游戏的基本操作。用户可以自己练习和娱乐。本系统需要满足以下几点要求。

(1) 利用方向键来改变蛇的运行方向。

(2) 吃到食物就变成新的蛇体,碰到壁或自身则游戏结束,否则正常运行

2.2系统开发目标

采用基于Android的程序设计技术,以及用Eclipse开发Android程序的相关技术开发出具有良好用户界面,操作简单易学,游戏功能齐全且有益于智力开发的贪吃蛇游戏。

3.游戏策划

3.1系统需求分析

3.1.1业务需求分析

虽然现在市面上存在着各种各样的游戏版本,可是贪吃蛇其市场还是相当大的.因为它的特殊在于它能吸引人更深入,爱不释手.随着游戏速度不断加快,其刺激性也更强.可以说该游戏的优势在于它的简单易行,不论是手机,还是小游戏机,都能很快顺利的运行.对于在外忙碌的人,不可能花费大量时间在娱乐上,大型游戏是行不通的,这样的小游戏刚好迎合了他们的需求。

手机贪吃蛇游戏基于JAVA平台编写,采用Android技术开发的一款手机游戏。手机游戏程序是一项精度要求很高的程序系统,因为其代码利用率很高。一个实时运行的最终作品,每秒都会运行成千上万行程序,绘图事件、键盘事件都会以极高的频率在后台等待响应,若有丝毫的差别都将很容易导致程序在运行不久后可能出现严重错误,甚至死循环。因此,其逻辑设计应当相当严谨,需将所有可能发生的事件及意外情况考虑在设计中。JAVA是基于虚拟机的半解释型编译系统,其执行效率较C++等完全编译后的程序会低很多,程序如果不进行精简和优化,将可能导致运行的不流畅。 2.3.2用户需求分析

游戏开发成功后,最终用户为各个年龄段的人群,用户范围相当广泛。但是用户对软件产品也有一定的要求。

1. 简单易学 时间不管对于谁来说都是宝贵的,用户不会愿意花大量时间去学习使用一款软件。一款软件如果操作界面不友好,让用户难以上手使用,那就说明这款软件开发得不够成功,从而失去大量的用户。所以本项目必须拥有良好的交互界面,让用户安装之后就能进行游戏。

2. 运行稳定

一款软件设计得不够好,那么就有可能在运行的时候出现一些不可预料的错误从而造成软件崩溃。如果用户正在游戏突然软件崩溃了,无疑会让用户感到非常不舒服,有可能就把软件给卸载了,这样也会失去很多用户。

3. 操作简便 用户不是程序员,他们不知道程序的内部逻辑。所以程序员必须提供给用户便捷的操作接口来供用户操作,灵活便捷的操作性直接影响了一款软件的好坏。 3.2.3功能需求分析

本项目是一个运行在Android手机上的游戏,所以在应该实现以下功能。 1. 响应键盘事件

玩家可以从手机键盘或者触屏开始游戏,操控游戏。 2. 绘制游戏图形界面 玩家开始游戏后,在手机屏幕上绘制出一条初始由7个节点组成小蛇。蛇能够在屏幕上移动,屏幕上随机位置出现食物,当蛇吃到食物后蛇的身体会增长一

截同时移动速度也会响应加快。蛇不能向相反方向移动,例如蛇头朝南时则不能向北移动。当蛇头碰到墙壁或者碰到自己的身体时游戏结束。

3. 记录玩家分数

当游戏结束后,显示玩家在游戏中所获得的分数。

4游戏设计

4.1程序流程设计

本游戏的流程如下图4-1所示。

N 退出界面 如图4-1

Y 继续N 蛇死亡? Y 蛇吃到食? Y 蛇长大 N 蛇开始运动 游戏者按键选择 放置食物 初始化界面和蛇身 开始 4.2概要设计

4.2.1模块划分

从面向对象程序设计的角度,本项目总体可以分为游戏界面模块、数据存储模块和控制模块。

如下图4-2所示。

如图4-2

4.2.2游戏主界面模块

游戏界面主框架主要包括游戏图形区域界面、游戏的开始按钮、暂停按钮、游戏的退出按钮。

游戏界面主框架的主要结构图如图4-3所示。

如图4-3

4.2.3游戏控制模块

游戏控制模块主要通过手机键盘上的按钮来控制游戏的开始、游戏的退出这两个功能以及控制游戏分数变化。在这个模块中,需要给各个按钮添加响应事件代码,来对上述的功能加以进一步的实现。游戏控制模块的主要框架如图4-4所示。

如图4-4

4.3 模块实现原理

4.3.1游戏界面模块实现

1. 实现游戏背景

本游戏采用Activity作为游戏背景的载体,在Android中一个Activity就相当于windows中的一个窗口,Activity上可以放置许多类型的控件。一个activity主要有三个状态。

当在屏幕前台时(位于当前任务堆栈的顶部),它是活跃或运行的状态。它就是相应用户操作的activity。

当它失去焦点但仍然对用户可见时,它处于暂停状态。即是:在它之上有另外一个activity。这个activity也许是透明的,或者未能完全遮蔽全屏,所以被暂停的activity仍对用户可见。暂停的activity仍然是存活状态(它保留着所有的状态和成员信息并连接至窗口管理器),但当系统处于极低内存的情况下,仍然可以杀死这个activity。如果它完全被另一个activity覆盖是,它处于停止状态。它仍然保留所有的状态和成员信息。然而它不在为用户可见,所以它的窗口将被隐藏,如果其它地方需要内存,则系统经常会杀死这个activity。

如果一个activity处于暂停或停止状态,系统可以通过要求它结束(调用它的 finish() 方法)或直接杀死它的进程来将它驱出内存。当它再次为用户可见的时候,它只能完全重新启动并恢复至以前的状态。

当一个activity从这个状态转变到另一个状态时,它被以下列protected方法所通知。

void onCreate(Bundle savedInstanceState) 、void onStart() 、void onRestart() 、void onResume() 、void onPause() 、

void onStop() 、void onDestroy()

2. 实现蛇的身体

蛇可以看做是一个个节点组成的,因此可以用一个链表来存储蛇身的元素,在画蛇时遍历这个链表讲里面的元素一一画出,这样就实现了一条蛇。

3. 实现蛇的移动

用一个timer(定时器)来不断地刷新游戏画面,每刷新一次就再蛇头的前面(链表的尾部)增加一个新元素,同时把蛇尾的一个元素删掉,这样从视觉上看起来就实现了蛇的移动。

4. 实现蛇吃食物

蛇移动的过程中如果蛇头的坐标与食物出现的坐标重合了,那么就在蛇头的位置增加一个元素同时不删除蛇尾的最后一个元素,这样蛇每迟到一个食物 身体就会变长一截。

4.3.2游戏控制模块实现

1. 实现操作蛇的移动方向

在Android系统中,手机上的每个按钮都会有一个对应的键值跟它对应,所在可以给对应的按钮设置监听器OnClickListener,监听器是一个接口,该接口中有一个方法onClick(View v)。当按钮被点击的时候系统会自动调用该监听器的onClick(View v)方法。所以实现游戏控制的具体代码将被写到该方法中。

因为本游戏中不允许蛇向与蛇头相反的方向移动,所以当用户操作时需要判断用户操作的方向是不是跟规则冲突,若冲突则无视该操作,若不冲突则响应该操作,所以需要用一个变量来记录蛇头的当前方向。

2. 实现游戏暂停

在Activity的生命周期中,有一个onPause()方法.该方法在Activity变得不可见的时候被系统自动调用.在玩游戏过程中,如果有来电或是其它事件中断,这时应该把当前状态保存。以便返回时,还可以继续玩游戏。这就使用onSaveInstanceState实现保存当前状态。 3. 实现游戏恢复

Activity生命周期的onResume()方法.该方法在Activity从不可见的状态下变得

可见时被系统自动调用.在用户接完电话或者在暂停状态下触摸屏幕后可以在该生命周期方法中对游戏进行恢复。

4. 实现游戏退出

当一个Activity退出或者被调用finish()方法后,系统会调用其生命周期方法onDestroy().当用户退出游戏时,可以在这个方法中对资源进行释放。 4.3.3TileView类的设计

TileView是游戏的界限,即蛇头若触碰到界限则游戏结束。因为界限也需要被现实到屏幕上,所以TileView需要继承Android.view.View类。

Android.view.View类是描绘块状视图的基类。View会绘制一个包含Drawing

是event事件的方形块。View是所有与用户交互的组件的Widgets的基类(Buttons,textField等),View的子类ViewGroup是layouts类的基类,layouts类可以包含其他的View/ViewGroup组件并且定义展示的属性。

实现一个View,首先需要实现框架中一些所有Views公用的方法。不必重写所有所有的方法,可以仅仅重写onDraw(Android.graphics.Canvas)。 4.3.4 SnakeView类的设计

SnakeView是本游戏的业务逻辑类,该类中包含了游戏数据和一些处理数据的方法以及一些内部类。SnakeView的方法摘要。

1. 判断按键的方法 在Android手机上,每个按键都会有一个唯一的键值与它对应,可以通过获得键值来判断哪个键被按下了并采取相应的动作。

2. 设置提示信息的方法

通过程序判断动态地设置用户提示信息,如游戏结束。 3. 在随机位置出现食物的方法

通过随机数在屏幕范围内随机出现一个食物,但是不允许同一时刻有两个食物存在。

4. 刷新蛇的当前位置的方法

主要用于刷新蛇的当前位置。 5. 判断蛇是否吃到食物的方法 因为食物和蛇都会有一个坐标,所以可以通过判断蛇头坐标是否跟食物坐标相等的方法来判断蛇是否吃到了食物。

4.4 程序主结构

本次所设计贪吃蛇游戏的程序主结构如图4-5所示。

如图4-5

4.5 程序类图

该程序由以下三个类实现,分别为TileView,Snake,SnakeView。各个类之间的关系及所包括的方法如图4-6所示。

4.6 详细代码

4.6.1 Snake类的详细设计

Snake类是蛇头以及蛇头的控制部分,包括判断newDirection和

oldDirection是否为相反方向,用于选取有效方向。判断蛇头是否和蛇身的某个节点的坐标重合,即蛇是否吃到自己。实现蛇身沿着蛇头的轨迹移动。根据方向键,改变蛇头的x、y的值,即改变方向。以及让蛇不停走动。

Snake.java

public class TileView extends View {

/** * Parameters controlling the size of the tiles and their range within view. * Width/Height are in pixels, and Drawables will be scaled to fit to these * dimensions. X/Y Tile Counts are the number of tiles that will be drawn. */ protected static int mTileSize; // ��ͼtile�Ĵ�С����ʵ���ǵ�Ŀ�͸ߣ���һ���͸ֵ�� protected static int mXTileCount;// ��ͼ��x��͸͸����ɵ�tile���������������� protected static int mYTileCount;

private static int mXOffset;// ��ͼ����ʼ���� private static int mYOffset;

/** * A hash that maps integer handles specified by the subclasser to the * drawable that will be used for that reference */ private Bitmap[] mTileArray;

// ��ͼ��tile��Ӧ��ͼƬ���顣ÿһ��tile����Ӧһ��bitmap������mTileArray[1]���Dzص͸�bitmap���������ơ� /** * A two-dimensional array of integers in which the number represents the * index of the tile that should be drawn at that locations */ private int[][] mTileGrid; // ��ͼ�ϵ�tile�����顣����int[1][1]=0˵��������Dzص͸�int[1][2]=1˵���������ƻ�� // ��ʵ˼�������ô�͸���ʽ�����и�͸ֵ��� private final Paint mPaint = new Paint();// ���ʡ���ͼ��Ҫ��������Ӧ�ú�������⡣��͸ֵʡ���ɫ����ɫ�� public TileView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);

// �����õ���TypeArray�������͸ЬҪȥgoogle�¡���googleŪ������һ����ʽ���飬��ʵ������һ���͸������Եļ��ϡ� TypedArray a = context.obtainStyledAttributes(attrs,

R.styleable.TileView);

mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);

a.recycle(); }

public TileView(Context context, AttributeSet attrs) { super(context, attrs);

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);

mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);

a.recycle(); }

/** * Rests the internal array of Bitmaps used for drawing tiles, and sets the * maximum index of tiles to be inserted * * @param tilecount */ public void resetTiles(int tilecount) { mTileArray = new Bitmap[tilecount]; } // ������Ϊ����������DZȽ�����˼�ġ������view��һ���ص��������ʼ��ʼ����ʱ��view�Ĵ�С����0��������layoutֵ��ÿ��view��ȷ���˴�С�������Ϳ�ʼ�ص���������� @Override

protected void onSizeChanged(int w, int h, int oldw, int oldh) { mXTileCount = (int) Math.floor(w / mTileSize);

mYTileCount = (int) Math.floor(h / mTileSize);

mXOffset = ((w - (mTileSize * mXTileCount)) / 2); mYOffset = ((h - (mTileSize * mYTileCount)) / 2);

mTileGrid = new int[mXTileCount][mYTileCount]; clearTiles(); }

/** * Function to set the specified Drawable as the tile for a particular * integer key. * * @param key * @param tile * �͸�����Ǹ���Key����tile���͸�����ص�ͼtile��ͼƬ��������һ��drawable��Ҫ���bitmap�� */ public void loadTile(int key, Drawable tile) {

Bitmap bitmap = Bitmap.createBitmap(mTileSize, mTileSize, Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(bitmap);

tile.setBounds(0, 0, mTileSize, mTileSize); tile.draw(canvas);

mTileArray[key] = bitmap; }

/** * Resets all tiles to 0 (empty) * */ public void clearTiles() {

for (int x = 0; x < mXTileCount; x++) { for (int y = 0; y < mYTileCount; y++) { setTile(0, x, y); }

} }

/** * Used to indicate that a particular tile (set with loadTile and referenced * by an integer) should be drawn at the given x/y coordinates during the * next invalidate/draw cycle. * * @param tileindex * @param x * @param y * ��͸�������ÿһ��tile����ͼ�ϵĵ㣩��Ӧ������һ��ͼƬ�������tileindex���Ǵ�����mTileArray[]�е�index */ public void setTile(int tileindex, int x, int y) { mTileGrid[x][y] = tileindex; }

// ����������ǻ�����ͼ�ˡ�������ͼ�ĵ㣬Ȼ���ÿ��tile�����궼���������Ȼ��һ������tile��draw��canvas�Ϲ� @Override

public void onDraw(Canvas canvas) { super.onDraw(canvas);

for (int x = 0; x < mXTileCount; x += 1) { for (int y = 0; y < mYTileCount; y += 1) { if (mTileGrid[x][y] > 0) {

canvas.drawBitmap(mTileArray[mTileGrid[x][y]], mXOffset + x * mTileSize, mYOffset + y * mTileSize, mPaint); }

} } } }

4.6.2 TileView类的详细设计

TileView是游戏的界限,即蛇头若触碰到界限则游戏结束。因为界限也需要被现实到屏幕上,所以TileView需要继承Android.view.View类。

Android.view.View类是描绘块状视图的基类。View会绘制一个包含Drawing是event事件的方形块。View是所有与用户交互的组件的Widgets的基类(Buttons,textField等),View的子类ViewGroup是layouts类的基类,layouts类可以包含其他的View/ViewGroup组件并且定义展示的属性。

实现一个View,首先需要实现框架中一些所有Views公用的方法。不必重写所有所有的方法,可以仅仅重写。 TileView.Java

public class TileView extends View {

/** * Parameters controlling the size of the tiles and their range within view. * Width/Height are in pixels, and Drawables will be scaled to fit to these * dimensions. X/Y Tile Counts are the number of tiles that will be drawn. */ protected static int mTileSize; // ��ͼtile�Ĵ�С����ʵ���ǵ�Ŀ�͸ߣ���һ���͸ֵ�� protected static int mXTileCount;// ��ͼ��x��͸͸����ɵ�tile���������������� protected static int mYTileCount;

private static int mXOffset;// ��ͼ����ʼ���� private static int mYOffset;

/** * A hash that maps integer handles specified by the subclasser to the * drawable that will be used for that reference */ private Bitmap[] mTileArray;

// ��ͼ��tile��Ӧ��ͼƬ���顣ÿһ��tile����Ӧһ��bitmap������mTileArray[1]���Dzص͸�bitmap���������ơ� /** * A two-dimensional array of integers in which the number represents the * index of the tile that should be drawn at that locations */ private int[][] mTileGrid; // ��ͼ�ϵ�tile�����顣����int[1][1]=0˵��������Dzص͸�int[1][2]=1˵���������ƻ�� // ��ʵ˼�������ô�͸���ʽ�����и�͸ֵ��� private final Paint mPaint = new Paint();// ���ʡ���ͼ��Ҫ��������Ӧ�ú�������⡣��͸ֵʡ���ɫ����ɫ�� public TileView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);

// �����õ���TypeArray�������͸ЬҪȥgoogle�¡���googleŪ������һ����ʽ���飬��ʵ������һ���͸������Եļ��ϡ� TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);

mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);

a.recycle(); }

public TileView(Context context, AttributeSet attrs) { super(context, attrs);

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);

mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);

a.recycle(); }

/** * Rests the internal array of Bitmaps used for drawing tiles, and sets the * maximum index of tiles to be inserted * * @param tilecount */ public void resetTiles(int tilecount) { mTileArray = new Bitmap[tilecount]; }

// ������Ϊ����������DZȽ�����˼�ġ������view��һ���ص��������ʼ��ʼ����ʱ��view�Ĵ�С����0��������layoutֵ��ÿ��view��ȷ���˴�С�������Ϳ�ʼ�ص���������� @Override

protected void onSizeChanged(int w, int h, int oldw, int oldh) { mXTileCount = (int) Math.floor(w / mTileSize); mYTileCount = (int) Math.floor(h / mTileSize);

mXOffset = ((w - (mTileSize * mXTileCount)) / 2); mYOffset = ((h - (mTileSize * mYTileCount)) / 2);

mTileGrid = new int[mXTileCount][mYTileCount]; clearTiles(); }

/** * Function to set the specified Drawable as the tile for a particular * integer key. * * @param key * @param tile * �͸�����Ǹ���Key����tile���͸�����ص�ͼtile��ͼƬ��������һ��drawable��Ҫ���bitmap�� */ public void loadTile(int key, Drawable tile) {

Bitmap bitmap = Bitmap.createBitmap(mTileSize, mTileSize, Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(bitmap);

tile.setBounds(0, 0, mTileSize, mTileSize); tile.draw(canvas);

mTileArray[key] = bitmap; } /** * Resets all tiles to 0 (empty) * */ public void clearTiles() {

for (int x = 0; x < mXTileCount; x++) { for (int y = 0; y < mYTileCount; y++) { setTile(0, x, y); } } }

/** * Used to indicate that a particular tile (set with loadTile and referenced * by an integer) should be drawn at the given x/y coordinates during the * next invalidate/draw cycle. * * @param tileindex * @param x * @param y * ��͸�������ÿһ��tile����ͼ�ϵĵ㣩��Ӧ������һ��ͼƬ�������tileindex���Ǵ�����mTileArray[]�е�index */ public void setTile(int tileindex, int x, int y) {

mTileGrid[x][y] = tileindex; }

// ����������ǻ�����ͼ�ˡ�������ͼ�ĵ㣬Ȼ���ÿ��tile�����궼���������Ȼ��һ������tile��draw��canvas�Ϲ� @Override

public void onDraw(Canvas canvas) {

super.onDraw(canvas);

for (int x = 0; x < mXTileCount; x += 1) { for (int y = 0; y < mYTileCount; y += 1) {

if (mTileGrid[x][y] > 0) {

canvas.drawBitmap(mTileArray[mTileGrid[x][y]], mXOffset + x * mTileSize, mYOffset + y * mTileSize, mPaint); } } }

} }

4.6.3 SnakeView类的详细设计

SnakeView是本游戏的业务逻辑类,该类中包含了游戏数据和一些处理数据的方法以及一些内部类。

SnakeView的方法摘要。 1. 判断按键的方法 在Android手机上,每个按键都会有一个唯一的键值与它对应,可以通过获得键值来判断哪个键被按下了并采取相应的动作。

2. 设置提示信息的方法

通过程序判断动态地设置用户提示信息,如游戏结束。 3. 在随机位置出现食物的方法

通过随机数在屏幕范围内随机出现一个食物,但是不允许同一时刻有两个食物存在。

4. 刷新蛇的当前位置的方法主要用于刷新蛇的当前位置。 5. 判断蛇是否吃到食物的方法

因为食物和蛇都会有一个坐标,所以可以通过判断蛇头坐标是否跟食物坐标相等的方法来判断蛇是否吃到了食物。 SnakeView.java

public class SnakeView extends TileView implements OnClickListener {

private static final String TAG = \"SnakeView\";

/** * Current mode of application: READY to run, RUNNING, or you have already * lost. static final ints are used instead of an enum for performance * reasons. */ private int mMode = READY;// �������Ϸ��5��״�� public static final int PAUSE = 0; public static final int READY = 1; public static final int RUNNING = 2; public static final int LOSE = 3; /** * Current direction the snake is headed. */ private int mDirection = NORTH; // �ߵ���͸ֵ������һ��ǰ���ķ��� private int mNextDirection = NORTH; private static final int NORTH = 1; private static final int SOUTH = 2; private static final int EAST = 3; private static final int WEST = 4;

/** * Labels for the drawables that will be loaded into the TileView class */ private static final int RED_STAR = 1;// ��������ǩ͸ֵ�����ʾ�͸��tile��drawable������RED_STAR��������ߵ����ӵĵ㣨tile�� private static final int YELLOW_STAR = 2;

private static final int GREEN_STAR = 3;

/** * mScore: used to track the number of apples captured mMoveDelay: number of * milliseconds between snake movements. This will decrease as apples are * captured. */ private long mScore = 0; // �ɼ������˶��ص�ƻ�� private long mMoveDelay = 600;// ������ص�������ƶ�һ�� /** * mLastMove: tracks the absolute time when the snake last moved, and is * used to determine if a move should be made based on mMoveDelay. */ private long mLastMove; // ��һ���ƶ���ʱ�� /** * mStatusText: text shows to the user in some run states */ private TextView mStatusText;// ����ǿ�ʼ��ʱ�����ʾ�� /** * mSnakeTrail: a list of Coordinates that make up the snake's body * mAppleList: the secret location of the juicy apples the snake craves. */ private ArrayList mSnakeTrail = new ArrayList();// �ߵ����У��㣩tile���������� private ArrayList mAppleList = new ArrayList();// ƻ�������У��㣩tile���������� /** * Everyone needs a little randomness in their life */ private static final Random RNG = new Random();// ����� /** * Create a simple handler that we can use to cause animation to happen. We * set ourselves as a target and we can use the sleep() function to cause an * update/invalidate to occur at a later date. */ private RefreshHandler mRedrawHandler = new RefreshHandler(); // �ص�˵���͸͸ɡ���ʵ������������ĵص�����������˵�ġ����������ˡ����ˢ�µ�handler�������������Handler�͸Ь��ǿ�ҽ����Դ�롣�� // �����Handler���Լ�������Ϣ�������ӳ�30����Լ���һ����Ϣ��������ʵ����һ��ѭ����͸͸������뷨�� private Button mStart; // ����������ť͸ֵ����Ҽӵġ���ΪͿ���͸ֵ����Ӧ�������Ҽ��IJ�������������㴥���IJ���Ч���������ص����ļ��п���صIJ�͸ֵ� private Button mLeft;

private Button mRight;

private Button mTop;

private Button mBottom;

// ����������������ĵص��ˡ�������sleep��Ȼ���ӳ�delayMillis�͸ֵ�͸����Լ�һ����Ϣ��Ȼ�������Ϣ��handleMessage�б������ˡ� // ����Ĺ�͸͸�������update������update����͸ֵ�����sleep����������һ��������ѭ���Ϳ�ʼ�ˡ� class RefreshHandler extends Handler {

@Override

public void handleMessage(Message msg) { SnakeView.this.update(); SnakeView.this.invalidate(); }

public void sleep(long delayMillis) { this.removeMessages(0);

sendMessageDelayed(obtainMessage(0), delayMillis); } }; /** * Constructs a SnakeView based on inflation from XML * * @param context * @param attrs */ public SnakeView(Context context, AttributeSet attrs) {

super(context, attrs); initSnakeView(); }

public SnakeView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle); initSnakeView(); }

private void initSnakeView() { setFocusable(true);

Resources r = this.getContext().getResources();

// ������tile������������ resetTiles(4);

loadTile(RED_STAR, r.getDrawable(R.drawable.redstar));

loadTile(YELLOW_STAR, r.getDrawable(R.drawable.yellowstar)); loadTile(GREEN_STAR, r.getDrawable(R.drawable.greenstar)); }

private void initNewGame() { mSnakeTrail.clear(); mAppleList.clear();

// For now we're just going to load up a short default eastbound snake // that's just turned north // ��ʼ���ߵ����� mSnakeTrail.add(new Coordinate(7, 30)); mSnakeTrail.add(new Coordinate(6, 30)); mSnakeTrail.add(new Coordinate(5, 30)); mSnakeTrail.add(new Coordinate(4, 30)); mSnakeTrail.add(new Coordinate(3, 30)); mSnakeTrail.add(new Coordinate(2, 30)); // ���ƶ��ķ��� mNextDirection = NORTH;

// Two apples to start with // �������������ƻ�� addRandomApple(); addRandomApple();

mMoveDelay = 100; mScore = 0; }

/** * Given a ArrayList of coordinates, we need to flatten them into an array * of ints before we can stuff them into a map for flattening and storage. * * @param cvec * : a ArrayList of Coordinate objects * @return : a simple array containing the x/y values of the coordinates as * [x1,y1,x2,y2,x3,y3...] ����һ�����������ת����һά����ĺ���������һ���������෴�ġ� */ private int[] coordArrayListToArray(ArrayList cvec) { int count = cvec.size();

int[] rawArray = new int[count * 2];

for (int index = 0; index < count; index++) { Coordinate c = cvec.get(index); rawArray[2 * index] = c.x;

rawArray[2 * index + 1] = c.y; }

return rawArray; }

/** * Save game state so that the user does not lose anything if the game * process is killed while we are in the background. * * @return a Bundle with this view's state */ public Bundle saveState() {

Bundle map = new Bundle();

map.putIntArray(\"mAppleList\", coordArrayListToArray(mAppleList));

map.putInt(\"mDirection\", Integer.valueOf(mDirection));

map.putInt(\"mNextDirection\", Integer.valueOf(mNextDirection)); map.putLong(\"mMoveDelay\", Long.valueOf(mMoveDelay)); map.putLong(\"mScore\", Long.valueOf(mScore)); map.putIntArray(\"mSnakeTrail\", coordArrayListToArray(mSnakeTrail));

return map; }

/** * Given a flattened array of ordinate pairs, we reconstitute them into a * ArrayList of Coordinate objects * * @param rawArray * : [x1,y1,x2,y2,...] * @return a ArrayList of Coordinates */ private ArrayList coordArrayToArrayList(int[] rawArray) {

ArrayList coordArrayList = new ArrayList();

int coordCount = rawArray.length;

for (int index = 0; index < coordCount; index += 2) {

Coordinate c = new Coordinate(rawArray[index], rawArray[index + 1]);

coordArrayList.add(c); }

return coordArrayList; }

/** * Restore game state if our process is being relaunched * * @param icicle * a Bundle containing the game state * ������Ϸ�����ݡ�������Ϸ�С�����home�г�ȥ�ˡ������Ϳ��Ա�����Ϸ�����ݡ��л���ʱ��͸͸����� */ public void restoreState(Bundle icicle) { setMode(PAUSE);

mAppleList =

coordArrayToArrayList(icicle.getIntArray(\"mAppleList\")); mDirection = icicle.getInt(\"mDirection\");

mNextDirection = icicle.getInt(\"mNextDirection\"); mMoveDelay = icicle.getLong(\"mMoveDelay\"); mScore = icicle.getLong(\"mScore\"); mSnakeTrail =

coordArrayToArrayList(icicle.getIntArray(\"mSnakeTrail\")); }

/* * handles key events in the game. Update the direction our snake is * traveling based on the DPAD. Ignore events that would cause the snake to * immediately turn back on itself. * * (non-Javadoc) * * @see android.view.View#onKeyDown(int, android.os.KeyEvent) */ @Override

public boolean onKeyDown(int keyCode, KeyEvent msg) {

if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { if (mMode == READY | mMode == LOSE) { /* * At the beginning of the game, or the end of a previous one, * we should start a new game. */ initNewGame(); setMode(RUNNING); update();

return (true); }

if (mMode == PAUSE) { /* * If the game is merely paused, we should just continue where * we left off. */ setMode(RUNNING); update();

return (true); }

if (mDirection != SOUTH) { mNextDirection = NORTH; }

return (true); }

if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { if (mDirection != NORTH) {

mNextDirection = SOUTH; }

return (true); }

if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { if (mDirection != EAST) { mNextDirection = WEST; }

return (true); }

if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { if (mDirection != WEST) { mNextDirection = EAST; }

return (true); }

return super.onKeyDown(keyCode, msg); }

/** * Sets the TextView that will be used to give information (such as \"Game * Over\" to the user. * * @param newView */ public void setTextView(TextView newView) { mStatusText = newView; }

public void setStartButton(Button button) { mStart = button;

mStart.setOnClickListener(this); } /** * Updates the current mode of the application (RUNNING or PAUSED or the * like) as well as sets the visibility of textview for notification * * @param newMode * ������ǰ����Ϸ״ */ public void setMode(int newMode) {

int oldMode = mMode; mMode = newMode;

if (newMode == RUNNING & oldMode != RUNNING) { mStatusText.setVisibility(View.INVISIBLE); update(); return;

}

Resources res = getContext().getResources(); CharSequence str = \"\"; if (newMode == PAUSE) {

str = res.getText(R.string.mode_pause); }

if (newMode == READY) {

str = res.getText(R.string.mode_ready);

}

if (newMode == LOSE) {

str = res.getString(R.string.mode_lose_prefix) + mScore + res.getString(R.string.mode_lose_suffix); }

mStatusText.setText(str);

mStatusText.setVisibility(View.VISIBLE); mStart.setVisibility(View.VISIBLE);

mLeft.setVisibility(View.INVISIBLE); mRight.setVisibility(View.INVISIBLE); mTop.setVisibility(View.INVISIBLE); mBottom.setVisibility(View.INVISIBLE); }

/** * Selects a random location within the garden that is not currently covered * by the snake. Currently _could_ go into an infinite loop if the snake * currently fills the garden, but we'll leave discovery of this prize to a * truly excellent snake-player. * */ private void addRandomApple() { Coordinate newCoord = null; boolean found = false; while (!found) {

// Choose a new location for our apple int newX = 1 + RNG.nextInt(mXTileCount - 2); int newY = 1 + RNG.nextInt(mYTileCount - 2); newCoord = new Coordinate(newX, newY);

// Make sure it's not already under the snake boolean collision = false;

int snakelength = mSnakeTrail.size();

for (int index = 0; index < snakelength; index++) {

if (mSnakeTrail.get(index).equals(newCoord)) { collision = true; } }

// if we're here and there's been no collision, then we have // a good location for an apple. Otherwise, we'll circle back // and try again found = !collision; }

if (newCoord == null) {

Log.e(TAG, \"Somehow ended up with a null newCoord!\"); }

mAppleList.add(newCoord); }

/** * Handles the basic update loop, checking to see if we are in the running * state, determining if a move should be made, updating the snake's * location. */ public void update() {

if (mMode == RUNNING) {

long now = System.currentTimeMillis();

if (now - mLastMove > mMoveDelay) { clearTiles(); updateWalls(); updateSnake();

updateApples(); mLastMove = now; }

mRedrawHandler.sleep(mMoveDelay); } }

/** * Draws some walls. ������͸͸�ǽ */ private void updateWalls() {

for (int x = 0; x < mXTileCount; x++) { setTile(GREEN_STAR, x, 0);

setTile(GREEN_STAR, x, mYTileCount - 1); }

for (int y = 1; y < mYTileCount - 1; y++) { setTile(GREEN_STAR, 0, y);

setTile(GREEN_STAR, mXTileCount - 1, y); } } /** * Draws some apples. ����ƻ�� */ private void updateApples() {

for (Coordinate c : mAppleList) { setTile(YELLOW_STAR, c.x, c.y); } }

/** * Figure out which way the snake is going, see if he's run into anything * (the walls, himself, or an apple). If he's not going to die, we then add * to the front and subtract from the rear in order to simulate motion. If * we want to grow him, we don't subtract from the rear. �����ߡ���ʵ���Dz������ƶ���Ч���� */ private void updateSnake() { boolean growSnake = false;

// grab the snake by the head Coordinate head = mSnakeTrail.get(0);

Coordinate newHead = new Coordinate(1, 1);

mDirection = mNextDirection;

switch (mDirection) { case EAST: {

newHead = new Coordinate(head.x + 1, head.y); break; }

case WEST: {

newHead = new Coordinate(head.x - 1, head.y); break;

}

case NORTH: {

newHead = new Coordinate(head.x, head.y - 1); break; }

case SOUTH: {

newHead = new Coordinate(head.x, head.y + 1); break; }

}

// Collision detection // For now we have a 1-square wall around the entire arena if ((newHead.x < 1) || (newHead.y < 1) || (newHead.x > mXTileCount - 2) || (newHead.y > mYTileCount - 2)) { setMode(LOSE); return;

}

// Look for collisions with itself int snakelength = mSnakeTrail.size();

for (int snakeindex = 0; snakeindex < snakelength; snakeindex++) {

Coordinate c = mSnakeTrail.get(snakeindex); if (c.equals(newHead)) { setMode(LOSE);

return;

} }

// Look for apples int applecount = mAppleList.size();

for (int appleindex = 0; appleindex < applecount; appleindex++) { Coordinate c = mAppleList.get(appleindex); if (c.equals(newHead)) { mAppleList.remove(c); addRandomApple();

mScore++;

mMoveDelay *= 0.9;

growSnake = true; } }

// push a new head onto the ArrayList and pull off the tail mSnakeTrail.add(0, newHead);

// except if we want the snake to grow if (!growSnake) {

mSnakeTrail.remove(mSnakeTrail.size() - 1); }

int index = 0;

for (Coordinate c : mSnakeTrail) { if (index == 0) {

setTile(YELLOW_STAR, c.x, c.y); } else {

setTile(RED_STAR, c.x, c.y); }

index++; } }

/** * Simple class containing two integer values and a comparison function. * There's probably something I should use instead, but this was quick and * easy to build. ������� */ private class Coordinate {

public int x; public int y;

public Coordinate(int newX, int newY) { x = newX; y = newY; }

public boolean equals(Coordinate other) { if (x == other.x && y == other.y) { return true; }

return false;

}

@Override

public String toString() {

return \"Coordinate: [\" + x + \+ y + \"]\"; } }

// �����Ҽӵġ���һ���͸���Ӧ�������ҵĵ�������Դ�������Ϸ�� public void onClick(View v) {

switch (v.getId()) { case R.id.start:

if (mMode == READY | mMode == LOSE) { initNewGame(); setMode(RUNNING); update();

mStart.setVisibility(View.GONE); mLeft.setVisibility(View.VISIBLE); mRight.setVisibility(View.VISIBLE); mTop.setVisibility(View.VISIBLE); mBottom.setVisibility(View.VISIBLE); }

if (mMode == PAUSE) { setMode(RUNNING); update();

mStart.setVisibility(View.GONE); mLeft.setVisibility(View.VISIBLE); mRight.setVisibility(View.VISIBLE); mTop.setVisibility(View.VISIBLE); mBottom.setVisibility(View.VISIBLE); }

break;

case R.id.left:

if (mDirection != EAST) { mNextDirection = WEST; }

break;

case R.id.right:

if (mDirection != WEST) { mNextDirection = EAST; }

break;

case R.id.top:

if (mDirection != SOUTH) { mNextDirection = NORTH; }

break;

case R.id.bottom:

if (mDirection != NORTH) { mNextDirection = SOUTH; } break; default: break;

} }

// ���÷���� public void setControlButton(Button left, Button right, Button top, Button bottom) { mLeft = left; mRight = right; mTop = top;

mBottom = bottom;

mLeft.setOnClickListener(this); mRight.setOnClickListener(this); mTop.setOnClickListener(this); mBottom.setOnClickListener(this); } }

5.总结游戏报告

在本游戏的编写过程中,我遇到了许多问题,首先要自己先自学Android的游戏编程,虽然和java的思想差不多,但是Android上有许多控件和类在java上没有的,在这方面我遇到了很大的难题。在遇到问题时要学会如何去分析错误,再要学会如何去解决问题。在遇到问题时,不要有害怕的情绪,解决问题也是一个不断进步的过程,遇到的问题越多,学到的东西也就越多。因此在遇到困难时,不要轻言放弃,要努力向前解决问题。在2011年11月份开始了我的毕业论文工作,认真总结,经过长时间的学做到现在论文基本完成。写毕业论文不是一件容易的事情,需要不断的进行精心的修改,不断的研究各方面的文献。经过很久的努力,终于在6月份完成了毕业论文,在这次毕业论文的写作过程中,我拥有了无数难忘的感动和收获。后来,在与导师交流讨论中我的题目定了下来,我便立刻在学校的图书馆和网上找了一些着手资料的收集工作中,当时面对众多网络资料库的文章真的有些不知所措,不知如何下手。我将这一困难告诉了指导老师。在老师细心的指导下,终于使我了解了应该怎么样利用学校图书馆的资料找到自己需要的Android方面的资料。认真阅读总结笔记,为自己的论文打好基础。写毕业论文是我们每个大学生必须经历的一段过程,也是我们毕业前的一段宝贵的回忆。当我们看到自己的努力有收获的时候,总是会有那么一点点的自豪和激动,任何事情都是这样的,需要我们脚踏实地的去做,一步一个脚印的完成,认真严谨,有了好的态度才能做好一件事情,一开始都觉得毕业论文的一个很困难的任务,大家都难免有一点畏惧之情,但是经过长时间的努力和积累,经过不断地查找资料和总结,我们很好的按老师要求完成了毕业论文,这种收获的喜悦相信每

个人都能够体会到。这是一个毅力的磨练,是对我实际能力的一个提升,相信对我未来的学习和工作都有很大的帮助。

每次在遇到困难的时候,我也得到了同学的帮助,共同商量相关专业问题,这种交流对于即将面临毕业的我们来说是一次很有意义的经历,大学四年都一起走过,在最后我们可以聚在一起讨论学习,研究专业问题,进而更好地了解我们每个人的兴趣之所在,明确我们的人生理想,进而在今后的生活和工作中更好地发挥自己的优势,学好自己选择的这项专业技术,让自己在这条路上的明天会更好。

因篇幅问题不能全部显示,请点此查看更多更全内容