原文: 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.最终效果

使用上的注意事项

  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()
}
}

参考

Libgdx游戏开发(7)——开始游戏界面实现的更多相关文章

  1. Libgdx游戏开发(2)——接水滴游戏实现

    原文:Libgdx游戏开发(2)--接水滴游戏实现 - Stars-One的杂货小窝 本文使用Kotlin语言开发 通过本文的学习可以初步了解以下基础知识的使用: Basic file access ...

  2. [libGDX游戏开发教程]使用libGDX进行游戏开发(12)-Action动画

    前文章节列表:  使用libGDX进行游戏开发(11)-高级编程技巧   使用libGDX进行游戏开发(10)-音乐音效不求人,程序员也可以DIY   使用libGDX进行游戏开发(9)-场景过渡   ...

  3. [libGDX游戏开发教程]使用libGDX进行游戏开发(1)-游戏设计

    声明:<使用Libgdx进行游戏开发>是一个系列,文章的原文是<Learning Libgdx Game Development>,大家请周知.后续的文章连接在这里 使用Lib ...

  4. 精通libGDX游戏开发-RPG实战-开发游戏的基本前提

    说起RPG,大概国人是不会陌生的. 这不得不从中国单机游戏市场说起,由于早期软件市场被盗版杀死,顺带的,单机游戏软件作为软件市场的分支,也没赚什么钱,养不活公司纷纷倒闭,只到RPG游戏<仙剑奇侠 ...

  5. [libgdx游戏开发教程]使用Libgdx进行游戏开发(11)-高级编程技巧 Box2d和Shader

    高级编程技巧只是相对的,其实主要是讲物理模拟和着色器程序的使用. 本章主要讲解利用Box2D并用它来实现萝卜雨,然后是使用单色着色器shader让画面呈现单色状态:http://files.cnblo ...

  6. [libgdx游戏开发教程]使用Libgdx进行游戏开发(10)-音乐和音效

    本章音效文件都来自于公共许可: http://files.cnblogs.com/mignet/sounds.zip 在游戏中,播放背景音乐和音效是基本的功能. Libgdx提供了跨平台的声音播放功能 ...

  7. [libgdx游戏开发教程]使用Libgdx进行游戏开发(2)-游戏框架搭建

    让我们抛开理论开始code吧. 入口类CanyonBunnyMain的代码: package com.packtpub.libgdx.canyonbunny; import com.badlogic. ...

  8. 精通libGDX游戏开发-RPG实战-欢迎来到RPG的世界

    欢迎来到RPG的世界 本章我会快速的使用tiled这样的瓷砖地图工具,来带领大家创造所设想的世界. 创建并编辑瓷砖地图 瓷砖地图(tile-based map)是广泛应用于各种游戏类型的地图格式,li ...

  9. [libgdx游戏开发教程]使用Libgdx进行游戏开发(9)-场景过渡

    本章主要讲解场景过渡效果的使用.这里将用到Render to Texture(RTT)技术. Libgdx提供了一个类,实现了各种常见的插值算法,不仅适合过渡效果,也适合任意特定行为. 在本游戏里面, ...

  10. [libgdx游戏开发教程]使用Libgdx进行游戏开发(7)-屏幕布局的最佳实践

    管理多个屏幕 我们的菜单屏有2个按钮,一个play一个option.option里就是一些开关的设置,比如音乐音效等.这些设置将会保存到Preferences中. 多屏幕切换是游戏的基本机制,Libg ...

随机推荐

  1. 前后端分离项目集成PageOffice——实现在线编辑Word文件的版本控制

    PageOffice本身提供了SaveFilePage的js方法,但是由于该方法不支持代理且不能跨域导致在前后端分离项目中无法使用 功能:实现三个按钮分别保存不同版本的文件 1.PageOffice可 ...

  2. java启动参考

    启动参数 mvn clean package -Dmaven.test.skip=true -Ptest - java - -server - -Xms2G - -Xmx2G - -Xss256K - ...

  3. openstack 错误(报错)集合

    1. 执行nova命令报错: ERROR (CommandError): You must provide a username or user ID via --os-username, --os- ...

  4. VSCode + JTAG调试合宙ESP32C3的经历

    VSCode + JTAG调试合宙ESP32C3 环境 Windows10 VSCode + ESP-IDF 合宙ESP32C3(无串口芯片版本) 理论 想要直接使用内置JTAG,USB要求连接GPI ...

  5. 【WPF】 BasedOn的用法

    BasedOn 用于样式的继承. 这里的已经继承了一个样式 此时,我们想在Resource中让他附加新的样式,但是这样不成功 修改如下: 去掉了之前的样式选择 我们使用BasedOn让其叠加样式

  6. ProcessStartInfo 类

    定义 命名空间: System.Diagnostics 程序集: System.Diagnostics.Process.dll 指定启动进程时使用的一组值. C#复制   public sealed ...

  7. Windows Server系统 PaddleOCR失败解决方案

    因PaddleOCR引用了Opencv,在windows server 上 使用opencv出现 DLL load failed错误,发现缺失部分dll:MFPlat.dll.MF.dll.MFRea ...

  8. GPT-4o和GPT-4有什么区别?我们还需要付费开通GPT-4?

    GPT-4o 是 OpenAI 最新推出的大模型,有它的独特之处.那么GPT-4o 与 GPT-4 之间的主要区别具体有哪些呢?今天我们就来聊聊这个问题. 目前来看,主要是下面几个差异. 响应速度 G ...

  9. FRDM-MCXN947开发板之RGB灯

    一.背景 RGB LED:通过红.绿.蓝三种颜色组合发光的LED,可以理解由三个不同发光属性的LED组成,这个是LCD平板显示原理的基础,一个LED相当于屏幕上面的一个像素 FRDM-MCXN947集 ...

  10. 使用Python实现深度学习模型:序列到序列模型(Seq2Seq)

    本文分享自华为云社区<使用Python实现深度学习模型:序列到序列模型(Seq2Seq)>,作者: Echo_Wish. 序列到序列(Seq2Seq)模型是一种深度学习模型,广泛应用于机器 ...