GameDevDiary: Adding Rivers And Forests

Andrew Lukes
5 min readOct 3, 2023

--

Making a more detailed environment for my videogame

Welcome Back, I would like to welcome you to the 12th installment of this game dev diary, as well as invite you to test out the 3rd demo version of the project. Today I will be covering the addition of forests and rivers as well as many bug fixes.

Let's go!

· Rivers
Bezier Curves
River Segments
Converging
Blocking Entry
· Forests
Design
Blocking Projectiles
· Bug Fixes
· Conclusion
Demo
Check out the last article

Rivers

Generating Bezier Curves

What are they in short? Bezier curve is an algorithm that takes at least 3 points in a 2D space and calculates an array of points that create a smooth arc. It is used to describe shapes that cannot be described by a simple mathematical expression. If you want to read more about this algorithm, check out this article from the official Godot documentation.

Utilization: I have altered my original bezier curve generator. It now has only one control point instead of two. It usually results in smoother lines, which look better, when the rivers converge together. Here is my code for curve calculation. It returns segments instead of points, as I can then easily create rectangles out of them.

func generate_bezier_curve(start:Vector2, end:Vector2, control_point:Vector2,  num_segments:int):
var t:float = 0
var points = []
var segments = []
while t <= 1:
# Define the control points for the Bézier curve
var q0 = start.lerp(control_point, t)
var q1 = control_point.lerp(end , t)
var point = q0.lerp(q1, t)
# Generate two random points between the start and end points
points.append(point)
# print(line.points.size)
if len( points) >= 2:
segments.append([ round(points[-1]), round(points[-2]) ])
# add_river_segment(points[len( points )-1], points[len( points)-2],true)
t += 1.0/num_segments
return segments

River Segments

Each river is divided into segments defined by two points in the bezier curve. They contain a line2D and a collision rectangle to detect collisions with other units.

Converging

I have again reused some of the old code from the pygame project and altered it a bit to handle scenes instead of simple arrays of numbers. Here is a view of a bit of the logic:

  ## generating the start and end point
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 )
# Vector2(100,0)Vector2(500,500) Vector2(500, screen_size.y)
var start_point = top_point if randi() % 2 == 0 else left_point
var control_point = Vector2(randi_range(100, get_viewport().size.x-100), randi_range(100, get_viewport().size.y-100))
var end_point = bottom_point if randi() % 2 == 0 else right_point

## generating points from them
var new_segments = generate_bezier_curve(start_point, end_point, control_point, 10)
var non_intersecting_segments = []
var intersecting_segment
## throwing out intersecting segments
for segment in new_segments:
for existing_segment in all_segments:
if do_lines_intersect(segment[0], segment[1], existing_segment[0], existing_segment[1]) :
print("SEGMENTS INTERSECTING", segment[0], segment[1], existing_segment[0], existing_segment[1] )
intersecting_segment = segment
## adding a convergence segment to the new river
non_intersecting_segments.append([segment[1], existing_segment[0]])
if intersecting_segment:
break
non_intersecting_segments.append(segment)
if intersecting_segment:
print(len(non_intersecting_segments), len(new_segments))
for segment in non_intersecting_segments:
all_segments.append(segment )
var river_instance = river_scene.instantiate() as Node2D
for segment in non_intersecting_segments:
river_instance.add_river_segment(segment[0], segment[1], false)
$Structures.add_child(river_instance)
Result

Blocking Entry

In my game, the river acts as an impassable barrier that can be only crossed through certain crossings and bridges. I will hopefully add them in the later versions. I also stop bought units from being placed on top of the rivers.

Forests

Design

I have created 3 different polygons with collision shapes and a forest node that chooses one of them to be its own collision shape, I then resize and skew the shape at random to create a more unique polygon, which usually creates a nice effect. Also when two different forest polygons are next to each other, it creates a new more interesting polygon

class_name Forrest
extends Node2D

var forrest_scene_number = randi_range(1, 3)
var forrest_shape_1 = preload("res://structures/forrest/forrest_shape_1.tscn")
var forrest_shape_2 = preload("res://structures/forrest/forrest_shape_2.tscn")
var forrest_shape_3 = preload("res://structures/forrest/forrest_shape_3.tscn")
func _ready():
var forrest_shape_instance
match forrest_scene_number:
1:
forrest_shape_instance = forrest_shape_1.instantiate() as Polygon2D
2:
forrest_shape_instance = forrest_shape_2.instantiate() as Polygon2D
3:
forrest_shape_instance = forrest_shape_3.instantiate() as Polygon2D
_:
print("NOTHING MATCHED, running default instance",forrest_scene_number)
forrest_shape_instance = forrest_shape_1.instantiate() as Polygon2D
add_child(forrest_shape_instance)
position = Vector2(randi_range(50, get_viewport().size.x-50) ,randi_range(50, get_viewport().size.y-50))
scale = Vector2( randf_range(0.5,1.2), randf_range(0.5,1.2) )
# get_node("Polygon2D").modulate = Color("green")
get_node("forrest_shape").color = Color(0, 1, 0)
Forrest generation

Blocking Projectiles

I have declared that no projectile passes through forests and explodes on impact with the trees. So they will now provide cover for advancing units.

##projectile.gd
func _on_area_entered(area):
if area.get_parent() == shooting_unit:
return false
if area.get_parent().get_parent() is Forrest:
_play_explosion()
return true

Bug Fixes

  • support unit again selectable
  • stopped placing new units on top of other units
  • fixed showing of the support unit support range
  • The attack range circle is now centered properly
  • The highlighted units are corresponding to the units that are inside the attacking circle
  • Fixed buying of support units

Conclusion

So this is it for the 12th installment! The project has seen a lot of bug fixes and an improvement to the battleground environment. In the next diary entry, if everything goes smoothly, I will cover roads and their function inside the game. Hope you will like the changes!

Demo

Download Battleforge 0.3 here: BattleForge 0.3

Check out the last article

Check Out the next article

--

--

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