扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
在本页阅读全文(共3页)
用GameCanvas类处理Graphics对象
本文,我将介绍Graphics对象类在游戏中被应用的主要方面。在Tumbleweed游戏中,我要画一个穿越草原的牛仔。在设计中,我将玩家的得分情况显示在屏幕底端,而游戏时间显示在顶端(为使样例不至太过复杂,我仅仅在玩家使用玩规定的时间后才自动终止游戏)。因为牛仔独自在走,我的想法是让他的背景向右或者向左滚动(否则在这个狭小的屏幕上他好象根本没走多远),而时间和分数的显示不动。
为了实现这个设计,我用JumpCanvas类来画屏幕顶端和底端稳定不动的显示条,而动态有趣的图形是用LayerManager类来实现的。
JumpCanva类创建时,你必须先分析要使用的显示屏幕。有一些显示屏幕信息来源于Graphics对象,一些来自于display对象,还有一些直接来源于GameCanvas类的方法。这些信息用来计算对象应当显示的位置,包括计算显示区域的尺寸,LayerManager子类(JumpManager)将在这些区域重复显示对象。如果你坚持维护Java“一处写就,处处运行”的特性,基于动态屏幕尺寸而不是基于常量尺寸设置屏幕显示区域显然更方便合理。
当然,如果从一种目标显示设备转向另一种游戏需要巨大的代码更改量,显然应该根据不同设备显示维护多个游戏版本,运行时根据显示设备信息调用游戏的不同版本,而不是将所有代码置于一个版本中。
在样例游戏中,如果实际显示的设备与游戏中定义的显示设备差别太大,将有一个异常抛出以便Jump类捕获并且显示警告给玩家。为了显得更专业,你应当确保给玩家的警告信息明确标明他针对当前的设备应该下载的版本号。
也许有点多余,但我后面仍然会告诉大家我认为顶端和底端区域合适的尺寸,paint(Graphics g)方法将描绘出顶端的白色和底端的绿色,g.drawString()方法用来记录使用的时间和分数。(不用问我为什么游戏的草原中都是颜色相同的绿草和风滚草;唯一的解释是我对Java的了解当然远甚于西部荒原。)
LayerManager类
一个MIDP游戏中富有乐趣的图形对象常常是用javax.microedition.lcdui.game.Layer类的子类来展示。背景层是javax.microedition.lcdui.game.TiledLayer的实例化,游戏的主人公(和他的敌人)是javax.microedition.lcdui.game.Sprite的实例,这两个类都是Layer的子类。LayerManager类用来组织所有的图形对象层,追加你的图层对象到管理器LayerManager的顺序,决定了他们运行时被描绘的次序(后进先出,最先追加的最后显示)。规则是上面的图层将覆盖下面的图层,不过你可以通过创建包含透明区域的图象文件(上层)来部分地显示下层图层中你想要的部分。
LayerManager类实际中最有用的也许是你可以创建一个远大于显示屏幕的图形对象,然后选择它将在屏幕中被显示的部分显示出来。你可以想象事先做出一幅巨大的、精心描绘的图画,然后用一张纸去覆盖它,而纸上有一个方孔在图画上自由移动。巨幅的整张图画代表你保存在LayberManager中的显示信息,而方孔是任一给定时刻在屏幕上显示图画的窗口。游戏代码设计中,一个比实际屏幕大得多的虚拟显示屏幕对于通常运行在小屏幕显示设备上的游戏是极其重要的,它将为你节省大量的时间和工作。
举个例子,比如你的游戏中包含主人公在一个复杂的地牢中摸索前进的场景,最麻烦的是你必须处理两个独立坐标系统。GameCanva的图形对象有一个坐标系,但是根据LayerManager坐标系的要求,图形中的不同图层又需要被置于LayerManager中。所以,一定要牢记LayerManager.paint(Graphics g, int x, int y)方法根据GameCanvas的坐标系在屏幕上描绘图层,而LayerManager.setViewWindow(int x, int y, int width, int height)方法根据LayerManager坐标系的要求设置LayerManager的可视矩形的显示属性。
在样例中,我设计了一个简单的背景(仅仅是一系列重复显示的草丛),但我想让牛仔从右向左行走的时候总是显示在屏幕中央,所以我需要不断改变LayerManager的图形可显示区域。这项工作是通过在LayerManager的子类JumpManager类中方法paint(Graphics g)中调用setViewWindow(int x, int y, int width, int height)方法完成的。更准确地说,逻辑是这样的:GameThread中的主循环调用JumpCanvas.checkKeys()来检查按键状态,并且通知JumpManager类牛仔此时应该向左、向右还是应该跳跃了。JumpCanva类通过调用setLeft(boolean left)或者jump()方法将信息传至JumpManager。如果信息表明牛仔此时应该向左走(向右与之类似),那么当GameThead调用JumpCanvas告诉JumpManager继续的时候(循环的下一步),JumpManager就告诉牛仔对象向左移动一个象素,同时通过向左移动视窗一个象素来保持牛仔在屏幕中央。你可以通过增加字段myCurrentLeftX的值(这个作为X坐标值传至setViewWindow(int x,int y,int width,int height)),接着调用myCowboy.advance(gameTicks, myLeft)来实现这两个动作。
当然,我可以不移动牛仔,也不把他追加到LayerManager,而只是独立地描绘他,这样也可以确保牛仔在屏幕中央。但显然通过将所有的移动对象置于相对静止的一系列图层中然后将视窗集中在牛仔对象上,借此保持所有对象的运动轨迹的方法要更容易。在通知牛仔前进的同时,风滚草对象Sprites和青草对象TiledLayer也也同时移动,然后检测牛仔是否被风滚草撞倒(在以下章节里我将说明实现这个功能的更多细节)。在移动了所有游戏对象后,JumpManager类调用wrap()方法来检查是否前端窗口到达了背景窗口的边缘,如果是,移动所有的有些对象以便视窗能继续在任一方向无限显示,接着JumpCanvas类重画每一个对象,再次执行游戏主循环。
Wrap()方法需要多说几句。很不幸,如果你想让你的简单背景不确定地重复显示,LayerManager类并不提供现成的隐藏方法. 当传至方法setViewWindow(int x, int y, int width, int height)的坐标参数值大于Integer. MAX_VALUE 时,LayerManager的图形区域将被隐藏,但这似乎对我们的想法没有任何帮助。因此,你必须写自己的函数来避免牛仔Sprite对象离开背景区域。
样例中,背景草丛总是在间隔数值Grass.TILE_WIDTH*Grass.CYCLE后被重画,所以任何时候视窗的X坐标值(myCurrentLeftX)都是背景图形宽度的整数倍。这样每次重画时,我都可以将视窗移回到显示中心,并且同时在相同的方向上移动Sprites对象,这样显然能平滑地阻止牛仔到达背景边界。
Listing 4 JumpManager.java.
|
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者