I GameDevDiary: Road Network and Improved Movement

Andrew Lukes
5 min readOct 8, 2023

--

Check out the progress in the making of version 0.4 of my strategy game

Hi! This time I have worked on the road network as I promised in the last entry. This involved connecting cities using my old algorithm and adding bridges to connect the rest of the map. I think that I have a lot of interesting features to talk about so…

…Let’s get started!

· Road Network
Road Scene
Connecting Cities
Connecting Edges To Cities
Bridges
· Improved Movement
How it works
· Miscellaneous
outline for forests:
outline for units:
new background:
the game starts on the start screen:
settings screen:
· Bug Fixes
· Conclusion
· Demo
Check out the previous article

Road Network

Road Scene

I have created the road out of an area 2D with, a collision shape and a Line2D. Since a similar structure could be found inside the rivers, it wasn’t hard to implement.

Connecting Cities

I have rewritten my algorithm for connecting towns in Python and couldn’t help but notice, how shorter it became thanks to the built-in features of Godot which allow me to easily get the global position of nodes and to find the position between two points on a canvas.

The code works by finding the 2 closest cities to the city. If those cities aren’t connected to more than 3 cities themselves, the town connects itself to them by saving them inside of its connected cities array. It then instantiates roads between itself and the city using another function.

func instantiate_roads(start, end):
var road_instance = road_scene.instantiate() as Node2D
var collision_area = road_instance.get_node("Area2D")
collision_area.position = Vector2(start + end ) / 2
collision_area.rotation = start.direction_to(end).angle()
var length = start.distance_to(end)
var rect = RectangleShape2D.new()
var collision_shape = collision_area.get_node("CollisionShape2D")
rect.extents = Vector2(length / 2, 7)
collision_shape.shape = rect
road_instance.get_node("Line2D").add_point( end )
road_instance.get_node("Line2D").add_point( start )
$Structures.add_child(road_instance)

Connecting Edges To Cities

Here I first take a random point at each edge of the game viewport. Then I find the town whose center is closest to it and instantiate a road using the same function as in the previous paragraph. The code looks like this

func create_roads_to_edges(): 
var top_point = Vector2(randi_range(100, get_viewport().size.x -100), 0)
var right_point = Vector2( get_viewport().size.x , randi_range(100, get_viewport().size.y -100))
var left_point = Vector2( 0 , randi_range(100, get_viewport().size.y -100 ))
var bottom_point = Vector2(randi_range(100, get_viewport().size.x -100 ), get_viewport().size.y )
var edge_points = [top_point,right_point,left_point,bottom_point]
for edge_point in edge_points:
var closest_town_center_pos
for town in get_tree().get_nodes_in_group("towns"):
print(town, Utils. get_collision_shape_center(town ) )
print( Utils. get_collision_shape_center(town ).distance_to(edge_point) , closest_town_center_pos)
if closest_town_center_pos == null:
closest_town_center_pos = Utils. get_collision_shape_center(town )
elif Utils. get_collision_shape_center(town ).distance_to(edge_point) < closest_town_center_pos.distance_to(edge_point):
closest_town_center_pos = Utils. get_collision_shape_center(town )
if closest_town_center_pos == null:
return
instantiate_roads(closest_town_center_pos, edge_point)

Bridges

When a unit has a collision with a bridge it ignores collisions with the river, which allows it to traverse the bridge easily. I also made the bridges face the river at a 90-degree angle.

Bridge Crossing
func add_bridges():
for river in get_tree().get_nodes_in_group("rivers"):
var has_crossing = false
for segment in river.get_children():
if has_crossing:
break
# print(segment.get_node("Area2D").get_overlapping_areas())
for area in segment.get_node("Area2D").get_overlapping_areas():
# print(area, area.get_parent() , area.get_parent().get_parent())
if area.get_parent() is Road:
has_crossing = true
print("ROAD CROSSES THIS RIVER", river)
break
if !has_crossing:
var segment_num = randi_range(0, len(river.get_children())-1)
var new_bridge_position = to_global(river.get_child(segment_num).get_node("Area2D").global_position)
var bridge_instance = bridge_scene.instantiate() as Sprite2D
bridge_instance.position = new_bridge_position
$Structures.add_child(bridge_instance)
print(river, " NEEDS A BRIDGE ", new_bridge_position )

Improved Movement

In the last version, the unit could move inside of a circle around its starting position. This didn't allow me to expand upon much, so I finally got around to adding a revamped movement system.

How it works

This time every time a unit moves a pixel, it subtracts 1 point in its remaining distance variable. When this value reaches zero a function to abort the movement is called moving the unit back to center resetting its value.

On top of that, I can augment the amount subtracted using a movement modifiers variable, where I put values for being in a forest or on a road.

var movement_modifieres:Dictionary = {
"base_modifier": 1
}
distance_just_traveled = floor( owner.center.distance_to(mouse_pos) ) * current_movement_modifier

Miscellaneous

outline for forests:

For better contrast, I have added an outline for the forests using a function that creates a line 2d out of a polygon 2d below.

func polygon_to_line2d(polygon: Polygon2D, width: float, color: Color = Color(1, 1, 1, 1)) -> Line2D:
var line = Line2D.new()
var vertecies = polygon.get_polygon()
vertecies.append(vertecies[0]) ## so the line is circumnavigating the whole polygon
# print(vertecies)
line.width = width
line.modulate = color
for i in range( len(vertecies) ):
line.add_point(vertecies[i])
return line

outline for units:

Using the same function I have added an outline for the units as well

new background:

The previous background was a little too bright for my taste, so I turned it down.

the game starts on the start screen:

To polish the game a bit, I finally got around to changing the beginning scene to the start screen so you aren’t immediately thrown in the middle of the battle. Here you can choose to go to the settings screen, start a new game, or end the game

settings screen:

You are now able to customize the battleground by setting the values in the settings screen. So far I have set up sliders for the income of players and for the generation of the game.

basic settings screen

Bug Fixes

  • The towns are no longer overlapping
  • The visual connection between a support unit and a supported unit is now properly centered
  • When a bullet hits a forest or the unit isn't able to attack an animation is played
Attack_Failed Cross

Conclusion

I have just described the improvements in the movement and environment of my video game. I am striving to create a fun strategic experience and would be grateful for any feedback.

Demo

Download BattleForge 0.4 here: BattleForge 0.4

--

--

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