logo资料库

10编写推箱子游戏程序(第五步).docx

第1页 / 共9页
第2页 / 共9页
第3页 / 共9页
第4页 / 共9页
第5页 / 共9页
第6页 / 共9页
第7页 / 共9页
第8页 / 共9页
资料共9页,剩余部分请下载后查看
编写推箱子游戏程序(第五步)——指挥搬运工走动
一、本文目标
二、实现思路和步骤
2.1 实现思路
2.2 实现步骤
三、实现搬运工向下走动功能
3.1 onTouchEvent方法的实现
参考文献:
3.2 touch_below_to_man方法的实现
3.3 getRect方法的实现
3.4 onDraw方法的实现
3.5 GameBitmaps类
四、实现搬运工向右走动功能
五、禁止搬运工走出边界
作业
编写推箱子游戏程序(第五步)——指挥搬运工走动1 叶常春(iamdouble@163.com) 一、本文目标 上一篇文章结尾,实现了绘制游戏区域和搬运工的功能。这一功能是玩家点击开始游戏 按钮并选择关卡后触发的,如图 1 所示。下文中,我们把选择关卡后触发的界面叫做游戏界 面。 图 1 玩家点击开始游戏按钮并选择关卡后触发游戏界面 本文在游戏界面实现玩家指挥搬运工走动的功能,描述如下: 1. 当玩家手指触摸搬运工的上方(下方、左侧、右侧)单元格,则搬运工将走到上方 1本文遵循 Apache License 2.0 协议。你可以修改和再发布本文档,但须保留原著者和采用 Apache License 2.0 协议。
(下方、左侧、右侧)单元格。如图 2 所示。 2. 不允许出边界。 3. 玩家触摸到搬运工上方(下方、左侧、右侧)单元格之外的区域,则视为无效指令, 搬运工不走动。 图 2 游戏界面上,搬运工走动功能的效果图 你将学到的知识内容有: 1. 利用回调函数 onTouchEvent 处理屏幕触摸事件。 2. 利用 invalidate 或 postInvalidate 方法刷新视图。 二、实现思路和步骤 2.1 实现思路 在游戏界面,玩家触摸手机屏幕的时候,将引发 Android 系统执行 onTouchEvent 回调函 数,在此回调函数内执行以下工作: 1. 判断触摸位置是否落在搬运工的上方(下方、左侧、右侧)单元格。 2. 若是,则修改搬运工的位置到上方(下方、左侧、右侧)单元格;要求重新绘制画 面。 游戏画面重新绘制后,玩家将看到搬运工走动了。 那么,如何判断触摸位置是否落在搬运工的上方(下方、左侧、右侧)单元格呢?以判 断是否落在上方单元格为例,答案是: 1. 记住搬运工当前所处的单元格(记作(mManRow, mManColumn),也就是说搬运工处 在 mManRow 行 mManColumn 列)。 2. 我们知道,每一单元格是一个正方形,记它的宽度为 mCellWidth。搬运工上方单元 格的矩形区域 above 的左上角是: 1) 左端:mManColumn * mCellWidth
2) 上端:(mManRow – 1) * mCellWidth 上方单元格的矩形区域 above 的右下角是: 3) 右端:(mManColumn + 1) * mCellWidth 4) 下端:mManRow * mCellWidth 3. 获取通过 onTouchEvent 回调函数的参数传入的触摸位置(touch_x, touch_y)。 4. 如果触摸位置(touch_x, touch_y)落在上方单元格 above 内,则得出“是”的结论, 否则得出“否”的结论。 类似地,我们可以判断触摸位置是否落在搬运工下方(below)、左侧(left)、右侧(right) 的单元格内。 2.2 实现步骤 我们遵循以下步骤来实现游戏界面上玩家指挥搬运工走动功能: 1. 实现搬运工向下走动。 2. 实现搬运工向右走动。 3. 禁止搬运工走出边界。 4. 搬运工向上、向左走动功能留作作业。 三、实现搬运工向下走动功能 往下阅读前,在你的脑子里再过一遍上一章讲的实现思路。你理顺了没有? 3.1 onTouchEvent 方法的实现 上面提到,玩家在游戏界面触摸手机屏幕的时候,将引发 Android 系统执行 onTouchEvent 回调函数。这一回调函数是 View 类的方法。我们需要在 GameView 类内添加此回调函数(做 法参阅“本系列文章《Android Studio 用法》的‘重载/覆盖(Override)父类方法’一节”)。 代码如表 1 所示。 表 1 GameView 类的 onTouchEvent 回调函数 com.yescorp.moveboxgame.GameView.java public boolean onTouchEvent(MotionEvent event) { if (event.getAction() != MotionEvent.ACTION_DOWN) return true; int touch_x = (int) event.getX(); //触摸点的x坐标 int touch_y = (int) event.getY(); //触摸点的y坐标 if (touch_blow_to_man(touch_x, touch_y, mManRow, mManColumn)) mManRow++; postInvalidate(); 1 2 3 4 5 6 7 8 9
return true; 10 11 } 对表 1 的代码,说明如下:  第 1 行,onTouchEvent 返回 boolean 类型值,即 true 或 false,返回 true 表明触摸事件 已经处理完毕,返回 false 表明触摸事件需要进一步处理。在这里,不存在进一步处理 的情形,故返回 true 就可以了。第 3 行和第 10 行就是这么做的。  第 1 行中,MotionEvent 类型的 event 参数是一类事件对象,它内部包含动作信息和触 摸点的坐标信息。  第 2 行,是判断触摸动作是否是按下(ACTION_DOWN),若不是,则忽略这一事件,不 处理这一事件。我们用手指触摸屏幕,滑动手指,直至离开屏幕,会产生三类动作:按 下(ACTION_DOWN)、滑动(ACTION_MOVE)和弹起(ACTION_UP)。在这里,我们不 处理后两类动作。关于触摸事件,参阅“参考文献(1)”。  第 2,3 行的作用就是,忽略动作类型为滑动(ACTION_MOVE)和弹起(ACTION_UP)的 触摸事件。这样,第 4~10 行代码针对的是动作类型为按下(ACTION_DOWN)的触摸事 件。  第 5,6 行得到触摸点的 x, y 坐标。(int)是类型强制转换。由于 event.getX()返回的是 float 型的值,所以需要从 float 转为 int。  第 7 行 是 判 断 触 摸 点 是 否 落 在 搬 运 工 的 下 方 单 元 格 。touch_blow_to_man(touch_x, touch_y, mManRow, mManColumn)的作用是判断触摸点(touch_x, touch_y)是否落在搬 运工的下方单元格。touch_blow_to_man 函数的代码实现在后面讲述。变量 mManRow 和 mManColumn 的作用是记住搬运工当前所处的单元格的行号和列号。行号和列号都 从 0 开始计数。row 的中文意思是行,column 的中文意思是列。变量 mManRow 和 mManColumn 是 GameView 类的成员变量,定义如下: private int mManRow = 0; private int mManColumn = 0; 这两个变量都初始化为 0,这使得选择关卡后进入游戏界面时,搬运工落在左上角单元 格。  第 8 行在第 7 行的判断成立的情况下执行,作用是使搬运工向下走一步——行号增 1, 列号不变。  第 9 行 postInvalidate 方法的作用是要求使游戏界面(即 GameView 视图)失效。这将 引发 GameView 的 onDraw 方法的执行。我们知道,绘制画面的操作只能放在 onDraw 方法内。那么,在 onDraw 方法外要求重新绘制画面,该怎么做呢?这里的情形是,在 onTouchEvent 方法内,搬运工向下走了一步,要求重新绘制画面呈现搬运工的新位置, 该怎么做呢?答案是,调用 invalidate 方法或 postInvalidate 方法。关于这两个方法,参 阅参考文献(2)。  第 9 行执行后,将引发 GameView 的 onDraw 方法执行一次。onDraw 方法内,将获取搬 运工的新位置,而后绘制搬运工。onDraw 方法的代码实现在后面说明。  第 10 行,返回 true 值表明触摸事件处理完毕。 参考文献: (1) Android 中 TouchEvent 触摸事件机制。 http://www.open-open.com/lib/view/open1470468705188.html。
(2) Android 笔记:invalidate()和 postInvalidate() 的区别及使用 http://blog.csdn.net/mars2639/article/details/6650876 。 3.2 touch_below_to_man 方法的实现 接上一节,touch_below_to_man(touch_x, touch_y, mManRow, mManColumn)方法的作用 是判断触摸点(touch_x, touch_y)是否落在搬运工的下方单元格。搬运工目前处在(mManRow, mManColumn)单元格内。这一方法的代码如表 2 所示。 表 2 GameView 类的 touch_blow_to_man 方法 com.yescorp.moveboxgame.GameView.java private boolean touch_blow_to_man(int touch_x, int touch_y, int manRow, int manColumn) { int belowRow = manRow + 1; Rect belowRect = getRect(belowRow, manColumn); return belowRect.contains(touch_x, touch_y); } 1 2 3 4 5 对于表 2 中的代码,说明如下:  第 1 行,touch_below_to_man 方法返回 boolean 类型值。返回 true 表明触摸点落在搬 运工的下方单元格。返回 false 表明没有落在搬运工的下方单元格。前两个参数是触摸 点(touch_x, touch_y)。后两个参数是搬运工目前所处的单元格。  第 2 行,求得下方单元格的行号。  第 3 行,调用 getRect 方法得到单元格的矩形区域。getRect 方法的定义在下面讲述。矩 形区域用 Rect 类型的 belowRect 保存。Rect 类是 Android SDK 预定义类,使用方法参阅 本节参考文献(1)。  第 4 行是调用 Rect 类的 contains(x, y)方法来得出坐标点(x, y)是否落在矩形区域内。 belowRect.contains(touch_x, touch_y) 是 得 出 坐 标 点 (touch_x, touch_y) 是 否 落 在 belowRect 内,若是,则返回 true,否则返回 false。 参考文献: (1) android.graphics.Rect 类的详解 http://blog.csdn.net/huangxiaominglipeng/article/details/21597575 。 3.3 getRect 方法的实现 接上一节,getRect 方法的作用是得到单元格(row, column)的矩形区域。代码如表 3 所示。 表 3 GameView 类的 getRect 方法 com.yescorp.moveboxgame.GameView.java private Rect getRect(int row, int column) { int left = (int)(column * mCellWidth); 1 2
int top = (int) (row * mCellWidth); int right = (int)((column + 1) * mCellWidth); int bottom = (int)((row + 1) * mCellWidth); return new Rect(left, top, right, bottom); 3 4 5 6 7 } 对表 3 的代码,说明如下:  第 1 行是 getRect 方法的签名。它返回 Rect 类型的值,即一个矩形区域。参数 row 和 column 分别是单元格的行号和列号,都是从 0 开始编号。这一函数的作用就是得出单 元格在屏幕中的矩形区域。  第 2 行,left 变量存储单元格左边界的 x 坐标值。列号 column 乘以单元格宽度,就是 左边界的 x 坐标值。这一坐标值是一个浮点数,而 left 是整型变量,故使用(int)进行类 型转换。  第 3,4,5 行与第 2 行类似,分别得到单元格上边界的 y 坐标值(存入 top 变量)、右 边界的 x 坐标值(存入 right 变量),下边界的 y 坐标值(存入 bottom 变量)。  第 6 行,是生成一个覆盖单元格的矩形区域对象,并返回。 3.4 onDraw 方法的实现 上面提到,onTouchEvent 方法内会调用 postInvalidate()方法,引发 onDraw 方法执行一 次,作用是刷新 GameView 视图。 为更新搬运工的位置, onDraw 方法的代码做了一处修改:用表 4 中第 19 行代码替换 第 18 行代码。对这一修改,说明如下。  第 18 行是修改前的代码。这行代码的作用是在屏幕左上角绘制搬运工。这一行代 码被删除。  第 19 行代码是根据搬运工所处的单元格(mManRow, mManColumn),求得覆盖该 单元格的矩形区域,存入 destRect。getRect 方法的说明见上一节。  第 20 行,将搬运工图片绘制到 destRect 对应的区域。 表 4 GameView 类的 onDraw 方法 com.yescorp.moveboxgame.GameView.java protected void onDraw(Canvas canvas) { super.onDraw(canvas); //背景色 Paint background = new Paint(); background.setColor(getResources().getColor(R.color.background)); canvas.drawRect(0, 0, getWidth(), getHeight(), background); //绘制游戏区域 Paint linePaint = new Paint(); linePaint.setColor(Color.BLACK); for (int r = 0; r <= CELL_NUM_PER_LINE; r++) canvas.drawLine(0, r * mCellWidth, getWidth(), r * mCellWidth, linePaint); 1 2 3 4 5 6 7 8 9 10 11 12
for (int c = 0; c <= CELL_NUM_PER_LINE; c++) canvas.drawLine(c * mCellWidth, 0, c * mCellWidth, CELL_NUM_PER_LINE* mCellWidth, linePaint); //绘制搬运工 Rect srcRect = new Rect(0, 0, GameBitmaps.ManBitmap.getWidth(), GameBitmaps.ManBitmap.getHeight()); RectdestRect=newRect(0,0,(int)mCellWidth,(int)mCellWidth); Rect destRect = getRect(mManRow, mManColumn); canvas.drawBitmap(GameBitmaps.ManBitmap, srcRect, destRect, null); 13 14 15 16 17 18 19 20 21 } 至此,实现了搬运工向下走动功能。在手机模拟器上或者你自己的真机上跑一跑看看吧。 在手机模拟器上,要用“鼠标点击”来模拟手指触摸。 3.5 GameBitmaps 类 表 4 中使用了 GameBitmaps.ManBitmap ,见第 17,20 行。GameBitmaps 是一个辅助类,功能 是加载和管理图片资源。GameBitmaps 类放在 GameBitmaps.java 文件内,代码如下: public class GameBitmaps { public static Bitmap ManBitmap= null; //需要为每一幅图片安排一个 static 变量 public static void loadGameBitmaps(Resources res){ if (ManBitmap== null) //如果为 null 加载图片;否则说明已经加载过了。 ManBitmap= BitmapFactory.decodeResource(res, R.drawable.eggman_48x48); } //释放图片对象占据的内存 public static void releaseGameBitmaps(){ if (ManBitmap!= null) { ManBitmap.recycle(); ManBitmap= null; } } } 上面的代码只是处理一幅图片。如果有更多图片,则需要: 1. 为每一幅图片定义一个静态(static)变量。 2. 在 loadGameBitmaps 方法内加载每一幅图片。 3. 在 releaseGameBitmaps 方法内释放每一幅图片。 GameView 类的构造函数要调用加载图片的 loadGameBitmaps 方法。如下: public class GameView extends View{ private float mCellWidth; public static final int CELL_NUM_PER_LINE= 12; private Bitmap ManBitmap = null; //改成使用 GameBitmaps 类的图片对象
public GameView(Context context) { super(context); ManBitmap= BitmapFactory.decodeResource(res, R.drawable.eggman_48x48); GameBitmaps.loadGameBitmaps(getResources()); //加载图片。getResources()获取资源管理器对象。 } …… } //省略了若干代码 四、实现搬运工向右走动功能 往下阅读前,在你的脑子里再过一遍第二章讲的实现思路。你理顺了没有? 要实现搬运工向右走动功能,做法是在上文表 1 代码的第 8 行之后,增加以下两行代码: if (touch_right_to_man(touch_x, touch_y, mManRow, mManColumn)) //按在右侧 mManColumn++; 这两行代码的作用是,判断玩家的触摸点(touch_x, touch_y)是否在搬运工的右侧,若是,则 向右侧走动一步。列号加 1(mManColumn++),正是向右侧走动一步。 touch_right_to_man 方法的定义如下: private boolean touch_right_to_man(int touch_x, int touch_y, int manRow, int manColumn) { int rightColumn = manColumn + 1; Rect rightRect = getRect(manRow, rightColumn); return rightRect.contains(touch_x, touch_y); //右侧单元格列号 //求右侧单元格的矩形区域 //落在右侧单元格内吗? } 对照上一章 3.2 节“touch_below_to_man 方法的实现”,我们很容易明白 touch_right_to_man 方法的代码的作用。这里不再赘述。 对于 getRect 方法和 onDraw 方法,相对于第三章,这里无需做任何修改。 至此,实现了搬运工向右走动功能。在手机模拟器上或者你自己的真机上跑一跑看看吧。 在手机模拟器上,要用“鼠标点击”来模拟手指触摸。 五、禁止搬运工走出边界 前面实现了搬运工向下、向右走动的功能,相信你很快能实现右上、向左走动功能。接 下来,我们来实现禁止搬运工走出游戏区域边界。 走出游戏区域边界的效果如图 3 所示。你看搬运工都跑哪里去了……呃,出问题了。
分享到:
收藏