原文: Libgdx游戏开发(6)——游戏暂停-Stars-One的杂货小窝

暂停也是一个游戏的必要功能了,本文研究了Libgdx实现游戏暂停

例子以桌面端游戏实现讲解为主,至于移动端,可能之后会进行补充...

本文最终实现的就是

按下esc暂停,之后会出现一个界面提示,表示当前已经暂停

重新按下esc,则返回继续游戏

本篇稍微学习了下libgdx里的输入事件监听

最初方案1

最初看的教程是,通过一个boolean变量来控制render渲染,这里我们以上文例子Libgdx游戏开发(5)——碰撞反弹的简单实践-Stars-One的杂货小窝代码为例,增加一个暂停功能

注:下面贴的是全部代码,发布各位自行运行,但后续代码例子只看CircleBallTest这个类,为了方便阅读其他类就不再贴出了...

import com.badlogic.gdx.ApplicationAdapter
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.GL20
import com.badlogic.gdx.graphics.glutils.ShapeRenderer class CircleBallTest : ApplicationAdapter() {
lateinit var shape: ShapeRenderer val ball by lazy { Ball() }
val line by lazy { MyBan() } override fun create() {
shape = ShapeRenderer()
} //增加一个变量标识
var isPause = false override fun render() { //监听按下esc键,修改标识
if (Gdx.input.isKeyPressed(Input.Keys.ESCAPE)) {
isPause = !isPause
} if (isPause) {
//如果是暂停,则不再进行绘制
return
} Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) line.control()
ball.gundon() line.draw(shape)
ball.draw(shape) ball.checkFz()
//检测碰撞到数横条
ball.checkLineP(line) }
} 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()
} fun control() {
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
x -= 200 * Gdx.graphics.deltaTime
} if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
x += 200 * Gdx.graphics.deltaTime
} //这里屏蔽y坐标改变,只给控制左右移动
return if (Gdx.input.isKeyPressed(Input.Keys.UP)) {
y += 200 * Gdx.graphics.deltaTime
} if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
y -= 200 * Gdx.graphics.deltaTime
}
}
} class Ball {
var size = 5f var x = 50f
var y = 50f var speedX = 5f
var speedY = 5f fun checkLineP(myBan: MyBan) {
if (y - size <= myBan.y) {
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次esc,但是发现好像没暂停,什么原因导致的?

原因很简单,render()方法是每帧进行渲染的,所以导致我们的监听会执行多次,我们加个日志打印就能发现端倪,如下图:

按下了一次,但由于每帧都会渲染,所以触发了多次

优化方案2 - 事件拦截器监听按键

经过了一番百度和GPT询问,得知Libgdx里有一个输入事件的拦截器接口InputProcessor

为了方便我们不必重写每个此接口的每个方法,我们可以使用InputAdapter(这个是InputProcessor接口的空实现类),之后重写需要的方法即可

我们以上面需求,实现监听esc键的监听,代码如下:

class InputP:InputAdapter(){
//暂停标志
var isPause = false override fun keyDown(keycode: Int): Boolean {
if (keycode == Input.Keys.ESCAPE) {
//按下esc按键则修改状态
isPause=!isPause
} //ps:如果想监听android上的返回键,则可以使用Input.Keys.BACK,不过得先调用
return true
}
}

接着通过Gdx.input.inputProcessor(我这里是kotlin写法,java的话则是个setinputProcessor*()方法)进行设置拦截事件的拦截

class CircleBallTest : ApplicationAdapter() {
lateinit var shape: ShapeRenderer val ball by lazy { Ball() }
val line by lazy { MyBan() } //懒加载创建拦截器对象
val inputPro by lazy { InputP() } override fun create() {
shape = ShapeRenderer() //开始之前就设置拦截器
Gdx.input.inputProcessor = inputPro
} override fun render() {
//通过拦截器里的标志判断当前是否暂停绘制
if (inputPro.isPause) { } else {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
line.draw(shape)
ball.draw(shape) line.control()
ball.gundon()
ball.checkFz() //检测碰撞到数横条
ball.checkLineP(line)
} }
}

效果:

从动图效果来看,暂停功能是实现了,但是出现了闪动的问题

这里虽然具体原理不清楚,但是根据之前做过Android动态壁纸的研究,知道这种底层还是使用OpenGl绘制,所以直接猜测OpenGl渲染缓存中有上一帧数据,导致了此问题

优化方案3 - 暂停状态重绘

根据上面的原因,所以有下解决思路:

暂停状态下,重新绘制当前的UI,但不改变物体的x,y坐标

这里就提到了上章节说的,为什么要将绘制和坐标逻辑计算分开不同方法来写的原因了

如下代码(只贴核心代码):

override fun render() {
if (inputPro.isPause) {
//这里重新绘制
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
line.draw(shape)
ball.draw(shape) return
}
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
line.draw(shape)
ball.draw(shape) line.control()
ball.gundon()
ball.checkFz() //检测碰撞到数横条
ball.checkLineP(line)
}

效果如下图:

但个人感觉这种方案,在暂停了但仍然会不停的绘制,感觉有些性能浪费,于是有了下面的方案4

优化方案4

再回到问题上来,因为是上一帧和当前帧切换导致的问题,所以我们将上一帧和当前帧整成一样,绘制的时候就不会出现闪动的状态了吧,得到一个新的解决思路:

每次进入暂停状态后,绘制2遍帧数据,保证上一帧和当前帧相同,之后即可跳过绘制过程,由于前2帧一直是相同的,所以就不会出现抖动的效果,即则完成我们需要的效果和优化的效果

class InputP : InputAdapter() {
var isPause = false override fun keyDown(keycode: Int): Boolean {
if (keycode == Input.Keys.ESCAPE) {
isPause = !isPause
}
//如果不是暂停状态,则重置
if (isPause.not()) {
count=0
}
return true
} var count = 0 fun handlePase(drawAction: () -> Unit) {
//这里保证绘制完2帧
if (count > 1) {
return
} drawAction.invoke()
count++ }
} class CircleBallTest : ApplicationAdapter() {
lateinit var shape: ShapeRenderer val ball by lazy { Ball() }
val line by lazy { MyBan() } override fun create() {
shape = ShapeRenderer()
Gdx.input.inputProcessor = inputPro
} val inputPro by lazy { InputP() } override fun render() {
if (inputPro.isPause) {
inputPro.handlePase {
draw()
} return
} draw() line.control()
ball.gundon()
ball.checkFz() //检测碰撞到数横条
ball.checkLineP(line)
} private fun draw() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
line.draw(shape)
ball.draw(shape)
}
}

由于效果与上面相同,这里就不上图了

优化方案5

上面已经完成实现暂停功能了,现在我们在上面基础上个加个暂停文字提示(之前章节已经讲过如何绘制文字了),这里简单起见,我们直接显示pause单词

class CircleBallTest : ApplicationAdapter() {
lateinit var shape: ShapeRenderer val ball by lazy { Ball() }
val line by lazy { MyBan() } val batch: SpriteBatch by lazy { SpriteBatch() }
val font: BitmapFont by lazy { BitmapFont() } override fun create() {
shape = ShapeRenderer()
Gdx.input.inputProcessor = inputPro
} val inputPro by lazy { InputP() } override fun render() {
if (inputPro.isPause) {
inputPro.handlePase {
draw() //绘制暂停提示
Gdx.gl.glClearColor(0f, 0f, 0f, 0.8f); // 设置清屏颜色为透明度80%的黑色
batch.begin()
font.draw(batch, "Pause", 100f, 150f)
batch.end()
} return
} draw() line.control()
ball.gundon()
ball.checkFz() //检测碰撞到数横条
ball.checkLineP(line)
} private fun draw() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
line.draw(shape)
ball.draw(shape)
}
}

效果如下:

补充 - 监听android手机的返回键

如果想要监听android手机的返回键,则需要先设置Gdx.input.setCatchKey(Input.Keys.BACK, true),之后和上述一样监听keycode==Keys.BACK即可实现,如下图代码示例

这里就不放演示动图了,实际测试效果按下返回键即可暂停,但好像分辨率没有兼容,导致小球特别小,后续优化的时候再研究了...

参考

Libgdx游戏开发(6)——游戏暂停的更多相关文章

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

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

  2. JavaFX横幅类游戏开发 教训 游戏贴图

    上一节课,我们即将完成战旗Demo有了一个大概的了解.教训这,我们将学习绘制游戏地图. 由于JavaFX 2.2中添加了Canvas相关的功能,我们就能够使用Canvas来实现游戏绘制了. 游戏地图绘 ...

  3. java游戏开发杂谈 - 游戏物体

    现实生活中,有很多物体,每个物体的长相.行为都不同. 物体存在于不同的空间内,它只在这个空间内发生作用. 物体没用了,空间就把它剔除,不然既占地方,又需要花精力管理. 需要它的时候,就把它造出来,不需 ...

  4. java游戏开发杂谈 - 游戏编程浅析

    每个游戏,你所看到的它的一切,都是计算机画出来的! 地图是画出来,人物是画出来的,树木建筑是画出来的,菜单按钮是画出来的,滚动的文字.闪烁的图标.云雾烟火,都是画出来的. 游戏编程,所要做的,就是控制 ...

  5. FXGL游戏开发-JavaFX游戏框架

    FXGL 是一个JavaFX 游戏开发的框架,这个框架有两个版本,其中基于JDK1.8的版本已经不再维护,目前最新的是基于JDK11的版本,也就是Openjfx的版本. FXGL 提供了各种游戏范例: ...

  6. Unity3D游戏开发之游戏读/存档功能在Unity3D中的实现

    喜欢我的博客请记住我的名字:秦元培,我的博客地址是:http://qinyuanpei.com 转载请注明出处,本文作者:秦元培, 本文出处:http://blog.csdn.net/qinyuanp ...

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

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

  8. 【Unity3d游戏开发】游戏中的贝塞尔曲线以及其在Unity中的实现

    RT,马三最近在参与一款足球游戏的开发,其中涉及到足球的各种运动轨迹和路径,比如射门的轨迹,高吊球,香蕉球的轨迹.最早的版本中马三是使用物理引擎加力的方式实现的足球各种运动,后来的版本中使用了根据物理 ...

  9. 三、微信小游戏开发 --- 小游戏API调用Platform

    微信小游戏API Platform主要是Egret用于来调用平台的SDK的. 在Egret中使用接口定义Platform. Egret项目中默认的platform值是DebugPlatform. 发布 ...

  10. csdn vip文章:Unity游戏开发-小游戏-非常简单的游戏-unity-

    原文https://blog.csdn.net/qq_20484877/article/details/81841190 1*创建物体 Create菜单下 3D Object菜单下Cube 1.1设置 ...

随机推荐

  1. 羽夏看Linux内核——简述

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  2. 自定义的基于System.Net.Http.HttpClient的WebClient,可以作为微信支付宝的发起请求时的基础请求类

    个人编写的,自己用于自己的微信api的请求的实现当中,源码公开,大家可以查看反编译源码.以下是使用方法: 第一步 搜索和安装zmjtool 第二步 发起请求 1 /**引入命名空间*/ 2 using ...

  3. cesium教程8-官方示例翻译-图层亮度对比度调整

    完整示例代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="u ...

  4. Seata原理浅析

    前言 Seata是阿里开源的分布式事务解决方案,本文将详细介绍 Seata 的事务模式.原理以及使用.了解之前需清楚什么是分布式事务. 一.什么是 Seata Seata 是一款开源的分布式事务解决方 ...

  5. 4G EPS 第四代移动通信系统

    目录 文章目录 目录 4G EPS 4G EPS 4G(the 4th generation mobile communication technology,第四代移动通信技术)提供了 3G 不能满足 ...

  6. Istio(十):istio多集群部署模式

    目录 一.模块概览 二.多集群部署 2.1 多集群部署 2.2 网络部署模式 2.3 控制平面部署模型 2.4 网格部署模型 2.5 租户模式 2.6 最佳多集群部署 一.模块概览 在本模块中,我们将 ...

  7. 关于对于Java中Entity以及VO,以及DTO中Request对象序列化的学习

    关于 Serializable的探讨 前提引入 是由于软件测试上有同学提到说,什么该字段在程序刚运行时,导致jvm激增,所以吸引了我的注意 回顾代码 MybatisPlus Generator自动生成 ...

  8. 【web】自定义协议Protocol URL

    URL Protocol(自定义协议)可以让web页面调用本地exe程序,这个神奇的功能是怎么实现的呢? URL ProtocolURL Protocol,没错就是标题里所说的自定义协议.这玩意儿大家 ...

  9. 一种基于E3处理器平台的NAS完整方案(从电脑组装到网站部署)

    一种基于E3处理器平台的NAS完整方案(从电脑组装到网站部署) 本文将简要简要介绍本人自建NAS的完整配置,截至发文此NAS已经连续良好运行一年,应当说具有良好的稳定性. 本文所述配置包含洋垃圾成分, ...

  10. itest(爱测试) 接口测试,敏捷测试管理平台 10.4.0 发布

    一:itest work 简介 itest work 开源敏捷测试管理,包含极简的任务管理,测试管理,缺陷管理,测试环境管理,接口测试,接口Mock,还有压测 ,又有丰富的统计分析,8合1工作站.可按 ...