Libgdx游戏开发(5)——碰撞反弹的简单实践

原文: Libgdx游戏开发(5)——碰撞反弹的简单实践-Stars-One的杂货小窝

本篇简单以一个小球运动,一步步实现碰撞反弹的效果

本文代码示例以kotlin为主,且需要有一定的Libgdx入门基础

注:下面动态图片看着有些卡顿,是录制的问题,实际上运行时很流畅的

水平滚动

简单起见,我们通过ShapeRenderer绘制一个圆形,作为我们的小球,并让其从开始位置向右水平移动

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      override fun create() {         shape = ShapeRenderer()     }      var x = 50f     var y = 50f      override fun render() { 		//每次渲染绘制前,清除屏幕         Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) 		 		x += 5 		 		//设置填充模式,圆形默认即为白色         shape.begin(ShapeRenderer.ShapeType.Filled); 		//圆形半径为50,起点位置位于(50,50)         shape.circle(x, y, 50f)         shape.end()     } 	 	//这里忽略了相关资源释放代码逻辑... } 

启动游戏代码(方便阅读,下文中此代码不会再贴出!):

package com.arthurlumertz.taplixic;  import com.badlogic.gdx.backends.lwjgl3.*;  public class DesktopLauncher {  	public static void main (String[] arg) { 		Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration(); 		config.setWindowedMode(960, 540); 		config.setForegroundFPS(60); 		new Lwjgl3Application(new CircleBallTest(), config); 	} }  

效果如下:

Libgdx游戏开发(5)——碰撞反弹的简单实践

水平滚动并反弹

上述已经实现了一个小球滚动,但发现滚动到边缘就不见了,我们加个效果,碰到右边缘就反弹

package com.arthurlumertz.taplixic  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      override fun create() {         shape = ShapeRenderer()     }      var x = 50f     var y = 50f      var isRight = true      override fun render() {         //每次渲染绘制前,清除屏幕         Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)          if (isRight) {             x += 5         } else {             x-=5         }          //设置填充模式,圆形默认即为白色         shape.begin(ShapeRenderer.ShapeType.Filled);         //圆形半径为50,起点位置位于(50,50)         shape.circle(x, y, 50f)         shape.end()          //右边缘检测 圆心的x坐标加上半径大于或等于当前游戏屏幕宽度          if (x + 50 >= Gdx.graphics.width) {             isRight=false         }                  //左边缘检测 圆心的x坐标减去半径小于或等于0(起点)         if (x - 50 <=0) {             isRight=true         }     }      //这里忽略了相关资源释放代码逻辑... } 

效果如下(录制效果的时候没加左边缘检测):

Libgdx游戏开发(5)——碰撞反弹的简单实践

这里实际可以直接将对应的+5-5统一转为一个速度加量,方向需要反转的时候乘以-1即可

同时,我们上述小球相关代码封装为一个Ball类来进行使用,优化后的代码如下:

//定义一个ball类实现相关操作 class Ball{     var size = 50f      var x = 50f     var y = 50f      var speedX = 5f      fun gundon() {         x += speedX     }      fun draw(shape: ShapeRenderer) {         shape.begin(ShapeRenderer.ShapeType.Filled)         shape.circle(x, y, size)         shape.end()     }      //检测边缘反弹     fun checkFz() {         //到达右边缘,加量变反         if (x + size >= Gdx.graphics.width) {             speedX = speedX * -1         }          //到达左边缘,加量变反         if (x - size <= 0) {             speedX = speedX * -1         }     } } 

游戏代码:

class CircleBallTest : ApplicationAdapter() {     lateinit var shape: ShapeRenderer      val ball by lazy { Ball() }      override fun create() {         shape = ShapeRenderer()     }      override fun render() {         Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)          ball.draw(shape)          ball.gundon()          ball.checkFz()     }      //这里忽略了相关资源释放代码逻辑... } 

这里代码我是将绘制,坐标和边缘碰撞检测分别封装对应的方法

  • draw() 绘制
  • gundon()修改坐标的方法
  • checkFz()则是进行碰撞检测的方法

这里为什么要将绘制和修改坐标抽成2个方法,是因为我研究游戏暂停的时候发现的,这里先卖个关子,之后会讲到(算是自己无意摸索出来的小技巧)

四面滚动反弹

上述我们只是在水平方向移动,现在想要小球斜方向发出,之后四周反弹,应该如何实现呢?

想要斜方向发出,我们还需要在上面实现的基础上加个y坐标加量,同时修改x,y坐标,就能让小球斜着运动了(数学中的线性方程,或者可以看做是给了小球上方向和右方向的力)

当然,如果你修改对应的增加量数值,可以实现不同斜率方向

这里我固定x和y的加量相同,即45度方向运动

四周反弹其实可以拆分为左右和上下方向,碰到左和右就反转x的增量,碰到上和下就反转y的增量

 import com.badlogic.gdx.ApplicationAdapter import com.badlogic.gdx.Gdx 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() }          override fun create() {         shape = ShapeRenderer()     }      override fun render() {         Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)          ball.draw(shape)          ball.checkFz()     } }  class Ball{     var size = 50f      var x = 50f     var y = 50f      var speedX = 5f     //y坐标增量     var speedY = 5f      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变反         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         }     } } 

效果如下:

Libgdx游戏开发(5)——碰撞反弹的简单实践

加个板子进行弹球

在上面的基础上,我们添加一个板子用来接球

  1. 使用ShapeRenderer对象绘制实心矩形作为板子
  2. 考虑板子和球的碰撞
  3. 方向键左右可控制板子移动
  4. 碰到下边缘,球消失

shape.rect()方法用来绘制一个矩形,在x,y坐标绘制一个定义的宽高矩形,(x,y)坐标即为此矩形的左上角

圆心的y坐标 - 半径 >= 矩形的y坐标,圆心x坐标-半径小于矩形的x坐标,圆心x坐标+半径大于或等于矩形的x坐标+矩形宽度,即视为两者碰撞

下面代码和Ball一样,封装了一个MyBan类,实现板子的绘制和控制移动

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()     }      override fun render() {         Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)          line.draw(shape)         line.control()          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(myB: MyBan) {         if (y - size <= myB.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         }     } }  

效果如下:

Libgdx游戏开发(5)——碰撞反弹的简单实践

参考

发表评论

相关文章