使用 RAD 进行 Intel x86 Android 游戏开发
设计一个跨平台兼容的 x86 Android 游戏
该.zip文件还包含已编译的"MonkeyGame-debug.apk"二进制文件,可以在模拟器中运行。在"game.build"文件夹中有一个HTML5版本。要测试游戏,请在Chrome浏览器中运行MonkeyGame.html。
介绍
这里列出了许多支持x86 Android的RAD跨平台游戏开发工具/引擎:http://software.intel.com/en-us/blogs/2012/03/13/game-engines-for-android
我们将使用Monkey游戏引擎为 基于Intel®架构的设备 构建一个全新的、基于NDK的Android游戏。它的优点是为Android上的ARM和x86架构提供了预编译的原生GL共享库,否则我们就必须使用NDK工具链重新编译它们。你可以在随附源代码的"jni"文件夹中找到这些库。Android打包器包含x86和ARM共享库在.apk中,在部署期间,它会根据目标设备是ARM还是x86安装必要的库。
因此,生成的.apk是跨平台兼容的,不需要为两种不同的架构进行原生重新编译。 
背景
记忆游戏是经典游戏的变体,你一次翻开两张牌来找到匹配的对。当所有匹配的对都被翻开时,游戏结束。目标是用最少的步数翻开所有对。在此游戏变体中使用一副部分扑克牌。
游戏特色
- 24张牌以增加难度
- 带声音和粒子效果的动画卡片
- 基于Tween的前端
技术特性
- 游戏状态类
- 碰撞类
- 程序化字体生成类
- 自动适应类,使用硬编码的虚拟分辨率来缩放以适应不同的实际设备长宽比和分辨率 - 这非常重要,因为Android设备碎片化是一个主要问题
- Tween类
- 粒子引擎类
- 高分榜类
- 计时器类
我选择这款游戏作为本文的教学工具,因为它包含了所有核心游戏元素,并清晰地展示了游戏循环的实时性以及将要使用的设计原则。它是一款2D游戏,因为对于现代触摸屏移动设备来说,与2D精灵交互提供了更直观的界面。
技术信息
引擎提供的快速原型开发的一些要求
- 快速调试-编译-运行周期可以在任何符合标准的HTML5浏览器中执行。
- 游戏循环定义清晰,包含OnCreate()、OnUpdate()和OnRender()方法。
- 脚本语言是面向对象的。它具有用于精灵操作和游戏数据结构的基本语言构造。自动垃圾回收。
使用代码
GameState变量使状态机在其各个阶段之间循环。它在开始时初始化为STATE_MENU。此变量还控制代码的更新和渲染部分。
Method SetState (state:Int)
     GameState = state
     Select GameState
           Case STATE_MENU
               //The initial menu layer
           Case STATE_PLAYING
               //The game layer
           Case STATE_PAUSED
               //The game layer paused
           Default
               Print "ERROR: Unknown game state!"
     End
End
      
      
      
      
      
      让我们检查输入并更新游戏状态
在OnUpdate()方法中,我们调用碰撞类的Card[i].Update()方法。根据指针是否与卡片精灵碰撞,它返回卡片的可见或隐藏状态。如果点击了两张卡片,我们会增加尝试次数。
Case STATE_PLAYING
//check for two card clicks. show cards that were clicked by hiding corresponding cardback
 For Local i : Int = 0 Until NUM_CARDS
  Card[i].Update()
  If Card[i].Pressed = True And Card[i].Visible = True And click < 2 Then 
    Card[i].Visible = False
    savecard[click] = i
    click = click + 1
        If click = 2 Then
        slowdown = Millisecs()
        turns = turns + 1   //increment tries after two cards have been clicked
        End If
    PlaySound boom
   End If                    
 Next
 
     
如果找到匹配的对,我们将保持这些卡片翻开;否则,如果对不匹配,我们会使相应的卡背可见并将卡片再次隐藏。1.5秒的延迟允许两张卡片都足够长的时间可见,以便玩家看到它们不匹配。
If click = 2  Then        
    If coinplace[savecard[0]] = coinplace[savecard[1]] Then  //a matching pair is found
    click =0
    Else            
       If  Millisecs()-slowdown > slowlimit Then  // wait before covering cards again        
      Card[savecard[0]].Visible = True
      Card[savecard[1]].Visible = True
      click =0                          // reset to accept clicks on next two cards
       Endif            
    Endif
End If
Local drawncard:Bool = False
For Local i : Int = 0 Until NUM_CARDS
    If Card[i].Visible = True Then                
    drawncard = True
    End If            
    Next
If drawncard = False Then Restart()    // If no cards are left to uncover End game
    
    当没有任何卡片有背卡覆盖时,因为碰撞类返回背卡可见状态为False,drawncard布尔值将变为False,游戏结束。
摇动这些卡片
没有动画的游戏没什么乐趣。让我们来制作一些眼球糖果。整型变量n完成了神奇的操作。n = -1*n可以在+1和-1之间切换n。DrawImage()方法具有旋转和缩放参数。我们调整卡片精灵的旋转角度在+1度和-1度之间,以创建震动卡片的效果。
//refactored because the renderscreen method appeared to work in the pause state, and halts 
// game update, except for cards vibrating
Method RenderScreen()
     Cls
    
         // for a resolution of 800x600 render cards in 6 columns and 4 rows, starting at 
        // x offset 33 pixels and y offset 30 pixels
     For Local i:Int =  0 Until NUM_CARDS        
      DrawImage coins[coinplace[i]], 33+((i Mod 6)*100), 30 +(Int(i/6)*140)
     Next
                        
    //as n toggle between +-1, the card sprites oscillate(animation) between +-1 degree
    //if collision check returns visible attribute show cardback else hide it
     For Local i : Int = 0 Until NUM_CARDS                        
      If Card[i].Visible = True                         
                     n = -1*n
            DrawImage (card, Card[i].PositionX, Card[i].PositionY, n*1.0, 1.0, 1.0)
      Else
        DrawImage (cardclick, Card[i].PositionX, Card[i].PositionY, 0, 1.0, 1.0)
      End If
     Next
                        
     DrawImage (sidebar, 620, 0)
     DrawImage pointer.image, pointer.x, pointer.y
                        
    font.Draw( turns, 710, 290, 3, 3, 0.5, 1.0 )  //render the procedural font for number of tries
    
End下载中的代码注释很丰富,几乎不需要解释,但是类成员和方法将在下面详细介绍。- MyGame类
- OnCreate()方法:实例化游戏对象 - 图像、声音、成员和变量。
- OnUpdate()方法:轮询/中断检查鼠标/触摸屏/键盘输入,并相应地更改游戏状态和对象状态。
- OnRender()方法:根据游戏状态(菜单、进行中或暂停)和游戏对象的状态属性渲染图形。
- Collision类:接收触摸/鼠标坐标和碰撞的精灵,并返回相应精灵对象的可见或隐藏属性。
- Font类:这是一个程序化生成的字体。
- Tween类:缓动精灵对象,使用不同的方程使它们入场或出场
- Particle类:触摸时生成粒子,并在粒子生命周期结束后"删除"它们
- Highscore类:按升序或降序对高分进行排序
- Timer类:用于延迟和同步
- Autofit类:提供一个硬编码的"虚拟"分辨率,并缩放图形以适应不同的屏幕分辨率(长宽比),而不会沿x、y屏幕空间坐标轴引起失真。如果
- 物理设备的长宽比与"虚拟"长宽比不同。
- 屏幕方向发生变化。
遇到的问题
对于性能较低的移动设备,我尝试实现了一个粒子引擎,因为集成的粒子类对资源占用有点多。编译器不喜欢我在游戏循环的特定点创建一个精灵对象列表!但我确信,只要有时间,肯定有解决办法。最终实现了粒子类。粒子精灵是在OnCreate()方法中静态创建的。我们不是在粒子生命周期结束后销毁精灵对象,而是通过在数组中动态交换它们来"移除"它们。 
我也非常怀念集成的调试器。
开发和测试
最初的开发/调试是在浏览器中进行的,以实现快速的周转时间。缩放和实际运行时性能的设备测试是在一台1024x600、7英寸的Android平板电脑上进行的。最后的测试是在Android模拟器中的x86 AVD上进行的。尽管HTML5编译很顺利,但在Android目标上测试时,从碰撞类返回图形对象的句柄时抛出了一个空对象异常,但实现了一个合适的解决方法。
屏幕截图 - 游戏在Android模拟器的x86 AVD上运行


鸣谢
感谢Monkey编码论坛上的所有伙计,他们总是在问题出现时随时提供帮助。
总结
源代码就在那里供您随意玩耍,我希望您能在x86 Android平台上将其应用到您自己的游戏中。如果需要任何澄清或解释,请提问。另外,如果您发现任何需要纠正的地方,请告知我。
您可以尝试更改粒子引擎的参数,并使用不同的缓动效果来查看它们的工作原理。
历史
8/11/2012
5/11/2012
待办事项
- 在线全球高分榜
- 避免功能蔓延  



