在kotlin(desktop)中使用libGDX展示一块3D的巧克力

简介: java做游戏,总会让人感觉不太放心,但如果你尝试了libGDX,也许会改变你的看法,它是一款非常优秀的游戏开发引擎,2d场景与3d场景都支持,还支持Windows+Android+iOS+HTML全平台,一起来跟随本文学习一下吧

java做游戏,总会让人感觉不太放心,似乎唯一让人想到的就是我的世界(Java版),即便是这么个好作品,可依然出了个c++的基岩版(Bedrock),如果不是java难以满足玩家的需要,又何必推翻重做一个c++版呢?这就更让人失去信心,不过2020年的时候,我又接触了一款很不错的java开源游戏,Mindustry

image.png

缘起Mindustry

它自称为RTS游戏,不过核心还是以塔防为主,大约是2019年9月发布到Steam,我2020年4月就发现了它,真是有缘,我很快又发现原来它是开源游戏,虽说花了26块钱,不过就当它是买了一个云存档功能,而且也确实值得支持:

image.png

然后我就仔细看了看这个项目是怎么做起来的,看了看GitHub,发现到现在几乎都是游戏创始者[Anuken]进行主要的游戏维护,紧接着发现它的游戏引擎[Arc],更是几乎完全依赖[Anuken]的维护,一个人能做这么好,让人十分惊叹,不过这个[Arc]其实并非[Anuken]的从零开始的啦,它也是基于一个java游戏框架二次开发的,这个框架就是本篇的主角——[libGDX]

这个[libGDX],可以说是历史悠久了,早在2009年就已经有这个项目了,一开始叫做AFK,2010年就在Google Code开源了,然后一直到2014年,做好了第一个稳定的正式版1.0,,2d场景与3d场景都支持,还支持Windows+Android+iOS+HTML全平台。不过很可惜国内使用较少,我也是很晚才听到这个框架,不过只要开始学习,时机就不算晚,下面一起看看怎么开始做一个3d的小demo吧(类似于hello world)。

项目配置

如果我们依照官方文档,按libGDX Creating a Project所说,下载个gdx-setup.jarjava -jar运行,再在里面设置些东西,最后才能在IDE中打开,感觉有点不符合常情。还有可能让人感觉,你这框架是不是配置特别复杂,才需要专门的这么一个东西来生成项目呢?

image.png
其实如果只是上手,并没有多少配置需要关心,并不是很繁杂。经过我的一些探索,发现如果在Windows桌面开发,只要在gradle引入几个包就可以:

plugins {
    kotlin("jvm") version "1.8.0"
    application
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("com.badlogicgames.gdx:gdx:1.12.0")
    implementation("com.badlogicgames.gdx:gdx-platform:1.12.0:natives-desktop")
    implementation("com.badlogicgames.gdx:gdx-backend-lwjgl3:1.12.0")
}

kotlin {
    jvmToolchain(11)
}

application {
    mainClass.set("MainKt")
}

就是这样,没有任何的其他配置,还是相当简单的吧。然后就可以开始写代码了。

初始化窗口

可以先Main中写点东西了:

fun main(args: Array<String>) {
    val config = Lwjgl3ApplicationConfiguration()
    config.setTitle("巧克力展示")
    config.setWindowedMode(1200, 800)
    config.setForegroundFPS(60)
    Lwjgl3Application(GameApp(), config)
}

在Windows上做东西,一般就是要用这个Lwjgl3Application做窗口,然后要给一个具体应用的参数和配置参数,配置类也就是Lwjgl3ApplicationConfiguration了,这个setForegroundFPS进行帧数设置可以说是游戏框架的必备了,除了上面几个基本的设置,还有比较常见的setResizable设置,表示是否能让用户调节窗口大小;以及setWindowIcon设置窗口的logo;setWindowPosition,指定窗口出现位置等等。

但如上面代码中所示,并非有Main里这几行东西就能简单的展示出一个窗口,还需要一个具体的应用,也就是Lwjgl3Application的第一个参数GameApp(),下面就新建这个GameApp类:

import com.badlogic.gdx.ApplicationListener
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.GL20
import com.badlogic.gdx.graphics.PerspectiveCamera
import com.badlogic.gdx.graphics.VertexAttributes.Usage
import com.badlogic.gdx.graphics.g3d.*
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder

class GameApp : ApplicationListener {
    private val lights: Environment = Environment()
    private lateinit var modelBatch: ModelBatch
    private lateinit var model: Model
    private lateinit var camera: PerspectiveCamera
    private lateinit var instance: ModelInstance

    override fun create() {
        lights.set(ColorAttribute(ColorAttribute.AmbientLight, Color.TAN))
        modelBatch = ModelBatch()

        val cam = PerspectiveCamera(70f, Gdx.graphics.width.toFloat(), Gdx.graphics.height.toFloat())
        cam.position[-9f, 15f] = 9f
        cam.lookAt(0f, 1f, 2f)
        cam.near = 1f
        cam.far = 300f
        cam.update()
        this.camera = cam

        val modelBuilder = ModelBuilder()
        model = modelBuilder.createBox(
            12f, 2f, 12f,
            Material(ColorAttribute.createDiffuse(Color.GRAY)),
            (Usage.Position or Usage.Normal).toLong()
        )
        instance = ModelInstance(model)
    }

    override fun render() {
        Gdx.gl.glViewport(0, 0, Gdx.graphics.width, Gdx.graphics.height)
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT or GL20.GL_DEPTH_BUFFER_BIT)
        modelBatch.begin(camera)
        modelBatch.render(instance, lights)
        modelBatch.end()
    }

    override fun dispose() {
        modelBatch.dispose();
        model.dispose();
    }

    override fun resize(width: Int, height: Int) {
    }

    override fun pause() {
    }

    override fun resume() {
    }
}

这里面最主要关心的,就是create()render()这两个,create()负责初始化一些固定的对象和内容,reader()则是负责每次刷新屏幕需要渲染的东西。

Environment主要是做一些环境属性设置,其实主要就是做光源的,它并非单个光源而是一个集合,可以放各种各样的光源。常见的例如DirectionalLight方向光源、PointLight点光源、SpotLight聚光灯等等。

PerspectiveCamera是镜头,这个是最主要的,表示视角的方向。position表示相机所在的位置,lookAt设置相机观察的方向,而nearfar是用来定义相机的视锥体的。

其余的ModelBatchModelModelInstance这些都是管理模型对象的。

有了上面的代码,就可以运行出效果了:

image.png

是我们想要的矩形,这也是一个最基本的效果,但有很多问题要优化。

加光源与消锯齿

它的面都是完全一样的,不清晰,这时候就需要多加一个测方位的光源,在lights.set(ColorAttribute...下面在多加一行青灰色光源:

lights.add(DirectionalLight().set(Color.SLATE, 25f, -5f, 5f))

就这样加在偏左侧一点照过来,就能把3个面看的很清晰了,再运行,就是这样的效果:

lights.add(DirectionalLight().set(Color.SLATE, 25f, -5f, 5f))

就这样加在偏左侧一点,就能把3个面看的很清晰了,再运行,就是这样的效果:

image.png

3个面各有不同的亮度,不过发现锯齿非常明显了,没有关系,回到Main中,给config加一个配置:

config.setBackBufferConfig(8, 8, 8, 8, 16, 0, 4)

前4个参数都是颜色,后面的是深度和模板,这里都按默认值设置了。最后一个参数就是多重采样抗锯齿的参数,这里我们调成4就好,它有点类似于有损图片压缩会让边缘模糊一些,这个4就是采样的像素值,设置之后就可以看到锯齿好了很多:

![%QY3EIWZN7C{JA@07OM@OQ_tmb.png

根据鼠标缩放

这里就要做一个类InMultiplexer,名字随意,要继承InputMultiplexer,然后重写一个scrolled()方法:

import com.badlogic.gdx.InputMultiplexer

class InMultiplexer : InputMultiplexer() {

    override fun scrolled(amountX: Float, amountY: Float): Boolean {
        ...
        return super.scrolled(amountX, amountY)
    }

}

但这个类无法直接传递修改,而且由于是继承,也不好写构造方法,所以只能写个方法传一个消费者对象进来:

import com.badlogic.gdx.InputMultiplexer
import java.util.function.Consumer

class InMultiplexer : InputMultiplexer() {

    private var yListener: Consumer<Float>? = null

    fun listener(c: Consumer<Float>): InputMultiplexer {
        yListener = c
        return this
    }

    override fun scrolled(amountX: Float, amountY: Float): Boolean {
        yListener?.accept(amountY)
        return super.scrolled(amountX, amountY)
    }

}

再回到GameApp类的render()里,加一个Gdx.input.inputProcessor的赋值,建议放到Gdx.gl.glClear...的下面:

Gdx.input.inputProcessor = InMultiplexer().listener {
    camera.fieldOfView = camera.fieldOfView + it * 5
    camera.update()
}

滑轮滑动一下,根据当前位置挪动滑动位置*5的距离,这个5可以当做滑轮的step,每次滑动的距离,可以根据情况调节。就这样效果就完成了:

Animation2.gif

可以看到并不是很复杂,不过确实要一些代码量,算不上非常简洁,但也还是比较方便的,后续我还会发一些关于[libGDX]的文章,可以关注我了解关于[libGDX]的更多知识细节。当然了也可以去看一看[Anuken]的作品,真的非常棒。

本文于2023年7月19日~20日同时写作并发布在lyrieek的稀土掘金社区与阿里云开发者社区。

目录
相关文章
|
6月前
AutoJs源码---神级大分享
AutoJs源码---神级大分享
168 0
|
6月前
QT实现植物大战僵尸中文版工具代码
QT实现植物大战僵尸中文版工具代码
|
6月前
|
Python
python小项目之利用pygame实现代码雨动画效果(附源码 可供学习)
python小项目之利用pygame实现代码雨动画效果(附源码 可供学习)
182 1
|
6月前
|
编解码 前端开发 UED
CocosCreator 面试题(十一)Cocos Creator 屏幕适配
CocosCreator 面试题(十一)Cocos Creator 屏幕适配
264 0
|
Python
python植物大战僵尸二十三之添加音乐
python植物大战僵尸二十三之添加音乐
88 0
|
Python
10个python经典小游戏(上)(动图演示+源码分享)(上)
10个python经典小游戏(上)(动图演示+源码分享)
381 0
|
Python
10个python经典小游戏(上)(动图演示+源码分享)(下)
10个python经典小游戏(上)(动图演示+源码分享)
391 0
|
Dart JavaScript 前端开发
面向Android开发者的Dart学习教程
近日Google发布了Flutter2.0, 使用Flutter开发的App可以在不做修改的情况下发布到更多的主流平台;再加上早些时候Fuchsia也宣布将Dart作为主要的UI开发语言,如果未来你想
190 0
|
Python
python---飞机大战小游戏(提供源码)
python---飞机大战小游戏(提供源码)
368 0
|
程序员
代码转图片Carbon-高雅程序员必备!
代码转图片Carbon-高雅程序员必备!
239 0
代码转图片Carbon-高雅程序员必备!