libgdx教程

创造游戏并展现世界

要将世界渲染到屏幕上,我们需要为其创建一个屏幕,并告诉它渲染世界。 在libgdx中,有一个名为Game的便利类,我们将把StarAssault类重写为StarAssault提供的Game类的子类。

关于屏幕

一个游戏可以包含多个屏幕。 甚至我们的游戏也将具有3个基本屏幕。 “开始游戏”屏幕,“播放”屏幕和“游戏结束”屏幕。 每个屏幕都与发生的事情有关,彼此之间并不关心。 例如,“开始游戏”屏幕将包含菜单选项“播放”和“退出” 。 它具有两个元素(按钮),并且关注于处理这些元素上的单击/触摸。 它不知疲倦地渲染这两个按钮,并且如果单击/触摸了“播放”按钮,它将通知主游戏加载“播放屏幕”并摆脱当前屏幕。 播放屏幕将运行我们的游戏,并将处理与游戏有关的所有内容。 达到“游戏结束”状态后,它会告诉主游戏过渡到“游戏结束”屏幕,其唯一目的是显示高分并收听“重播”按钮上的单击。

现在,让我们重构代码并仅创建游戏的主屏幕。 我们将跳过屏幕的开始和游戏。

GameScreen.java

package net.obviam.starassault.screens;import com.badlogic.gdx.Screen;public class GameScreen implements Screen {@Overridepublic void render(float delta) {// TODO Auto-generated method stub}@Overridepublic void resize(int width, int height) {// TODO Auto-generated method stub}@Overridepublic void show() {// TODO Auto-generated method stub}@Overridepublic void hide() {// TODO Auto-generated method stub}@Overridepublic void pause() {// TODO Auto-generated method stub}@Overridepublic void resume() {// TODO Auto-generated method stub}@Overridepublic void dispose() {// TODO Auto-generated method stub}
}

StarAssault.java将变得非常简单。

package net.obviam.starassault;import net.obviam.starassault.screens.GameScreen;import com.badlogic.gdx.Game;public class StarAssault extends Game {@Overridepublic void create() {setScreen(new GameScreen());}
}

GameScreen实现了非常类似于ApplicationListenerScreen接口,但是它添加了2个重要方法。show() –当主游戏激活该屏幕时调用hide() –当主游戏激活另一个屏幕时调用

StarAssault仅实现了一种方法。 create()无非就是激活新实例化的GameScreen 。 换句话说,它创建了它,调用了show()方法,随后将在每个周期调用其render()方法。

GameScreen将成为下一部分的焦点,因为它是游戏的生存地。 请记住,游戏循环是render()方法。 但是要渲染一些东西,我们首先需要创造世界。 可以在show()方法中创建世界,因为我们没有任何其他屏幕可以打断我们的游戏玩法。 当前,仅在游戏开始时才显示GameScreen

我们将在类中添加两个成员并实现render(float delta)方法。

private World world;private WorldRenderer renderer;/** Rest of methods ommited **/@Overridepublic void render(float delta) {Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1);Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);renderer.render();}

world属性是World实例,其中包含块和Bob。renderer是一个类,它将在屏幕上绘制/渲染世界(我很快就会展示出来)。render(float delta)让我们创建WorldRenderer类。WorldRenderer.java

package net.obviam.starassault.view;import net.obviam.starassault.model.Block;
import net.obviam.starassault.model.Bob;
import net.obviam.starassault.model.World;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Rectangle;public class WorldRenderer {private World world;private OrthographicCamera cam;/** for debug rendering **/ShapeRenderer debugRenderer = new ShapeRenderer();public WorldRenderer(World world) {this.world = world;this.cam = new OrthographicCamera(10, 7);this.cam.position.set(5, 3.5f, 0);this.cam.update();}public void render() {// render blocksdebugRenderer.setProjectionMatrix(cam.combined);debugRenderer.begin(ShapeType.Rectangle);for (Block block : world.getBlocks()) {Rectangle rect = block.getBounds();float x1 = block.getPosition().x + rect.x;float y1 = block.getPosition().y + rect.y;debugRenderer.setColor(new Color(1, 0, 0, 1));debugRenderer.rect(x1, y1, rect.width, rect.height);}// render BobBob bob = world.getBob();Rectangle rect = bob.getBounds();float x1 = bob.getPosition().x + rect.x;float y1 = bob.getPosition().y + rect.y;debugRenderer.setColor(new Color(0, 1, 0, 1));debugRenderer.rect(x1, y1, rect.width, rect.height);debugRenderer.end();}
}

WorldRenderer只有一个目的。 获取世界的当前状态并将其当前状态呈现到屏幕上。 它有一个公共的render()方法,该方法由主循环( GameScreen )调用。 渲染器需要访问world因此我们在实例化渲染器时将其传递。 第一步,我们将渲染元素的边界框(块和鲍勃),以查看到目前为止的内容。 在OpenGL中绘制图元非常繁琐,但是libgdx带有ShapeRenderer ,这使此任务非常容易。重点说明。

#14 –将world声明为成员变量。#15 –我们声明一个OrthographicCamera。 我们将使用此相机从正交摄影法“看”世界。 当前世界很小,只能放在一个屏幕上,但是当我们需要一个宽广的水准并且Bob在其中移动时,我们将不得不跟随Bob移动摄像机。 它类似于现实生活中的相机。 有关正射投影的更多信息,请参见此处。#18ShapeRenderer被声明。 我们将使用它为实体绘制图元(矩形)。 这是一个辅助渲染器,可以绘制诸如线,矩形,圆之类的图元。 对于熟悉基于画布的图形的人来说,这应该很容易。#20 –以world为参数的构造函数。#22 –我们创建的相机的视口为10个单位宽和7个单位高。 这意味着用单位块(宽度=高度= 1)填充屏幕将导致在X轴上显示10个框,在Y轴上显示7个框。重要:这与分辨率无关。 如果屏幕分辨率为480×320,则480像素代表10个单位,因此一个框将为48像素宽。 这也意味着320像素代表7个单位,因此屏幕上的框将为45.7像素高。 这将不是一个完美的广场。 这是由于长宽比。 在我们的情况下,长宽比为10:7。#23 –这条线将摄像机定位在房间中间。 默认情况下,它查看房间的角落(0,0)。 相机的(0,0)位于普通相机的中间。 下图显示了世界和相机设置坐标。

#24 –相机的内部矩阵已更新。 每次对摄像机进行操作(移动,缩放,旋转等)时,都必须调用update方法。 OpenGL隐藏得很漂亮。render()方法:#29 –我们将相机中的矩阵应用于渲染器。 这是必需的,因为我们将摄像机定位在了并且希望它们相同。#30 –我们告诉渲染器我们要绘制矩形。#31 –我们将绘制块,以便我们遍历世界上的所有块。#32 –#34 –提取每个块的边界矩形的坐标。 OpenGL使用顶点(点),因此要绘制矩形,必须知道起点和宽度的坐标。 请注意,我们使用的相机坐标与世界坐标重合。#35 –将矩形的颜色设置为红色。#36 –使用给定的widthheightx1,y1处绘制矩形。#39 –#44 –我们对Bob做同样的事情,但是这次矩形是绿色的。#45 –我们让渲染器知道我们已经完成绘制矩形。

我们需要将rendererworld添加到GameScreen (主循环)中,然后将其运行。像这样修改GameScreen

package net.obviam.starassault.screens;import net.obviam.starassault.model.World;
import net.obviam.starassault.view.WorldRenderer;import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL10;public class GameScreen implements Screen {private World world;private WorldRenderer renderer;@Overridepublic void show() {world = new World();renderer = new WorldRenderer(world);}@Overridepublic void render(float delta) {Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1);Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);renderer.render();}/** ... rest of method stubs omitted ... **/}

render(float delta)方法有3行。 前两行用黑色清除屏幕,第三行仅调用渲染器的render()方法。显示屏幕时,将创建WorldWorldRenderer

要在台式机和Android上进行测试,我们必须为这两个平台创建启动器。 创建桌面和Android启动器

我们在开始时又创建了2个项目。star-assault-desktopstar-assault-android ,后者是一个Android项目。对于桌面项目而言,这简直就是简单。 我们需要创建一个带有main方法的类,该类实例化libgdx提供的应用程序。在桌面项目中创建StarAssaultDesktop.java类。

package net.obviam.starassault;import com.badlogic.gdx.backends.lwjgl.LwjglApplication;public class StarAssaultDesktop {public static void main(String[] args) {new LwjglApplication(new StarAssault(), "Star Assault", 480, 320, true);}
}

就是这个。 7号线是一切发生的地方。 它实例化一个新的LwjglApplication应用程序,并传入一个新的StarAssault实例,该实例是Game实现的。 第二个和第三个参数告诉窗口的尺寸。 我之所以选择480×320,是因为它是许多Android手机支持的分辨率,并且我想在台式机上使用它。 最后一个参数告诉libgdx使用OpenGL ES 2。作为普通Java程序运行该应用程序应产生以下结果:

如果遇到一些错误,请追溯并确保设置正确,并遵循所有步骤,包括在“后卫”项目属性->构建路径上的导出选项卡上检查gdx.jar。安卓版

star-assault-android项目中,只有一个Java类,称为StarAssaultActivity 。更改为:StarAssaultActivity.java

package net.obviam.starassault;import android.os.Bundle;import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;public class StarAssaultActivity extends AndroidApplication {/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();config.useAccelerometer = false;config.useCompass = false;config.useWakelock = true;config.useGL20 = true;initialize(new StarAssault(), config);}
}

请注意,新活动扩展了AndroidApplication 。在第13行中,创建了AndroidApplicationConfiguration对象。 我们可以设置有关Android平台的所有类型的配置。 它们是不言自明的,但是请注意,如果我们要使用Wakelock ,则还需要修改AndroidManifest.xml文件。 这需要获得Android的许可才能保持设备开机并防止屏幕变暗(如果我们不触摸设备的话)。AndroidManifest.xml下行添加到<manifest>标记内的AndroidManifest.xml文件中。

<uses-permission android:name="android.permission.WAKE_LOCK"/>

同样在第17行中,我们告诉Android使用OpenGL ES2。这意味着我们将只能在设备上对其进行测试,因为仿真器不支持OpenGL ES2。如果出现问题,请将其切换为false第18行会初始化并启动Android应用程序。将设备连接到eclipse后,就可以直接部署它,并且在下面可以看到在一个nexus设备上运行的应用程序的照片。 它看起来与台式机版本相同。

MVC模式

我们在这么短的时间内走了多远,真是令人印象深刻。 注意使用MVC模式。 这是非常有效和简单的。 模型是我们要显示的实体。 视图是渲染器。 该视图将模型绘制到屏幕上。 现在我们需要与实体(尤其是Bob)进行交互,并且我们还将引入一些控制器。要阅读有关MVC模式的更多信息,请查看我的其他文章或在网上搜索。 非常有用

添加图像

到目前为止,一切都很好,但是绝对我们想使用一些适当的图形。 MVC的功能派上用场,我们将修改渲染器,以便绘制图像而不是矩形。在OpenGL中显示图像是一个相当复杂的过程。 首先,需要将其加载,转化为纹理,然后映射到由某些几何形状描述的表面。 libgdx使此操作极其容易。 要将磁盘上的图像转换为纹理,需要一个衬板。我们将使用2个图像,因此将使用2个纹理。 Bob的一种纹理,块的一种。 我创建了两个图像,一个块和一个鲍勃。 鲍勃(Bob)是《星际卫队》第一季的模仿者。 这些是简单的png文件,我将它们复制到assets/images目录中。 我有两个图像: block.pngbob_01.png 。 最终,鲍勃(Bob)将成为动画角色,所以我给它加上了数字后缀(以备将来使用)。首先,让我们WorldRenderer清理一下WorldRenderer ,即将矩形的图形提取到一个单独的方法中,因为我们将其用于调试目的。我们将需要加载纹理并将其相应渲染到屏幕。看看新的WorldRenderer.java

package net.obviam.starassault.view;import net.obviam.starassault.model.Block;
import net.obviam.starassault.model.Bob;
import net.obviam.starassault.model.World;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Rectangle;public class WorldRenderer {private static final float CAMERA_WIDTH = 10f;private static final float CAMERA_HEIGHT = 7f;private World world;private OrthographicCamera cam;/** for debug rendering **/ShapeRenderer debugRenderer = new ShapeRenderer();/** Textures **/private Texture bobTexture;private Texture blockTexture;private SpriteBatch spriteBatch;private boolean debug = false;private int width;private int height;private float ppuX; // pixels per unit on the X axisprivate float ppuY; // pixels per unit on the Y axispublic void setSize (int w, int h) {this.width = w;this.height = h;ppuX = (float)width / CAMERA_WIDTH;ppuY = (float)height / CAMERA_HEIGHT;}public WorldRenderer(World world, boolean debug) {this.world = world;this.cam = new OrthographicCamera(CAMERA_WIDTH, CAMERA_HEIGHT);this.cam.position.set(CAMERA_WIDTH / 2f, CAMERA_HEIGHT / 2f, 0);this.cam.update();this.debug = debug;spriteBatch = new SpriteBatch();loadTextures();}private void loadTextures() {bobTexture = new  Texture(Gdx.files.internal("images/bob_01.png"));blockTexture = new Texture(Gdx.files.internal("images/block.png"));}public void render() {spriteBatch.begin();drawBlocks();drawBob();spriteBatch.end();if (debug)drawDebug();}private void drawBlocks() {for (Block block : world.getBlocks()) {spriteBatch.draw(blockTexture, block.getPosition().x * ppuX, block.getPosition().y * ppuY, Block.SIZE * ppuX, Block.SIZE * ppuY);}}private void drawBob() {Bob bob = world.getBob();spriteBatch.draw(bobTexture, bob.getPosition().x * ppuX, bob.getPosition().y * ppuY, Bob.SIZE * ppuX, Bob.SIZE * ppuY);}private void drawDebug() {// render blocksdebugRenderer.setProjectionMatrix(cam.combined);debugRenderer.begin(ShapeType.Rectangle);for (Block block : world.getBlocks()) {Rectangle rect = block.getBounds();float x1 = block.getPosition().x + rect.x;float y1 = block.getPosition().y + rect.y;debugRenderer.setColor(new Color(1, 0, 0, 1));debugRenderer.rect(x1, y1, rect.width, rect.height);}// render BobBob bob = world.getBob();Rectangle rect = bob.getBounds();float x1 = bob.getPosition().x + rect.x;float y1 = bob.getPosition().y + rect.y;debugRenderer.setColor(new Color(0, 1, 0, 1));debugRenderer.rect(x1, y1, rect.width, rect.height);debugRenderer.end();}
}

我将指出重要的几行:#17#18 –声明了视口尺寸的常数。 用于相机。#27#28 –声明将用于Bob和块的2个纹理。#30 –声明SpriteBatchSpriteBatch为我们处理所有纹理映射,显示等。#31 –这是在构造函数中设置的属性,用于了解是否需要渲染调试屏幕。 记住,调试渲染只会渲染游戏元素的盒子。#32 –#35 –这些变量对于正确显示元素是必需的。 widthheight保持屏幕大小(以像素为单位),并在resize步骤从操作系统传入。 ppuXppuY是每单位像素数。因为我们将相机设置为在世界坐标中具有10×7的视口(这意味着我们可以水平显示10个框,垂直显示7个框),并且我们要处理最终结果中的像素,所以我们需要将这些值映射到实际像素座标。 我们选择以480×320的分辨率工作。 这意味着水平480像素等于10个单位,这意味着一个单位将由屏幕上的48个像素组成。如果我们尝试对高度(48个像素)使用相同的单位,则会得到336个像素(48 * 7 = 336)。 但是我们只有320像素可用,我们想显示整个7个块的高度。 对垂直部分进行相同的操作,我们得到垂直1个单位将为320/7 = 45.71像素。 我们需要稍微扭曲每个图像以适合我们的世界。很好,OpenGL很容易做到。 当我们更改电视机的长宽比,有时图像会被拉长或挤压以适合屏幕上的所有内容时,或者仅选择剪切图像但保持长宽比的选项时,就会发生这种情况。注意:为此,即使屏幕分辨率使用整数,我们也使用float ,而OpenGL则更喜欢float,我们也这样做。 OpenGL将计算出尺寸和放置像素的位置。#36 –每次调整屏幕大小时都会调用setSize (int w, int h)方法,并且该方法仅(重新)计算像素单位。#43 –构造函数进行了一些更改,但它做的非常重要。 它实例化SpriteBatch并加载纹理(第50行)。#53loadTextures()做到了:加载纹理。 看看它多么简单。 要创建纹理,我们需要传入一个文件处理程序,它会从中创建一个纹理。 libgdx中的文件处理程序非常有用,因为我们不区分Android还是Deskop,我们仅指定要使用内部文件,并且知道如何加载它。 请注意,对于该路径,我们跳过了assets因为assets被用作源目录,这意味着该目录中的所有内容都将复制到最终捆绑包的根目录中。 因此assets充当根目录。#58 –新的render()方法仅包含几行。#59#62 –包含一个SpriteBatch绘图块/会话。 每次我们想通过SpriteBatch在OpenGL中渲染图像时,都必须调用begin() ,然后在完成后绘制东西和end() 。 这样做很重要,否则将无法正常工作。 您可以在此处阅读有关SpriteBatch的更多信息。#60#61 –只需调用2个方法来首先渲染块,然后渲染Bob。#63#64 –如果启用了debug ,则调用该方法以渲染这些框。 drawDebug已经详细介绍了drawDebug方法。#67 –#76drawBlocksdrawBob方法相似。 每个方法都调用带有纹理的spriteBatchdraw方法。 了解这一点很重要。第一个参数是纹理(从磁盘加载的图像)。第二个和第三个参数告诉spriteBatch显示图像的位置。 注意,我们使用从世界坐标到屏幕坐标的坐标转换。 这是使用ppuXppuY地方。 您可以手动进行计算,并查看图像的显示位置。 默认情况下, SpriteBatch使用坐标系,其原点(0,0)位于左下角。

而已。 只要确保您修改了GameScreen类,以便在渲染器上调用resize ,并将渲染器的debug设置为trueGameScreen的修改位

/** ... omitted ... **/public void show() {world = new World();renderer = new WorldRenderer(world, true);}public void resize(int width, int height) {renderer.setSize(width, height);}
/** ... omitted ... **/

运行该应用程序应产生以下结果:没有调试

并带有调试渲染

大! 也可以在Android上尝试一下,看看外观如何。

处理输入-在台式机和Android上

我们已经走了很长一段路,但是到目前为止,世界是静止的,没有发生任何有趣的事情。 要使其成为一款游戏,我们需要添加输入处理,拦截按键和触摸并根据这些操作创建一些动作。桌面上的控制架构非常简单。 箭头键将Bob左右移动, z会使Bob跳跃, x会发射武器。 在Android上,我们将采用不同的方法。 我们将为这些功能指定一些按钮,并将其放置在屏幕上,并通过触摸相应的区域,我们将考虑所按下的键之一。

为了遵循MVC模式,我们将从模型和视图类中分离出控制Bob和世界其他地方的类。 创建包net.obviam.starassault.controller ,所有控制器都将进入该目录。首先,我们将通过按键控制Bob。 要玩游戏,我们要跟踪4个键的状态:向左移动,向右移动,跳跃和射击。 因为我们将使用两种类型的输入(键盘和触摸屏),所以实际事件需要输入到可以触发动作的处理器中。每个动作均由事件触发。当按下左箭头键或触摸屏幕的特定区域时,事件将触发向左移动动作。按下z键等时,将触发跳转动作。让我们创建一个非常简单的控制器WorldControllerWorldController.java

package net.obviam.starassault.controller;import java.util.HashMap;
import java.util.Map;
import net.obviam.starassault.model.Bob;
import net.obviam.starassault.model.Bob.State;
import net.obviam.starassault.model.World;public class WorldController {enum Keys {LEFT, RIGHT, JUMP, FIRE}private World  world;private Bob  bob;static Map<Keys, Boolean> keys = new HashMap<WorldController.Keys, Boolean>();static {keys.put(Keys.LEFT, false);keys.put(Keys.RIGHT, false);keys.put(Keys.JUMP, false);keys.put(Keys.FIRE, false);};public WorldController(World world) {this.world = world;this.bob = world.getBob();}// ** Key presses and touches **************** //public void leftPressed() {keys.get(keys.put(Keys.LEFT, true));}public void rightPressed() {keys.get(keys.put(Keys.RIGHT, true));}public void jumpPressed() {keys.get(keys.put(Keys.JUMP, true));}public void firePressed() {keys.get(keys.put(Keys.FIRE, false));}public void leftReleased() {keys.get(keys.put(Keys.LEFT, false));}public void rightReleased() {keys.get(keys.put(Keys.RIGHT, false));}public void jumpReleased() {keys.get(keys.put(Keys.JUMP, false));}public void fireReleased() {keys.get(keys.put(Keys.FIRE, false));}/** The main update method **/public void update(float delta) {processInput();bob.update(delta);}/** Change Bob's state and parameters based on input controls **/private void processInput() {if (keys.get(Keys.LEFT)) {// left is pressedbob.setFacingLeft(true);bob.setState(State.WALKING);bob.getVelocity().x = -Bob.SPEED;}if (keys.get(Keys.RIGHT)) {// left is pressedbob.setFacingLeft(false);bob.setState(State.WALKING);bob.getVelocity().x = Bob.SPEED;}// need to check if both or none direction are pressed, then Bob is idleif ((keys.get(Keys.LEFT) && keys.get(Keys.RIGHT)) ||(!keys.get(Keys.LEFT) && !(keys.get(Keys.RIGHT)))) {bob.setState(State.IDLE);// acceleration is 0 on the xbob.getAcceleration().x = 0;// horizontal speed is 0bob.getVelocity().x = 0;}}
}

#11 –#13 –为鲍勃执行的动作定义一个enum 。 每次按键/触摸都可以触发一个动作。#15 –声明游戏中的World 。 我们将控制世界上发现的实体。#16 –将Bob声明为私人成员,并且在游戏世界中仅引用Bob ,但我们将需要它,因为与每次需要他时都比检索它更容易引用它。#18 –#24 –这是键及其状态的静态HashMap 。 如果按下该键,则为true ,否则为false 。 它是静态初始化的。 该映射将在控制器的update方法中使用,以确定如何处理Bob。#26 –这是构造函数,使用World作为参数,并同时获取对Bob的引用。#33 –#63 –这些方法是简单的回调,只要按下操作按钮或触摸指定区域就会被调用。 这些方法是从我们使用的任何输入中调用的方法。 他们只需在地图中设置各个按键的值即可。 如您所见,控制器也是状态机,其状态由keys映射指定。#66 –#69 –在主循环的每个循环中调用的update方法。 当前,它执行2件事:1 –处理输入,2 –更新Bob。 鲍勃(Bob)有专用的update方法,我们将在后面介绍。#72 –#92processInput方法轮询keys映射以获取键,并相应地在Bob上设置值。 例如, #73 –#78行检查是否按下了键以向左移动,如果是,则将Bob的面朝左设置,将其state设置为State.WALKING ,将其速度设置为Bob的速度,但负数标志。 该符号是因为在屏幕上,负方向向左(原点位于左下方并指向右侧)。同样的权利。 如果同时按下两个键或不按下两个键,则需要进行一些额外的检查,在这种情况下,Bob变为State.IDLE且其水平速度为0

让我们看看Bob.java发生了什么变化。

public static final float SPEED = 4f; // unit per secondpublic void setState(State newState) {this.state = newState;}public void update(float delta) {position.add(velocity.tmp().mul(delta));}

只需将SPEED常数更改为每秒4个单位(块)。还添加了setState方法,因为我之前忘记了它。最有趣的是新获取的update(float delta)方法,该方法从WorldController 。 这种方法只是根据鲍勃的速度来更新鲍勃的位置。 为简单起见,我们仅在不检查他的状态的情况下执行此操作,因为控制器会根据Bob的朝向和状态来设置其速度。 我们在这里使用向量数学,而libgdx可以提供很多帮助。我们只需将以delta秒为单位的行进距离加上鲍勃的当前position 。 我们使用velocity.tmp()是因为tmp()创建了一个新的对象,该对象具有与velocity相同的值,并将该对象的值乘以经过的时间delta 。 在Java中,我们必须谨慎使用引用,因为速度位置都是Vector2对象。 有关矢量的更多信息,请参见http://en.wikipedia.org/wiki/Euclidean_vector 。

我们几乎拥有一切,我们只需要在事件发生时调用正确的事件即可。 libgdx具有一个输入处理器,该输入处理器具有一些回调方法。 因为我们将GameScreen用作游戏表面,所以也可以将其用作输入处理程序。 为此, GameScreen将实现libgdx InputProcessor 。新的GameScreen.java

package net.obviam.starassault.screens;import net.obviam.starassault.controller.WorldController;
import net.obviam.starassault.model.World;
import net.obviam.starassault.view.WorldRenderer;import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL10;public class GameScreen implements Screen, InputProcessor {private World    world;private WorldRenderer  renderer;private WorldController controller;private int width, height;@Overridepublic void show() {world = new World();renderer = new WorldRenderer(world, false);controller = new WorldController(world);Gdx.input.setInputProcessor(this);}@Overridepublic void render(float delta) {Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1);Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);controller.update(delta);renderer.render();}@Overridepublic void resize(int width, int height) {renderer.setSize(width, height);this.width = width;this.height = height;}@Overridepublic void hide() {Gdx.input.setInputProcessor(null);}@Overridepublic void pause() {// TODO Auto-generated method stub}@Overridepublic void resume() {// TODO Auto-generated method stub}@Overridepublic void dispose() {Gdx.input.setInputProcessor(null);}// * InputProcessor methods ***************************//@Overridepublic boolean keyDown(int keycode) {if (keycode == Keys.LEFT)controller.leftPressed();if (keycode == Keys.RIGHT)controller.rightPressed();if (keycode == Keys.Z)controller.jumpPressed();if (keycode == Keys.X)controller.firePressed();return true;}@Overridepublic boolean keyUp(int keycode) {if (keycode == Keys.LEFT)controller.leftReleased();if (keycode == Keys.RIGHT)controller.rightReleased();if (keycode == Keys.Z)controller.jumpReleased();if (keycode == Keys.X)controller.fireReleased();return true;}@Overridepublic boolean keyTyped(char character) {// TODO Auto-generated method stubreturn false;}@Overridepublic boolean touchDown(int x, int y, int pointer, int button) {if (x < width / 2 && y > height / 2) {controller.leftPressed();}if (x > width / 2 && y > height / 2) {controller.rightPressed();}return true;}@Overridepublic boolean touchUp(int x, int y, int pointer, int button) {if (x < width / 2 && y > height / 2) {controller.leftReleased();}if (x > width / 2 && y > height / 2) {controller.rightReleased();}return true;}@Overridepublic boolean touchDragged(int x, int y, int pointer) {// TODO Auto-generated method stubreturn false;}@Overridepublic boolean touchMoved(int x, int y) {// TODO Auto-generated method stubreturn false;}@Overridepublic boolean scrolled(int amount) {// TODO Auto-generated method stubreturn false;}
}

变化:#13 –该类实现InputProcessor#19 – Android触摸事件使用的屏幕的宽度和高度。#25 –用世界实例化WorldController#26 –将此屏幕设置为应用程序的当前输入处理器。 libgdx将其视为全局输入处理器,因此如果每个屏幕不共享相同的内容,则必须设置不同的屏幕。 在这种情况下,屏幕本身会处理输入。#47&#62 –我们将活动的全局输入处理器设置为null只是为了进行清理。#68 –每当在物理键盘上按下某个键时,就会触发keyDown(int keycode)方法。 参数keycode是按下的键的值,这样我们就可以轮询它,并在需要的时候进行操作。 这正是正在发生的事情。 基于所需的键,我们将事件传递给控制器​​。 该方法还返回true以使输入处理器知道已处理输入。#81keyUpkeyDown方法的确切逆函数。 释放键后,它仅委托给WorldController#111 –#118 –这就是有趣的地方。 这仅在触摸屏上发生,并且坐标与指针和按钮一起传递。 指针用于多点触摸,表示它捕获的触摸的ID。控件非常简单,仅用于简单演示目的。 屏幕分为四个部分,如果触摸降到了左下象限,它将被视为向左移动触发器,并将与桌面相同的事件传递给controller 。对于touchUp来说完全一样。

警告: –这是非常多的错误并且不可靠,因为没有实现touchDragged,并且每当手指在象限上拖动时,都会弄乱东西。 这当然是固定的,目的是演示多个硬件输入以及如何将它们捆绑在一起。

在台式机和Android上运行应用程序将演示控件。 在台式机上,箭头键;在Android上,通过触摸屏幕的下角,将移动Bob。在台式机上,您会注意到使用鼠标模拟触摸也可以使用。 这是因为touchXXX还可以处理桌面上的鼠标输入。 要解决此问题,请将以下行添加到touchDowntouchUp方法的开头:

if (!Gdx.app.getType().equals(ApplicationType.Android))return false;

如果应用程序不是Android,并且不执行该方法的其余部分,则返回false 。 请记住, false表示未处理输入。

如我们所见,鲍勃已经搬家了。

简短回顾

到目前为止,我们已经涵盖了很多游戏开发,并且已经有一些需要展示的东西。逐步地,我们将工作片引入了我们的应用程序中,并且逐步地我们取得了一些成就。

我们仍然需要添加:

  • 地形交互(块碰撞,跳跃)
  • 动画
  • 跟随Bob的大关卡和相机
  • 敌人和一把炸药
  • 声音
  • 精致的控件和微调
  • 更多屏幕可供游戏结束并开始
  • 与libgdx一起玩乐

确保选中第2部分(仍在进行中)以打勾上述列表。 但是请务必自己动手做,任何反馈都值得赞赏。

还可以查看libgdx及其令人敬畏的社区。 该项目的源代码可以在这里找到: https : //github.com/obviam/star-assault

要使用git进行检查:git clone git@github.com:obviam/star-assault.git

您也可以将其下载为zip文件。

在此处检查本教程的下一部分。

参考: 使用libgdx进行Android游戏开发入门–一天创建一个工作原型–我们的JCG合作伙伴Impaler在反对谷物博客上的教程第1部分。

翻译自: https://www.javacodegeeks.com/2012/05/android-game-development-with-libgdx_03.html

libgdx教程

libgdx教程_使用libgdx进行Android游戏开发–一天中的原型,第1b部分相关推荐

  1. libgdx教程_使用libgdx进行Android游戏开发–一天中的原型,第1a部分

    libgdx教程 在本文中,我将绕开游戏引擎和组件的构建模块,并演示如何使用libgdx库快速制作游戏原型. 您将学到什么: 创建一个非常简单的2D Shooter Platformer游戏. 完整的 ...

  2. libgdx开发指南_使用libgdx进行Android游戏开发–一天中的原型,第1a部分

    libgdx开发指南 在本文中,我将绕开游戏引擎和组件的构建模块,并演示如何使用libgdx库快速制作游戏原型. 您将学到什么: 创建一个非常简单的2D Shooter Platformer游戏. 完 ...

  3. 适用于 Visual Studio 的 Android 游戏开发扩展

    在 Windows 计算机上设置 Android 游戏开发扩展并在 Android 设备或模拟器上运行示例 Visual Studio C++ 项目. 先决条件 按照本节中的步骤准备您的 Window ...

  4. Android 游戏开发工具大升级

    不同的硬件厂商为 Android 用户带来了不同尺寸和体验的设备,因此,我们也一直努力地帮助开发者们将游戏呈现到尽多的 Android 设备并使得开发过程更加高效轻松.本文将向您介绍众多新的 Andr ...

  5. Android游戏开发教程汇总

    Android游戏开发教程汇总 把最近搜集到的Android游戏开发教程列出来,希望对大家有用. 1.Android2.0游戏开发实战宝典源代码,当然可能大家已经把书也买回家啦. 2.Pro.Andr ...

  6. Android游戏开发教程

    几个月前,我们收到了来自Java开发人员Tamas Jano的电子邮件,要求加入我们的JCG合作伙伴计划. 令我们惊讶的是,他维护了一个名为" Against The Grain " ...

  7. Android游戏开发教程:手教你写跳跃类游戏

    Android游戏开发教程:手教你写跳跃类游戏 package jumpball.game; import android.app.Activity; import android.os.Bundle ...

  8. Android游戏开发Android软件开发【教程三十篇】

    Android软件开发之发送短信与系统短信库解析(三十)  New Android软件开发之获取通讯录联系人信息(二十九)  New Android软件开发之PreferenceActivity中的组 ...

  9. android游戏开发教程之基本概念

    对于不管从事任何行业 使用任何编程语言的人们来说 当接触一个新知识 或者新出现的一个新平台来说 了解其基础知识非常重要 这在这里我指针对与android游戏开发的人来说 刚接触它的时候不了解andro ...

最新文章

  1. .dat文件还原数据库
  2. STM 软件事务内存——本质是为提高并发,通过事务来管理内存的读写访问以避免锁的使用...
  3. 空值替换为0_「Excel」是零值还是空值,你可以自由掌控
  4. shell 函数的高级用法
  5. wxpython入门_wxpython笔记:Wxpython入门
  6. Memcached 在linux上安装笔记
  7. 阿里云混合云Apsara Stack 2.0发布 加速政企数智创新
  8. PIR 宣布被营利性机构收购,.org 顶级域名注册费用或上涨
  9. iPhone X 穿越回 1957 年计算力相当的电脑,将会是什么样?
  10. 我的电脑能装苹果吗?
  11. 关于一些Silverlight中常用的尺寸,慢慢记录
  12. 最新最全git使用大全
  13. js中 urlencode编码
  14. cad详图怎么画_CAD标准图框怎么画
  15. 计算机演示文稿步骤,计算机二级Msoffice演示文稿(解题步骤)总结.doc
  16. 为什么有网络微信却显示未连接服务器,设备公众号显示未连接,为什么公众号设备显示未连接?...
  17. 双问号??在 js 中的应用
  18. Scaled-YOLOv4: Scaling Cross Stage Partial Network 论文翻译
  19. Winform PropertyGrid使用方法
  20. poj2245Lotto

热门文章

  1. 西建大历年电子与通信工程复试真题_电子与通信工程学院考研复试经验
  2. msf系统漏洞利用说明
  3. 电子劳动合同,为企业用工护航
  4. bridge 的运用和数码照片的处理
  5. 数字图像处理-图像基础-复习总结
  6. Z-Wave 700应用程序框架第四章 - 从零开始一个Z-Wave Plus应用
  7. C语言康威生命游戏,【2020存档】康威生命游戏(CGoL)研究进展
  8. 小学计算机课在玩中学,学中玩,玩中学的作文(共5篇)
  9. PHP实现获取url地址中一级域名
  10. 手把手教你用Python网络爬虫实现上海证券交易所定期报告pdf文件下载(附代码)...