首页 > 其他分享 >Libgdx游戏开发(2)——接水滴游戏实现

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

时间:2022-10-05 16:22:53浏览次数:81  
标签:游戏 Gdx rainDrop Libgdx bucket batch 雨滴 64 水滴

原文:Libgdx游戏开发(2)——接水滴游戏实现 - Stars-One的杂货小窝

本文使用Kotlin语言开发

通过本文的学习可以初步了解以下基础知识的使用:

  • Basic file access
  • Clearing the screen
  • Drawing images
  • Using a camera
  • Basic input processing
  • Playing sound effects

游戏玩法

游戏的主要玩法有以下5点:

  1. 使用桶接水滴
  2. 桶只能左右移动
  3. 水滴会从顶部并加速下落
  4. 玩家可以通过鼠标或键盘来移动桶
  5. 游戏没有结束一说,可以一直玩

预览动图:

步骤

1.创建项目

由于我是要使用Kotlin开发,所以勾选了Kotlin开发的选项

实际上,创建出来的项目,还是Java文件写的,所以,为了方便,我用了Android Studio把Java文件转为了Kotlin文件

2.添加资源文件

之后,我们需要添加该有的素材文件,总共有四个文件

  • drop.wav 水滴掉落在桶里的声音
  • rain.mp3 雨声(背景声)
  • bucket.png 桶图片
  • drop.png 水滴图片

都放在assets资源文件夹中

资源文件下载可以点击下载 蓝奏云下载

3.设置游戏配置

找到desktop文件夹目录下的代码文件,进行代码的修改,调整游戏窗口大小为800*480,并开启垂直同步

//设置游戏窗口大小为800*480
config.setWindowedMode(800, 480)
//设置开启垂直同步
config.useVsync(true)

4.加载资源文件

我们进入到core目录下的CatchWater文件,可以看到具体的代码结构

这里可以看到我们的类是继承ApplicationAdapter,从名字上就可以让我们猜测到是使用的设计模式中的适配器模式来兼容不同平台(没深入验证,仅是猜测)

ApplicationAdapter是抽象类方法,提供了几个需要重写的方法,感觉和Android开发中的Activity差不多,应该就是Libgdx游戏的生命周期方法了,这里先不深入扩展了

因为在游戏开始前,我们得先加载上述我们复制到项目的一些图片和音乐的资源文件,所以我们选择在create()方法中进行初始化我们的资源文件

添加以下代码:

lateinit var dropImage: Texture
lateinit var bucketImage: Texture
lateinit var dropSound: Sound
lateinit var rainMusic: Music

override fun create() {
    // load the images for the droplet and the bucket, 64x64 pixels each
    dropImage = Texture(Gdx.files.internal("drop.png"))
    bucketImage = Texture(Gdx.files.internal("bucket.png"))

    // load the drop sound effect and the rain background "music"
    dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"))
    rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"))

}

这里需要注意下,我们两张图片(水滴和桶)分辨率都是64*64

我们使用了Gdx.files.internal()方法来获取assets文件夹里的内容,之后游戏如果是运行在Android平台上,这个方法也是通用的

如果是assets文件夹里还有一层文件夹,可以这样写:

Gdx.files.internal("iamge/myimg.png")

稍微讲解下用到的几个类,具体知识得后面再新开文章进行研究

  • Texture 这个英文翻译是纹理,但其实可以看做成图片的内存对象,类似Android开发里的Bitmap
  • Sound 比较短的那种音效文件
  • Music 时长较长的音频文件

5.播放背景音乐

之后我们可以实现播放背景音乐了,这个我们也直接在资源文件加载完毕之后播放吧

//设置循环播放背景音乐
rainMusic.setLooping(true)
rainMusic.play()

这个时候,我们可以进入到desktop里的那个文件,点击箭头运行游戏

游戏界面是黑屏的,但是已经开始播放音乐了,这里就不放图了

6.绘制图形

上述加载资源文件,我们已经得到了两个Texture对象,这个时候我们需要将其画出来,可以通过SpriteBatch这个类来实现

我们直接新建一个全局变量,然后也是在create()方法初始化即可

之后,在render()方法里,将我们的图片绘制出来

override fun render() {
    //设置屏幕背景色
    ScreenUtils.clear(0f, 0f, 0.2f, 1f)
    
    //绘制图片
    batch.begin()
    batch.draw(bucketImage, 400f, 400f)
    batch.end()
}

效果如下图所示:

这里需要注意一个问题:

在Libgdx中,默认坐标系是以左下角为原点,也就是常规的数学坐标系,当然,也可以通过Camare进行修改,具体如何修改,这里先不研究

上述代码,我们将图片对象的左下角,绘制到屏幕的(400,400)坐标位置上

上面虽然我们成功绘制了一个图片,但是考虑到下述几个问题:

  • 雨滴掉落需要改变y坐标,但是y坐标如何存储呢?
  • 桶左右移动,需要改变x坐标,x坐标应该如何存储呢?
  • 如何确认雨滴和桶的边界关系呢?

考虑到上述的问题,单纯的坐标点记录会使后续的流程代码变得十分的复杂,这个时候我们可以引入一个范围来记录相关的坐标点信息

这里我们可以选用矩形Rectangle来存储我们的图片绘制位置

PS:实际上,不只有Rectangle矩形,还有其他的形状,但具体的使用,还是留在后面教程再进行补充吧

我们通过定义矩形的左上角(x,y)坐标点和宽高,就可以确认一个Rectangle矩形范围了,如下代码所示:

val bucket = Rectangle().apply {
    //桶放中间
    x = (800 / 2 - 64 / 2).toFloat()
    y = 20.toFloat()
    width = 64.toFloat()
    height = 64.toFloat()
}

由于此矩形区域是我们用来找回进行边界关系的比对,所以将其宽高都设置为与图片图像的分辨率一样(64*64)

这样一来,我们使用batch.draw()方法绘制的时候,能将图片对象刚好覆盖到矩形范围里

batch.begin()
batch.draw(bucketImage, bucket.x, bucket.y)
batch.end()

7.雨滴下落实现

按照上述的逻辑,我们也创建一个雨滴的矩形范围,并将其绘制出来

雨滴默认在最上面,由于绘制图片的时候以左下角来绘制的,所以,最大y坐标减去64,即是雨滴开始的固定高度

然后雨滴的x坐标是随机的,但是最大范围为800减去宽度64

MathUtilsLibgdx提供的随机数工具类

val rainDrop = Rectangle().apply {
    //x坐标随机
    x = MathUtils.random(0, 800 - 64).toFloat()
    y = (480 - 64).toFloat()
    width = 64.toFloat()
    height = 64.toFloat()
}

override fun render() {
    batch.projectionMatrix = camera.combined
    batch.begin()
    batch.draw(dropImage, rainDrop.x, rainDrop.y)
    batch.end()
}

效果如下所示:

接下来,我们需要实现雨滴的下落功能,这里,我们可以采用时间作为变量,随着时间的变长来改rainDrop对象的y坐标数值

override fun render() {
    batch.begin()
    batch.draw(dropImage, rainDrop.x, rainDrop.y)
    batch.end()

    rainDrop.y -= 200 * Gdx.graphics.deltaTime
}

效果如下:

可以看到,下落效果实现了,但是似乎出现了重复的东西,其实就是我们在开始的没清除掉上次绘制的图像,加上清除的代码即可:

override fun render() {
     //清除并设置屏幕背景色
    ScreenUtils.clear(0f, 0f, 0.2f, 1f)
    
    batch.begin()
    batch.draw(dropImage, rainDrop.x, rainDrop.y)
    batch.end()
    
    //每帧的时间,高度减少200
    rainDrop.y -= 200 * Gdx.graphics.deltaTime
}

8.判断雨滴是否掉落在桶里

这里,就是需要判断边界了,上面也说到,使用Rectangle矩形,就是方便我们判断是否水滴和桶接触了

Rectangle对象中,有个overlaps()方法,就是专门来判断两个矩形是否重叠了

//判断两个矩形的接触面积有重叠,即水滴掉落在桶里
if (rainDrop.overlaps(bucket)) {
    //播放音效
    dropSound.play()
}

9.键盘控制改变桶位置

上面的功能已经基本完成了,那么还差通过键盘来控制桶的位置就能实现了

我们判断是否按下方向键左或右来改变桶对应矩形的x坐标,这样就能改变桶的绘制位置了

override fun render() {
    //清除设置屏幕背景色
    ScreenUtils.clear(0f, 0f, 0.2f, 1f)
    batch.projectionMatrix = camera.combined

    batch.begin()
    batch.draw(bucketImage, bucket.x, bucket.y)
    batch.draw(dropImage, rainDrop.x, rainDrop.y)
    batch.end()

    rainDrop.y -= 200 * Gdx.graphics.deltaTime

    //键盘输入判断
    if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) bucket.x -= 200 * Gdx.graphics.deltaTime
    if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) bucket.x += 200 * Gdx.graphics.deltaTime

    //判断两个矩形的接触面积有重叠,即水滴掉落在桶里
    if (rainDrop.overlaps(bucket)) {
        //播放音效
        dropSound.play()
        //模拟消失(让水滴图片消失在游戏屏幕内)
        rainDrop.y = -64f
    }
}

效果如下图:

10.随机雨滴

最后,上述完成的只是一个雨滴,那么,我们需要生成多个雨滴,可以使用一个List来存储雨滴的位置

  • 当雨滴掉落在地上,将雨滴从列表中移除;
  • 当雨滴被桶接到,雨滴也从列表中移除;
class CatchWater : ApplicationAdapter() {
    lateinit var dropImage: Texture
    lateinit var bucketImage: Texture
    lateinit var dropSound: Sound
    lateinit var rainMusic: Music

    lateinit var camera: OrthographicCamera
    lateinit var batch: SpriteBatch

    //最后雨滴下落时间
    var lastDropTime = TimeUtils.millis()

    val bucket = Rectangle().apply {
        //桶放中间
        x = (800 / 2 - 64 / 2).toFloat()
        y = 20.toFloat()
        width = 64.toFloat()
        height = 64.toFloat()
    }

    val rainDropList = arrayListOf<Rectangle>()

    override fun create() {

        // load the images for the droplet and the bucket, 64x64 pixels each
        dropImage = Texture(Gdx.files.internal("drop.png"))
        bucketImage = Texture(Gdx.files.internal("bucket.png"))

        // load the drop sound effect and the rain background "music"
        dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"))
        rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"))

        // start the playback of the background music immediately
        rainMusic.setLooping(true)
        rainMusic.play()

        // create the camera and the SpriteBatch
        camera = OrthographicCamera()
        camera.setToOrtho(false, 800f, 480f)
        batch = SpriteBatch()

        //开始先默认生成一个雨滴
        generateRainDrop()
    }

    private fun generateRainDrop() {
        val rainDrop = Rectangle().apply {
            //x坐标随机
            x = MathUtils.random(0, 800 - 64).toFloat()
            y = (480 - 64).toFloat()
            width = 64.toFloat()
            height = 64.toFloat()
        }
        rainDropList.add(rainDrop)
    }
   
    override fun render() {
        //清除设置屏幕背景色
        ScreenUtils.clear(0f, 0f, 0.2f, 1f)

        // tell the camera to update its matrices.
        camera.update()

        // tell the SpriteBatch to render in the
        // coordinate system specified by the camera.
        batch.projectionMatrix = camera.combined

        batch.begin()
        batch.draw(bucketImage, bucket.x, bucket.y)
        //绘制雨滴列表
        rainDropList.forEach {
            batch.draw(dropImage, it.x, it.y)
        }
        batch.end()

        // 触摸(手机端的操作和鼠标操作)
        if (Gdx.input.isTouched) {
            val touchPos = Vector3()
            touchPos[Gdx.input.x.toFloat(), Gdx.input.y.toFloat()] = 0f
            bucket.x = touchPos.x - 64 / 2
            camera.unproject(touchPos)
        }

        //键盘操作
        if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) bucket.x -= 200 * Gdx.graphics.deltaTime
        if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) bucket.x += 200 * Gdx.graphics.deltaTime

        //500ms生成一个雨滴
        if (TimeUtils.millis() - lastDropTime > 500) {
            generateRainDrop()
            lastDropTime = TimeUtils.millis()
        }

        val iter: MutableIterator<Rectangle> = rainDropList.iterator()
        while (iter.hasNext()) {
            val raindrop = iter.next()
            raindrop.y -= 200 * Gdx.graphics.deltaTime

            //如果雨滴掉落在地上
            if (raindrop.y + 64 < 0) iter.remove()

            //判断两个矩形的接触面积有重叠,即水滴掉落在桶里
            if (raindrop.overlaps(bucket)) {
                //播放音效
                dropSound.play()
                //将此雨滴移除列表
                iter.remove()
            }
        }

    }

    override fun dispose() {
        //资源释放
        dropImage.dispose();
        bucketImage.dispose();
        dropSound.dispose();
        rainMusic.dispose();
        batch.dispose();
    }
}

上面有个涉及到手机和鼠标的操作,因为和Camera对象一起使用,还没过于研究,之后看看再深入一下吧

打包

关于打包的操作,可以通过Gradle的Task来进行操作

Android Studio4.2之后版本,把task给隐藏掉了,所以需要通过设置开启出来

之后Gradle重构当前项目,右侧的Gradle就会出现Task列表了

打包Android的和Android项目开发打包步骤一样的,这里不再赘述

如果是要打包的jar包,则可以点击右侧的Task任务,如下图所示:

生成的jar文件,在desktop\build\libs目录下

至于打包成exe,暂时还没研究,各位可以参考下这篇文章libGDX游戏开发之打包游戏(十二)_漫浅的博客-CSDN博客_libgdx开发的游戏

参考

标签:游戏,Gdx,rainDrop,Libgdx,bucket,batch,雨滴,64,水滴
From: https://www.cnblogs.com/stars-one/p/16755770.html

相关文章

  • hacknet游戏记录-自由更新2
    命令看右侧shell窗口新邮件(删除游戏档)新邮件(经典的反黑客宫机)简单的一把梭就不写了,这关在过代理,用工具爆端口的时会出现倒计时,在倒计时结束前断开主机连接就可结束倒计时,然......
  • hacknet游戏记录-自由更新1
    那么,就开始游戏吧~~~~根据提示完成教学,熟悉一下基本操作完成教学后会受到一封邮件(右上角)初次联系看其他人操作,这个文件都是改名,不建议删除,不知道后面会不会有用。操作完成直......
  • hacknet游戏记录-安装
    最近发现hacknet这款游戏很不错,满足我的黑客梦。哈哈……游戏思路:这款游戏对性能要求不高。为了保障意外情况就弄个虚拟机以便随时保存“进度”虚机:4C8G60Gwin10,因为游戏......
  • VIJOS 1240朴素的网络游戏
    朴素的网络游戏描述佳佳最近又迷上了某款类似于虚拟人生的网络游戏。在游戏中,佳佳是某旅行团的团长,他需要安排客户住进旅馆。旅馆给了佳佳的旅行团一个房间数的限制。每......
  • python pygame 生命的游戏
    importsysimportpygameimportrandom#参数设置box_w,box_h=10,10#盒子宽高window_w,window_h=400,400x,y=0,0#使用pygame之前必须初始化pygame.init()#设......
  • 数字游戏
    题目:        这道题目是一点数学成分+一点DFS主要介绍DFS以及其调用的变量DFS:dfs(x,y,dir,rem,d_t)1.DFS变量介绍(x,y):出发x,y坐标dir:向对于整个地图来......
  • 【C语言】经典猜数字游戏
    ​​#include<stdio.h>​​​​#include<stdlib.h>​​​​#include<time.h>​​​​voidmenu()​​​​{​​​​printf("**********\n");​​​​printf("*****......
  • 在强化学习算法性能测试时使用训练好的模型运行游戏,此时如何控制实时游戏画面的帧数
    问题:在强化学习算法性能测试时使用训练好的模型运行游戏,此时如何控制实时游戏画面的帧数?  ========================================  看到很多训练好的模型与游戏交......
  • C语言猜单词游戏
    C语言猜单词游戏C语言猜单词游戏1问题描述请从一片英文的短文中任意提取一个单词,给出该单词的字母数量,让游戏者猜单词的拼写字母,游戏者每次只能猜一个字母,如果游戏者猜......
  • 基于思维导图的情境游戏教学对学生在英语课程中的写作表现、学习感知和消化导出知识的
    基于思维导图的情境游戏教学对学生在英语课程中的写作表现、学习感知和消化导出知识的影响(Impactsofamindmapping-basedcontextualgamingapproachon EFLstuden......