Skip to content

Instantly share code, notes, and snippets.

@acanthite1855
Last active November 12, 2020 19:11
Show Gist options
  • Select an option

  • Save acanthite1855/185de1ca81e86fcb4ebdafa600ddbd11 to your computer and use it in GitHub Desktop.

Select an option

Save acanthite1855/185de1ca81e86fcb4ebdafa600ddbd11 to your computer and use it in GitHub Desktop.
A simple CameraController for LibGDX from one of my old projects. Allows smooth zooming, translation, rotation and setting padding for OrthographicCamera. All the same!
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.math.Interpolation
import com.badlogic.gdx.math.MathUtils
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.math.Vector3
import kotlin.math.min
class CameraController(
var camera: OrthographicCamera,
worldWidth: Int,
worldHeight: Int
) {
private var targetZoom: Float
private var currentRotation = 0f
private var targetRotation = 0f
private val targetPosition = Vector3()
var scrollSpeed = ScrollSpeed.NORMAL
val worldSize = Vector2()
var clampPosition = true
var clampZoom = true
var smoothSrolling = true
var interpolation = Interpolation.exp10Out
var paddingTop = 0f
var paddingLeft = 0f
var paddingBottom = 0f
var paddingRight = 0f
val rotation get() = currentRotation
val zoom get() = camera.zoom
val position get() = camera.position
init {
worldSize.set(worldWidth.toFloat(), worldHeight.toFloat())
targetZoom = camera.zoom
targetPosition.set(camera.position)
}
companion object {
private const val ROUNDING_ERROR = 0.01f
}
fun setPadding(paddingX: Float, paddingY: Float) {
setPadding(paddingY, paddingX, paddingY, paddingX)
}
fun setPadding(paddingTop: Float, paddingLeft: Float, paddingBottom: Float, paddingRight: Float) {
this.paddingTop = paddingTop
this.paddingLeft = paddingLeft
this.paddingBottom = paddingBottom
this.paddingRight = paddingRight
}
fun update(deltaTime: Float) {
zoomCamera(deltaTime)
translateCamera(deltaTime)
rotateCamera(deltaTime)
camera.update()
}
private fun zoomCamera(deltaTime: Float) {
if (!MathUtils.isEqual(camera.zoom, targetZoom)) {
if (smoothSrolling) {
camera.zoom = applyInterpolation(camera.zoom, targetZoom, deltaTime)
return
}
}
camera.zoom = targetZoom
}
private fun translateCamera(deltaTime: Float) {
if (!camera.position.epsilonEquals(targetPosition, ROUNDING_ERROR)) {
if (smoothSrolling) {
camera.position.x = applyInterpolation(camera.position.x, targetPosition.x, deltaTime)
camera.position.y = applyInterpolation(camera.position.y, targetPosition.y, deltaTime)
camera.position.z = 0f
return
}
}
camera.position.set(targetPosition)
}
private fun rotateCamera(deltaTime: Float) {
if (!MathUtils.isEqual(currentRotation, targetRotation)) {
if (smoothSrolling) {
setCurrentRotation(applyInterpolation(currentRotation, targetRotation, deltaTime))
return
}
}
setCurrentRotation(targetRotation)
}
private fun applyInterpolation(start: Float, end: Float, delta: Float): Float {
return interpolation.apply(start, end, scrollSpeed.alpha(delta))
}
private fun setCurrentRotation(angle: Float) {
// The API of OrthographicCamera does not provide getter for
// rotation angle, and we the previous angle from the camera
// object. So we just store it a private field
val previousAngle = currentRotation
currentRotation = angle
camera.rotate(angle - previousAngle)
}
fun addZoom(value: Float) {
zoomTo(camera.zoom + value)
}
fun multiplyZoom(scale: Float) {
targetZoom *= scale
clampTargetZoomIfNeeded()
}
fun zoomTo(value: Float) {
targetZoom = value
clampTargetZoomIfNeeded()
}
private fun clampTargetZoomIfNeeded() {
if (clampZoom) {
targetZoom = MathUtils.clamp(targetZoom, 0.1f, maxZoomValue())
}
}
fun maxZoomValue(): Float {
val paddingX = (paddingLeft + paddingRight)
val paddingY = (paddingTop + paddingBottom)
val xZoom = (worldSize.x + paddingX) / camera.viewportWidth
val yZoom = (worldSize.y + paddingY) / camera.viewportHeight
return min(xZoom, yZoom)
}
fun zoomToFitViewport() {
zoomTo(maxZoomValue())
}
fun resetZoom() {
zoomTo(1f)
}
fun center() {
targetPosition.set(worldSize, 0f).scl(0.5f)
clampTargetPositionIfNeeded()
}
fun moveTo(position: Vector2) {
moveTo(position.x, position.y)
}
fun moveTo(x: Float, y: Float) {
targetPosition[x, y] = 0f
clampTargetPositionIfNeeded()
}
fun translate(delta: Vector2) {
translate(delta.x, delta.y)
}
fun translate(deltaX: Float, deltaY: Float) {
targetPosition.add(deltaX, deltaY, 0f)
clampTargetPositionIfNeeded()
}
private fun clampTargetPositionIfNeeded() {
if (clampPosition) {
targetPosition.x = MathUtils.clamp(targetPosition.x, 0f, worldSize.x)
targetPosition.y = MathUtils.clamp(targetPosition.y, 0f, worldSize.y)
targetPosition.z = 0f
}
}
fun resetRotation() {
rotate(0f)
}
fun addRotation(angle: Float) {
rotate(currentRotation + angle)
}
fun rotate(angle: Float) {
targetRotation = angle % 360f
}
fun resize(width: Int, height: Int) {
camera.viewportWidth = width.toFloat()
camera.viewportHeight = height.toFloat()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment