Generation and Render of 2D Map with Terrain in Dart + Flutter. Adding Forest Spots. Part 2

Search for a command to run...

No comments yet. Be the first to comment.
This is the series related to my experience building game with Flutter and Dart. It is basically a diary. At first I implement feature and then add a post about it. https://locadederta.com/sloboda
Introduction In Previous Part 2 we added generation for Forests. In this post we will add Lakes. Final result The map with several lakes looks like this: Implementation Requirements There are different ways to render the lakes. A lake could occupy o...
Build. Craft. Explore.

Заселяємо південь України!

Hey! We just published the second content update for our game Sloboda 2. This update is free for anyone who already purchased our game. If you want to purchase it, then pick your platforms: Apple App Store: https://apps.apple.com/us/app/loca-deserta-...

Привіт, компана! Ось і настав час Січневого оновлення для нашої гри Слобода 2! Це оновлення безкоштовне для всіх, хто придбав гру. Якщо ж ще не придбали, то можете це зробити по цих посиланнях: Apple App Store: https://apps.apple.com/ua/app/loca-dese...

Статистика запуску гри Трохи більше двох тижнів тому ми випустили гру на всіх магазинах. І одразу ж попали в Топ платних ігор 🇺🇦українського App Store! Купити можна на iOS по посиланню: https://apps.apple.com/ua/app/loca-deserta-sloboda-2/id164250...

In Part 1 we created all needed methods to generate map with Grass and River. In this part we will add Forest spots to the map. A lot of code is reused from Part 1 but it is gradually enhanced step-by-step.
The forest is added in different points and different shapes:

Before we start jumping into Forest, we need to enhance code written in previous part.
One of the things I did not like about it was that we directly accessed tiles list via indexes. We had to play with rows/columns/x/y variables and place them into correct spots.
With refactoring all manipulations with tiles is now done via special methods:
MapTile tileAtPoint(Point point) {
return tiles[point.y][point.x];
}
void setTileAtPoint(Point point, MapTile tile) {
tiles[point.y][point.x] = tile;
}
void setTile(MapTile tile) {
tiles[tile.y][tile.x] = tile;
}
We no longer operate with pure x/y/row/col variables. All coordinates are instances of class Point (from dart:math).
For example, getting random point on map returns Point:
Point _getRandomPoint() {
var col = Random().nextInt(width - 1);
var row = Random().nextInt(height - 1);
return Point(col, row);
}
With these enhancements the code looks less mathy and more OOPish.
Implementing these requirements is pretty straightforward given that you also already wrote some useful utility functions :)
void addForest() {
ForestMapTile forest;
// amount of tries to pick the spot for forest
var tries = 10;
while (tries-- > 0) {
var point = _getRandomPoint();
var tile = tileAtPoint(point);
if (tile.type != MAP_TILE_TYPES.GRASS) {
continue;
}
// first tile of the new forest spot
forest = ForestMapTile(x: point.x, y: point.y);
setTile(forest);
// with 75% change grow the forest in random direction
while (willGrow(75)) {
var nextPoint = _nextPoint(Point(forest.x, forest.y));
var nextTile = tileAtPoint(nextPoint);
if (nextTile.type == MAP_TILE_TYPES.GRASS) {
forest = ForestMapTile(x: nextPoint.x, y: nextPoint.y);
setTileAtPoint(nextPoint, forest);
}
}
// forest growing is done
break;
}
}
willGrow method is enhanced and allows to provide % for success as input argument
bool willGrow(int chance) {
var next = Random().nextInt(100);
return next <= chance;
}
Another useful method is _nextPoint. It returns a nearby point to the given input point:
Point _nextPoint(Point point) {
var decision = leftDownRight();
var nextX = point.x;
var nextY = point.y;
if (decision == 0) {
nextY = nextY + 1;
}
if (decision == -1) {
nextX = nextX - 1;
}
if (decision == 1) {
nextX = nextX + 1;
}
// truncate out of bounds values back to point inside the map
if (nextX >= width) {
nextX = width - 1;
}
if (nextY >= height) {
nextY = height - 1;
}
if (nextX < 0) {
nextX = 0;
}
if (nextY < 0) {
nextY = 0;
}
return Point(nextX, nextY);
}
Another method addForests is used to generate multiple forest spots:
void addForests() {
width.timesRepeat(addForest);
}
timesRepeat is an Extension for int type:
extension TimesRepeat on int {
void timesRepeat(Function f) {
for (var i = 0; i < this; i++) {
f();
}
}
}
Now you can call addRiver and addForests methods to initialize map:
final WorldMap map = WorldMap(width: 20, height: 20)
..addRiver()
..addForests();
The map generated map looks something like this:

In Part 3 we will generate lakes and further refactor existing functions. Stay tuned!