Skip to content
Francisco Cunha edited this page Jul 22, 2017 · 4 revisions

Contents

  1. State Management
  2. Transitions
  3. World Coordinates
  4. The Base Application

Punk offers a fairly simple and clean API, which makes it easy to use the. Throughout this Wiki page, you'll get to know all of the essential parts of the core package.

State Management

State

A State is, in some aspects, similar to a stage in Scene2D. All states should be capable of:

  • Handling input (like touches and key presses).
  • Updating their inner logic.
  • Rendering their internal components.
  • Disposing resources when they're not needed anymore.

StateAdapter

The StateAdapter is a convenience implementation of the State interface. It contains input conversion methods, and every concrete state that you'd implement would inherit from it. When you create a new state that extends this class, you must atleast override these:

class MyState : StateAdapter() {

  override fun update(delta: Float) {}
  override fun render(batch: Batch) {}
  override fun dispose() {}

}

StateManager

The StateManager is an entity that manages how the states flow. With minimal setup (as you'll see soon, in the base application section, it manages stuff like state updates, renders and transitions. You have global access to it, making it easy to use.

The two key functions that you should be aware off are setup and go.

#setup lets you initialize the state manager with a Camera and a Viewport- call this once:

val camera: Camera = ...
val viewport: Viewport = ...
StateManager.setup(camera, viewport)

#go lets you jump around different states:

StateManager.go(otherState)
StateManager.go(otherState, transition)

The API offers more, though, but after an initial setting up phase, you won't need to bother much with them!

fun update(delta: Float)
fun render()
fun resize(width: Int, height: Int)
fun pause()
fun resume()
fun dispose()

Transitions

A state Transition makes up for a smoother UX - instead of changing states abruptely, you can use a transition.

Fade - The screen fades from one state to another.

fade-gif

HorizontalSlide - Left-Right or Right-Left. Watch in 60 fps here.

slide-gif

It's easy to make your own transitions - just make sure you implement the Transition interface! Besides the implementation itself of the transition, you don't need to worry about its API - the StateManager will take care of managing the transitions for you!

World Coordinates

To keep a consistent application - that's independent of real device size or asset size - it's a good idea to:

  • Define a virtual resolution (here called world dimensions), that can be whatever you want.
  • Set a Camera to use that resolution.
  • Use a Viewport to adapt the game screen to the different physical devices.

These virtual dimensions will, thus, be the boundaries of our game screen - they'll influence all of the game's rendering. Because of this, two static variables, WORLD_WIDTH and WORLD_HEIGHT have been declared over at WorldConfig - important for the next step.

Base Application

This is the final element of the core - our root/base class - the one that extends ApplicationAdapter. More on the life-cycle of a libGDX app can be seen here. Now that we know about world coordinates, states and how the state manager controls how states flow, we can easily set it up!

class App : ApplicationAdapter()

You can override whatever you want from the ApplicationAdapter. I'd recommend overriding atleast the following functions:

override fun create()
override fun render()
override fun dispose()
override fun resize(width: Int, height: Int)

Start out by initializing the StateManager, passing it a Camera and a Viewport:

override fun create() {
  val camera: Camera = createCamera()
  val viewport: Viewport = createViewport(camera)
  StateManager.setup(camera, viewport)
}

// Private functions for an uncluttered #create.

private fun createCamera(): Camera {
  return OrthographicCamera(WORLD_WIDTH.toFloat(), WORLD_HEIGHT.toFloat()).apply {
    position.set(viewportWidth / 2f, viewportHeight / 2f, 0f)
  }
}

private fun createViewport(camera: Camera): Viewport {
  return ExtendViewport(WORLD_WIDTH.toFloat(), WORLD_HEIGHT.toFloat(), camera)
}

Usually, you'll want your game to start at some State. Imagine that we have a state called MainMenu:

val mainMenu: State = MenuState()

// Option 1
StateManager.setup(camera, viewport, mainMenu)

// Option 2
StateManager.setup(camera, viewport)
StateManager.go(mainMenu)

That's it for the #create method! Now, the #render method is very simple:

override fun render() {
  Gdx.gl.glClearColor(230f / 255f, 230f / 255f, 230f / 255f, 1f)
  Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
  StateManager.update(Gdx.graphics.deltaTime)
  StateManager.render()
}

Finally, #dispose and #resize are as simple as it gets:

override fun dispose() = StateManager.dispose()
override fun resize(width: Int, height: Int) = StateManager.resize(width, height)
Clone this wiki locally