原文: 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. 通过 OpenAPI 部署 Npcf_PolicyAuthorization-PostAppSessions API Service

    目录 文章目录 目录 OpenAPI 部署步骤 OpenAPI 官方网站:https://github.com/OAI/OpenAPI-Specification 支持通过标准的 yaml 文件来生成 ...

  2. MyBatis-Plus 实现多租户管理的实践

    本文主要讲解使用Mybatis-Plus结合dynamic-datasource来实现多租户管理 在现代企业应用中,多租户(Multi-Tenant)架构已经成为一个非常重要的设计模式.多租户架构允许 ...

  3. Java计算百分比保留整数

    1.Java计算百分比保留整数的方法步骤 在Java中计算百分比并保留整数,通常涉及以下步骤: (1)计算原始数值与基准数值的百分比(通常使用 (原始数值 / 基准数值) * 100 的公式). (2 ...

  4. 腾讯面试:如何提升Kafka吞吐量?

    Kafka 是一个分布式流处理平台和消息系统,用于构建实时数据管道和流应用.它最初由 LinkedIn 开发,后来成为 Apache 软件基金会的顶级项目. Kafka 特点是高吞吐量.分布式架构.支 ...

  5. Python ipset iptables 实现蜜罐 自动封堵扫描者IP

    Python ipset iptables 实现蜜罐 自动封堵扫描者IP 蜜罐可以诱捕入侵者,但无法实时封堵入侵者,必须在事后通过日志进行手工封堵. 有没有什么办法可以实现自动封堵入侵者IP? ipt ...

  6. Java 对象的揭秘

    前言 作为一个 Java 程序员,我们在开发者最多的操作要属创建对象了.那么你了解对象多少?它是如何创建?如何存储布局以及如何使用的?本文将对 Java 对象进行揭秘,以及讲解如何使用 JOL 查看对 ...

  7. this,构造器,static,final,单例模式

    this关键字 在java中this是一个引用变量,即指向当前对象地址的引用(指针),→可以把this当作当前对象,便于更好的索引. this() 实际是调用了当前对象的构造器 1. 引用当前对象的属 ...

  8. GIT文件上传演示

    Be Written By Handat.憨大头 注:以下内容默认你已经做好了git工具的用户账户配置. (1)创建Gitee线上代码仓库,HTTPS协议地址就是仓库地址,如例https://gite ...

  9. kettle从入门到精通 第四十一课 kettle 事务(单个转换文件)

    1.大家都知道,我们在平常写java或者C#等代码时,如果涉及操作多个表时为了保持数据一致性需要开启事务,同样kettle也支持事务,今天我们一起来学习下kettle 单个转换文件内的事务特性. 转换 ...

  10. INFINI Labs 产品更新 | Console 告警中心 UI 全新改版,新增 Dashboard 全屏模式等功能

    本次 INFINI Labs 产品更新主要发布 Console v1.7.0,重点优化了 Console 告警中心和数据看板 Dashboard 可视化功能.详细介绍如下: 优化告警中心 UI 上个版 ...