GameDev Diary: Regrouping Code And Improving Buy Areas

Andrew Lukes
4 min readSep 29, 2023

--

How I Once More Utilized Components for Faster Game Development

In this installment of the BattleForge game-dev diary, I will share the latest advances I made in redesigning the architecture of the unit's attacking and movement systems as well as making several bug fixes and visual improvements.

Let`s dive in

· Attack Component
· Movement Component
· Reworked Buying System
Highlighting
· Quality Of Life Improvements
Working Scrollbar
Aborting Movement
· Bug Fixes
· Conclusion
· Demo

Attack Component

In the last version, every unit contained an attack collision area that sent signals to it when something entered it. The unit then handled the logic of attacking with the information on what units were in the collision area. In this version, I have created a component that keeps track of what is inside it, what to attack, and how to attack (I have two children of this component for melee attacks and ranged attacks). So far it works quite well, the only addition I want to make is, to incorporate a support component that behaves in a quite similar way (selecting a unit to support, taking action, having a certain range, etc.) to save on many lines of code logic.

extends Area2D
class_name DefaultAttackComponent
signal remain_actions_updated(new_attacks)
var base_actions:int = 1
var remain_actions: int = base_actions:
set(new_attacks):
remain_actions = new_attacks
emit_signal("remain_actions_updated", new_attacks)

var units_in_action_range:Array= []
var attack_range:int = 100
var attack_range_modifiers = {"base_modifier": 1}
var center


func try_attack( ):


## ranged attack has an overide for this function
func attack():
Globals.last_attacker = get_parent()

func check_can_attack():


func _ready():
$AttackRangeShape.shape = CircleShape2D.new()
$AttackRangeShape.shape.radius = attack_range # * attack_range_modifiers["base_modifier"]
$AttackRangeShape.hide()

func update_for_next_turn():
remain_actions = base_actions

func _process(_delta):
if Globals.action_taking_unit == get_parent():
$AttackRangeShape.show()
else:
$AttackRangeShape.hide()

func toggle_action_screen():

func highlight_units_in_range():
var other_units = get_tree().get_nodes_in_group("living_units")
for enemy in other_units:
if units_in_action_range.has(enemy):
enemy.get_node("ColorRect").modulate = Color("white")
else:
enemy.get_node("ColorRect").modulate = enemy.color
func unhighlight_units_in_range():
for enemy in units_in_action_range:
enemy.get_node("ColorRect").modulate = enemy.color

func _on_area_entered(area):
if area.get_parent() == get_parent():
return
if area.name == "CollisionArea" and area.get_parent() is BattleUnit and area.get_parent().color != get_parent().color and not units_in_action_range.has(area.get_parent()) :
units_in_action_range.append(area.get_parent())


func _on_area_exited(area):
if area.name == "CollisionArea" and units_in_action_range.has(area.get_parent()):
units_in_action_range.erase(area.get_parent())

Movement Component

Far easier to implement as I really have only one way for the scene to move, which is drag and drop. The main advantage of creating this component is, that I could potentially add it to non-BattleUnit components, which would allow for some crazy ideas like, for example, moving cities which probably doesn't make quite a sense, but would make any fan of the tetralogy novel Mortal Engines interested.

extends Node2D
signal remain_movement_changed(new_movement)
const base_movement:int = 1
const base_movement_range:int = 250
var parent_size:int
@onready var start_turn_position :Vector2 = get_global_transform().get_origin() # Vector2((position[0]+round(size[0]/2)),(position[1]+round(size[1]/2)))
var remain_movement:int = base_movement:
set(new_movement):
remain_movement = new_movement
emit_signal("remain_movement_changed", new_movement)

func _ready():
$MovementRangeArea/MovementRangeArea.shape = CircleShape2D.new()
$MovementRangeArea/MovementRangeArea.shape.radius = base_movement_range
$MovementRangeArea/MovementRangeArea.hide()


func move(size_of_scene, center):
print(start_turn_position, "start")
var mouse_pos = get_global_mouse_position()
var distance_to_mouse = start_turn_position.distance_to(mouse_pos)
var new_position = position
if distance_to_mouse > base_movement_range:
var direction_to_mouse = (mouse_pos - start_turn_position).normalized()
new_position = start_turn_position + direction_to_mouse * base_movement_range - size_of_scene / 2
else:
new_position = mouse_pos - size_of_scene / 2


position = new_position
center = to_global(position + size_of_scene/2)
#MovementPosition CollisionShape
$MovementRangeArea/MovementRangeArea.position = center - Vector2($MovementRangeArea/MovementRangeArea.shape.radius, $MovementRangeArea/MovementRangeArea.shape.radius)
# else:
# abort_movement()
return position

func abort_movement():
position = start_turn_position
Globals.moving_unit = null
return position

func set_new_start_turn_point():

Reworked Buying System

After almost a month of coding in Godot, I felt confident enough to reimplement the drag-and-drop buy system from my previous pygame version into Godot. Now instead of a new unit spawning at a random place in the buy area, you can take the unit from the buy bar and drag it there yourself. You can also put it into the occupied cities just like in the pygame version

Highlighting Buy Area

The buy bar now shows its area when you are about to buy a unit, so you know where you can place it, this works for the cities too.

Quality Of Life Improvements

Working Scrollbar

Imagine that a bug you have been trying to solve can be simply solved by a change of a random node. I changed the vScrollBar for Scroll Container and magically, it started to work how I intended. This means, that you can now scroll to see more of the players units

Aborting Movement

When Moving a unit and clicking on the right button, it returns to the original position and unselects, so you can essentially abort the movement.

Highlighting Units In Range

When doing action, you will now see all the units in the range of the attacking unit white.

Bug Fixes

  • The blast animation is rendered, where it is supposed to be
  • Resized the canon image in the buy bar

Conclusion

So this is the current state of the game. The most important for the player is probably the reworked buy bar. The bug fixes should make the game a little bit more polished. I hope you will like the new improvements and do not forget to check the demo version below

Demo

Download Battleforge 0.2 here: Battleforge 0.2

Check out the last entry here

Check out the next article here

Photo by Kari Shea 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