65.9K
CodeProject 正在变化。 阅读更多。
Home

Diggers:跨平台2D游戏

2012年11月5日

CPOL

4分钟阅读

viewsIcon

34880

downloadIcon

1

使用 SDL 和 Open GLES 2.0 的 2D 跨平台游戏

免责声明

这篇文章是我们 Android at Play:安卓游戏应用大赛 的一部分,并且有不同的要求,需要不那么严格的指导方针。

引言

本文涵盖了为 Windows、Android X86 和 Android ARM 开发 2D 跨平台游戏。

一段时间前,我看到了游戏《矮人要塞》,我非常喜欢它。我想制作一个类似的游戏,它将拥有一个充满机会的世界,矮人们将在其中建立强大的堡垒,并在发展生产的同时组织他们的定居点。

描述

游戏“挖矿者”是一个 2D 平台游戏。玩家控制几个矮人来建立力量和生产力。当你开始游戏时,世界是随机生成的。

图 1. 总览。

世界由区块组成,每个区块的大小为 64x64 个方块。区块是 boimes.json 文件中列出的几个生物群落之一:“BiomePlain.json”、“BiomeDesert.json”、“BiomeHill.json”、“BiomeShallowMetalls.json”、“BiomeLake.json”、“BiomeCave.json”

每个生物群落由某些矿物方块组成,这些方块在相应的 JSON 文件 biome 中列出。

矮人有情绪和需求,这些都显示在思考气泡中,例如闲置的矮人会陷入悲伤的情绪。

图 2. 矮人。

游戏中的生产和敌人尚未完成。

技术信息

游戏运行在 Windows、Android ARM (在 HTC EVO 3D 和 Samsung Galaxy SII 上测试) 和 Android x86 (在模拟器上测试) 上。Windows 的构建使用 Visual Studio 2010 和 .sln 文件,Android 的构建使用 .mk 文件以及所需的 target

APP_ABI: = x86 for x86,  
APP_ABI: = armeabi armeabi-v7a for ARM    

游戏使用 SDL 库和许多其他依赖项 - SDL_ttf 和 libFreetype 用于渲染文本,SDL_Mixer 和 libTremor 用于声音,libJPEG 和 libPNG 用于渲染图像,JSONCPP 用于使用 JSON 文件。感谢上帝,所有依赖项都成功构建在所有平台 - Windows、Android ARM 和 Android x86 上。

图 2. 挖矿者在 Android X86 模拟器中运行。

让我们来看看一些有趣的技术点。

渲染

游戏使用 OPEN GLES 2.0 进行渲染。

我遇到的问题之一是优化渲染。屏幕上可以同时显示多达 200,000 个矿物方块,甚至更多,它们必须快速绘制。如果我们使用 glDrawElements 绘制每个方块,绘制将会非常慢。

图 3. 显示模式开启,渲染了许多矿物方块,显示了一些洞穴。

为了快速渲染,将相同的方块合并成批次 (类 SpriteBlockBatch)。每个批次由相同类型的方块组成 - 例如,对于所有土壤方块,渲染创建一个批次,对于所有石灰岩方块,渲染创建另一个批次,等等。此外,由于 OPEN GLES 2.0 中 glDrawElements 调用中的元素数量限制为无符号短整型,因此我们必须将批次分解成几个较小的内部批次。渲染可以分为两个阶段 - 批次的创建和批次的显示。

当批次发生变化时,我们需要重新创建它。例如,当矮人挖掘土壤方块时,会发生三件事:土壤方块必须被删除,背景土壤方块必须被创建,并且被挖的土壤方块也必须被创建。在这种情况下,我们需要重新创建三个批次 - 土壤的批次,背景土壤的批次和被挖的土壤的批次。

批次在方法中创建

void SpriteBlockBatch :: MakeBatch (void)

在此方法中,我们遍历所有所需类型的矿物方块(例如所有土壤方块)并创建一个土壤方块批次。

批次在方法中显示

bool Render :: Update (float _dt)

在此方法中,我们为每个批次调用 update 方法

bool SpriteBlockBatch::Update(float _dt) 

光照

有平滑的昼夜变化和光照。此外,游戏还有光源 - 例如雨中的闪电或发光的植物。

图 4. 夜晚,你可以看到星星和发光的植物

光照主要在着色器 fragmentShaderMineralsText 中实现。这是一个复杂的着色器,有很多输入参数 - 昼夜时间、环境光(例如闪电)、矿物的深度、背景或前景矿物类型。

const char * fragmentShaderMineralsText =
# Ifndef WIN32
"Precision lowp float; \ n"
# Endif
"Varying float vDeleted; \ n"
"Varying vec3 vDeep; \ n"
"Varying vec3 vDay; \ n"
"Varying vec3 vLighting; \ n"
"Varying vec2 vTC; \ n"
"Uniform sampler2D s_texture; \ n"
"Void main () \ n"
"{\ N"
"Float alpha = texture2D (s_texture, vTC). A; \ n"
"Float deep = 1.0; \ n"
"If ((vDeep.x + vDeep.y + vDeep.z) <0.001) deep = 0.0; \ n"
"Float red = texture2D (s_texture, vTC). R * (vDay.x * deep * (vDeep.x - vTC.y * 0.8) + vLighting.x) * vDeleted; \ n"
"Float green = texture2D (s_texture, vTC). G * (vDay.y * deep * (vDeep.y - vTC.y * 0.8) + vLighting.y) * vDeleted; \ n"
"Float blue = texture2D (s_texture, vTC). B * (vDay.z * deep * (vDeep.z - vTC.y * 0.8) + vLighting.z) * vDeleted; \ n"
"Gl_FragColor = vec4 (red, green, blue, alpha); \ n"
"} \ N";

此外,还有用于夜间星星渲染的 fragmentShaderStarsText 着色器和用于渲染不依赖深度的表面对象的 fragmentShaderSurfaceObjectsText 着色器。

Java 互操作

在游戏中,我们需要 Java 和原生代码之间的交互,例如保存和加载游戏。这种交互发生在文件 JavaBridge.h 和 JavaBridge.cpp 中。例如,让我们看看保存游戏。

原生方法(在文件 JavaBridge.cpp 中)

unsigned int JavaBridge :: WriteSave (const char * _buffer, unsigned int _size) 

调用此 Java 方法(在文件 SDLActivity.java 中)

public static void writeSave (byte [] _buffer, int _size) 

它看起来有点复杂,但如果你仔细查看源代码,一切都会变得清晰。

第三方内容

对于此游戏,我使用了具有 CC 许可证的一些资源

来自 http://www.jamendo.com/en/artist/355362/marc-teichert 的音乐

以及来自 http://opengameart.org/content/dwarf-fixed 的矮人模型


© . All rights reserved.