您将学到什么:
- 创建一个非常简单的2D Shooter Platformer游戏。
- 完整的游戏架构是什么样的。
- 如何在不了解OpenGL的情况下在OpenGL中使用2D图形。
- 游戏由什么不同的实体组成,以及它们如何在游戏世界中联系在一起。
- 如何在游戏中添加声音。
- 如何在桌面上构建游戏并将其部署到Android上-是的,这就是魔术。
创建游戏的步骤
- 1.有一个游戏想法。
- 2.在纸上草拟一些方案,以类似于您的愿景及其外观。
- 3.分析想法,通过调整来迭代几个版本,并确定游戏在其初始版本中将具有什么功能。
- 4.选择一种技术并开始制作原型。
- 5.开始编码并创建游戏资产。
- 6.进行游戏测试,改进并持续不断地朝着完成它迈进。
- 7.抛光并释放!
游戏理念
因为这将是一天的项目,所以可以使用的时间非常有限,目标是学习制作游戏的技术,而不是实际过程。 为此,我可以自由地从其他游戏中借鉴想法,并专注于此过程的技术方面。
我将从《 星际争霸》游戏中大量借钱。 这是真空花制造的一颗小宝石。 去获取游戏并签出。 一个非常简单的射击平台游戏,具有简单的样式和老式的拱廊感觉。 这个想法是通过杀死敌人并躲避一切试图杀死我们的东西来引导我们的英雄穿越关卡。 控件很简单, 箭头键将英雄向左或向右移动, Z跳跃, X射击激光。 按住跳跃按钮的时间越长,英雄跳跃的程度就越高。 他可以在空中改变方向,也可以射击。 稍后我们将看到如何将这些控件转换为Android。
可以跳过接下来的步骤(第2步和第3步),因为由于功能正常,我们已经做好了处理。
启动Eclipse
这是我们的起点。 我将使用libgdx库来创建游戏。 为什么选择libgdx? 在我看来,这是最好的库,使开发游戏变得容易而又不了解底层技术。 它允许开发人员在桌面上创建他们的游戏并将其部署到Android,而无需进行任何修改。 它提供了在游戏中使用它的所有元素,并且隐藏了处理特定技术和硬件的复杂性。 随着我们的前进,它将变得更加明显。
设置项目
按照libgdx文档中的说明进行操作,我们必须首先下载该库。
转到http://libgdx.badlogicgames.com/nightlies/并下载libgdx-nightly-latest.zip
文件并解压缩。
在eclipse中创建一个简单的Java项目。 我称它为“ star-assault
。
保留默认设置,创建项目后, 右键单击它,然后选择New-> Folder并创建一个名为libs
的目录。
从解压缩的libgdx-nighly-latest
目录中,将gdx.jar
文件复制到新创建的libs
目录中。 gdx-sources.jar
文件复制到libs
目录中。 它是在sources
解压GDX目录的子目录。 您只需将jar文件拖到eclipse中的目录中即可。 如果使用资源管理器或查找器或任何其他方式复制它们,请不要忘记按F5键后刷新Eclipse项目。
结构应如下图所示:
将gdx.jar
添加为项目的依赖项。 通过右键单击项目标题并选择Properties 来执行此操作。 在此屏幕上,选择Java Build Path ,然后单击Libraries选项卡。 点击Add JARs… ,导航到libs
目录并选择gdx.jar
,然后点击OK 。
为了能够访问gdx源代码并能够轻松调试我们的游戏,最好将源添加到gdx.jar文件中。 为此,请展开gdx.jar
节点,选择“ 源附件” ,单击“ 编辑” ,然后单击“ 工作区” ,然后选择gdx-sources.jar
然后单击“确定”,直到关闭所有弹出窗口。
有关使用libgdx设置项目的完整文档,请参见官方Wiki 。
该项目将成为游戏的核心项目。 它将包含游戏机制,引擎以及所有内容。 我们将需要再创建两个项目,基本上是我们要定位的2个平台的启动器。 一种用于Android,另一种用于桌面。 这些项目将非常简单,并且仅包含在相应平台上运行游戏所需的依赖项。 将它们视为包含main方法的类。
为什么我们需要这些单独的项目? 因为libgdx隐藏了处理底层操作系统(图形,音频,用户输入,文件I / O等)的复杂性,所以每个平台都有一个特定的实现,我们将仅包括所需的那些实现(绑定)。有针对性的。 同样,由于应用程序生命周期,资产加载(图像,声音等的加载)和应用程序的其他常见方面都得到了大大简化,因此平台特定的实现驻留在不同的JAR文件中,并且只需要包含所需的那些文件即可。针对我们定位的平台。
桌面版
如上一步那样创建一个简单的Java项目,并将其命名为star-assault-desktop
。 还请按照以下步骤创建libs
目录。 这次,从下载的zip
文件中所需的jar
文件是:
gdx-natives.jar
,
gdx-backend-lwjgl.jar
, gdx-backend-lwjgl-natives.jar
。 还像以前的项目一样,将这些jar
文件添加为项目的依赖项。 ( 右键单击项目 -> 属性 -> Java构建路径 -> 库 -> 添加JAR ,选择三个JAR,然后单击确定 。) 我们还需要将star-assault
项目添加到依赖项。 为此,请单击“ 项目”选项卡,单击“ 添加” ,检查star-assault
项目,然后单击“ 确定” 。
重要! 我们需要使“ star-assault
项目成为可传递的依赖项,这意味着该项目的依赖项将成为与此相关的项目的依赖项。 为此,请执行以下操作: 右键单击主项目 -> Properties- > Java Build Path- > Order and Export- > 检查gdx.jar文件 ,然后单击OK 。
安卓版
为此,您将需要安装Android SDK 。
在eclipse中创建一个新的Android项目: File- > New- > Project- > Android Project 。
将其命名为star-assault-android
。 对于构建目标,请选中“ Android 2.3?”。 指定软件包名称net.obviam
或您自己的偏好。 在“创建活动”旁边,输入StarAssaultActivity
。 点击完成 。
转到项目目录并创建一个名为libs的子目录(可以从eclipse中执行此操作)。 在nightly zip
,将gdx-backend-android.jar
以及armeabi
和armeabi-v7a
目录放置在新创建的libs
目录中。
在eclipse中, 右键单击项目 -> 属性 -> Java构建路径 -> 库 -> 添加JAR ,选择gdx-backend-android.jar
并单击确定 。
再次单击Add JARs,在主项目( star-assault
)下选择gdx.jar ,然后单击OK 。
单击“ 项目”选项卡,单击“ 添加” ,检查主项目,然后单击“ 确定”两次。
这是结构的样子:
重要!
对于ADT版本17和更高版本,需要将gdx jar
文件显式标记为要导出。
去做这个 点击Android项目 选择属性 选择Java构建路径 (步骤1) 选择订单和导出 (步骤2) 检查所有引用,例如gdx.jar
, gdx-backend-android.jar
,主项目等(第3步)。 下图显示了新状态。
另外,有关此问题的更多信息,请参见此处 。
共享资产(图像,声音和其他数据)
由于游戏在台式机和Android上都是相同的,但是每个版本都需要与不同的项目分开构建,因此我们希望将图像,声音和其他数据文件保存在共享位置。 理想情况下,这应包含在主项目中,因为它同时包含在Android和台式机中,因为Android对所有这些文件的存放位置都有严格的规定,因此我们必须将资产保留在那里。 它位于Android项目中自动创建的assets
目录中。 在Eclipse中,可以像在linux / mac上的符号链接或Windows中的快捷方式那样链接目录。 要将assets
目录从Android项目链接到桌面项目,请执行以下操作:
右键单击 star-assault-desktop
项目-> Properties- > Java Build asssets
> Source选项卡-> Link Source ...- > Browse ...- >浏览到star-assault-android
项目中的asssets
目录,然后单击Finish 。 您也可以扩展 变量…,而不是浏览到assets
目录。 推荐这样做是因为它使项目文件系统独立。
还要确保assets
目录作为源文件夹包括在内。 为此, 右键单击 eclipse(桌面项目)中的assets
目录,选择Build Path- > Use as Source Folder 。
在此阶段,我们已经准备就绪,可以继续进行游戏了。
创造游戏
计算机应用程序是在计算机上运行的一块软件。 它会启动,执行某些操作(即使没什么),然后以某种方式停止。 电脑游戏是一种特殊的应用程序,其中“做某事”部分充满了游戏。 开始和结束是所有应用程序的共同点。 游戏还具有基于连续循环的非常简单的架构。 您可以在此处和此处找到有关体系结构和循环的更多信息。
多亏了libgdx,我们的游戏可以在剧院的舞台剧中拼凑而成。 您需要做的就是将游戏视为戏剧作品。 我们将定义阶段,演员,他们的角色和行为,但我们会将编排委派给玩家。
因此,要设置我们的游戏/游戏,我们需要采取以下步骤:
- 1.启动应用程序。
- 2.加载所有图像和声音并将其存储在内存中。
- 3.与演员及其行为(他们之间的互动规则)一起为我们的戏剧创造舞台。
- 4.将控件交给播放器。
- 5.根据从控制器接收的输入,创建将在舞台上操纵演员的引擎。
- 6.确定播放何时结束。
- 7.结束演出。
它看起来非常简单,实际上确实如此。 我将介绍概念和元素的出现。
要创建游戏,我们只需要一个类。
让我们在star-assault
项目中创建StarAssault.java
。 将在此项目中创建每个类,但有2个例外。
package net.obviam.starassault;
import com.badlogic.gdx.ApplicationListener;
public class StarAssault implements ApplicationListener {
@Override
public void create() {
// TODO Auto-generated method stub
}
@Override
public void resize(int width, int height) {
// TODO Auto-generated method stub
}
@Override
public void render() {
// TODO Auto-generated method stub
}
@Override
public void pause() {
// TODO Auto-generated method stub
}
@Override
public void resume() {
// TODO Auto-generated method stub
}
@Override
public void dispose() {
// TODO Auto-generated method stub
}
}
只需从gdx
实现ApplicationListener
,eclipse就会为需要实现的方法生成存根。
这些都是我们需要在应用程序生命周期中实现的所有方法。 考虑到Android所需的所有设置代码,或在桌面上初始化OpenGL上下文以及所有这些无聊(且困难)的任务,这非常简单。
首先调用create()
方法。 当应用程序准备就绪并且我们可以开始加载我们的资产并创建舞台和参与者时,就会发生这种情况。 想一想所有东西都运到这里并准备好之后,在剧院里搭建舞台。 根据剧院所在的位置以及您的到达方式,后勤工作可能是一场噩梦。 您可以手动运输,也可以通过飞机或卡车运输……我们不知道。 我们在里面,准备好东西,就可以开始组装了。 这就是libgdx为我们所做的。 无论平台如何,我们都将其运送并交付。
每次调整可绘制表面的大小时,都会调用resize(int width, int height)
方法。 这使我们有机会在继续播放之前重新排列位。 例如,当调整窗口大小(如果游戏在其中运行)时,就会发生这种情况。
每个游戏的核心都是render()
方法,它只不过是无限循环。 这将连续调用,直到我们确定游戏结束并想要终止程序为止。 这是进行中的戏。
注意 :对于计算机,游戏结束并不等同于程序结束。 所以这只是一个状态。 该程序处于游戏结束状态,但仍在运行。
当然,可以暂停播放,也可以暂停播放,然后继续播放。 每当应用程序进入桌面或Android的后台时,都会调用pause()
方法。 当应用程序到达前台时,它会继续运行,并会调用resume()
方法。
游戏结束且应用程序关闭时, dispose()
调用dispose()
,此时该进行一些清理了。 比赛结束,观众离开并拆除舞台时,情况类似。 没有更多回来。 更多关于生命周期的信息 。
演员们
让我们开始迈向实际游戏。 第一个里程碑是拥有一个我们的人可以在其中移动的世界。 世界由关卡组成,每个关卡都由地形组成。 地形无非是我们的人无法穿越的一些障碍。
识别游戏中到目前为止的演员和实体很容易。
我们有一个家伙(我们叫他鲍勃-libgdx有鲍勃的教程)和构成世界的块。
扮演星际卫队之后,我们可以看到鲍勃有几个州。 当我们什么都没碰时,鲍勃很闲。 他也可以移动(双向),也可以跳跃。 同样,当他死了时,他什么也做不了。 在任何给定时间,Bob只能处于四个已识别状态之一。 还有其他州,但我们暂时将其排除在外。
鲍勃的州:
- 空闲–不活动或跳跃时还活着
- 移动–以恒定速度向左或向右移动。
- 跳跃-也面对左或右,高或低。
- 死了-他甚至不可见,也没有产卵。
街区是其他演员。 为了简单起见,我们仅提供块。 该关卡由放置在二维空间中的块组成。 为简单起见,我们将使用网格。
将Star Guard的开始变成一个块和Bob结构,将看起来像这样:
顶部是原始的,底部是我们的世界代表。
我们已经想象了世界,但是我们需要在一个可以理解的测量系统中工作。 为简单起见,我们将说世界上的一个街区是1单位宽和1单位高。 我们可以使用仪表使之更简单,但是由于Bob的半个单位,所以使他半个米。 假设游戏世界中的4个单位组成1米,那么Bob的身高将达到2米。
这很重要,因为例如当我们计算Bob的运行速度时,我们需要知道我们在做什么。
让我们创造世界。
我们的主要角色是Bob
。
Bob.java
类如下所示:
package net.obviam.starassault.model;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
public class Bob {
public enum State {
IDLE, WALKING, JUMPING, DYING
}
static final float SPEED = 2f; // unit per second
static final float JUMP_VELOCITY = 1f;
static final float SIZE = 0.5f; // half a unit
Vector2 position = new Vector2();
Vector2 acceleration = new Vector2();
Vector2 velocity = new Vector2();
Rectangle bounds = new Rectangle();
State state = State.IDLE;
boolean facingLeft = true;
public Bob(Vector2 position) {
this.position = position;
this.bounds.height = SIZE;
this.bounds.width = SIZE;
}
}
第16-#21行定义了Bob的属性。 这些属性的值在任何给定时间定义Bob的状态。
position
– Bob在世界上的职位。 这是用世界坐标表示的(稍后会详细介绍)。 acceleration
–这将确定鲍勃跳跃时的加速度。 velocity
-将被计算并用于移动鲍勃。 bounds
-游戏中的每个元素都会有一个边界框。 为了知道鲍勃是否撞墙,被子弹杀死或击中敌人并击中,这不过是一个矩形。 它将用于碰撞检测。 想想玩多维数据集。 state
– Bob的当前状态。 当我们发出步行动作,国家将WALKING
并在此基础上的状态,我们知道该怎么画到屏幕上。 facingLeft
表示鲍勃的方位。 作为一个简单的2D平台游戏,我们只有2个方面。 左和右。
第12-#15行定义了一些常量,我们将使用这些常量来计算世界上的速度和位置。 这些将在以后进行调整。
我们还需要一些障碍物来构成世界。
Block.java
类如下所示:
package net.obviam.starassault.model;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
public class Block {
static final float SIZE = 1f;
Vector2 position = new Vector2();
Rectangle bounds = new Rectangle();
public Block(Vector2 pos) {
this.position = pos;
this.bounds.width = SIZE;
this.bounds.height = SIZE;
}
}
块无非就是放置在世界上的矩形。 我们将使用这些块来构成地形。 我们有一个简单的规则。 没有什么可以穿透它们。
libgdx注意
您可能已经注意到,我们正在使用Vector2
类型。 这使我们的生活变得更加轻松,因为它提供了处理欧几里德向量所需的一切。 我们将使用向量来定位实体,计算速度以及移动事物。
关于坐标系和单位
作为现实世界,我们的世界具有维度。 想想一个公寓里的房间。 它具有宽度,高度和深度。 我们将其设为2维,并摆脱深度。 如果房间宽5米,高3米,则可以说我们用公制描述了房间。 可以想象在中间放置一个1米宽,1米高的桌子。 我们无法穿过桌子,要越过桌子,我们需要跳到桌子上面,走1米,然后跳下。 我们可以使用多个表创建金字塔并在房间中创建一些奇怪的设计。
在我们的星际攻击世界中,世界代表房间,现实中的桌子和单元,仪表被遮挡。
如果我以10 km / h的速度运行,则转换为2.77777778米/秒(10 * 1000/3600) 。 要将其转换为“星际突击”世界坐标,我们将说类似于10 km / h的速度,我们将使用2.7单位/秒。
检查以下表示世界坐标系中边界框和Bob的图。
红色正方形是块的边界框。 绿色方块是鲍勃的边界框。 空方是空的。 网格仅供参考。 这是我们将在其中创建模拟的世界。坐标系的原点位于左下角,因此以10.000单位/小时的速度向左行走意味着Bob的位置的X坐标每秒将减少 2.7单位 。
另请注意,对成员的访问权限是程序包的默认设置,而模型位于单独的程序包中。 我们将必须创建访问器方法(getter和setter)以从引擎访问它们。
创造世界
第一步,我们只是将世界创建为硬编码的小房间。 它将是10个单位宽和7个单位高。 我们将在下图所示的位置放置Bob和块。
World.java
看起来像这样:
package net.obviam.starassault.model;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
public class World {
/** The blocks making up the world **/
Array<Block> blocks = new Array<Block>();
/** Our player controlled hero **/
Bob bob;
// Getters -----------
public Array<Block> getBlocks() {
return blocks;
}
public Bob getBob() {
return bob;
}
// --------------------
public World() {
createDemoWorld();
}
private void createDemoWorld() {
bob = new Bob(new Vector2(7, 2));
for (int i = 0; i < 10; i++) {
blocks.add(new Block(new Vector2(i, 0)));
blocks.add(new Block(new Vector2(i, 7)));
if (i > 2)
blocks.add(new Block(new Vector2(i, 1)));
}
blocks.add(new Block(new Vector2(9, 2)));
blocks.add(new Block(new Vector2(9, 3)));
blocks.add(new Block(new Vector2(9, 4)));
blocks.add(new Block(new Vector2(9, 5)));
blocks.add(new Block(new Vector2(6, 3)));
blocks.add(new Block(new Vector2(6, 4)));
blocks.add(new Block(new Vector2(6, 5)));
}
}
对于世界上的实体来说,这是一个简单的容器类。 当前,实体是块和鲍勃。 在构造函数中,将块添加到blocks
数组并创建Bob
。 暂时都进行了硬编码。
请记住,原点位于左下角。
在此处查看本教程的其余部分。
参考: 使用libgdx进行Android游戏开发入门–一天创建一个有效的原型–我们的JCG合作伙伴 Impaler在《 反对谷物》博客上的教程第1部分 。
翻译自: https://www.javacodegeeks.com/2012/05/android-game-development-with-libgdx.html