GameDevDiary: Adding Rivers And Forests
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)
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)
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