implement tilemap chunk generator
This commit is contained in:
parent
83343c9be6
commit
7dc6a4c420
12 changed files with 244 additions and 1592 deletions
BIN
maps/tampere_10x10km_1024px.png
Normal file
BIN
maps/tampere_10x10km_1024px.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
14
maps/tampere_10x10km_1024px.png.import
Normal file
14
maps/tampere_10x10km_1024px.png.import
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="image"
|
||||||
|
type="Image"
|
||||||
|
uid="uid://dprlyk4ednkp8"
|
||||||
|
path="res://.godot/imported/tampere_10x10km_1024px.png-569f3285557c876bd7277d49b8414424.image"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://maps/tampere_10x10km_1024px.png"
|
||||||
|
dest_files=["res://.godot/imported/tampere_10x10km_1024px.png-569f3285557c876bd7277d49b8414424.image"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
BIN
maps/tampere_10x10km_4096px.png
Normal file
BIN
maps/tampere_10x10km_4096px.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 74 KiB |
14
maps/tampere_10x10km_4096px.png.import
Normal file
14
maps/tampere_10x10km_4096px.png.import
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="image"
|
||||||
|
type="Image"
|
||||||
|
uid="uid://bl4xbe4vb3nft"
|
||||||
|
path="res://.godot/imported/tampere_10x10km_4096px.png-a656fcf9b79fbe6e529e735982b88a37.image"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://maps/tampere_10x10km_4096px.png"
|
||||||
|
dest_files=["res://.godot/imported/tampere_10x10km_4096px.png-a656fcf9b79fbe6e529e735982b88a37.image"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
|
@ -19,6 +19,10 @@ config/icon="res://icon.svg"
|
||||||
|
|
||||||
Globals="*res://scripts/Globals.gd"
|
Globals="*res://scripts/Globals.gd"
|
||||||
|
|
||||||
|
[debug]
|
||||||
|
|
||||||
|
gdscript/warnings/integer_division=0
|
||||||
|
|
||||||
[display]
|
[display]
|
||||||
|
|
||||||
window/size/viewport_height=1080
|
window/size/viewport_height=1080
|
||||||
|
|
1476
scenes/Main.tscn
1476
scenes/Main.tscn
File diff suppressed because it is too large
Load diff
|
@ -22,7 +22,11 @@ func _ready():
|
||||||
|
|
||||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
func _process(_delta):
|
func _process(_delta):
|
||||||
debug_info.set_text(str(get_viewport().get_mouse_position()) +"\n" + "FPS " + str(Engine.get_frames_per_second()))
|
debug_info.set_text(
|
||||||
|
str(get_viewport().get_mouse_position()) +"\n" +
|
||||||
|
"FPS " + str(Engine.get_frames_per_second()) + "\n" +
|
||||||
|
"Zoom lvl: " + str(Globals.CAMERA_ZOOM_LEVEL)
|
||||||
|
)
|
||||||
|
|
||||||
# defines construction toolbar buttons
|
# defines construction toolbar buttons
|
||||||
func create_buttons():
|
func create_buttons():
|
||||||
|
|
|
@ -1,22 +1,11 @@
|
||||||
class_name World
|
class_name EntityPlacer
|
||||||
extends TileMap
|
extends Control
|
||||||
|
|
||||||
var has_placeable_building: bool = false
|
var has_placeable_building: bool = false
|
||||||
var building
|
var building
|
||||||
var building_type: String
|
var building_type: String
|
||||||
var scene
|
var scene
|
||||||
|
|
||||||
func _init():
|
|
||||||
Globals.world_map = self
|
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
|
||||||
func _ready():
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
|
||||||
func _process(_delta):
|
|
||||||
pass
|
|
||||||
|
|
||||||
func get_building_properties() -> Array:
|
func get_building_properties() -> Array:
|
||||||
var tileset_id = 0 # default value
|
var tileset_id = 0 # default value
|
||||||
var tilemap_tile_coords: Vector2i
|
var tilemap_tile_coords: Vector2i
|
||||||
|
@ -62,40 +51,40 @@ func _on_control_button_pressed(type):
|
||||||
scene = load(Globals.SCENE_PATH + "Building.tscn")
|
scene = load(Globals.SCENE_PATH + "Building.tscn")
|
||||||
building = scene.instantiate()
|
building = scene.instantiate()
|
||||||
#building.set_cell(0, Vector2i(0,0), building_properties[0], building_properties[1], 0)
|
#building.set_cell(0, Vector2i(0,0), building_properties[0], building_properties[1], 0)
|
||||||
add_child(building)
|
#add_child(building)
|
||||||
|
|
||||||
has_placeable_building = true
|
has_placeable_building = true
|
||||||
|
|
||||||
func _input(event):
|
#func _input(event):
|
||||||
# place the building
|
# # place the building
|
||||||
if event.is_action_pressed("place_building") and has_placeable_building:
|
# if event.is_action_pressed("place_building") and has_placeable_building:
|
||||||
has_placeable_building = false
|
# has_placeable_building = false
|
||||||
place_building_to_map()
|
# place_building_to_map()
|
||||||
|
#
|
||||||
# cancel placement
|
# # cancel placement
|
||||||
if event.is_action_pressed("cancel"):
|
# if event.is_action_pressed("cancel"):
|
||||||
if has_placeable_building:
|
# if has_placeable_building:
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
func calculate_grid_coordinates(map_position: Vector2) -> Vector2:
|
func calculate_grid_coordinates(map_position: Vector2) -> Vector2:
|
||||||
return (map_position).floor()
|
return (map_position).floor()
|
||||||
|
|
||||||
func place_building_to_map():
|
#func place_building_to_map():
|
||||||
var building_properties = get_building_properties()
|
# var building_properties = get_building_properties()
|
||||||
var tile_on_mouse = local_to_map(get_global_mouse_position())
|
# #var tile_on_mouse = local_to_map(get_global_mouse_position())
|
||||||
|
#
|
||||||
|
# if !Globals.are_coords_valid(
|
||||||
|
# tile_on_mouse.y,
|
||||||
|
# Vector2i(0, Globals.map_image_size.y),
|
||||||
|
# Globals.ERROR_TILE_Y_COORDS_OUT_OF_BOUNDS
|
||||||
|
# ):
|
||||||
|
# return false
|
||||||
|
# elif !Globals.are_coords_valid(
|
||||||
|
# tile_on_mouse.x,
|
||||||
|
# Vector2i(0, Globals.map_image_size.x),
|
||||||
|
# Globals.ERROR_TILE_X_COORDS_OUT_OF_BOUNDS
|
||||||
|
# ):
|
||||||
|
# return false
|
||||||
|
|
||||||
if !Globals.are_coords_valid(
|
# set_cell(Globals.LAYER_BUILDINGS, tile_on_mouse, building_properties[0], building_properties[1], 0)
|
||||||
tile_on_mouse.y,
|
|
||||||
Vector2i(0, Globals.map_image_size.y),
|
|
||||||
Globals.ERROR_TILE_Y_COORDS_OUT_OF_BOUNDS
|
|
||||||
):
|
|
||||||
return false
|
|
||||||
elif !Globals.are_coords_valid(
|
|
||||||
tile_on_mouse.x,
|
|
||||||
Vector2i(0, Globals.map_image_size.x),
|
|
||||||
Globals.ERROR_TILE_X_COORDS_OUT_OF_BOUNDS
|
|
||||||
):
|
|
||||||
return false
|
|
||||||
|
|
||||||
set_cell(Globals.LAYER_BUILDINGS, tile_on_mouse, building_properties[0], building_properties[1], 0)
|
|
||||||
|
|
|
@ -20,8 +20,17 @@ func are_coords_valid(value:int, bounds:Vector2i, errmsg:String) -> bool:
|
||||||
func choose_randomly(list_of_entries):
|
func choose_randomly(list_of_entries):
|
||||||
return list_of_entries[randi() % list_of_entries.size()]
|
return list_of_entries[randi() % list_of_entries.size()]
|
||||||
|
|
||||||
var world_map: TileMap
|
# map size is based on input image x*y pixel size
|
||||||
var map_image_size:Vector2i
|
var map_size:Vector2i
|
||||||
|
|
||||||
|
# store terrain type (water, land, forest etc. for every map cell)
|
||||||
|
var map_terrain_data:Array[Array] = [[]]
|
||||||
|
|
||||||
|
# preprocess and store exact tile for every map cell to speed up setting tiles
|
||||||
|
var map_tile_data:Array[Array] = [[]]
|
||||||
|
|
||||||
|
# current camera zoom level
|
||||||
|
var CAMERA_ZOOM_LEVEL: float = 1.0
|
||||||
|
|
||||||
# FILE PATHS
|
# FILE PATHS
|
||||||
const SCENE_PATH:String = "res://scenes/"
|
const SCENE_PATH:String = "res://scenes/"
|
||||||
|
@ -52,8 +61,6 @@ const TYPE_ROADS:String = "roads"
|
||||||
const TYPE_DEMOLISH:String = "demolish"
|
const TYPE_DEMOLISH:String = "demolish"
|
||||||
|
|
||||||
# camera movement settings
|
# camera movement settings
|
||||||
var CAMERA_ZOOM_LEVEL: float = 1.0
|
|
||||||
|
|
||||||
const CAMERA_MIN_ZOOM_LEVEL: float = 0.1
|
const CAMERA_MIN_ZOOM_LEVEL: float = 0.1
|
||||||
const CAMERA_MAX_ZOOM_LEVEL: float = 2.0
|
const CAMERA_MAX_ZOOM_LEVEL: float = 2.0
|
||||||
const CAMERA_ZOOM_FACTOR: float = 0.1
|
const CAMERA_ZOOM_FACTOR: float = 0.1
|
||||||
|
@ -65,10 +72,10 @@ const GROUND_TILE_COLOR_IN_MAP_FILE: Color = Color(0,0,0,1)
|
||||||
const WATER_TILE_COLOR_IN_MAP_FILE: Color = Color(1,1,1,1)
|
const WATER_TILE_COLOR_IN_MAP_FILE: Color = Color(1,1,1,1)
|
||||||
|
|
||||||
# min and max sizes for a map so the map won't be unreasonably small or large
|
# min and max sizes for a map so the map won't be unreasonably small or large
|
||||||
const MAP_MIN_HEIGHT:int = 100
|
const MAP_MIN_HEIGHT:int = 128
|
||||||
const MAP_MAX_HEIGHT:int = 1000
|
const MAP_MAX_HEIGHT:int = 4096
|
||||||
const MAP_MIN_WIDTH:int = 100
|
const MAP_MIN_WIDTH:int = 128
|
||||||
const MAP_MAX_WIDTH:int = 1000
|
const MAP_MAX_WIDTH:int = 4096
|
||||||
|
|
||||||
# tile size
|
# tile size
|
||||||
const TILE_SIZE_X:int = 16
|
const TILE_SIZE_X:int = 16
|
||||||
|
|
|
@ -12,10 +12,15 @@ extends Node
|
||||||
signal set_camera_position(pos:Vector2)
|
signal set_camera_position(pos:Vector2)
|
||||||
|
|
||||||
# The idea is for the user to be able to choose the map from GUI later
|
# The idea is for the user to be able to choose the map from GUI later
|
||||||
#var map_filename: String = "res://maps/tampere_10x10km_1000px.png"
|
var map_filenames:Array = [
|
||||||
var map_filename:String = "res://maps/tampere_200px_crop.png"
|
"res://maps/tampere_10x10km_1000px.png",
|
||||||
var _world := World.new()
|
"res://maps/tampere_10x10km_1024px.png",
|
||||||
var _world_generator := WorldGeneration.new()
|
"res://maps/varkaus_256x256px_test.png"
|
||||||
|
]
|
||||||
|
var map_filename:String = map_filenames[2]
|
||||||
|
var _world_generator:WorldGenerator
|
||||||
|
var _tilemap_generator:TileMapGenerator
|
||||||
|
|
||||||
|
|
||||||
func _init():
|
func _init():
|
||||||
# DisplayServer.window_set_size(
|
# DisplayServer.window_set_size(
|
||||||
|
@ -26,20 +31,27 @@ func _init():
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready():
|
func _ready():
|
||||||
if !_world:
|
# create a new world and worldgenerator
|
||||||
push_error(Globals.ERROR_MAKING_WORLD_INSTANCE)
|
_world_generator = WorldGenerator.new()
|
||||||
quit_game()
|
_tilemap_generator = TileMapGenerator.new()
|
||||||
|
|
||||||
# generate terrain. quit game if generation fails.
|
# generate terrain. quit game if generation fails.
|
||||||
if !_world_generator.generate_world(map_filename):
|
if !_world_generator.generate_world(map_filename):
|
||||||
push_error(Globals.ERROR_WHILE_GENERATING_MAP)
|
push_error(Globals.ERROR_WHILE_GENERATING_MAP)
|
||||||
quit_game()
|
quit_game()
|
||||||
|
|
||||||
|
if !_tilemap_generator:
|
||||||
|
push_error(Globals.ERROR_MAKING_WORLD_INSTANCE)
|
||||||
|
quit_game()
|
||||||
|
|
||||||
|
_tilemap_generator.start()
|
||||||
|
_tilemap_generator.test_func()
|
||||||
|
|
||||||
# center camera to world map
|
# center camera to world map
|
||||||
emit_signal(
|
emit_signal(
|
||||||
"set_camera_position",
|
"set_camera_position",
|
||||||
Vector2(Globals.map_image_size.x / 2.0 * Globals.TILE_SIZE_X,
|
Vector2(Globals.map_size.x / 2.0 * Globals.TILE_SIZE_X,
|
||||||
Globals.map_image_size.y / 2.0 * Globals.TILE_SIZE_Y)
|
Globals.map_size.y / 2.0 * Globals.TILE_SIZE_Y)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
|
57
scripts/TileMapGenerator.gd
Normal file
57
scripts/TileMapGenerator.gd
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
class_name TileMapGenerator
|
||||||
|
extends CanvasLayer
|
||||||
|
|
||||||
|
# one tilemap is one chunk
|
||||||
|
# map consists of many chunks
|
||||||
|
# chunks are loaded to view when needed
|
||||||
|
# chunks are deleted after they are no longer needed (in view)
|
||||||
|
|
||||||
|
# This is done to speed up game loading and avoiding setting one large tilemap in one go
|
||||||
|
# which is extremely slow in godot 4.0, 4096x4096 takes minutes to fill with set_cell() commands
|
||||||
|
|
||||||
|
const CHUNK_SIZE = Vector2i(128,128)
|
||||||
|
|
||||||
|
var map_tiles:Array[Array] = [[]]
|
||||||
|
|
||||||
|
func start() -> void:
|
||||||
|
# Initialize the map tile array with enough chunks to cover the whole map
|
||||||
|
|
||||||
|
var ms:int = Globals.map_size.y/CHUNK_SIZE.y
|
||||||
|
map_tiles.resize(2)
|
||||||
|
print("map tiles size y: ", map_tiles.size())
|
||||||
|
|
||||||
|
for y in ms:
|
||||||
|
map_tiles[y].resize(2)
|
||||||
|
print("map tiles size x: ", map_tiles[y].size())
|
||||||
|
for x in ms:
|
||||||
|
map_tiles[y][x] = TileMap.new()
|
||||||
|
|
||||||
|
func test_func():
|
||||||
|
self.set_chunk_tiles(Vector2i(1,0))
|
||||||
|
#self.map_tiles[0][1].visible()
|
||||||
|
print(map_tiles[0][1])
|
||||||
|
|
||||||
|
func clear_chunk_tiles(chunk_pos:Vector2i) -> void:
|
||||||
|
map_tiles[chunk_pos.y][chunk_pos.x].clear()
|
||||||
|
|
||||||
|
func set_chunk_tiles(chunk_pos:Vector2i) -> void:
|
||||||
|
# Set an invidiual chunk's tiles based on map terrain data
|
||||||
|
|
||||||
|
# Try to load the world tilemap where we place the tiles
|
||||||
|
if (map_tiles[chunk_pos.y][chunk_pos.x] == null):
|
||||||
|
var errmsg = Globals.ERROR_TILEMAP_NODE_MISSING
|
||||||
|
push_error(errmsg % str(chunk_pos))
|
||||||
|
#return false
|
||||||
|
|
||||||
|
for y in CHUNK_SIZE.y:
|
||||||
|
for x in CHUNK_SIZE.x:
|
||||||
|
var tile_data: Array = Globals.map_tile_data[chunk_pos.y*CHUNK_SIZE.y][chunk_pos.x*CHUNK_SIZE.x]
|
||||||
|
|
||||||
|
# layer | tile coords at tilemap | tilemap id | coords of the tile at tileset | alternative tile
|
||||||
|
map_tiles[chunk_pos.y][chunk_pos.x].set_cell(
|
||||||
|
Globals.LAYER_TERRAIN,
|
||||||
|
Vector2i(x, y),
|
||||||
|
2,
|
||||||
|
tile_data[0],
|
||||||
|
tile_data[1]
|
||||||
|
)
|
|
@ -1,8 +1,8 @@
|
||||||
class_name WorldGeneration
|
class_name WorldGenerator
|
||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
||||||
var image:Image = Image.new()
|
var image:Image = Image.new()
|
||||||
var map_tile_data:Array[Array] = [[]] # store map tile info to a 2d array
|
|
||||||
var directions:Array = [
|
var directions:Array = [
|
||||||
Vector2i(0,1), # south
|
Vector2i(0,1), # south
|
||||||
Vector2i(1,0), # east
|
Vector2i(1,0), # east
|
||||||
|
@ -10,15 +10,15 @@ var directions:Array = [
|
||||||
Vector2i(-1,0) # west
|
Vector2i(-1,0) # west
|
||||||
]
|
]
|
||||||
|
|
||||||
func choose_tile(tile:Vector2i, selected, surrounding) -> void:
|
func choose_tile(tile:Vector2i, selected, surrounding) -> Array:
|
||||||
var surrounding_tiles:Array = []
|
var surrounding_tiles:Array = []
|
||||||
|
|
||||||
# determine which directions have land around the tile
|
# determine which directions have land around the tile
|
||||||
for dir in directions:
|
for dir in directions:
|
||||||
# avoid index out of bounds
|
# avoid index out of bounds
|
||||||
if (tile.y+dir.y >= Globals.map_image_size.y) or (tile.x+dir.x >= Globals.map_image_size.x):
|
if (tile.y+dir.y >= Globals.map_size.y) or (tile.x+dir.x >= Globals.map_size.x):
|
||||||
surrounding_tiles.append(surrounding)
|
surrounding_tiles.append(surrounding)
|
||||||
elif map_tile_data[tile.y+dir.y][tile.x+dir.x] == surrounding:
|
elif Globals.map_terrain_data[tile.y+dir.y][tile.x+dir.x] == surrounding:
|
||||||
surrounding_tiles.append(surrounding)
|
surrounding_tiles.append(surrounding)
|
||||||
else:
|
else:
|
||||||
surrounding_tiles.append(selected)
|
surrounding_tiles.append(selected)
|
||||||
|
@ -33,14 +33,10 @@ func choose_tile(tile:Vector2i, selected, surrounding) -> void:
|
||||||
else:
|
else:
|
||||||
tile_coords = selected_tile[0]
|
tile_coords = selected_tile[0]
|
||||||
|
|
||||||
# layer | position coords | tilemap id | coords of the tile at tilemap | alternative tile
|
return [
|
||||||
Globals.world_map.set_cell(
|
|
||||||
Globals.LAYER_TERRAIN,
|
|
||||||
Vector2i(tile.x, tile.y),
|
|
||||||
2,
|
|
||||||
tile_coords,
|
tile_coords,
|
||||||
0 if selected_tile else Globals.choose_randomly([0,1,2,3])
|
0 if selected_tile else Globals.choose_randomly([0,1,2,3])
|
||||||
)
|
]
|
||||||
|
|
||||||
# Generates biomes, like forest and bog
|
# Generates biomes, like forest and bog
|
||||||
func generate_biomes() -> void:
|
func generate_biomes() -> void:
|
||||||
|
@ -56,25 +52,25 @@ func generate_biomes() -> void:
|
||||||
|
|
||||||
var water_next_to_tile:bool = false
|
var water_next_to_tile:bool = false
|
||||||
|
|
||||||
for y in map_tile_data.size():
|
for y in Globals.map_terrain_data.size():
|
||||||
for x in map_tile_data[y].size():
|
for x in Globals.map_terrain_data[y].size():
|
||||||
|
|
||||||
# replace non-water with biomes
|
# replace non-water with biomes
|
||||||
if map_tile_data[y][x] > 0:
|
if Globals.map_terrain_data[y][x] > 0:
|
||||||
water_next_to_tile = false
|
water_next_to_tile = false
|
||||||
|
|
||||||
# don't put forest next to water
|
# don't put forest next to water
|
||||||
for dir in directions:
|
for dir in directions:
|
||||||
if (y+dir.y >= Globals.map_image_size.y) or (x+dir.x >= Globals.map_image_size.x):
|
if (y+dir.y >= Globals.map_size.y) or (x+dir.x >= Globals.map_size.x):
|
||||||
continue
|
continue
|
||||||
if map_tile_data[y+dir.y][x+dir.x] == Globals.TILE_WATER:
|
if Globals.map_terrain_data[y+dir.y][x+dir.x] == Globals.TILE_WATER:
|
||||||
water_next_to_tile = true
|
water_next_to_tile = true
|
||||||
|
|
||||||
# if there's no water next to a land tile, it can be replaced with forest
|
# if there's no water next to a land tile, it can be replaced with forest
|
||||||
if !water_next_to_tile:
|
if !water_next_to_tile:
|
||||||
var noise_sample = fnl.get_noise_2d(x,y)
|
var noise_sample = fnl.get_noise_2d(x,y)
|
||||||
if noise_sample < 0.1:
|
if noise_sample < 0.1:
|
||||||
map_tile_data[y][x] = Globals.TILE_FOREST
|
Globals.map_terrain_data[y][x] = Globals.TILE_FOREST
|
||||||
# can add other tresholds here for other biomes
|
# can add other tresholds here for other biomes
|
||||||
|
|
||||||
func generate_world(filename) -> bool:
|
func generate_world(filename) -> bool:
|
||||||
|
@ -86,58 +82,67 @@ func generate_world(filename) -> bool:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Check if image is too small or too large
|
# Check if image is too small or too large
|
||||||
Globals.map_image_size = image.get_size()
|
Globals.map_size = image.get_size()
|
||||||
if !validate_mapgen_params():
|
if !validate_mapgen_params():
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
var start = Time.get_ticks_usec()
|
||||||
read_image_pixel_data()
|
read_image_pixel_data()
|
||||||
|
var end = Time.get_ticks_usec()
|
||||||
|
print("read image data ", (end-start)/1000.0, "ms")
|
||||||
|
|
||||||
|
start = Time.get_ticks_usec()
|
||||||
smooth_land_features(Globals.TILE_WATER) # smooth water
|
smooth_land_features(Globals.TILE_WATER) # smooth water
|
||||||
|
end = Time.get_ticks_usec()
|
||||||
|
print("smooth water ", (end-start)/1000.0, "ms")
|
||||||
|
|
||||||
|
start = Time.get_ticks_usec()
|
||||||
generate_biomes()
|
generate_biomes()
|
||||||
|
end = Time.get_ticks_usec()
|
||||||
|
print("generate biomes ", (end-start)/1000.0, "ms")
|
||||||
|
|
||||||
|
start = Time.get_ticks_usec()
|
||||||
smooth_land_features(Globals.TILE_FOREST) # smooth out forest
|
smooth_land_features(Globals.TILE_FOREST) # smooth out forest
|
||||||
|
end = Time.get_ticks_usec()
|
||||||
|
print("smooth forest ", (end-start)/1000.0, "ms")
|
||||||
|
|
||||||
|
start = Time.get_ticks_usec()
|
||||||
set_tilemap_tiles()
|
set_tilemap_tiles()
|
||||||
|
end = Time.get_ticks_usec()
|
||||||
|
print("set tiles ", (end-start)/1000.0, "ms")
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
func read_image_pixel_data():
|
func read_image_pixel_data():
|
||||||
# initialize the array to have enough rows
|
# initialize the array to have enough rows
|
||||||
map_tile_data.resize(Globals.map_image_size.y)
|
Globals.map_terrain_data.resize(Globals.map_size.y)
|
||||||
|
Globals.map_tile_data.resize(Globals.map_size.y)
|
||||||
|
|
||||||
for y in Globals.map_image_size.y:
|
for y in Globals.map_size.y:
|
||||||
#initialize the row to have enough columns
|
#initialize the row to have enough columns
|
||||||
map_tile_data[y].resize(Globals.map_image_size.y)
|
Globals.map_terrain_data[y].resize(Globals.map_size.y)
|
||||||
|
Globals.map_tile_data[y].resize(Globals.map_size.y)
|
||||||
|
|
||||||
for x in Globals.map_image_size.x:
|
|
||||||
|
for x in Globals.map_size.x:
|
||||||
if image.get_pixel(x, y) == Globals.WATER_TILE_COLOR_IN_MAP_FILE:
|
if image.get_pixel(x, y) == Globals.WATER_TILE_COLOR_IN_MAP_FILE:
|
||||||
map_tile_data[y][x] = Globals.TILE_WATER
|
Globals.map_terrain_data[y][x] = Globals.TILE_WATER
|
||||||
else:
|
else:
|
||||||
map_tile_data[y][x] = Globals.TILE_TERRAIN
|
Globals.map_terrain_data[y][x] = Globals.TILE_TERRAIN
|
||||||
|
|
||||||
func set_tilemap_tiles() -> void:
|
func set_tilemap_tiles() -> void:
|
||||||
for y in map_tile_data.size():
|
for y in Globals.map_terrain_data.size():
|
||||||
for x in map_tile_data[y].size():
|
for x in Globals.map_terrain_data[y].size():
|
||||||
# layer | position coords | tilemap id | coords of the tile at tilemap | alternative tile
|
# layer | position coords | tilemap id | coords of the tile at tilemap | alternative tile
|
||||||
match map_tile_data[y][x]:
|
match Globals.map_terrain_data[y][x]:
|
||||||
Globals.TILE_WATER: # water or shoreline
|
Globals.TILE_WATER: # water or shoreline
|
||||||
choose_tile(Vector2i(x, y), Globals.TILE_WATER, Globals.TILE_TERRAIN)
|
Globals.map_tile_data[y][x] = choose_tile(Vector2i(x, y), Globals.TILE_WATER, Globals.TILE_TERRAIN)
|
||||||
|
|
||||||
Globals.TILE_TERRAIN: #terrain or forest edge
|
Globals.TILE_TERRAIN: #terrain or forest edge
|
||||||
# Globals.world_map.set_cell(
|
Globals.map_tile_data[y][x] = choose_tile(Vector2i(x,y), Globals.TILE_TERRAIN, Globals.TILE_FOREST)
|
||||||
# Globals.LAYER_TERRAIN,
|
|
||||||
# Vector2i(x, y),
|
|
||||||
# 2,
|
|
||||||
# Vector2i(0,0),
|
|
||||||
# Globals.choose_randomly([0,1,2,3])
|
|
||||||
# )
|
|
||||||
choose_tile(Vector2i(x,y), Globals.TILE_TERRAIN, Globals.TILE_FOREST)
|
|
||||||
|
|
||||||
Globals.TILE_FOREST:
|
Globals.TILE_FOREST:
|
||||||
Globals.world_map.set_cell(
|
Globals.map_tile_data[y][x] = [Vector2i(5,1), Globals.choose_randomly([0,1,2,3])]
|
||||||
Globals.LAYER_TERRAIN,
|
|
||||||
Vector2i(x, y),
|
|
||||||
2,
|
|
||||||
Vector2i(5,1),
|
|
||||||
Globals.choose_randomly([0,1,2,3])
|
|
||||||
)
|
|
||||||
|
|
||||||
_: #default
|
_: #default
|
||||||
pass
|
pass
|
||||||
|
@ -147,9 +152,9 @@ func set_tilemap_tiles() -> void:
|
||||||
# Do it recursively with limit of n recursions!
|
# Do it recursively with limit of n recursions!
|
||||||
func smooth_land_features(tile_type:int) -> void:
|
func smooth_land_features(tile_type:int) -> void:
|
||||||
# TODO for testing avoid map borders to make it simpler to implement
|
# TODO for testing avoid map borders to make it simpler to implement
|
||||||
for y in range(1, Globals.map_image_size.y-1):
|
for y in range(1, Globals.map_size.y-1):
|
||||||
for x in range(1, Globals.map_image_size.x-1):
|
for x in range(1, Globals.map_size.x-1):
|
||||||
if map_tile_data[y][x] != tile_type:
|
if Globals.map_terrain_data[y][x] != tile_type:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match tile_type:
|
match tile_type:
|
||||||
|
@ -173,26 +178,26 @@ func smooth_forest_recursively(pos:Vector2i, selected:int, comp:int) -> void:
|
||||||
|
|
||||||
# determine which directions have land around the tile
|
# determine which directions have land around the tile
|
||||||
for dir in directions:
|
for dir in directions:
|
||||||
if map_tile_data[pos.y+dir.y][pos.x+dir.x] == comp:
|
if Globals.map_terrain_data[pos.y+dir.y][pos.x+dir.x] == comp:
|
||||||
surrounding_tiles.append(comp)
|
surrounding_tiles.append(comp)
|
||||||
elif map_tile_data[pos.y+dir.y][pos.x+dir.x] == selected:
|
elif Globals.map_terrain_data[pos.y+dir.y][pos.x+dir.x] == selected:
|
||||||
surrounding_tiles.append(selected)
|
surrounding_tiles.append(selected)
|
||||||
|
|
||||||
match surrounding_tiles:
|
match surrounding_tiles:
|
||||||
[1,1,1,2]: #west
|
[1,1,1,2]: #west
|
||||||
map_tile_data[pos.y][pos.x] = comp
|
Globals.map_terrain_data[pos.y][pos.x] = comp
|
||||||
pos.x -= 1
|
pos.x -= 1
|
||||||
[1,1,2,1]: #north
|
[1,1,2,1]: #north
|
||||||
map_tile_data[pos.y][pos.x] = comp
|
Globals.map_terrain_data[pos.y][pos.x] = comp
|
||||||
pos.y -= 1
|
pos.y -= 1
|
||||||
[1,2,1,1]: #east
|
[1,2,1,1]: #east
|
||||||
map_tile_data[pos.y][pos.x] = comp
|
Globals.map_terrain_data[pos.y][pos.x] = comp
|
||||||
pos.x += 1
|
pos.x += 1
|
||||||
[2,1,1,1]: #south
|
[2,1,1,1]: #south
|
||||||
map_tile_data[pos.y][pos.x] = comp
|
Globals.map_terrain_data[pos.y][pos.x] = comp
|
||||||
pos.y += 1
|
pos.y += 1
|
||||||
[1,1,1,1]: # remove solo forests
|
[1,1,1,1]: # remove solo forests
|
||||||
map_tile_data[pos.y][pos.x] = comp
|
Globals.map_terrain_data[pos.y][pos.x] = comp
|
||||||
return
|
return
|
||||||
_:
|
_:
|
||||||
return
|
return
|
||||||
|
@ -205,23 +210,23 @@ func smooth_recursively(pos:Vector2i, selected:int, comp:int) -> void:
|
||||||
|
|
||||||
# determine which directions have land around the tile
|
# determine which directions have land around the tile
|
||||||
for dir in directions:
|
for dir in directions:
|
||||||
if map_tile_data[pos.y+dir.y][pos.x+dir.x] == comp:
|
if Globals.map_terrain_data[pos.y+dir.y][pos.x+dir.x] == comp:
|
||||||
surrounding_tiles.append(comp)
|
surrounding_tiles.append(comp)
|
||||||
elif map_tile_data[pos.y+dir.y][pos.x+dir.x] == selected:
|
elif Globals.map_terrain_data[pos.y+dir.y][pos.x+dir.x] == selected:
|
||||||
surrounding_tiles.append(selected)
|
surrounding_tiles.append(selected)
|
||||||
|
|
||||||
match surrounding_tiles:
|
match surrounding_tiles:
|
||||||
[1,1,1,0]: #west
|
[1,1,1,0]: #west
|
||||||
map_tile_data[pos.y][pos.x] = comp
|
Globals.map_terrain_data[pos.y][pos.x] = comp
|
||||||
pos.x -= 1
|
pos.x -= 1
|
||||||
[1,1,0,1]: #north
|
[1,1,0,1]: #north
|
||||||
map_tile_data[pos.y][pos.x] = comp
|
Globals.map_terrain_data[pos.y][pos.x] = comp
|
||||||
pos.y -= 1
|
pos.y -= 1
|
||||||
[1,0,1,1]: #east
|
[1,0,1,1]: #east
|
||||||
map_tile_data[pos.y][pos.x] = comp
|
Globals.map_terrain_data[pos.y][pos.x] = comp
|
||||||
pos.x += 1
|
pos.x += 1
|
||||||
[0,1,1,1]: #south
|
[0,1,1,1]: #south
|
||||||
map_tile_data[pos.y][pos.x] = comp
|
Globals.map_terrain_data[pos.y][pos.x] = comp
|
||||||
pos.y += 1
|
pos.y += 1
|
||||||
_:
|
_:
|
||||||
return
|
return
|
||||||
|
@ -230,21 +235,15 @@ func smooth_recursively(pos:Vector2i, selected:int, comp:int) -> void:
|
||||||
|
|
||||||
func validate_mapgen_params() -> bool:
|
func validate_mapgen_params() -> bool:
|
||||||
if !Globals.are_coords_valid(
|
if !Globals.are_coords_valid(
|
||||||
Globals.map_image_size.y,
|
Globals.map_size.y,
|
||||||
Vector2i(Globals.MAP_MIN_HEIGHT, Globals.MAP_MAX_HEIGHT),
|
Vector2i(Globals.MAP_MIN_HEIGHT, Globals.MAP_MAX_HEIGHT),
|
||||||
Globals.ERROR_IMAGE_HEIGHT_INCORRECT):
|
Globals.ERROR_IMAGE_HEIGHT_INCORRECT):
|
||||||
return false
|
return false
|
||||||
|
|
||||||
elif !Globals.are_coords_valid(
|
elif !Globals.are_coords_valid(
|
||||||
Globals.map_image_size.x,
|
Globals.map_size.x,
|
||||||
Vector2i(Globals.MAP_MIN_WIDTH, Globals.MAP_MAX_WIDTH),
|
Vector2i(Globals.MAP_MIN_WIDTH, Globals.MAP_MAX_WIDTH),
|
||||||
Globals.ERROR_IMAGE_WIDTH_INCORRECT):
|
Globals.ERROR_IMAGE_WIDTH_INCORRECT):
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Try to load the world tilemap where we place the tiles
|
|
||||||
if (Globals.world_map == null):
|
|
||||||
var errmsg = Globals.ERROR_WORLD_TILEMAP_NODE_MISSING % Globals.WORLD_NODE
|
|
||||||
push_error(errmsg)
|
|
||||||
return false
|
|
||||||
|
|
||||||
return true
|
return true
|
Loading…
Add table
Add a link
Reference in a new issue