Libgdx游戏开发(7)——开始游戏界面实现

原文: Libgdx游戏开发(7)——开始游戏界面实现-Stars-One的杂货小窝

上篇文章也是讲解了如何实现暂停,但实际上,上篇的做法可能不够优雅

因为暂停和游戏界面我们可以分成2个Screen对象,这样只需要监听键盘输入,更改显示不同的Screen对象即可

本文的实现目标:

使用Screen来实现,进入游戏前,先显示一个游戏主界面,按下enter再开始游戏

前置知识

还记得之前例子,都是继承ApplicationAdapter类,并在其的render方法中实现我们的游戏逻辑

由于我们要使用Screen对象,所以我们得使用Game对象类替换我们之前继承的ApplicationAdapter对象

实际上,Game对象和ApplicationAdapter最终父类都是ApplicationListener,只不过Game对象帮我们封装好了管理Screen的方法

Game对象提供了一个setScreen()方法来设置当前显示的Screen对象

基础使用

1.使用Game类作为入口

在我们启动方法中,使用Game作为启动的入口,下面的MyGame即为Game对象

object DesktopLauncher {     @JvmStatic     fun main(arg: Array<String>) {         val config = Lwjgl3ApplicationConfiguration()         config.setForegroundFPS(60)         //设置游戏窗口大小为800*480         config.setWindowedMode(800, 480)         //设置开启垂直同步         config.useVsync(true)         //Lwjgl3Application(CircleBallTest(), config)         Lwjgl3Application(MyGame(), config)     } } 

MyGame代码

class MyGame : Game() {      val batch: SpriteBatch by lazy { SpriteBatch() }      val font: BitmapFont by lazy { BitmapFont() }     val shape: ShapeRenderer by lazy { ShapeRenderer() }      override fun create() {          //这里调用下变量,实际相当于初始化了         batch         font         shape                  //注意这里,已经设置了首屏幕!!         this.setScreen(MainScreen(this))     }      override fun render() {         super.render()     }      override fun dispose() {         super.dispose()          //释放资源         shape.dispose()         font.dispose()         batch.dispose()     } } 

之后,这个MyGame将作为全局单例对象来进行使用;

由于是单例对象,所以,我们可以在其创建的时候,进行相关资源的创建,比如绘制图片和文字等对象创建(这里不再赘述,若是类有些陌生可详见之前文章讲解),以及嘴硬的资源释放,避免出现内存溢出问题

而官方给出的代码示例中,是将此MyGame对象作为之后Screen的构造函数传入(因为需要调用Game对象对应方法来设置当前显示屏幕)

但我觉得可能在整个全局静态类直接调用可能会好点?但不确定是否是最优做法

所以下面还是先按照官方例子走一遍

2.创建对应的Screen

假设我们先简单些,有2个Screen,一个是主界面MainScreen,另一个则是游戏运行界面GameScreen

和ApplicationAdapter类似,Screen接口也有一个ScreenAdapter空实现类

我们可以直接继承ScreenAdapter类,从而只重写我们需要的方法即可,代码更加清晰

MainScreen就简单绘制下游戏主界面的文字提示,代码如下:

class MainScreen(val game: MyGame) : ScreenAdapter() {       override fun render(delta: Float) {         game.apply {             batch.begin();             font.draw(batch, "Welcome to Drop!!! ", 100f, 150f);             font.draw(batch, "Tap anywhere to begin!", 100f, 100f)             batch.end();         }          //当鼠标点击则触发开始游戏,这里相信各位自己也能做些扩展,比如按下enter键来实现(前面文章也已经讲解过了)         if (Gdx.input.isTouched()) {             game.setScreen(GameScreen(game))             dispose();         }     }      }   

而我们的GameScreen,则是之前我们的相关代码,只是绘制的时候使用的是全局对象Game里的相关对象进行绘制

class GameScreen() : ScreenAdapter() {      val game by lazy { GloGame.game }      val ball by lazy { Ball() }     val line by lazy { MyBan() }      val pauseInput by lazy { PauseInput() }      override fun show() {         Gdx.input.inputProcessor = pauseInput     }      override fun render(delta: Float) {         if (pauseInput.handlePause {                 drawLogic()                  //绘制暂停的页面提示                 GloGame.game.apply {                     Gdx.gl.glClearColor(0f, 0f, 0f, 0.8f); // 设置清屏颜色为透明度80%的黑色                      batch.begin()                     font.draw(batch, "Pause", 100f, 150f)                     batch.end()                 }             }) {             return         }          drawLogic()         updateXy()      }      private fun drawLogic() {         Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)         val shape = game.shape         line.draw(shape)         ball.draw(shape)     }      fun updateXy() {         //运动的逻辑         ball.gundon()         line.control()          ball.checkFz()         //检测碰撞到数横条         ball.checkLineP(line)     } } 

这里我们封装了个简单的类实现游戏暂停(不过又觉得好像应该把暂停封装为一个Screen对象比较好)

class PauseInput() : InputAdapter() {     var isPaused = false      private var count = 0      override fun keyDown(keycode: Int): Boolean {         if (keycode == Input.Keys.ESCAPE) {             isPaused=isPaused.not()             return true // 表示已经处理了按键事件         }         return false; // 表示未处理按键事件     }      fun handlePause(action: () -> Unit): Boolean {         if (isPaused) {             //保证当前帧和上一帧相同后,就不再绘制了             if (count <= 1) {                 action.invoke()                 count++             }         } else {             count = 0         }           return isPaused     } } 

还有一些其他类,之前章节写的对应代码,为了方便实践,再贴一遍:

class MyBan {     var width = 200f     var height = 10f      var x = 0f     var y = height      fun draw(shape: ShapeRenderer) {         shape.begin(ShapeRenderer.ShapeType.Filled)         //这里注意: x,y是指矩形的左上角         shape.rect(x, height, width, height)         shape.end()     }      val spped = 400     fun control() {         if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {             x -= spped * Gdx.graphics.deltaTime         }          if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {             x += spped * Gdx.graphics.deltaTime         }          //这里屏蔽y坐标改变,只给控制左右移动         return          if (Gdx.input.isKeyPressed(Input.Keys.UP)) {             y += spped * Gdx.graphics.deltaTime         }          if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {             y -= spped * Gdx.graphics.deltaTime         }     } }  class Ball {     var size = 5f      var x = 50f     var y = 50f      var speedX = 5f     var speedY = 5f      //与板子的碰撞检测     fun checkLineP(myB: MyBan) {         val flag = x - size >= myB.x && x + size <= myB.x + myB.width         if (y - size <= myB.y && flag) {             speedY = speedY * -1         }     }      fun gundon() {         x += speedX         y += speedY     }      fun draw(shape: ShapeRenderer) {         shape.begin(ShapeRenderer.ShapeType.Filled)         shape.circle(x, y, size)         shape.end()     }      fun checkFz() {         //到达右边缘,x变反         if (x + size >= Gdx.graphics.width) {             speedX = speedX * -1         }          //到达下边缘,y变反         //todo 这个是判输条件!         if (y - size <= 0) {             //消失             //speedY = speedY * -1         }          //到达上边缘,y变反         if (y + size >= Gdx.graphics.height) {             speedY = speedY * -1         }          //到达左边缘,x变反         if (x - size <= 0) {             speedX = speedX * -1         }     } } 

3.最终效果

Libgdx游戏开发(7)——开始游戏界面实现

使用上的注意事项

  1. 切换到一个新的Screen的时候,如果之前的Screen不再使用,需要手动调用Screen.dispose方法,进行资源的释放
  2. 给Game对象设置Screen的时候,设置的新的那个Screen会调用onShow()方法,而之前的Screen会调用onHide()方法
  3. 如果有需要的话,一般在onShow()方法,给当前Screen设置一个输入监听器

优化尝试 - 全局game对象

使用一个全局静态类来管理game对象,取消对应Screen构造方法传game对象,测试发现似乎没啥问题

object GloGame{     lateinit var game: MyGame }   class MyGame : Game() {      val batch: SpriteBatch by lazy { SpriteBatch() }      val font: BitmapFont by lazy { BitmapFont() }     val shape: ShapeRenderer by lazy { ShapeRenderer() }      override fun create() {          //这里调用下变量,实际相当于初始化了         batch         font         shape         GloGame.game = this         this.setScreen(MainScreen())     }      override fun render() {         super.render()     }      override fun dispose() {         super.dispose()          //释放资源         shape.dispose()         font.dispose()         batch.dispose()     } } 

参考

发表评论

评论已关闭。

相关文章