GameDev Diary: Sound Effects, Music and Statistics

Andrew Lukes
6 min readOct 20, 2023

--

Putting the Finishing Touches Onto My Game

Hello once again! The project has been running for almost 3 months now, and in order to actually finish the development, I have decided to set the deadline to the 31st of October. This time I explored the realm of soundtrack and audio effects. Let's see what I have coded!

· Stats Tracker
· Music
How to add sound to the game:
Background music
Sound Effects
· Small Changes:
Unit Stats Bar:
Expanded Settings
Roads Expanded
Reachability Information
Unique Hp
Canon Ball Has a Longer Path
· Bug Fixes:
· Conclusion
· Demo

Stats Tracker

This object keeps track of all the stats in the playthrough these include:

  • Money Spent
  • Units Killed
  • Money Earney
  • Turns Played

I have made the object global in the project settings > autoload tab, so I could access the values and update them easily from the relevant parts of the codebase.

The stats are displayed at the game is ended and then reset for the next playthrough.

Music

How to add sound to the game:

You can add sound simply by attaching an AudioStreamPlayer node to the scene, attaching a .wav or .mp3 file to it, and in the script calling the node with the .play() function. It is really that simple.

If you want to play random sounds you can try my solution, where I created a random sound player component with an exported music array to set the sounds played in random order. Here is a reference to the component:

extends Node

var active = false
@export var sound_list:Array[AudioStream]
func play_random_sound():
var random_index = randi_range(0, len(sound_list) -1)
$AudioStreamPlayer.stream = sound_list[random_index]
$AudioStreamPlayer.play()

func _process(_delta: float) -> void:
if !$AudioStreamPlayer.playing and active:
print("PlAYING RANDOM SOUND")
play_random_sound()

Background music

In the start screen, you will be greeted with a mix of 5 war songs from the 30 Years War to the American Civil War. These Include:

In the end game screen, I have added another soundtrack, This time it isn't military-themed, but I thought it was appropriate for the scene.

Sound Effects

Here is a list of sound effects, that were added:

  • Bullets flying through the sky
  • BuyButton click sound
  • Marching sound
  • Sound for receiving ammo
  • Sound on wrong action
  • Explosion sound
  • Sound for selection

Small Changes:

Unit Stats Bar:

I have rearranged this scene a bit to contain also the movement modifier of the unit, which was previously displayed to the left of the unit

Left: Knight, Right: Musketeer; Top to Bottom: Hp, movement points, ammo, movement modifier

Expanded Settings

In the BattleForge settings menu, you can now set the number of troops each player will spawn with (except for the commanders of course). I have also tweaked some of the slider's max values because the game generated better that way. For example, the town slider now has a maximum value of 8 and the river slider 5.

Roads Expanded

Roads were widened a bit to be more visible.

Reachability Information

In the case the enemy unit is behind a forest, but inside a range of a ranged unit, it will be highlighted differently, to signify, that the unit can't hit this unit.

This was achieved using RayCast2D scanning for forest areas between the ranged unit and every enemy in range. I have created a whole new component for this that shoots Raycast 2ds into a specific location with a specific collision mask.

func check_position_reachable(point, projectile_size):
print("CHECKING POSITION REACHABLE")
var raycast = RayCast2D.new()
raycast.hide()
var collision_shape = CollisionShape2D.new()
raycast.add_child(collision_shape)

# Create a RectangleShape2D to represent the width of the "ray"
var rectangle_shape = RectangleShape2D.new()
rectangle_shape.extents = Vector2(projectile_size, 1) # Adjust the width here

# Set the shape of the CollisionShape2D
collision_shape.shape = rectangle_shape
for index in range(16):
if index in entity_mask_indexes:
raycast.set_collision_mask_value(index , true)
else:
raycast.set_collision_mask_value(index, false)
# rasycast.set_collision_mask_value(1, true)
print(raycast.get_collision_mask_value(7))
add_child(raycast)
raycast.collide_with_areas = true
# Set the starting position of the ray (assuming your units have a position property)
raycast.position = position#unit.position

# Set the direction and length of the ray to reach the target unit
raycast.target_position = to_local(point )#- target_unit.position
# for index in attack_obstructions_layer_indexes:
# Check if the ray hits anything
raycast.force_raycast_update()

Unique Hp

I have exported the health component hp variable, which allows me to easily change the value for every unit. Here are the starting hp values for all the units:

  • Commander: 2
  • Pikeman: 4
  • Knight: 3
  • Shield: 6
  • Musketeer: 2
  • Canon: 2
  • Medic: 2
  • Observer: 1
  • Supply Cart: 2

I might change the values in the later versions for balance purposes

Canon Ball Has a Longer Path

In order to increase the effect of the cannonball having piercing abilities, I have decided to increase its flight range 2x. The canon has still the same range as before, so the shell can only hit the farther targets by piercing a closer unit

canon ball shot at the pikeman

Bug Fixes:

  • When aborting movement, the action component doesn't fire up (the default unit now only calls the action component when the movement component is idle)
  • The unit doesn't get back its movement after being just moved, resulting in infinite movement (removed a line of code in exit_mmovement_state() guilty of this behavior)
  • You can put units again into captured towns (caused by a typo in the movement component)
  • Fixed value of blue medics setting bar (caused by another typo)
  • The support line is now updated when the unit is moved (a random condition in the process() function was false for some reason)

Conclusion

Thank you for your attention until the end of the article! This time we have looked at the aspect of audio in Godot, the Stats handler designed as a singleton, and talked about small changes in the whole experience. I hope I will see you next time. In the coming days, I will try to improve the balance of the game to hopefully release a final version at the end of the month.

Demo

Download BattleForge 0.6 on itch.io: BattleForge 0.6

Photo by Josh Rakower on Unsplash

--

--

Andrew Lukes

Introducing Andrew Lukes: a Prague web dev & language enthusiast who shares his ideas on Medium. You can visit andrewebdev.online to see some of my projects