61阅读

游戏设计与开发-基于Android的飞机大战游戏设计与开发

发布时间:2017-07-30 所属栏目:i wanna go back home

一 : 基于Android的飞机大战游戏设计与开发

直升机游戏 基于Android的飞机大战游戏设计与开发

LUOYANG NORMAL UNIVERSITY

基于Android的飞机大战游戏设计与开发

院(系)名称

专 业 名 称

指导教生姓名 号 师 信息技术学院 软件工程 XXX副教授

2013年5月 完 成 时 间

直升机游戏 基于Android的飞机大战游戏设计与开发

基于Android的飞机大战游戏设计与开发

摘要

相信Android(安卓)已经为人们熟知,经过2011年的洗礼,Android智能手机火速上位,甚至说现在手机系统由IOS和Android平分也不为过。随着Android智能手机在中国内地的风靡,基于Android平台的应用开发也逐渐成为IT开发的一大热门。游戏是智能机不可或缺的应用之一,“水果忍者”、“愤怒的小鸟”等Android游戏应用的成功,让人看到手机游戏在Android平台上的巨大发展空间。基于此,采用Eclipse和Android ADT作为集成开发平台,开发本Android游戏APP。

本应用为Android飞机大战游戏,主要有6个操作界面分别为开始界面,游戏界面,设置界面,得分界面,Win界面,Lose界面。玩家可以选择自己进入设置界面对游戏进行设置,或进入的分界面查看自己本次操作是否在前六名等操作。作为游戏背景音乐的存在是不可或缺的,本应用在不同的操作界面演奏不同的背景音乐,玩家还可以在设置界面设置背景音乐的大小。由于Android控件Activity的生命周期的特点,本应用会在被点击“EXIT”按钮的Activity中发送一个EXIT的广播,所有的本应用的Activity收到广播后会自动结束,使得本应用能够完全的退出系统。本应用采用Android的SurfaceView绘制游戏界面,游戏的主界面美观,赏心悦目,以提高玩家对游戏的兴趣。游戏的控制模块应该做到易懂、易操作,以给玩家一个很好的游戏环境。

关键词:Android开发;手机游戏;Java;SQLite

I

洛阳师范学院2013届本科生毕业设计

Abstract

Android has been known for people, after 2011 years of baptism, Android smartphone developing fast . Android occupies a large share in the market . Android game application is successful, it make a person see that mobile games on the Android platform of the huge development space. Based on this,we using Eclipse and Android ADT as integrated development platform, to develop the Android game application.

This application for Android plane war games include six operating interface, respectively as the start screen, the game interface and set interface, Win interface, Lose interface. Players can choose interface to play the game. As the mobile phone game background music is indispensable, the application play different background music in different interface, players can also set the size of the background music in setting interface.

This application using the Android SurfaceView to draw game interface, game interface pleasing to the eye by people to play. I n order to give players a good game environment , the control module of game should be simple, easy to operate.

Keywords : Android Programing; Mobile Phone Games ; Java Programing ;SQLite

II

基于Android的飞机大战游戏设计与开发

目 录

第1章 绪论 ............................................................ 1

1.1 系统开发背景 ..................................................... 1

1.2 系统研究目的和意义 ............................................... 1

1.3 可行性分析 ....................................................... 1

第2章 系统需求分析 .................................................... 2

2.1 用户功能需求分析 ................................................. 2

2.2 系统性能要求 ..................................................... 3

2.3 业务流程分析 ..................................................... 3

第3章 系统总体设计 .................................................... 4

3.1 系统功能模块分析 ................................................. 4

3.1.1系统管理 ...................................................... 4

3.1.2 APP应用设置 .................................................. 4

3.1.3 玩家信息管理 .................................................. 4

3.2 系统类关系图 ..................................................... 4

3.3 系统总体设计 ..................................................... 6

第4章 系统详细设计 .................................................... 8

4.1 开发工具简介 ..................................................... 8

4.2 数据库设计 ....................................................... 8

4.2.1 DAO(数据库访问对象) ........................................... 9

4.2.2 SQLite数据库 ................................................ 12

4.3 游戏界面设计 .................................................... 12

4.3.1开始界面 ..................................................... 12

4.3.2游戏界面 ..................................................... 19

4.3.3设置界面 ..................................................... 27

4.3.4 得分界面 ..................................................... 35

4.3.5 Win界面 ..................................................... 41

4.3.6 Lose界面 .................................................... 47

第5章 软件测试和调试 ................................................. 52

5.1 白盒测试法 ...................................................... 52

5.2 黑盒测试法 ...................................................... 53

第6章 工作总结和展望 ................................................. 54

参考文献 .............................................................. 55

致 谢 ................................................................ 56

III

基于Android的飞机大战游戏设计与开发

第1章 绪论

1.1 系统开发背景

随着科技的发展,现在手机的功能已不仅仅是简单的接打电话、收发短信了。更多的手机用户希望在工作、学习之余通过方便灵巧可随身携带的仪器休闲娱乐。因此,为了迎合众多用户的需求并适应现在手机的规模,我们开发出一套适合各阶层人士的具有很强的娱乐性和交互性的飞机小游戏。

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

1.2 系统研究目的和意义

在如今社会,人们的工作学习压力逐渐增大,生活节奏逐渐加快,大多数人没有足够的时间去休闲娱乐,放松自己。这款小型的手机游戏,可以让我们随时随地都能享受游戏,从繁重的日常生活中解脱出来。

游戏的主界面应该力求美观,赏心悦目,以提高玩家对游戏的兴趣。游戏的控制模块应该做到易懂、易操作,以给玩家一个很好的游戏环境。

1.3 可行性分析

该系统采用Eclipse集成Android ADT为开发平台进行APP开发。Eclipse是一个开放的源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。虽然大都数用户很乐于Eclipse当作Java集成开发环境(IDE)来使用,但Eclipse的目标却不仅限于此。Eclipse还包括插件开发环境,这个组件主要针对希望扩展Eclipse的软件开发人员,因为它允许他们构建与Eclipse环境无缝集成的工具。由于Eclipse中的每样东西都是插件,对于Eclipse中的每样东西都是插件,对于给Eclipse提供插件,以及给用户提供一致和统一的集成开发环境而言,所有工具开发人员都具有同等的发挥场所。

基于Eclipse的应用程序的插件开发诸如Siemens公司的PLM产品Teamcenter的插件级开发。

Android专门针对Eclipse开发做了ADT(Android Developer Tools)开发插件,因此该应用使用Eclipse开发完全可行。

1

洛阳师范学院2013届本科生毕业设计

第2章 系统需求分析

2.1 用户功能需求分析

由于本程序简单易操作,交互性好,对用户没什么特别要求。一般用户经过几分钟练系都可以熟悉本游戏的规则。

直升机游戏 基于Android的飞机大战游戏设计与开发

直升机游戏 基于Android的飞机大战游戏设计与开发

图2-1 功能界面示例图

直升机游戏 基于Android的飞机大战游戏设计与开发

直升机游戏 基于Android的飞机大战游戏设计与开发

图2-2 游戏界面示例图

2

基于Android的飞机大战游戏设计与开发

2.2 系统性能要求

1.实时性

本应用为手机游戏因此对于用户的操作必须做出立即响应,否则本游戏即为失败。

2.易操作性

单机手机游戏的最大特点即为易操作性,用户在不看说明的情况下也能够玩,并且在玩过几遍之后即熟悉本游戏的规则。这是本应用对于操作性的要求。

2.3 业务流程分析

依据系统的需求分析,得到系统的流程图如图2-3所示:

直升机游戏 基于Android的飞机大战游戏设计与开发

图2-3 系统流程图

3

洛阳师范学院2013届本科生毕业设计

第3章 系统总体设计

3.1 系统功能模块分析

当前应用使用的SurfaceView来绘制的页面因此相对来说大部分逻辑都是由Activity来做处理的。本系统的功能模块也根据Activity的不同来划分成6个功能模块。

3.1.1系统管理

各个操作界面布局适当,颜色搭配等要美观。各个Activity之间的切换要快速(PS:它们各自的背景音乐也要随之切换,给用户以顺畅、自然的感觉)。用户在任何一个Activity点击“EXIT”按钮或ContextMenu中的“退出”都要成功的将当前应用挂起的多个Acitivity和当前Android虚拟机显示的Activity顺利的停止,并销毁。

3.1.2 APP应用设置

设置应用系统背景音乐声音大小和游戏难度。 使用SeekBar来控制系统音量,使用RadioGroup来控制飞机难度。

3.1.3 玩家信息管理

在游戏结束时对于玩家的名称和得分进行记录,并在玩家查看排名情况时,以倒序形式显示前六名玩家的得分和姓名等信息。

3.2 系统类关系图

系统实体类、边界类、控制类之间的关系如图3-1所示:

4

基于Android的飞机大战游戏设计与开发

图3-1 实体类、控制类、边界类之间的关系

各个界面类之间的关系如图3-2所示:

直升机游戏 基于Android的飞机大战游戏设计与开发

图3-2 APP边界类关系

5

直升机游戏 基于Android的飞机大战游戏设计与开发

洛阳师范学院2013届本科生毕业设计

3.3 系统总体设计

系统的中只有玩家一种用户,不必向其他的网站或者是教务系统一样进行身份验证等操作。玩家点击应用图标直接进入应用的开始界面。用户的操作总体可以归并成如3-3玩家用例图所显示的内容,子弹的用例图如图3-4所示,飞机的用例图如图3-5所示。

用户的操作基本可以划分为“开始游戏”,“设置游戏”,“玩游戏”,“查看分数”,“退出游戏”这五个用例。

直升机游戏 基于Android的飞机大战游戏设计与开发

图3-3 玩家用例图

6

基于Android的飞机大战游戏设计与开发

图3-4 子弹用例图

直升机游戏 基于Android的飞机大战游戏设计与开发

图3-5 飞机用例图

7

直升机游戏 基于Android的飞机大战游戏设计与开发

洛阳师范学院2013届本科生毕业设计

第4章 系统详细设计

4.1 开发工具简介

Android开发工具(ADT)是一个插件的Eclipse IDE,目的是给开发人员提供一个强大的、集成的环境中构建Android应用程序。

扩展能力的Eclipse ADT让你迅速建立新的Android项目,创建一个应用程序的用户界面,添加基于安卓框架的API,调试您的应用程序使用Android SDK工具,生成apk文件在使用Eclipse运行Android应用的时候Eclipse会自动的将生成的apk文件自动的注册到Android虚拟机中。

在Eclipse ADT发展与高度推荐,是一种最快的方式开始。与引导项目设置它提供,以及工具集成、定制XML编辑器和调试输出窗格,ADT给了你极大的提高在发展中Android应用程序。

使用Eclipse集成Android ADT做Android应用开发是目前企业中常用的Android应用开发方式。

本项目在开发环境:

? JDK 1.7

? Eclipse 3.7.0

? Android ADT4.03

4.2 数据库设计

本应用使用的是Android虚拟机做开发,因此使用的为Android虚拟机中自带的数据库SQLite。本应用只是在针对用户得分及用户姓名等基本信息进行存储,因此数据库非常简单,仅仅是设计了一个用来存储用户排名信息的表结构。用来存储用户基本信息的compositor_table的字段信息如图4-1所示。

CREATE TABLE [compositor_table] (

id INTEGER PRIMARY KEY,

name VARCHAR(40) NOT NULL,

score INTEGER NOT NULL)

8

基于Android的飞机大战游戏设计与开发

图4-1 compositor表

Android中自带的SQLiteOpenHelper 作用:一个帮助类,帮助创建数据库和数据库版本管理。

本应用直接创建一个SkyGameDataBaseHelper类继承Android自带的数据库操作类SQLiteOpenHelper。在onCreate()方法中创建表compositor_table。

public void onCreate(SQLiteDatabase db) {

// TODO Auto-generated method stub

String create_table =

this.context.getResources().getString(R.string.create_compos_table);

// CREATE TABLE [compositor_table] (id INTEGER PRIMARY KEY,name

VARCHAR(40) NOT NULL,score INTEGER NOT NULL);

db.execSQL(create_table);

}

4.2.1 DAO(数据库访问对象)

本应用中的数据库访问对象SkyGameDataBaseDao采用了单例模式,以保证当前应用中只存在一个DAO数据库访问对象。使用了最简单的单例,并未从线程安全的角度进行进一步的限制,原因在于考虑到当前应用中只有3个Activity使用了DAO,由图4-2可知Activity之间的切换类似于进程对于CPU的占用一样,当前显示在界面上的Activity是活动状态而其他Activity则是挂起状态,因此不必担心它们的线程同时去创建DAO对象以引起当前系统中多个DAO对象的状况。

public class SkyGameDataBaseDao {

private static SkyGameDataBaseDao instance = null;

private SQLiteDatabase database = null;

private Context context = null;

private SkyGameDataBaseHelper helper = null;

public static SkyGameDataBaseDao getInstance (Context context){

if(instance == null){

instance = new SkyGameDataBaseDao(context);

}

9

直升机游戏 基于Android的飞机大战游戏设计与开发

洛阳师范学院2013届本科生毕业设计

return instance;

}

private SkyGameDataBaseDao(Context context){

this.context = context;

helper = new SkyGameDataBaseHelper(context,1);

while((this.database = helper.getWritableDatabase())==null);

}

public void insertPlayer(SkyGamePlayer player){

String sql = context.getResources().getString(R.string.compos_table);

ContentValues values = new ContentValues();

values.put("score",player.getScore());

values.put("name", player.getName());

if(!this.database.isOpen()){

this.helper.onOpen(this.database);

}

try {

this.database.beginTransaction();

this.database.insert(sql, null, values);

this.database.setTransactionSuccessful();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

this.database.endTransaction();

}

}

public ArrayList<SkyGamePlayer> getPlayers(){

ArrayList<SkyGamePlayer> players = new ArrayList<SkyGamePlayer>(); String sql =

context.getResources().getString(R.string.select_form_compos_table_count);

Cursor cursor = database.rawQuery(sql, null);

cursor.moveToFirst();

if(cursor.getCount() > 0){

int count = 0;

count++;

players.add(new SkyGamePlayer(count,cursor.getString(1),cursor.getInt(2))); while(cursor.moveToNext()){

count ++;

players.add(new

SkyGamePlayer(count,cursor.getString(1),cursor.getInt(2)));

}

}

return players;

}

10

基于Android的飞机大战游戏设计与开发

} public void delete(){ String sql = context.getResources().getString(R.string.delete_from_compos_table); if(!this.database.isOpen()){ this.helper.onOpen(this.database); } try { this.database.beginTransaction(); this.database.execSQL(sql); this.database.setTransactionSuccessful(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ this.database.endTransaction(); } } public void close(){ if(this.database.isOpen()){ this.database.close(); } }

直升机游戏 基于Android的飞机大战游戏设计与开发

图4-2 Android中Activity生命周期状态图

11

洛阳师范学院2013届本科生毕业设计

4.2.2 SQLite数据库

SQLite,是一款轻量级的关系型数据库。由于它占用的资源非常少,所以在很多嵌入式设备都是用SQLite来存储数据。

Android作为目前主流的移动操作系统,完全符合SQLite占用资源少的优势,故在Android平台上,集成了一个嵌入式关系型数据库—SQLite。

由于SQLite是轻量级的关系型数据库,它支持的SQL语句也是有限的,在使用SQL语句获得前6名玩家的信息时直接使用了SQLite不支持的TOP语句引起了异常。在查阅相关资料后才发现SQLite不支持TOP语句,因此使用语句DESC LIMIT来代替TOP达到了自己想要的只获得表compositor_table中的score字段值最大的前六个记录信息。

SELECT * FROM [compositor_table] ORDER BY [score] DESC LIMIT 6

4.3 游戏界面设计

4.3.1开始界面

使用SurfaceView将图4-3中的未被按下的按钮和图4-5游戏开始界面背景图片绘制成游戏开始界面图4-6。

直升机游戏 基于Android的飞机大战游戏设计与开发

图4-3 未被按下的按钮图标集图

12

基于Android的飞机大战游戏设计与开发

4-4 被按下的按钮图标集

直升机游戏 基于Android的飞机大战游戏设计与开发

图4-5 开始界面背景图片

13

直升机游戏 基于Android的飞机大战游戏设计与开发

洛阳师范学院2013届本科生毕业设计

图4-6 开始界面

4.3.1.1 AndroidManifest.xml

Android应用程序中,并没有像C++和Java这样有main函数来作为应用程序的入口。Android应用程序提供的是入口Activity,而非入口函数。

AndroidManifest.xml文件中定义了整个Android应用所包含的Activity.在AndroidManifest.xml中将SkyGameStartActivity设置为当前SkyGame启动时,默认加载的Activity,代码如下:

<activity android:name=".SkyGameStartActivity"

android:launchMode="singleTask">

<intent-filter >

<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/> </intent-filter>

</activity>

4.3.1.2 Activity中注册layout

在SkyGameStartActivity的onCreate方法中设置要显示的layout,方法如下所示: protected void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub

super.onCreate(savedInstanceState);

14

直升机游戏 基于Android的飞机大战游戏设计与开发

基于Android的飞机大战游戏设计与开发

layout

} setContentView(R.layout.sky_game_start_layout);//设置当前activity调用的//其他操作

4.3.1.3 BroadcastReceiver

由于本应用是有多个Activity为了解决多个Activity在其中任意一个Activity结束时都会相应一起退出系统,因此针对每一个Activity设置一个BroadcastReceiver来接收广播,一旦接收到广播当前Activity自动退出。Activity接收到广播后退出的代码如下所示:

private BroadcastReceiver exitReceiver = new BroadcastReceiver(){

@Override

public void onReceive(Context context, Intent intent) {

// TODO Auto-generated method stub

SkyGameScreenRollActivity.this.finish();

}

};

4.3.1.4 发送广播

而当前Activity在被按下“EXIT”或者“退出”按钮时,会向外界发送一个广播, 之后结束自己:

Activity activity = (Activity)context;

Intent intent = new Intent(EXIT);

intent.setAction(EXIT);

activity.sendBroadcast(intent);

activity.finish();

4.3.1.5 MediaPlayer(媒体播放器)

本应用使用Android的MediaPlayer来演奏每一个页面的背景音乐。

背景音乐可以存放在两个位置,一个是当前工程的/res/raw目录下,如果是存放在该目录下的话,在install当前Android工程时,需要耗费非常的时间将此类音频文件上传到Android虚拟机中。另一个存放位置是直接将音频文件上传到DDMS的/mnt/sdcard/Music文件夹下,这相当于将该音频文件放入到了Android手机的SD卡上了,相对于存放位置一来说,该方法在加载Android工程时消耗的时间比较少。具体向虚拟机中上传文件如图4-7所示:

15

洛阳师范学院2013届本科生毕业设计

图4-7 DDMS向Android虚拟机中上传文件

MediaPlayer使用方法:

private MediaPlayer startSound = null;

startSound = new MediaPlayer();

try {

/*

* 从中获得音频文件的路径

*/

startSound.setDataSource("/mnt/sdcard/Music/start.mp3");

startSound.prepare();

} catch (IllegalArgumentException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (SecurityException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IllegalStateException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

startSound.setLooping(true) ;//设置循环

由于当前应用针对不同的显示界面(Activity)有不同的背景音乐,因此当前Activity挂起时,对应的背景音乐也要挂起(startSound.pause())。而在当前Activity销毁

16

直升机游戏 基于Android的飞机大战游戏设计与开发

基于Android的飞机大战游戏设计与开发

(destory)时,需要对背景音乐进行停止(startSound.stop())和回收(startSound.release())操作。

4.3.1.6 layout中调用SurfaceView

普通的layout设置与直接调用SurfaceView的layout有所不同,一下列出调用SurfaceView的layout的格式:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical" >

<com.mengyawei.sky.game.activity.view.SkyGameStartView

android:id="@+id/skyGameStartPicture"

android:layout_width="wrap_content"

android:layout_height="wrap_content">

</com.mengyawei.sky.game.activity.view.SkyGameStartView>

</LinearLayout>

此处使用自定义SurfaceView的绝对路径来编写开始页面的layout文件,使得开始页面直接使用该SurfaceView绘制的Bitmap作为背景图片来显示。

SurfaceView所需要设置下它的SurfaceHolder 和Monitor(实现CallBack接口)属性

holder = getHolder();

monitor = new Monitor();

holder.addCallback(monitor);

Monitor类的方法:

方法surfaceCreated(SurfaceHolder holder)的主要作用是:在surfaceview创建的绘制SurfaceView的内容。

方法surfaceDestroyed(SurfaceHolder holder)的主要作用是:在surfaceview销毁时对于当前SurfaceView中的一些对象做销毁处理。

SurfaceView的方法:

onTouchEvent(MotionEvent event)可以捕获鼠标对于屏幕的触碰事件,在该方法中对于鼠标触碰的区域做出判断如果点击的为按钮图片的位置则会调用图4-4中对应的被按下按钮来显示,如此显示使用户有一个可感受的按钮被按下的动态即视感,用户体验更好。

public boolean onTouchEvent(MotionEvent event) {

// TODO Auto-generated method stub

int action = event.getAction();

float eventX = event.getX();

float eventY = event.getY();

if((eventX >= START_X)&&

(eventX <= START_X+startButton.getWidth())&&

(eventY >= START_Y)&&

(eventY <= START_Y+startButton.getHeight())){

if(action == MotionEvent.ACTION_DOWN){

17

洛阳师范学院2013届本科生毕业设计

startButton = BitmapFactory.decodeResource(getResources(),

R.drawable.startbutton2);

drawStartButton();

}

if(action == MotionEvent.ACTION_UP){

startButton = BitmapFactory.decodeResource(getResources(),

R.drawable.startbutton1);

drawStartButton();

Intent intent = new Intent(this.context,SkyGameScreenRollActivity.class); this.context.startActivity(intent);

}

}

else if((eventX >= OPTION_X)&&

(eventX <= OPTION_X+optionButton.getWidth())&&

(eventY >= OPTION_Y)&&

(eventY <= OPTION_Y+optionButton.getHeight())){

if(action == MotionEvent.ACTION_DOWN){

optionButton = BitmapFactory.decodeResource(getResources(),

R.drawable.option2);

drawOptionButton();

}

if(action == MotionEvent.ACTION_UP){

optionButton = BitmapFactory.decodeResource(getResources(),

R.drawable.option1);

drawOptionButton();

Intent intent = new

Intent(this.context,SkyGameOptionButtonOnClickActivity.class);

this.context.startActivity(intent);

}

}

else if((eventX >= SCORE_X)&&

(eventX <= SCORE_X+scoreButton.getWidth())&&

(eventY >= SCORE_Y)&&

(eventY <= SCORE_Y+scoreButton.getHeight())){

if(action == MotionEvent.ACTION_DOWN){

scoreButton = BitmapFactory.decodeResource(getResources(),

R.drawable.score2);

drawScoreButton();

}

if(action == MotionEvent.ACTION_UP){

scoreButton = BitmapFactory.decodeResource(getResources(),

R.drawable.score1);

drawScoreButton();

Intent intent = new

18

基于Android的飞机大战游戏设计与开发

Intent(this.context,SkyGameScoreButtonOnClickActivity.class);

this.context.startActivity(intent);

}

}

else if((eventX >= EXIT_X)&&

(eventX <= EXIT_X +exitButton.getWidth())&&

(eventY >= EXIT_Y)&&

(eventY <= EXIT_Y + exitButton.getHeight())){

if(action == MotionEvent.ACTION_DOWN){

scoreButton = BitmapFactory.decodeResource(getResources(),

R.drawable.exit_button_2);

drawExitButton();

}

if(action == MotionEvent.ACTION_UP){

scoreButton = BitmapFactory.decodeResource(getResources(),

R.drawable.exit_button_1);

drawExitButton();

Activity activity = (Activity)context;

Intent intent = new Intent(EXIT);

intent.setAction(EXIT);

activity.sendBroadcast(intent);

activity.finish();

}

}

return true;

}

4.3.2游戏界面

本界面与开始界面都是使用SurfaceView绘制的界面,由于本界面相对于开始界面更加复杂在主线程外创建了子线程来负责对于SurfaceView的绘制工作,主线程负责对于各类对象的控制计算等计算工作。

由于游戏界面涉及到的类比较多,且逻辑复杂,所以在这里只介绍下使用的空间、技术以及业务逻辑,粘贴部分技术代码。

绘制的游戏界面如图4-8所示:

19

洛阳师范学院2013届本科生毕业设计

图4-8 游戏界面

4.3.2.1 Activity中注册SurfaceView

游戏界面并没有像开始界面那样注册SurfaceView,而是直接在onCreate方法中使用代码注册的自定义SurfaceView,如下:

private SkyGameScreenRollView sr = null;//自定义surfaceview

protected void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub

super.onCreate(savedInstanceState);

sr = new SkyGameScreenRollView(this);

sr.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

sr.setId(0x000101);

setContentView(sr);

}

4.3.2.2 ContextMenu控件

游戏界面使用了Android的ContextMenu,ContextMenu显示的具体效果见图4-9。

20

直升机游戏 基于Android的飞机大战游戏设计与开发

基于Android的飞机大战游戏设计与开发

图4-9 ContextMenu效果

ContextMenu针对某个控件,一旦为某个控件设置了ContextMenu,那么程序员将不能再实现该控件的长按事件处理了。

ContextMenu的使用步骤:

1. ContextMenu针对的是控件而不是窗体,构建完ContextMenu后需要与一个控件实施

绑定。

绑定的代码为:super.registerForContextMenu(控件对象)。

2.构建ContextMenu的方法如下:

a.创建一个res/menu/**_context.xml的菜单(当前应用使用的该方法)。

b.重写onCreateContextMenu()回调函数。

c.super.registerForContextMenu(控件对象)。

3.为每个菜单项编写事件。

具体操作方法重写onCreateMenuItemSelected回调函数。

游戏界面的ContextMenu的playing_game_option_menu.xml内容如下:

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

<item android:id="@+id/pg_option_exit"

android:title="@string/option_menu_exit"

android:showAsAction="never"/>

<item android:id="@+id/pg_option_score"

android:title="@string/option_menu_score"

android:showAsAction="never"/>

<item android:id="@+id/pg_option_start"

android:title="@string/option_menu_start"

android:showAsAction="never"/>

<item android:id="@+id/pg_option_option"

android:title="@string/option_menu_option"

android:showAsAction="never"/>

</menu>

21

直升机游戏 基于Android的飞机大战游戏设计与开发

洛阳师范学院2013届本科生毕业设计

在游戏界面的SkyGameScreenRollActivity的方法onCreateOptionsMenu(Menu menu)中注册playing_game_option_menu.xml文件,具体方法如下所示:

public boolean onCreateOptionsMenu(Menu menu) {

// TODO Auto-generated method stub

MenuInflater inflater = new MenuInflater(this);

inflater.inflate(R.menu.playing_game_option_menu, menu);

return true;

}

在游戏界面的SkyGameScreenRollActivity的方法onOptionsItemSelected(MenuItem item)中为每个菜单选项编写响应事件,具体使用方法如下所示:

public boolean onOptionsItemSelected(MenuItem item) {

// TODO Auto-generated method stub

Intent intent = null;

switch(item.getItemId()){

case R.id.pg_option_exit:

intent = new Intent();

intent.setAction(EXIT);

this.sendBroadcast(intent);

this.finish();

break;

case R.id.pg_option_option:

intent = new

Intent(SkyGameScreenRollActivity.this,SkyGameOptionButtonOnClickActivity.class); this.startActivity(intent);

break;

case R.id.pg_option_score:

intent = new

Intent(SkyGameScreenRollActivity.this,SkyGameScoreButtonOnClickActivity.class);

this.startActivity(intent);

break;

case R.id.pg_option_start:

intent = new

Intent(SkyGameScreenRollActivity.this,SkyGameStartActivity.class);

this.startActivity(intent);

break;

}

return true;

}

4.3.2.3 发送短信

在用户赢得一关的时候会发送一条信息“通过第N关”,实现该功能的代码如下:

private SmsManager sms = null;

this.sms = SmsManager.getDefault();

22

基于Android的飞机大战游戏设计与开发

private String telNum = null;

telNum = "5554";//Android虚拟机的ID

public void sendMessage(String telNum,String Message){

sms.sendTextMessage(telNum, null, Message, sentIntent , null);

}

(PS:Android的发送短信存在一个Bug就是当前DalvikVM发送的短信它自己无法收到,如果同时开两个DalvikVM其中一个发送短信的话,另一个会收到短信。)

4.3.2.4 鼠标控制玩家飞机移动

使用鼠标控制玩家飞机的移动,在自定义的SurfaceView的onTouchEvent方法中对于按下区域是否在玩家飞机图片所在位置做出判定,如果在,则在拖动的过程中不断获得鼠标的坐标,并将该坐标传递给玩家飞机,以此来使玩家飞机随着鼠标的位置移动。具体根据鼠标移动控制玩家飞机移动的逻辑如下所示:

public boolean onTouchEvent(MotionEvent event) {

// TODO Auto-generated method stub

int action = event.getAction();

float x = event.getX();

float y = event.getY();

if(x >= this.player.getPlane_x()&&

x <= this.player.getPlane_x()+this.player.getWidth()&&

y >= this.player.getPlane_y()&&

y <= this.player.getPlane_y()+this.player.getHeight()){

if(action == MotionEvent.ACTION_DOWN){

playerIsTouch = true;

}

if(action == MotionEvent.ACTION_UP){

playerIsTouch = false;

this.player.setXY(x, y);

}

}

if(action == MotionEvent.ACTION_MOVE){

if(playerIsTouch){

this.player.setXY(event.getX(),event.getY()); }

}

return true;

}

23

洛阳师范学院2013届本科生毕业设计

图4-10 鼠标控制玩家飞机序列图

4.3.2.5 Activity之间传递数据

使用到了Activity之间传递数据的技术,将玩家当前获得的分数传递给下一个Activity以供使用。

1.将数据传出的代码:

Intent intent = new Intent(this.context,GameOverFailureActivity.class);

intent.putExtra("score", drawer.getScore());

this.context.startActivity(intent);

2.获得数据的代码:

Intent intent = getIntent();

this.score = intent.getIntExtra("score", 1000);

4.3.2.6 SurfaceView中绘制文字

Android的Paint对象在画布上实时绘制玩家的分数,代码如下:

public void drawScore(Canvas canvas){

Paint paint = new Paint();

paint.setColor(Color.BLUE);

paint.setTextSize(20);

canvas.drawText("分数:"+this.score, viewWidth-120, 30, paint);

24

直升机游戏 基于Android的飞机大战游戏设计与开发

基于Android的飞机大战游戏设计与开发

}

绘制文字的效果如图4-11所示:

直升机游戏 基于Android的飞机大战游戏设计与开发

图4-11 绘制文字

4.3.2.7 碰撞逻辑

判断飞机是否中弹的逻辑非常简单就是在绘制飞机与子弹的时候判断两个图片是否存在重合的部分,如果存在则在该重合位置绘制一个爆炸图片,之后将中弹飞机从飞机队列中删除,子弹同样的处理。

飞机中弹序列如图4-12所示:

直升机游戏 基于Android的飞机大战游戏设计与开发

图4-12 飞机中弹序列图

4.3.2.8 血条

绘制玩家飞机血线和Boss血线的逻辑是相同的,在统计飞机中弹次数的情况下使用图

25

洛阳师范学院2013届本科生毕业设计

4-13中的各个小图对血线进行绘制。

直升机游戏 基于Android的飞机大战游戏设计与开发

图4-13 血线图片集合

绘制血线的逻辑如图4-14所示:

直升机游戏 基于Android的飞机大战游戏设计与开发

图4-14 绘制血线逻辑

具体负责绘制血线的函数(PS:该函数仅负责绘制,各种逻辑判断的事情它不做)如下:

public void drawPlayerBloodLine(Canvas canvas){

int num = playerIsShortedCount/5;

int leng = fullBlood.getWidth()-bloodStep.getWidth()*num;

Bitmap blood = null;

Bitmap empty = null;

if(num == 0){

canvas.drawBitmap( fullBlood, 0,

viewHeight - fullBlood.getHeight(), null);

}else if(leng > bloodBegin.getWidth()){

blood = Bitmap.createBitmap( fullBlood, 0, 0, leng,

fullBlood.getHeight());

empty = Bitmap.createBitmap( emptyBlood, leng, 0,

emptyBlood.getWidth()-leng,

emptyBlood.getHeight());

canvas.drawBitmap( blood, 0,

viewHeight - blood.getHeight(), null);

canvas.drawBitmap( empty,

26

基于Android的飞机大战游戏设计与开发

} blood.getWidth(), viewHeight - empty.getHeight(), null); }else if(leng >0){ blood = bloodBegin; empty = Bitmap.createBitmap( emptyBlood, bloodBegin.getWidth(), 0, emptyBlood.getWidth()-bloodBegin.getWidth(), emptyBlood.getHeight()); canvas.drawBitmap( blood, 0, viewHeight - blood.getHeight(), null); canvas.drawBitmap( empty, blood.getWidth(), viewHeight - empty.getHeight(), null); }else{ canvas.drawBitmap( emptyBlood, 0, viewHeight - emptyBlood.getHeight(), null); isPlayerDead = true; }

4.3.3设置界面

当前界面使用的是普通的layout,生成的设置界面如图4-15所示:

直升机游戏 基于Android的飞机大战游戏设计与开发

图4-15 设置界面

27

洛阳师范学院2013届本科生毕业设计

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical" >

<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content" >

<TextView

android:id="@+id/tv_option_sound"

android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_weight="1"

android:gravity="center"

android:text="@string/tv_option_sound"

android:textSize="20dp" />

<SeekBar

android:id="@+id/option_sound_seekBar"

android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_weight="4" />

</LinearLayout>

<RadioGroup

android:id="@+id/option_grade_radio_group"

android:layout_width="wrap_content"

android:layout_height="wrap_content" >

<RadioButton

android:id="@+id/option_grade_simple"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:checked="true"

android:text="@string/option_radioButton_simple" /> <RadioButton

android:id="@+id/option_grade_middle"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/option_radioButton_middle" /> <RadioButton

android:id="@+id/option_grade_difficult"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/option_radioButton_difficult" /> </RadioGroup>

28

基于Android的飞机大战游戏设计与开发

<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content" >

<Button

android:id="@+id/btn_make_sure"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/btn_confirm" />

<Button

android:id="@+id/btn_cancle"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/btn_cancle" />

</LinearLayout>

</LinearLayout>

4.3.3.1 SeekBar&AudioManager

本界面使用了SeekBar为了能够通过拖动SeekBar上的按钮来调整声音的大小需要结合AudioManager来设置当前Activity的背景音乐。下面的代码是将SeekBar滚动按钮的位置设置为当前系统的声音相对于当前系统最大音量的位置:

audioManager = (AudioManager) this.getSystemService(AUDIO_SERVICE);

audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, seekBarProgress, 0); int currentProgress = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); int maxProgress =

audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);

soundSeekBar = (SeekBar) findViewById(R.id.option_sound_seekBar);

soundSeekBar.setOnSeekBarChangeListener(this);

soundSeekBar.setMax(maxProgress);

soundSeekBar.setProgress(currentProgress);

以下代码是根据SeekBar的滚动按钮的位置设置当前系统的音量:

seekBarProgress = seekBar.getProgress();

audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, seekBarProgress, 0);

4.3.3.2 SharedPreference

当前页面的信息使用SharedPreference存储在系统中,使用SharedPreference信息的代码如下:

SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(

SkyGameOptionButtonOnClickActivity.this);

rdId = rdGroup.getCheckedRadioButtonId();

Editor editor = sp.edit();

29

洛阳师范学院2013届本科生毕业设计

editor.putInt("seekBarProgress", seekBarProgress);

editor.putInt("rdGroupCheckedrdId", rdId);

editor.commit();

代码执行后可以在当前Eclipse的DDMS页面中包含文件自动生成的过程,可以使用此方法的/data/data/projectName/shared_prefs位置查找到生成的SharedPreference文件,如图4-16所示:

直升机游戏 基于Android的飞机大战游戏设计与开发

图4-16 查找并导出SharedPreference文件

点击导出,可以查看生成的SharedPreference文件内容,SharedPreference文件内容如图4-17所示:

直升机游戏 基于Android的飞机大战游戏设计与开发

图4-17 导出的SharedPreference文件内容

当前页面也是用了ContextMenu控件,具体详见4.3.2.2游戏界面对于ContextMenu的介绍,本处不做赘述。

SkyGameOptionButtonOnClickActivity具体内容如下:

public class SkyGameOptionButtonOnClickActivity extends Activity implements

SeekBar.OnSeekBarChangeListener{

private final String EXIT = "EXIT";

private MediaPlayer backgroundplayer = null;

private SeekBar soundSeekBar = null;

private AudioManager audioManager = null;

private Button btnMakeSure = null;

30

基于Android的飞机大战游戏设计与开发

private Button btnCancle = null;

private int seekBarProgress = 0;

private RadioGroup rdGroup = null;

private int rdId = 0;

private OnClickListener btnMakeSureHandler = new OnClickListener(){

public void onClick(View v) {

// TODO Auto-generated method stub

SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences( SkyGameOptionButtonOnClickActivity.this);

rdId = rdGroup.getCheckedRadioButtonId();

Editor editor = sp.edit();

editor.putInt("seekBarProgress", seekBarProgress);

editor.putInt("rdGroupCheckedrdId", rdId);

editor.commit();

Toast t = Toast.makeText(SkyGameOptionButtonOnClickActivity.this, "成功保存你的设置!", Toast.LENGTH_LONG);

t.show();

}

};

private OnClickListener btnCancleHandler = new OnClickListener(){

public void onClick(View v) {

// TODO Auto-generated method stub

initSeekBarProgress();

audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, seekBarProgress, 0);

soundSeekBar.setProgress(seekBarProgress);

rdGroup.check(rdId);

}

};

private OnCheckedChangeListener rdGroupOnCheckedChangeHandler = new OnCheckedChangeListener() {

@Override

public void onCheckedChanged(RadioGroup group, int checkedId) {

// TODO Auto-generated method stub

rdId = checkedId;

}

};

private BroadcastReceiver exitReceiver = new BroadcastReceiver(){

@Override

public void onReceive(Context context, Intent intent) {

// TODO Auto-generated method stub

SkyGameOptionButtonOnClickActivity.this.finish();

}

};

31

洛阳师范学院2013届本科生毕业设计

protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.option_layout); initSeekBarProgress(); initCompent(); registerReceiver(exitReceiver, new IntentFilter(EXIT)); } protected void onStart() { // TODO Auto-generated method stub super.onStart(); backgroundplayer.start(); } protected void onRestart() { // TODO Auto-generated method stub super.onRestart(); backgroundplayer.start(); } protected void onPause() { // TODO Auto-generated method stub super.onPause(); backgroundplayer.pause(); } protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); backgroundplayer.stop(); backgroundplayer.release(); unregisterReceiver(exitReceiver); } public boolean onCreateOptionsMenu(Menu menu) { // TODO Auto-generated method stub MenuInflater inflater = new MenuInflater(this); inflater.inflate(R.menu.option_option_menu, menu); return true; } public boolean onOptionsItemSelected(MenuItem item) { // TODO Auto-generated method stub Intent intent = null; switch(item.getItemId()){ case R.id.option_option_exit: intent = new Intent(); intent.setAction(EXIT); this.sendBroadcast(intent);

32

基于Android的飞机大战游戏设计与开发

this.finish();

break;

case R.id.option_option_score:

intent = new

Intent(SkyGameOptionButtonOnClickActivity.this,SkyGameScoreButtonOnClickActivity.class);

this.startActivity(intent);

break;

case R.id.option_option_start:

intent = new

Intent(SkyGameOptionButtonOnClickActivity.this,SkyGameStartActivity.class); this.startActivity(intent);

break;

}

return true;

}

public void initCompent(){

rdGroup = (RadioGroup)findViewById(R.id.option_grade_radio_group); rdGroup.setOnCheckedChangeListener(rdGroupOnCheckedChangeHandler); btnMakeSure = (Button) findViewById(R.id.btn_make_sure);

btnMakeSure.setOnClickListener(btnMakeSureHandler);

btnCancle = (Button) findViewById(R.id.btn_cancle);

btnCancle.setOnClickListener(btnCancleHandler );

audioManager = (AudioManager) this.getSystemService(AUDIO_SERVICE); rdGroup.check(rdId);

audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,

seekBarProgress, 0);

int currentProgress =

audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);

int maxProgress =

audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);

soundSeekBar = (SeekBar) findViewById(R.id.option_sound_seekBar);

soundSeekBar.setOnSeekBarChangeListener(this);

soundSeekBar.setMax(maxProgress);

soundSeekBar.setProgress(currentProgress);

backgroundplayer = new MediaPlayer();

try {

backgroundplayer.setDataSource("/mnt/sdcard/Music/start.mp3");

} catch (IllegalArgumentException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (SecurityException e) {

// TODO Auto-generated catch block

e.printStackTrace();

33

洛阳师范学院2013届本科生毕业设计

} catch (IllegalStateException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

try {

backgroundplayer.prepare();

} catch (IllegalArgumentException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (SecurityException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IllegalStateException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

backgroundplayer.setLooping(true);

}

public void onProgressChanged(SeekBar seekBar, int progress,

boolean fromUser) {

// TODO Auto-generated method stub

audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,seekBar.getProgress() ,0);

}

public void onStartTrackingTouch(SeekBar seekBar) {

// TODO Auto-generated method stub

}

public void onStopTrackingTouch(SeekBar seekBar) {

// TODO Auto-generated method stub

audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,

seekBar.getProgress(),0);

this.seekBarProgress = seekBar.getProgress();

}

private void initSeekBarProgress(){

SharedPreferences sp =

PreferenceManager.getDefaultSharedPreferences(SkyGameOptionButtonOnClickActivity.this);

34

基于Android的飞机大战游戏设计与开发

}

} seekBarProgress = sp.getInt("seekBarProgress", 50); rdId = sp.getInt("rdGroupCheckedrdId",R.id.option_grade_simple);

4.3.4 得分界面

本界面使用普通的layout,使用了一个ListView控件,生成的页面如图4-18所示:

直升机游戏 基于Android的飞机大战游戏设计与开发

图4-18 得分界面

该layout具体代码如下:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical" >

<TextView

android:id="@+id/score_layout_title"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:gravity="center_horizontal"

35

洛阳师范学院2013届本科生毕业设计

android:text="@string/score_layout_title" /> <LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content" >

<TextView

android:id="@+id/score_layout_name" android:layout_width="0dp"

android:layout_height="wrap_content" android:layout_weight="1"

android:gravity="center_horizontal"

android:text="@string/score_layout_name" /> <TextView

android:id="@+id/score_layout_score" android:layout_width="0dp"

android:layout_height="wrap_content" android:layout_weight="1"

android:gravity="center_horizontal"

android:text="@string/score_layout_score" /> <TextView

android:id="@+id/score_layout_rank" android:layout_width="0dp"

android:layout_height="wrap_content" android:layout_weight="1"

android:gravity="center_horizontal"

android:text="@string/score_layout_rank" /> </LinearLayout>

<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="vertical" >

<include layout="@layout/score_listview_layout"/> </LinearLayout>

<ListView

android:id="@+id/score_listview"

android:layout_width="match_parent"

android:layout_height="436dp" >

</ListView>

<Button

android:id="@+id/btn_delete"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_weight="1"

android:text="@string/btn_delete" />

</LinearLayout>

36

基于Android的飞机大战游戏设计与开发

4.3.4.1 ListView中数据的填充

当前页面与从Android虚拟机自带的SQLite数据库中compositor_table表读取信息,并将获取的信息放入到ListView中,形成玩家信息列表。

使用SimpleAdapter填充ListView中的内容,代码如下:

ArrayList<SkyGamePlayer> players = dao.getPlayers();

ArrayList<Map<String,Object>> datas = new ArrayList<Map<String,Object>>(); for(int i=0;i<players.size();i++){

SkyGamePlayer player = players.get(i);

Map<String,Object> map = new HashMap<String,Object>();

map.put("name", player.getName());

map.put("score", player.getScore());

map.put("rank", player.getId());

datas.add(map);

}

SimpleAdapter adapter = new SimpleAdapter(

SkyGameScoreButtonOnClickActivity.this,

datas,

R.layout.score_listview_layout,

new String[]{

"name",

"score",

"rank"},

new int[]{

R.id.score_layout_listview_name,

R.id.score_layout_listview_score,

R.id.score_layout_listview_rank});

this.scoreListView.setAdapter(adapter);

4.3.4.2 Button监听

当前页面的按钮“删除”的功能,清空之前数据库中的数据。

通过btnDelete = (Button)findViewById(R.id.btn_delete)找到该按钮在Android工程R文件中对应的变量

(PS:

该R文件为当前项目自动生成的R文件,Android自带的开发包中也有R文件,需要注意的是在使用findViewById时需要查看下加载的包名是否为当前工程的名字

该R文件为编译器自动生成的,如果发现当前工程的R文件消失了,说明你的layout里面有空间设置的不合法,一般xml文件不合法无法像Java那样直接报错,但往往会导致当前工程无法编译生成R文件)

给按钮设置监听:

btnDelete.setOnClickListener(btnDeleteHandler);

private OnClickListener btnDeleteHandler = new OnClickListener(){

37

洛阳师范学院2013届本科生毕业设计

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

dao.delete();

Toast t = Toast.makeText(SkyGameScoreButtonOnClickActivity.this, "删除成功!", Toast.LENGTH_LONG);

t.show();

initScoreListView();

}

};

SkyGameScoreButtonOnClickActivity的具体代码如下:

public class SkyGameScoreButtonOnClickActivity extends Activity {

private final String EXIT = "EXIT";

private MediaPlayer background = null;

private SkyGameDataBaseDao dao = null;

private ListView scoreListView = null;

private Button btnDelete = null;

private OnClickListener btnDeleteHandler = new OnClickListener(){

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

dao.delete();

Toast t = Toast.makeText(SkyGameScoreButtonOnClickActivity.this, "删除成功!", Toast.LENGTH_LONG);

t.show();

initScoreListView();

}

};

private BroadcastReceiver exitReceiver = new BroadcastReceiver(){

@Override

public void onReceive(Context context, Intent intent) {

// TODO Auto-generated method stub

SkyGameScoreButtonOnClickActivity.this.finish();

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub

super.onCreate(savedInstanceState);

setContentView(R.layout.score_layout);

initCompent();

registerReceiver(exitReceiver, new IntentFilter(EXIT));

}

@Override

38

基于Android的飞机大战游戏设计与开发

protected void onStart() { // TODO Auto-generated method stub super.onStart(); initScoreListView(); try { background.prepare(); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } background.start(); background.setLooping(true); } @Override protected void onRestart() { // TODO Auto-generated method stub super.onRestart(); try { background.prepare(); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } background.start(); initScoreListView(); } @Override protected void onPause() { // TODO Auto-generated method stub super.onPause(); background.pause(); } public boolean onCreateOptionsMenu(Menu menu) { // TODO Auto-generated method stub MenuInflater inflater = new MenuInflater(this); inflater.inflate(R.menu.score_option_menu, menu); return true; }

39

洛阳师范学院2013届本科生毕业设计

public boolean onOptionsItemSelected(MenuItem item) {

// TODO Auto-generated method stub

Intent intent = null;

switch(item.getItemId()){

case R.id.score_option_exit:

intent = new Intent();

intent.setAction(EXIT);

this.sendBroadcast(intent);

this.finish();

break;

case R.id.score_option_option:

intent = new

Intent(SkyGameScoreButtonOnClickActivity.this,SkyGameOptionButtonOnClickActivity.class);

this.startActivity(intent);

break;

case R.id.score_option_start:

intent = new

Intent(SkyGameScoreButtonOnClickActivity.this,SkyGameStartActivity.class);

this.startActivity(intent);

break;

}

return true;

}

protected void onDestroy() {

// TODO Auto-generated method stub

dao.close();

background.stop();

background.release();

unregisterReceiver(exitReceiver);

super.onDestroy();

}

public void initCompent(){

btnDelete = (Button)findViewById(R.id.btn_delete);

btnDelete.setOnClickListener(btnDeleteHandler);

background = new MediaPlayer();

try {

background.setDataSource("/mnt/sdcard/Music/start.mp3");

} catch (IllegalArgumentException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (SecurityException e) {

// TODO Auto-generated catch block

e.printStackTrace();

40

基于Android的飞机大战游戏设计与开发

} catch (IllegalStateException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

dao =

SkyGameDataBaseDao.getInstance(SkyGameScoreButtonOnClickActivity.this);

scoreListView = (ListView)findViewById(R.id.score_listview);

}

public void initScoreListView(){

ArrayList<SkyGamePlayer> players = dao.getPlayers();

ArrayList<Map<String,Object>> datas = new ArrayList<Map<String,Object>>(); for(int i=0;i<players.size();i++){

SkyGamePlayer player = players.get(i);

Map<String,Object> map = new HashMap<String,Object>();

map.put("name", player.getName());

map.put("score", player.getScore());

map.put("rank", player.getId());

datas.add(map);

}

SimpleAdapter adapter = new SimpleAdapter(

SkyGameScoreButtonOnClickActivity.this,

datas,

R.layout.score_listview_layout,

new String[]{

"name",

"score",

"rank"},

new int[]{

R.id.score_layout_listview_name,

R.id.score_layout_listview_score,

R.id.score_layout_listview_rank});

this.scoreListView.setAdapter(adapter);

}

}

4.3.5 Win界面

相对于前面的界面,本界面功能相对比较简单,仅仅是在玩家赢得游戏时,提醒用户输入名称,并将用户的信息(名称,得分等)插入数据库中,之后直接跳转到得分界面。 本界面使用普通的layout,具体代码如下,生成的界面见图4-19:

41

洛阳师范学院2013届本科生毕业设计

图4-19 Win界面

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

android:background="@drawable/win">

<TextView

android:id="@+id/success_tv_title"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:textSize="20dp"

android:textColor="#ffffff"

android:gravity="center_vertical|center_horizontal"

android:text="@string/game_over_title" />

<TextView

android:id="@+id/success_tv_message"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textSize="20dp"

android:textColor="#ffffff"

/>

42

直升机游戏 基于Android的飞机大战游戏设计与开发

基于Android的飞机大战游戏设计与开发

<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content" >

<TextView

android:id="@+id/success_tv_clew_leave_name"

android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_weight="1"

android:textSize="20dp"

android:textColor="#ffffff"

android:text="@string/leave_name" />

<EditText

android:id="@+id/success_et_name"

android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_weight="1"

android:ems="10" >

<requestFocus />

</EditText>

</LinearLayout>

<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content" >

<Button

android:id="@+id/success_btn_confirm"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/btn_confirm" />

</LinearLayout>

</LinearLayout>

4.3.5.1 Toast

Toast是Android的控件,Android中的Toast是一种简易的消息提示框,Toast提示框不能被用户点击,Toast会根据用户设置的显示时间后自动消失。使用Toast弹出的提示信息如图4-20所示:

43

洛阳师范学院2013届本科生毕业设计

图4-20 Toast效果图

在玩家点击“确定”按钮时,该按钮的onClick监听会对用户是否输入名称进行判定,如果用户没有输入姓名,则会弹出提示框提示:“用户名不能为空!请重新输入”。 如果用户输入了名称,则会调用DAO将数据信息插入数据库中,并提示用户:“信息已成功保存”

4.3.5.2 正则表达式

使用正则表达式来替换玩家得分语句“你赢了!你的分数是:[1000]”中的分数。替换的代码如下所示:

String ret = message.replaceAll("[.*?]","["+data+"]");

GameOverSuccessActivity 的具体逻辑如下:

public class GameOverSuccessActivity extends Activity {

private final String EXIT = "EXIT";

private MediaPlayer background = null;

private int score = 0;

private SkyGameDataBaseDao dao = null;

private TextView tvMessage = null;

private EditText etName = null;

private Button btnConfirm = null;

private OnClickListener btnConfirmHandler = new OnClickListener(){

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

String name = etName.getText().toString().trim();

if(name.equals("")){

Toast.makeText(GameOverSuccessActivity.this,"用户名不能为空!请重新输入", Toast.LENGTH_LONG).show();

}else{

SkyGamePlayer player = new SkyGamePlayer(name, score);

dao.insertPlayer(player);

44

直升机游戏 基于Android的飞机大战游戏设计与开发

基于Android的飞机大战游戏设计与开发

etName.setText("");

Toast.makeText(GameOverSuccessActivity.this,"信息已成功保存", Toast.LENGTH_LONG).show();

Intent intent = new

Intent(GameOverSuccessActivity.this,SkyGameScoreButtonOnClickActivity.class); GameOverSuccessActivity.this.startActivity(intent);

}

}

};

private BroadcastReceiver exitReceiver = new BroadcastReceiver(){

@Override

public void onReceive(Context context, Intent intent) {

// TODO Auto-generated method stub

GameOverSuccessActivity.this.finish();

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub

super.onCreate(savedInstanceState);

setContentView(R.layout.game_over_success_layout);

initCompent();

registerReceiver(exitReceiver, new IntentFilter(EXIT));

}

@Override

protected void onStart() {

// TODO Auto-generated method stub

super.onStart();

background.start();

}

@Override

protected void onRestart() {

// TODO Auto-generated method stub

super.onRestart();

try {

background.prepare();

} catch (IllegalStateException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

background.start();

45

洛阳师范学院2013届本科生毕业设计

} protected void onPause() { // TODO Auto-generated method stub super.onPause(); background.pause(); } protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); dao.close(); background.stop(); background.release(); unregisterReceiver(exitReceiver); } public void initCompent(){ background = new MediaPlayer(); try { background.setDataSource("/mne/sdcard/Music/gameover.mp3"); background.prepare(); background.setLooping(true); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } Intent intent = getIntent(); score = intent.getIntExtra("score", 1000); dao = SkyGameDataBaseDao.getInstance(this); tvMessage = (TextView)findViewById(R.id.success_tv_message); String message = this.getResources().getString(R.string.success_message_content); message = fillMessage(message, ""+score); tvMessage.setText(message); etName = (EditText)findViewById(R.id.success_et_name); btnConfirm = (Button) findViewById(R.id.success_btn_confirm); btnConfirm.setOnClickListener(btnConfirmHandler); }

46

基于Android的飞机大战游戏设计与开发

private String fillMessage(String message,String data){

String ret = message.replaceAll("[.*?]","["+data+"]");

return ret;

}

}

4.3.6 Lose界面

本界面与Win界面逻辑相似,对于所用到的技术等不做赘述。

本界面使用的layout生成的界面效果如图4-21所示,代码如下:

直升机游戏 基于Android的飞机大战游戏设计与开发

图4-21 Lose界面

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

android:background="@drawable/gameover" >

<TextView

android:id="@+id/tv_game_over_success_tv"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

47

洛阳师范学院2013届本科生毕业设计

android:textSize="20dp"

android:gravity="center_horizontal|center_vertical" android:text="@string/game_over_title"

/>

<TextView

android:id="@+id/tv_game_over_failure_tv" android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:textSize="20dp"

/>

<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content" >

<TextView

android:id="@+id/failure_tv_clew_leave_name" android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_weight="1"

android:textSize="20dp"

android:text="@string/leave_name" />

<EditText

android:id="@+id/et_name"

android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_weight="1"

android:ems="10" />

</LinearLayout>

<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content" >

<Button

android:id="@+id/btn_confirm"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/btn_confirm" />

</LinearLayout>

</LinearLayout>

GameOverFailureActivity具体代码如下:

public class GameOverFailureActivity extends Activity { private final String EXIT = "EXIT";

private MediaPlayer background = null;

private SkyGameDataBaseDao dao = null;

private int score = 0;

48

基于Android的飞机大战游戏设计与开发

private TextView tvMessage = null;

private EditText etName = null;

private Button btnConfirm = null;

private BroadcastReceiver exitReceiver = new BroadcastReceiver(){

public void onReceive(Context context, Intent intent) {

// TODO Auto-generated method stub

GameOverFailureActivity.this.finish();

}

};

private OnClickListener btnConfirmHandler = new OnClickListener(){

public void onClick(View v) {

// TODO Auto-generated method stub

String name = etName.getText().toString().trim();

if(name.equals("")){

Toast.makeText(GameOverFailureActivity.this, "用户名不能为空!请重新输入:", Toast.LENGTH_LONG).show();

}else{

SkyGamePlayer player = new SkyGamePlayer(name, score);

dao.insertPlayer(player);

etName.setText("");

Toast.makeText(GameOverFailureActivity.this, "信息已成功保存!", Toast.LENGTH_LONG).show();

Intent intent = new

Intent(GameOverFailureActivity.this,SkyGameScoreButtonOnClickActivity.class); GameOverFailureActivity.this.startActivity(intent);

}

}

};

protected void onStart() {

// TODO Auto-generated method stub

super.onStart();

background.start();

}

protected void onRestart() {

// TODO Auto-generated method stub

super.onRestart();

try {

background.prepare();

} catch (IllegalStateException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

49

洛阳师范学院2013届本科生毕业设计

} background.start(); } protected void onPause() { // TODO Auto-generated method stub super.onPause(); background.pause(); } protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); dao.close(); background.stop(); background.release(); unregisterReceiver(exitReceiver); } public void initCompent(){ background = new MediaPlayer(); try { background.setDataSource("/mnt/sdcard/Music/gameover.mp3"); background.prepare(); background.setLooping(true); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } Intent intent = getIntent(); this.score = intent.getIntExtra("score", 1000); tvMessage = (TextView) findViewById(R.id.tv_game_over_failure_tv); String message = this.getResources().getString(R.string.failure_message_content); message = fillMessage(message,""+score); tvMessage.setText(message); btnConfirm = (Button)findViewById(R.id.btn_confirm); btnConfirm.setOnClickListener(btnConfirmHandler ); etName = (EditText)findViewById(R.id.et_name);

50

基于Android的飞机大战游戏设计与开发

dao = SkyGameDataBaseDao.getInstance(GameOverFailureActivity.this); }

private String fillMessage(String message,String data){

String ret = message.replaceAll("[.*?]","["+data+"]");

return ret;

}

}

51

洛阳师范学院2013届本科生毕业设计

第5章 软件测试和调试

目前软件测试仍然是保证软件可靠性的主要手段。测试阶段的根本任务是发现并改正软件中的错误。设计测试方案是测试阶段的关键技术问题,基本目标是选用最少量的高效测试数据,做到尽可能完善的测试,从而尽可能多的发现软件中的问题。

白盒测试盒黑盒测试是软件测试的两类基本方法。一般来说,白盒测试检查程序现有的逻辑正确性;黑盒测试检查程序是否符合设计要求,是否存在隐患。通常在测试过程的早期阶段主要使用白盒方法,而在测试过程的后期阶段主要使用黑盒方法。两者各有所长,相互补充。

该软件主要运用两者相结合的测试方法,以黑盒测试为主。下面将给出详细的测试过程:

5.1 白盒测试法

白盒测试是指按照程序的执行逻辑进行路径检测,即在程序实际运行过程中所经历的每一路径都必须测试到,检查是否存在引起程序出现错误的语句。

1.查看代码中有无明显的逻辑问题(PS:个人认为在人思路清晰的时候查阅代码的逻辑是非常有效的一种检错方式)。

2.Debug

Android应用的开发也可以使用Debug来检测,具体操作与普通的JavaSE,J2EE应用并无差别。在Android虚拟机启动的情况下,不断测试也是非常的方便,只是Eclipse集成ADT开发环境设置的东西比较多,有些DalvikVM只有在第一次启动的时候才会注册Eclipse中的Android项目,在这种情况下Debug还是不是非常的科学。可以采用以下方法做Android开发的测试:

1. 插桩 Android.util.Log常用的方法有以下5个:Log.v() Log.d() Log.i() Log.w() 以及 Log.e() 。根据首字母对应VERBOSE(黑色),DEBUG(蓝色),INFO(绿色), WARN(橙色),ERROR(红色)。

直接打开Android的LogCat就可以看到打印出的信息了,由于Android虚拟机在启动的时候也会打印出很多日志,因此最好设置下过滤器如图5-1所示:

52

基于Android的飞机大战游戏设计与开发

在使用log打印语句时使用语句Log.e(“SkyGame”,”********”);

在Logcat的SkyGame过滤器中既可以看到所有的打印信息了。 图5-1 Logcat设置Filter

5.2 黑盒测试法

黑盒测试是指不关心程序内部的逻辑结构,只检查其输入和输出的内容。对某段程序输入各类参数值,观察其结果。输入的数据可以根据设计内容而确定。

由于游戏本身的特性既决定了本应用的主要测试方式是黑盒测试。

本应用对于鼠标点击玩家飞机移动这一功能就是使用的黑盒测试。对于敌机机群集体出动向我机发射子弹也是在黑盒测试时发现的性能性问题。

问题描述:之前设计的每一个飞机都有一发射的子弹序列。即将该子弹序列为飞机的属性,这样的话,每次绘制一个飞机的时候同时还要在遍历下它的子弹序列,这样的话对于在绘制那边直接调用飞机的绘制方法(PS:该方法中包含遍历子弹序列,并绘制该序列中的子弹),在测试的时候,使用这种方法绘制单个飞机还是可以显示该飞机的子弹的,但是在绘制n个敌机时,暴露出了飞机子弹没有绘制到屏幕上,并且飞机移动非常缓慢的情况。 解决方案:玩家飞机由鼠标控制移动,因此对于玩家飞机有一个单独的绘制函数,玩家子弹使用一个单独的序列来存储。

敌机机群使用一个序列来存储,所有敌机的子弹使用一个序列来管理。

这样的话在绘制玩家子弹的时候,判断当前绘制的子弹与敌机序列中的飞机是否有重叠,如果有则在子弹位置绘制爆炸图片并将该敌机从敌机序列中删除。

在绘制敌机子弹的时候判断当前子弹与玩家飞机的位置是否重叠,如果有则在子弹位置绘制爆炸,并对玩家飞机的血块做出减少操作。

53

直升机游戏 基于Android的飞机大战游戏设计与开发

洛阳师范学院2013届本科生毕业设计

第6章 工作总结和展望

本应用以Eclipse集成Android ADT为开发环境,在DalvikVM中对自己编写的应用进行测试。

当时在编写这个应用的时候遇到过很多的问题,有技术性的问题,有性能性的问题,各种常见的异常,空指针,数组越界等问题。当时在做这个项目时最大的压力是时间,在对Android不是非常熟悉的情况下,培训机构仅仅给了我们十几天的时间让我们做出一个完善的Android应用。

记得在刚下需求的时候,前两天一直都在研究需求,分析哪个功能点可以使用什么技术,哪个地方使用某种算法相对来说效率更高。记得有一次被卡在了activity之间的跳转上,由于是在SurfaceView中跳转到另一个Activity的,一时间对于如何在当前SurfaceView中获得它所在的Activity的引用感到不知所措,最后经历了两天的摸索,终于攻克了这一难点。经过自己认真研究攻克的技术点,总是会给人以很大的信心继续走下去,它所带来的那种成就感是难以用语言名状的。

经过十多天的开发自己完成了一个相对比较完整的Android应用,这对于之前一直想要从事开发的我来说真的是很大的一次激励。在这个项目之后,感觉自己在实习公司这种真实的开发环境下也很少能够体验到可以与该项目压力相当的需求了。

这个项目是我个人编程经历的一个节点,也是从这个项目之后,我正式进入公司实习真正的进入了IT这个行业。而这个项目所带给我的开发上的经验就是:无论当前的项目有多难,需求有多么复杂,时间有多么紧迫,一定要分析透需求,之后一点一点的按照常规的开发进行下去,遇到技术难点不要跳过,解决掉这个再进行下一步的开发,否则,问题越积越多你会对自己失去信心。

对于未来,希望自己做一个技术比较过硬的IT编程人员,能够将设计模式融会贯通到自己的代码中,希望自己能够有一个让人一看就一目了然的编程风格。

虽然有些碎碎念,但终归是开发过程中的一些真实的经历。值得自己铭记。

54

基于Android的飞机大战游戏设计与开发

参考文献

[1] 佘志龙等.Google Android SDK 开发范例大全.人民邮电出版社

[2] Android基础编程.上海海同信息科技(IOTEK)有限公司

[3] Android高级编程.上海海同信息科技(IOTEK)有限公司

[4] 辛立伟,张帆等.Java 从初学到精通.电子工业出版社

[5] 刘济华.漫谈设计模式——从面向对象开始.清华大学出版社

[6] Android中文翻译组——Android开发者指南(2).chm

[7] Android 中文翻译组——Android中文API合集(7).chm

[8] 萨师煊等.数据库系统概论[M].高等教育出版社,2001

[9] 严蔚敏等.数据结构[M].清华大学出版社,2001

[10] 张海藩.软件工程导论[M].清华大学出版社,2003

[11] 郭郑州.ORACLE完全学习手册.清华大学出版社,2011

[12] 闪四清.SQL Server 2005基础教程.清华大学出版社,2007

[13] Java正则表达式教程. http://blog.csdn.net/hiyu2218/article/details/3404591

55

洛阳师范学院2013届本科生毕业设计

致 谢

在整个毕业设计的过程中,老师和朋友给了我很大的帮助,特别是XXX老师给我提供了很多的指导和帮助,从他身上我学到了很多东西,他认真负责的工作态度和深厚的理论水平都使我受益匪浅。

此外,在系统的开发过程中,还有其他同学的热心帮助,共同解决了不少难点问题。在此不一一列举,谢谢大家对我的支持!

56

二 : 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

音乐商店 网上音乐商店的设计与开发

三 : 【Android2D游戏开发十九】(必看篇)剖析Back与Home键及

在这里先向各位童鞋道个歉!我解释下:当我在给大家讲解的时候会附带上源码,可是这个源码是演示代码,为了让大家看的清楚,所以我会尽可能把一些与其无关的删掉,但是发现演示代码还是被一些童鞋们效仿,导致不少童鞋问我为什么程序执行后切入后台重新进入会报异常的问题!(这里我就全面讲解下运行机制,希望以后大家有类似问题自己就能解决了哈~)

切入后台操作比如点击HOME按键,点击返回按键...

那么重新进入程序报异常主要Surfaceiew 有两点会报异常:

第一:提交画布异常!如下图(模拟器错误提示,以及Logcat Detail)



解决代码:

view sourceprint?01 public void draw() {

02 try {

03 canvas = sfh.lockCanvas();

04 if (canvas != null) {

05 canvas.drawColor(Color.WHITE);

06 canvas.drawBitmap(bmp, bmp_x, bmp_y, paint);

07 }

08 } catch (Exception e) {

09 Log.v("Himi", "draw is Error!");

10 } finally {//备注1

11 if (canvas != null)//备注2

12 sfh.unlockCanvasAndPost(canvas);

13 }

14 }

先看备注1这里,之前的文章中我给大家解释过为什么要把 sfh.unlockCanvasAndPost(canvas); 写在finally中,主要是为了保证能正常的提交画

布.今天主要说说备注2,这里一定要判定下canvas是否为空,因为当程序切入后台的时候,canvas是获取不到的!那么canvas一旦为空,提交画

布这里就会出现参数异常的错误!

下面来说另外一种情况:线程启动异常!如下图(模拟器错误提示,以及Logcat Detail)

  

这种异常只是在当你程序运行期间点击Home按钮后再次进入程序的时候报的异常,异常说咱们的线程已经启动!为什么返回按钮就没事?

OK,下面我们就要来先详细讲解一下Android中Back和Home按键的机制!然后分析问题,并且解决问题!

先看下面MySurfaceViewAnimation.Java的类中的代码:view sourceprint?01 package com.himi;

02 import java.util.Vector;

03 import android.content.Context;

04 import android.graphics.Bitmap;

05 import android.graphics.BitmapFactory;

06 import android.graphics.Canvas;

07 import android.graphics.Color;

08 import android.graphics.Paint;

09 import android.util.Log;

10 import android.view.GestureDetector;

11 import android.view.MotionEvent;

12 import android.view.SurfaceHolder;

13 import android.view.SurfaceView;

14 import android.view.View;

15 import android.view.GestureDetector.OnGestureListener;

16 import android.view.SurfaceHolder.Callback;

17 import android.view.View.OnTouchListener;

18 /**

19 *@author Himi

20 *@SurfaceView 运行机制详解

21 */

22 public class MySurfaceViewAnimation extends SurfaceView implements Callback, Runnable {

23 private Thread th;

24 private SurfaceHolder sfh;

25 private Canvas canvas;

26 private Paint paint;

27 private Bitmap bmp;

28 private int bmp_x, bmp_y;

29 public MySurfaceViewAnimation(Context context) {

30 super(context);

31 this.setKeepScreenOn(true);

32 bmp = BitmapFactory.decodeResource(getResources(), R.drawable.himi_dream);

33 sfh = this.getHolder();

34 sfh.addCallback(this);

35 paint = new Paint();

36 paint.setAntiAlias(true);

37 this.setLongClickable(true);

38 th = new Thread(this, "himi_Thread_one");

39 Log.e("Himi", "surfaceChanged");

40 }

41 public void surfaceCreated(SurfaceHolder holder) {

42 th.start();

43 Log.e("Himi", "surfaceCreated");

44 }

45 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

46 Log.e("Himi", "surfaceChanged");

47 }

48 public void surfaceDestroyed(SurfaceHolder holder) {

49 Log.e("Himi", "surfaceDestroyed");

50 }

51 public void draw() {

52 try {

53 canvas = sfh.lockCanvas();

54 if (canvas != null) {

55 canvas.drawColor(Color.WHITE);

56 canvas.drawBitmap(bmp, bmp_x, bmp_y, paint);

57 }

58 } catch (Exception e) {

59 Log.v("Himi", "draw is Error!");

60 } finally {//备注1

61 if (canvas != null)//备注2

62 sfh.unlockCanvasAndPost(canvas);

63 }

64 }

65 public void run() {

66 while (true) {

67 draw();

68 try {

69 Thread.sleep(100);

70 } catch (Exception ex) {

71 }

72 }

73 }

74 }

 

以上是我们常用的自定义SurfaceView,并且使用Runnable接口老框架了不多说了,其中我在本类的构造、创建、状态改变、消亡函数都加上打印!

OK,下面看第一张图:(刚运行程序)



上图的左边部分是Dubug!这里显示我们有一条线程在运行,名字叫"himi_Thread_one";

上图的左边部分是LogCat日志!大家很清晰的看到,当第一次进入程序的时候,会先进入view构造函数、然后是创建view、然后是view状态改变、OK,这个大家都知道!

下面我来点击Home(手机上的小房子)按键!这时程序处于后台!然后重新进入程序的过程!



上图可以看出我们的线程还是一条、这里主要观察从点击home到再次进入程序的过程:(过程如下):

点击home 调用了view销毁、然后进入程序会先进入view创建,最后是view状态改变!

上面的过程很容易理解,重要的角色上场了~Back 按钮!点我点击Back按钮看看发生了什么!



 

先看左边的Debug一栏,多了一条线程! 看LogCat发现比点击Home按键多调用了一次构造函数!

好了,从我们测试的程序来看,无疑,点击Home 和 点击 Back按钮再次进入程序的时候,步骤是不一样的,线程数量也变了!

那么这里就能解释为什么我们点击Back按钮不异常、点击Home会异常了!

原因:因为点击Back按钮再次进入程序的时候先进入的是view构造函数里,那么就是说这里又new了一个线程出来,并启动!那么而我们点击Home却不一样了,因为点击home之后再次进入程序不会进入构造函数,而是直接进入了view创建这个函数,而在view创建这个函数中我们有个启动线程的操作,其实第一次启动程序的线程还在运行,so~这里就一定异常了,说线程已经启动!

有些童鞋会问,我们为何不把th = new Thread(this, "himi_Thread_one");放在view创建函数中不就好了??!!

没错,可以!但是当你反复几次之后你发现你的程序中会多出很多条进程!(如下图)



 

虽然可以避免出现线程已经启动的异常,很明显这不是我们想要的结果!

那么下面给大家介绍最合适的解决方案:

修改MySurfaceViewAnimation.java

view sourceprint?01 package com.himi;

02 import android.content.Context;

03 import android.graphics.Bitmap;

04 import android.graphics.BitmapFactory;

05 import android.graphics.Canvas;

06 import android.graphics.Color;

07 import android.graphics.Paint;

08 import android.util.Log;

09 import android.view.SurfaceHolder;

10 import android.view.SurfaceView;

11 import android.view.SurfaceHolder.Callback;

12 /**

13 *@author Himi

14 *@SurfaceView 运行机制详解(修改后的最佳方式)

15 */

16 public class MySurfaceViewAnimation extends SurfaceView implements Callback, Runnable {

17 private Thread th;

18 private SurfaceHolder sfh;

19 private Canvas canvas;

20 private Paint paint;

21 private Bitmap bmp;

22 private int bmp_x, bmp_y;

23 private boolean himi; //备注1

24 public MySurfaceViewAnimation(Context context) {

25 super(context);

26 this.setKeepScreenOn(true);

27 bmp = BitmapFactory.decodeResource(getResources(), R.drawable.himi_dream);

28 sfh = this.getHolder();

29 sfh.addCallback(this);

30 paint = new Paint();

31 paint.setAntiAlias(true);

32 this.setLongClickable(true);

33 Log.e("Himi", "surfaceChanged");

34 }

35 public void surfaceCreated(SurfaceHolder holder) {

36 himi = true;

37 th = new Thread(this, "himi_Thread_one");//备注2

38 th.start();

39 Log.e("Himi", "surfaceCreated");

40 }

41 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

42 Log.e("Himi", "surfaceChanged");

43 }

44 public void surfaceDestroyed(SurfaceHolder holder) {

45 himi = false;//备注3

46 Log.e("Himi", "surfaceDestroyed");

47 }

48 public void draw() {

49 try {

50 canvas = sfh.lockCanvas();

51 if (canvas != null) {

52 canvas.drawColor(Color.WHITE);

53 canvas.drawBitmap(bmp, bmp_x, bmp_y, paint);

54 }

55 } catch (Exception e) {

56 Log.v("Himi", "draw is Error!");

57 } finally {

58 if (canvas != null)

59 sfh.unlockCanvasAndPost(canvas);

60 }

61 }

62 public void run() {

63 while (himi) {//备注4

64 draw();

65 try {

66 Thread.sleep(100);

67 } catch (Exception ex) {

68 }

69 }

70 }

71 }

这里修改的地方有以下几点:

1. 我们都知道一个线程启动后,只要run方法执行结束,线程就销毁了,所以我增加了一个布尔值的成员变量 himi(备注1),这里可以控制我们的线程消亡的一个开关!(备注4)

2.在启动线程之前,设置这个布尔值为ture,让线程一直运行.

3.在view销毁时,设置这个布尔值为false,销毁当前线程!(备注3)

4.把我们创建线程实例也放在view创建中!(备注2),为什么要放这里,因为不管点击Back还是Home都会执行view创建函数的!如果你写在构造里,那么点击home的时候就又空指针啦!

OK,这里图和解释够详细了,希望大家以后真正开发一款游戏的时候,一定要严谨代码,不要留有后患哈~

 

四 : Android游戏开发设计步骤

1 手机游戏开发简介

游戏的本质就是在屏幕上不断地显示和更新图片,只不过不是胡乱地更新,而是根据程序逻辑来控制。一款完整的游戏需要多方面的知识,比如游戏的创意、背景、 故事情节、游戏音效,游戏风格、游戏类型、运行速度、适配机型等。而且,游戏的开发需要策划、美工、程序、测试的协同工作和默契配合完成的。

2 游戏框架设计

首先需要一个用于显示游戏界面的视图类,接着需要构建一个整个游戏逻辑类来控制当前屏幕显示哪个界面,甚至对界面进行一些逻辑上的处理。在创建和控制了视图显示之后,要让游戏能够动起来,需要开启一个线程来实时更新视图显示界面并刷新视图。

3 地图设计

通常游戏中的地图是多个小块组成的一个完整的大地图,而组成这些小块的数据一般可以使用一个二维数组来存储,然后通过程序以最快的方式将这些地图数据对应 的小块映射到屏幕上组成一幅完整的地图。当然,这些数据也不是我们从键盘上一个个地输入进去的,一般情况下先由程序员做一个地图编辑器,在这个地图编辑器 中用鼠标点击再保存,或者是从网络上下载一些成熟的编辑器,比如用mappy这样的工具生成地图,再用脚本语言为mappy写一个应该保持成什么格式的程 序。通常地图分为45度角、俯视角和侧视角。

4 主角设计

游戏中的主角在这里成为“精灵”,当然精灵包括的范围很广,不仅仅是主角,还有npc、道具等。既然是精灵,必然有很多动画,动画本身就是将图片一帧一帧 地连接起来,循环地播放每一帧形成的。同样可以使用自己编写的精灵编辑器去编辑精灵,将精灵拆成很多部分,然后再组合起来,这样可以节省大量的空间。精灵 类的特性,每次只能使用一个图像而不是多个图像来填充屏幕,可以有好几帧,但是一次只有一个显示。

5 图层管理器

只需要将所有图层(包括地图、主角)一起添加到图层管理器中,然后设置视图查看时的位置及大小,调用图层管理器的paint方法就可以绘制出图层。绘制的顺序是按添加的反顺序,既先添加的后绘制,以免图层被覆盖之后显示不出来。

6 游戏音效

首先我们将游戏中的音效分为如下几类:背景音乐、剧情音乐、音效(动作的音效、使用道具音效、辅助音效)等。背景音乐一般需要一直播放,而剧情音乐则只需要在剧情需要的时候播放,音效则是很短小的一段。

7 游戏存档

游戏存档就是将玩家当前游戏的进度等信息存储下来,在玩家再次进入游戏时可以通过读取上次的存档来接着上次的进度继续游戏。

(1).明确需要存储的数据

首先,为了再次游戏能够顺利地转载上次的进度,需要保存主角的一些属性(包括位置,生命,攻击,防御等),还需要保存当前地图的一些属性(比如行,列,当 前层数),同样还需要保存对话的相关内容,最后需要保存游戏的整个地图数据(每一层),还有当前的音乐状态。

(2).保存数据

获取存储的数据->将数据打包到properties中->将properties写入到文件中。

(3).装载数据

打开文件->将文件流装载进properties中->通过properties.get方法得到指定标签的数据-》将得到的数据赋值给应用程序中对应的变量。

在退出游戏时,不管玩家是否保存都将自动保存下来。

本文标题:游戏设计与开发-基于Android的飞机大战游戏设计与开发
本文地址: http://www.61k.com/1055054.html

61阅读| 精彩专题| 最新文章| 热门文章| 苏ICP备13036349号-1