load and remove chunks around camera pos working

This commit is contained in:
Antti Hakkarainen 2023-02-12 22:17:21 +02:00
parent fccef57210
commit 0109934933
8 changed files with 137 additions and 73 deletions

View file

@ -3,22 +3,39 @@ extends TileMap
var x:int = -1 var x:int = -1
var y:int = -1 var y:int = -1
var should_remove:bool = true
# 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 _init(xpos:int, ypos:int): func _init(xpos:int, ypos:int):
self.x = xpos self.x = xpos
self.y = ypos self.y = ypos
self.name = "Chunk [%d,%d]" % [x, y] self.name = "Chunk [%d,%d]" % [x, y]
self.set_tileset(Globals.TILESET_TERRAIN) self.set_tileset(Globals.TILESET_TERRAIN)
self.position = Vector2i( self.position = Vector2i(
x*Globals.CHUNK_SIZE*Globals.TILE_SIZE_X, x*Globals.CHUNK_SIZE.x*Globals.TILE_SIZE_X,
y*Globals.CHUNK_SIZE*Globals.TILE_SIZE_Y y*Globals.CHUNK_SIZE.y*Globals.TILE_SIZE_Y
)
func _ready():
generate_chunk()
func _draw():
self.draw_rect(
Rect2(
Vector2(0,0),
Vector2(
Globals.CHUNK_SIZE.x*Globals.TILE_SIZE_X,
Globals.CHUNK_SIZE.y*Globals.TILE_SIZE_Y)
),
Color(0,0,0,0.5),
false
) )
func generate_chunk() -> void: func generate_chunk() -> void:
for row in Globals.CHUNK_SIZE: for row in Globals.CHUNK_SIZE.y:
for col in Globals.CHUNK_SIZE: for col in Globals.CHUNK_SIZE.x:
var tile_data: Array = Globals.map_tile_data[row+y*Globals.CHUNK_SIZE][col+x*Globals.CHUNK_SIZE] var tile_data: Array = Globals.map_tile_data[row+y*Globals.CHUNK_SIZE.y][col+x*Globals.CHUNK_SIZE.x]
# layer | tile coords at tilemap | tilemap id | coords of the tile at tileset | alternative tile # layer | tile coords at tilemap | tilemap id | coords of the tile at tileset | alternative tile
self.set_cell( self.set_cell(
Globals.LAYER_TERRAIN, Globals.LAYER_TERRAIN,

View file

@ -1,10 +0,0 @@
[gd_scene load_steps=3 format=3 uid="uid://54iw2fjk2oth"]
[ext_resource type="Script" path="res://scenes/Chunk.gd" id="1_lawsj"]
[ext_resource type="TileSet" uid="uid://cxiva1a6jdae2" path="res://scenes/Chunk.tres" id="1_x70ay"]
[node name="Chunk" type="TileMap"]
tile_set = ExtResource("1_x70ay")
format = 2
layer_0/tile_data = PackedInt32Array(0, 0, 65536, -1, 0, 65536)
script = ExtResource("1_lawsj")

View file

@ -10,7 +10,12 @@ func camera_zoom_in() -> void:
func camera_zoom_out() -> void: func camera_zoom_out() -> void:
_set_camera_zoom_level(Globals.CAMERA_ZOOM_LEVEL + Globals.CAMERA_ZOOM_DURATION) _set_camera_zoom_level(Globals.CAMERA_ZOOM_LEVEL + Globals.CAMERA_ZOOM_DURATION)
func get_camera_position():
return self.position
func _process(delta) -> void:
Globals.CAMERA_POSITION = self.position
func _set_camera_zoom_level(value: float) -> void: func _set_camera_zoom_level(value: float) -> void:
Globals.CAMERA_ZOOM_LEVEL = clamp(value, Globals.CAMERA_MIN_ZOOM_LEVEL, Globals.CAMERA_MAX_ZOOM_LEVEL) Globals.CAMERA_ZOOM_LEVEL = clamp(value, Globals.CAMERA_MIN_ZOOM_LEVEL, Globals.CAMERA_MAX_ZOOM_LEVEL)
@ -24,9 +29,8 @@ func _set_camera_zoom_level(value: float) -> void:
Globals.CAMERA_ZOOM_DURATION Globals.CAMERA_ZOOM_DURATION
) )
func _on_set_camera_position(_pos: Vector2) -> void: func _on_set_camera_position(pos: Vector2) -> void:
#self.position = pos self.position = pos
pass
func _unhandled_input(event): func _unhandled_input(event):
if event.is_action_pressed("camera_zoom_in"): if event.is_action_pressed("camera_zoom_in"):

View file

@ -9,36 +9,74 @@ extends Node2D
# This is done to speed up game loading and avoiding setting one large tilemap in one go # 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 # which is extremely slow in godot 4.0, 4096x4096 takes minutes to fill with set_cell() commands
var map_tiles:Array[Array] = [[]]
var chunks:Dictionary = {} var chunks:Dictionary = {}
var unused_chunks:Dictionary = {} var unready_chunks:Dictionary = {}
var chunk_amount = 16
var window_width = DisplayServer.window_get_size(0).x
var distance = abs((window_width/(Globals.CHUNK_SIZE.x*Globals.TILE_SIZE_X)) / 2 +1 )
func _init() -> void: func _init() -> void:
self.name = "ChunkHandler" self.name = "ChunkHandler"
func _process(_delta):
update_chunks()
clean_up_chunks()
reset_chunks()
func _ready(): func _ready():
#thread = Thread.new() #thread = Thread.new()
#print(distance)
pass pass
func add_chunk(x:int, y:int) -> void:
var key = str(y) + "," + str(x)
func start_handler() -> void: if chunks.has(key):
# Initialize the map tile array with enough chunks to cover the whole map return
map_tiles.resize(Globals.map_size/Globals.CHUNK_SIZE)
for y in map_tiles.size(): load_chunk(x, y, key)
map_tiles[y].resize(Globals.map_size/Globals.CHUNK_SIZE)
func add_chunk(x:int, y:int): func clean_up_chunks():
for key in chunks:
var chunk = chunks[key]
if chunk.should_remove:
chunk.queue_free()
chunks.erase(key)
func clear_chunk(pos:Vector2i) -> void:
self.chunks[pos.y][pos.x].clear()
func get_chunk(x:int, y:int):
var key = str(y) + "," + str(x)
if self.chunks.has(key):
return chunks.get(key)
return null
func load_chunk(x:int, y:int, key:String):
var chunk = Chunk.new(x,y) var chunk = Chunk.new(x,y)
var start = Time.get_ticks_usec()
chunk.generate_chunk()
self.add_child(chunk) self.add_child(chunk)
var end = Time.get_ticks_usec() chunks[key] = chunk
print("generate a chunk ", (end-start)/1000.0, "ms")
Globals.chunks_loaded += 1
#chunk.set_position(Vector2(x*Globals.CHUNK_SIZE,y*Globals.CHUNK_SIZE))
#map_tiles[chunk_pos.y][chunk_pos.x].clear() func reset_chunks():
for key in chunks:
chunks[key].should_remove = true
func update_chunks():
var p_x = int(Globals.CAMERA_POSITION.x- Globals.CHUNK_SIZE.x) / Globals.TILE_SIZE_X / Globals.CHUNK_SIZE.x
var p_y = int(Globals.CAMERA_POSITION.y- Globals.CHUNK_SIZE.y) / Globals.TILE_SIZE_Y / Globals.CHUNK_SIZE.y
for y in Globals.map_size/Globals.CHUNK_SIZE.y:
for x in Globals.map_size/Globals.CHUNK_SIZE.x:
if (abs(x - p_x) <= distance && abs(y - p_y) <= distance):
add_chunk(x, y)
var chunk = get_chunk(x,y)
if chunk != null:
chunk.should_remove = false

View file

@ -25,7 +25,9 @@ func _process(_delta):
debug_info.set_text( debug_info.set_text(
str(get_viewport().get_mouse_position()) +"\n" + str(get_viewport().get_mouse_position()) +"\n" +
"FPS " + str(Engine.get_frames_per_second()) + "\n" + "FPS " + str(Engine.get_frames_per_second()) + "\n" +
"Zoom lvl: " + str(Globals.CAMERA_ZOOM_LEVEL) "Zoom lvl: " + str(Globals.CAMERA_ZOOM_LEVEL) + "\n" +
"Chunks loaded: " + str(Globals.chunks_loaded) + "\n" +
"Camera pos: " + str(Globals.CAMERA_POSITION)
) )
# defines construction toolbar buttons # defines construction toolbar buttons

View file

@ -3,24 +3,18 @@
extends Node extends Node
# world map chunk size var chunks_loaded:int = 0
const CHUNK_SIZE:int = 16
###################################
# CHUNK AND TERRAIN SETTINGS #
###################################
# world map chunk size
const CHUNK_SIZE:Vector2i = Vector2i(32,32)
# tilemap tile types # tilemap tile types
enum {TILE_WATER, TILE_TERRAIN, TILE_FOREST, TILE_BOG} enum {TILE_WATER, TILE_TERRAIN, TILE_FOREST, TILE_BOG}
# tilemap layers # tilemap layers
enum {LAYER_TERRAIN, LAYER_BUILDINGS} enum {LAYER_TERRAIN, LAYER_BUILDINGS}
const TILESET_TERRAIN:TileSet = preload("res://scenes/Chunk.tres") const TILESET_TERRAIN:TileSet = preload("res://scenes/Chunk.tres")
func are_coords_valid(value:int, bounds:Vector2i, errmsg:String) -> bool:
if bounds.x > value or value > bounds.y:
errmsg = errmsg % [value, bounds.x, bounds.y]
push_error(errmsg)
return false
return true
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()]
@ -34,8 +28,20 @@ var map_terrain_data:Array[Array] = [[]]
# preprocess and store exact tile for every map cell to speed up setting tiles # preprocess and store exact tile for every map cell to speed up setting tiles
var map_tile_data:Array[Array] = [[]] var map_tile_data:Array[Array] = [[]]
###################################
# CAMERA SETTINGS #
###################################
# current camera zoom level # current camera zoom level
var CAMERA_ZOOM_LEVEL: float = 1.0 var CAMERA_ZOOM_LEVEL: float = 1.0
var CAMERA_POSITION
# camera movement settings
const CAMERA_MIN_ZOOM_LEVEL: float = 0.1
const CAMERA_MAX_ZOOM_LEVEL: float = 2.0
const CAMERA_ZOOM_FACTOR: float = 0.1
const CAMERA_ZOOM_DURATION: float = 0.1
const CAMERA_PAN_MULTI:float = 2.0
# FILE PATHS # FILE PATHS
const SCENE_PATH:String = "res://scenes/" const SCENE_PATH:String = "res://scenes/"
@ -65,13 +71,6 @@ const TYPE_POWERPLANT:String = "powerplant"
const TYPE_ROADS:String = "roads" const TYPE_ROADS:String = "roads"
const TYPE_DEMOLISH:String = "demolish" const TYPE_DEMOLISH:String = "demolish"
# camera movement settings
const CAMERA_MIN_ZOOM_LEVEL: float = 0.1
const CAMERA_MAX_ZOOM_LEVEL: float = 2.0
const CAMERA_ZOOM_FACTOR: float = 0.1
const CAMERA_ZOOM_DURATION: float = 0.1
const CAMERA_PAN_MULTI:float = 2.0
# city map generation file should have black ground (0,0,0) and white water (1,1,1) # city map generation file should have black ground (0,0,0) and white water (1,1,1)
const GROUND_TILE_COLOR_IN_MAP_FILE: Color = Color(0,0,0,1) 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)

View file

@ -16,11 +16,13 @@ var map_filenames:Array = [
"res://maps/tampere_10x10km_1000px.png", "res://maps/tampere_10x10km_1000px.png",
"res://maps/tampere_10x10km_1024px.png", "res://maps/tampere_10x10km_1024px.png",
"res://maps/varkaus_256x256px_test.png", "res://maps/varkaus_256x256px_test.png",
"res://maps/tampere_256px.png" "res://maps/tampere_256px.png",
"res://maps/tampere_10x10km_4096px.png"
] ]
var map_filename:String = map_filenames[3] var map_filename:String = map_filenames[1]
var _world_generator:WorldGenerator var _world_generator:WorldGenerator
var _chunk_handler:ChunkHandler var _chunk_handler:ChunkHandler
#var _2d_camera:CameraZoom2D
func _init(): func _init():
@ -28,23 +30,27 @@ func _init():
# #Vector2i(Globals.DEFAULT_X_RES, Globals.DEFAULT_Y_RES) # #Vector2i(Globals.DEFAULT_X_RES, Globals.DEFAULT_Y_RES)
# Vector2i(3800,2000) # Vector2i(3800,2000)
# ) # )
pass Globals.CAMERA_POSITION = Vector2(16*256/2, 16*256/2)
#func _process(_delta):
# Globals.CAMERA_POSITION = _2d_camera.position
# 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():
# create a new world and worldgenerator # create a new world and worldgenerator
_world_generator = WorldGenerator.new() _world_generator = WorldGenerator.new()
_chunk_handler = ChunkHandler.new() _chunk_handler = ChunkHandler.new()
add_child(_chunk_handler) #_2d_camera = CameraZoom2D.new()
var result = _world_generator.generate_world(map_filename) # add chunk handler if worldgen was successful
if _world_generator.generate_world(map_filename):
if result: add_child(_chunk_handler)
_chunk_handler.start_handler() #add_child(_2d_camera)
for y in Globals.map_size/Globals.CHUNK_SIZE:
for x in Globals.map_size/Globals.CHUNK_SIZE: # for y in Globals.map_size/Globals.CHUNK_SIZE.y:
if (y + x) % 2 == 0: # for x in Globals.map_size/Globals.CHUNK_SIZE.x:
_chunk_handler.add_chunk(x, y) # #if (y + x) % 2 == 0:
# _chunk_handler.load_chunk(x, y)
else: else:
push_error("World generation failed :-(") push_error("World generation failed :-(")

View file

@ -9,6 +9,14 @@ var directions:Array = [
Vector2i(0,-1), # north Vector2i(0,-1), # north
Vector2i(-1,0) # west Vector2i(-1,0) # west
] ]
func are_coords_valid(value:int, bounds:Vector2i, errmsg:String) -> bool:
if bounds.x > value or value > bounds.y:
errmsg = errmsg % [value, bounds.x, bounds.y]
push_error(errmsg)
return false
return true
func choose_tile(tile:Vector2i, selected, surrounding) -> Array: func choose_tile(tile:Vector2i, selected, surrounding) -> Array:
var surrounding_tiles:Array = [] var surrounding_tiles:Array = []
@ -249,13 +257,13 @@ func smooth_recursively(pos:Vector2i, selected:int, comp:int) -> void:
smooth_recursively(pos, selected, comp) smooth_recursively(pos, selected, comp)
func validate_mapgen_params() -> bool: func validate_mapgen_params() -> bool:
if !Globals.are_coords_valid( if !are_coords_valid(
Globals.map_size, Globals.map_size,
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 !are_coords_valid(
Globals.map_size, Globals.map_size,
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):