-
-
Notifications
You must be signed in to change notification settings - Fork 2
Chapter 12
This chapter teaches how to create a combat-based adventure game, with new features such as multiple types of weapons, NPC's with unique and dynamic dialogs, sword placement when swinging, more collision detection and hero knockback.
As suggested by the book the following features have been added:
- Audio
- A menu
- Bombs
- A restart button
- A new enemy
Bonus features:
- camera movement
- Arrow keys - for movement
- A - For shooting an arrow
- S - For swinging the sword
- E - For placing bombs
- B - To buy items from the shopkeeper NPC
Once again the Tiled map-editor is used as a helper tool to create a map with objects and tiles. The biggest take away from this chapter is the custom properties attached to the object tiles, which essentially is extra custom data for each object. See chapter 10 notes for more on tile maps.
Often spritesheets include multiple animation frames for each character action, as seen in the figure below.
To extract each animation per row LibGDX's TextureRegion class split method will be used.
South, West, East and North represents the direction the character is moving, the algorithm used is shown below:
val fileName = "assets/hero.png"
val rows = 4
val cols = 4
val texture = Texture(Gdx.files.internal(fileName), true)
val framewidth = texture.width / cols
val frameheight = texture.height / rows
val frameDuration = .2f
val temp = TextureRegion.split(texture, framewidth, frameheight)
val textureArray = Array<TextureRegion>()
for (c in 0 until cols) {
textureArray.add(temp[0][c])
}
south = Animation(frameDuration, textureArray, Animation.PlayMode.LOOP_PINGPONG)
textureArray.clear()
for (c in 0 until cols) {
textureArray.add(temp[1][c])
}
west = Animation(frameDuration, textureArray, Animation.PlayMode.LOOP_PINGPONG)
textureArray.clear()
for (c in 0 until cols) {
textureArray.add(temp[2][c])
}
east = Animation(frameDuration, textureArray, Animation.PlayMode.LOOP_PINGPONG)
textureArray.clear()
for (c in 0 until cols) {
textureArray.add(temp[3][c])
}
north = Animation(frameDuration, textureArray, Animation.PlayMode.LOOP_PINGPONG)
The character may swing a sword. Placement of the sword upon the different directions the character may be facing is as simple as a minor offset and placing the sword on top of or underneath the hero.
val offset = Vector2()
when (facingAngle) {
0f -> offset.set(.5f, .2f)
90f -> offset.set(.65f, .5f)
180f -> offset.set(.4f, .2f)
else -> offset.set(.25f, .2f)
}
sword.setPosition(hero.x, hero.y)
sword.moveBy(offset.x * hero.width, offset.y * hero.height)
// hero should appear in front of sword when facing north or west
if (facingAngle == 90f || facingAngle == 180f)
hero.toFront()
else
sword.toFront()
The actual swinging of the sword is done by Actions.rotateBy()
after a small offset.
val swordArc = 90f
sword.rotation = facingAngle - swordArc / 2
sword.originX = 0f
sword.isVisible = true
sword.addAction(Actions.rotateBy(swordArc, .25f))
sword.addAction(Actions.after(Actions.visible(false)))
If the character collides with an enemy they should be knocked back as to give the player a chance to avoid further hits and also deny the instant death from rapid successive collisions. Calculating the angle of the knockback is done by subtracting the vector from the character to the enemy.
if (hero.overlaps(flyer)) {
hero.preventOverlap(flyer)
flyer.setMotionAngle(flyer.getMotionAngle() + 180f)
val heroPosition = Vector2(hero.x, hero.y)
val flyerPosition = Vector2(flyer.x, flyer.y)
val hitVector = heroPosition.sub(flyerPosition)
hero.setMotionAngle(hitVector.angle())
hero.setSpeed(100f)
health--
hitHurtAudio.play()
}
Moving the hero can also be done as by using Actions.
hero.addAction(Actions.moveBy(hitVector.x, hitVector.y, .1f, Interpolation.exp10))
As detailed by Conner Anderson's camera effects tutorial, some are implemented by this project.
-
fun alignCamera()
has been modified with a lerping effect to smooth out the movement of the camera following the character. -
fun averageBetweenTargetsCamera()
Creates an enemy lock-on, following both the hero and the enemy on an average. -
fun searchFocalPoints()
When defined focal points (defined in Tiled) are within distance the camera no longer follows the character but locks on that focal point instead (used in both the NPC pens). -
fun bindCameraToWorld()
The binding of the camera was refactored to its own method.
- When the game is over, or it's paused, the Flyers and Skulls keep on flying and can move through anything.
- Explosions are a bit too small.
- The character cannot change direction before speed is zero.
- Arrows that hit bushes may play the thud sound too many times.
- If the character is hit while the sword is swinging, the sword does not follow the hero when knockback.
No new imports