From f9de1b5fcbd5b7604a3911181a1a65a6939b6638 Mon Sep 17 00:00:00 2001 From: Marc Gilleron Date: Sun, 16 Aug 2020 02:09:37 +0100 Subject: [PATCH] Refactor the way Blocks are loaded. Now a node to avoid cyclic references. --- project/blocky_game/blocks/block.gd | 23 ++++++ project/blocky_game/blocks/blocks.gd | 73 ++++++++----------- project/blocky_game/blocks/blocks.tres | 6 -- project/blocky_game/blocks/rail/rail.gd | 26 +++---- project/blocky_game/blocky_game.tscn | 7 +- .../blocky_game/gui/block_sprite_generator.gd | 4 + project/blocky_game/gui/hotbar/hotbar.gd | 11 ++- project/blocky_game/gui/hotbar/hotbar_slot.gd | 7 +- .../blocky_game/player/avatar_interaction.gd | 23 +++--- project/blocky_game/water.gd | 11 +-- 10 files changed, 101 insertions(+), 90 deletions(-) create mode 100644 project/blocky_game/blocks/block.gd delete mode 100644 project/blocky_game/blocks/blocks.tres diff --git a/project/blocky_game/blocks/block.gd b/project/blocky_game/blocks/block.gd new file mode 100644 index 0000000..37143be --- /dev/null +++ b/project/blocky_game/blocks/block.gd @@ -0,0 +1,23 @@ +extends Node + +# Note: can't import `Blocks` here, otherwise it's a cylcic ref... +# And I don't want to pollute global space of all the demos with this + +# Info packed into a class, +# to not pollute namespace of all the block scripts that could inherit from it +class BaseInfo: + var id := 0 + var name := "" + var gui_model_path := "" + var directory := "" + var rotation_type := 0 + var sprite_texture : Texture + var transparent := false + var backface_culling := true + # TODO Rename `variants` + var voxels := [] + + +var base_info := BaseInfo.new() + + diff --git a/project/blocky_game/blocks/blocks.gd b/project/blocky_game/blocks/blocks.gd index 48261f4..244c19c 100644 --- a/project/blocky_game/blocks/blocks.gd +++ b/project/blocky_game/blocks/blocks.gd @@ -1,5 +1,10 @@ -extends Resource +# Container for all block types. +# It is not a resource because it references scripts that can depend on it, +# causing cycles. So instead, it's more convenient to make it a node in the tree. +# IMPORTANT: Needs to be first in tree. Other nodes may use it in _ready(). +extends Node +const Block = preload("./block.gd") const Util = preload("res://common/util.gd") const ROTATION_TYPE_NONE = 0 @@ -30,20 +35,6 @@ const ROOT = "res://blocky_game/blocks" const AIR_ID = 0 - -class Block: - var id := 0 - var name := "" - var gui_model_path := "" - var directory := "" - var rotation_type := ROTATION_TYPE_NONE - var sprite_texture : Texture - var transparent := false - var backface_culling := true - var voxels := [] - var behavior = null - - class RawMapping: var block_id := 0 var variant_index := 0 @@ -165,8 +156,9 @@ func get_model_library() -> VoxelLibrary: func get_block_by_name(block_name: String) -> Block: for b in _blocks: - if b.name == block_name: + if b.base_info.name == block_name: return b + assert(false) return null @@ -191,8 +183,19 @@ func _create_block(params: Dictionary): "behavior": "" }) - var block = Block.new() - block.id = len(_blocks) + var block : Block + if params.behavior != "": + # Block with special behavior + var behavior_path := str(ROOT, "/", params.directory, "/", params.behavior) + var behavior = load(behavior_path) + block = behavior.new() + else: + # Generic + block = Block.new() + + # Fill in base info + var base_info = block.base_info + base_info.id = len(_blocks) for i in len(params.voxels): var vname = params.voxels[i] @@ -202,28 +205,25 @@ func _create_block(params: Dictionary): assert(id != -1) params.voxels[i] = id var rm = RawMapping.new() - rm.block_id = block.id + rm.block_id = base_info.id rm.variant_index = i if id >= len(_raw_mappings): _raw_mappings.resize(id + 1) _raw_mappings[id] = rm - block.name = params.name - block.directory = params.directory - block.rotation_type = params.rotation_type - block.voxels = params.voxels - block.transparent = params.transparent - block.backface_culling = params.backface_culling - if block.directory != "": - block.gui_model_path = str(ROOT, "/", params.directory, "/", params.gui_model) + base_info.name = params.name + base_info.directory = params.directory + base_info.rotation_type = params.rotation_type + base_info.voxels = params.voxels + base_info.transparent = params.transparent + base_info.backface_culling = params.backface_culling + if base_info.directory != "": + base_info.gui_model_path = str(ROOT, "/", params.directory, "/", params.gui_model) var sprite_path = str(ROOT, "/", params.directory, "/", params.name, "_sprite.png") - block.sprite_texture = load(sprite_path) - - if params.behavior != "": - var behavior_path := str(ROOT, "/", params.directory, "/", params.behavior) - call_deferred("_load_behavior", block, behavior_path) + base_info.sprite_texture = load(sprite_path) _blocks.append(block) + add_child(block) func _notification(what): @@ -232,15 +232,6 @@ func _notification(what): print("Deleting blocks.gd") -# TODO Find a better design. -# Workaround for now... Godot can't finish loading blocks.tres, -# because it has to load and reference block behavior scripts, which themselves -# are const-referencing blocks.gd... -func _load_behavior(block: Block, behavior_path: String): - var b = load(behavior_path) - block.behavior = b.new(block) - - static func _defaults(d, defaults): for k in defaults: if not d.has(k): diff --git a/project/blocky_game/blocks/blocks.tres b/project/blocky_game/blocks/blocks.tres deleted file mode 100644 index 84d4e67..0000000 --- a/project/blocky_game/blocks/blocks.tres +++ /dev/null @@ -1,6 +0,0 @@ -[gd_resource type="Resource" load_steps=2 format=2] - -[ext_resource path="res://blocky_game/blocks/blocks.gd" type="Script" id=1] - -[resource] -script = ExtResource( 1 ) diff --git a/project/blocky_game/blocks/rail/rail.gd b/project/blocky_game/blocks/rail/rail.gd index 943905f..ca3f443 100644 --- a/project/blocky_game/blocks/rail/rail.gd +++ b/project/blocky_game/blocks/rail/rail.gd @@ -1,7 +1,7 @@ +extends "../block.gd" -# TODO Check if this import causes a cyclic reference, hopefully not -const Blocks = preload("../blocks.tres") const Util = preload("res://common/util.gd") +const Blocks = preload("../blocks.gd") const _STRAIGHT = 0 const _TURN = 2 @@ -63,15 +63,8 @@ const _auto_orient_table = [ # -x | +x | -z | +z ] -var _variants : Array -var _rail_block_id : int - - -func _init(b): - # TODO Can't store whole block info, it would cause a cyclic reference. - # Need to think about a better design eventually. - _variants = b.voxels - _rail_block_id = b.id +func _get_blocks() -> Blocks: + return get_parent() as Blocks func place(voxel_tool: VoxelTool, pos: Vector3, look_dir: Vector3): @@ -94,7 +87,7 @@ func place(voxel_tool: VoxelTool, pos: Vector3, look_dir: Vector3): # Orient and place rail var variant_index := _get_auto_oriented_variant(pos, available_neighbors, look_dir) - voxel_tool.set_voxel(pos, _variants[variant_index]) + voxel_tool.set_voxel(pos, base_info.voxels[variant_index]) # Orient neighbors for di in available_neighbors: @@ -109,7 +102,7 @@ func place(voxel_tool: VoxelTool, pos: Vector3, look_dir: Vector3): var nn := _find_neighbor_rails(voxel_tool, neighbor.pos, connected_dirs) var neighbor_variant_index := _get_auto_oriented_variant(neighbor.pos, nn, Vector3()) - voxel_tool.set_voxel(neighbor.pos, _variants[neighbor_variant_index]) + voxel_tool.set_voxel(neighbor.pos, base_info.voxels[neighbor_variant_index]) static func _get_auto_oriented_variant( @@ -138,6 +131,7 @@ static func _get_auto_oriented_variant( func _find_neighbor_rails(voxel_tool: VoxelTool, pos: Vector3, direction_list: Array) -> Dictionary: var neighbors := {} + var blocks := _get_blocks() # We only want to keep one rail per direction. # Priority is given to rails at the same level, then upward, then downward. @@ -150,13 +144,13 @@ func _find_neighbor_rails(voxel_tool: VoxelTool, pos: Vector3, direction_list: A var npos := pos + Blocks.get_y_dir_vec(di) npos.y += dy var nv := voxel_tool.get_voxel(npos) - var nrm := Blocks.get_raw_mapping(nv) + var nrm := blocks.get_raw_mapping(nv) - if nrm.block_id == _rail_block_id: + if nrm.block_id == base_info.id: var group := _get_group_from_index(nrm.variant_index) # Decode rail neighbors[di] = { - #"id": nrm.block_id, + "id": nrm.block_id, "group": group, "rotation": nrm.variant_index - group, "pos": npos diff --git a/project/blocky_game/blocky_game.tscn b/project/blocky_game/blocky_game.tscn index e7546d6..917874b 100644 --- a/project/blocky_game/blocky_game.tscn +++ b/project/blocky_game/blocky_game.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=15 format=2] +[gd_scene load_steps=16 format=2] [ext_resource path="res://blocky_game/blocks/voxel_library.tres" type="VoxelLibrary" id=1] [ext_resource path="res://blocky_game/blocks/terrain_material.tres" type="Material" id=2] @@ -11,6 +11,7 @@ [ext_resource path="res://blocky_game/blocks/terrain_material_foliage.tres" type="Material" id=9] [ext_resource path="res://blocky_game/random_ticks.gd" type="Script" id=10] [ext_resource path="res://blocky_game/water.gd" type="Script" id=11] +[ext_resource path="res://blocky_game/blocks/blocks.gd" type="Script" id=12] [sub_resource type="ProceduralSky" id=1] sky_top_color = Color( 0.268204, 0.522478, 0.847656, 1 ) @@ -47,6 +48,9 @@ directory = "res://blocky_game/save" [node name="Main" type="Node"] script = ExtResource( 5 ) +[node name="Blocks" type="Node" parent="."] +script = ExtResource( 12 ) + [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource( 2 ) @@ -55,6 +59,7 @@ stream = SubResource( 3 ) voxel_library = ExtResource( 1 ) viewer_path = NodePath("../CharacterAvatar") generate_collisions = false +run_stream_in_editor = false material/0 = ExtResource( 2 ) material/1 = ExtResource( 3 ) material/2 = ExtResource( 9 ) diff --git a/project/blocky_game/gui/block_sprite_generator.gd b/project/blocky_game/gui/block_sprite_generator.gd index a62cdab..939aa60 100644 --- a/project/blocky_game/gui/block_sprite_generator.gd +++ b/project/blocky_game/gui/block_sprite_generator.gd @@ -15,6 +15,10 @@ var _current_block_id := -1 var _blocks := Blocks.new() +func _ready(): + add_child(_blocks) + + func _process(_delta): print("Block ", _current_block_id) diff --git a/project/blocky_game/gui/hotbar/hotbar.gd b/project/blocky_game/gui/hotbar/hotbar.gd index 5eec2c8..c1ce616 100644 --- a/project/blocky_game/gui/hotbar/hotbar.gd +++ b/project/blocky_game/gui/hotbar/hotbar.gd @@ -1,12 +1,11 @@ extends CenterContainer -const Blocks = preload("../../blocks/blocks.tres") - onready var _selected_frame = $HBoxContainer/HotbarSlot/HotbarSlotSelect onready var _slot_container = $HBoxContainer +onready var _block_types = get_node("/root/Main/Blocks") -var _inventory = [1, 2, 3, 4, 5, 6, 7, 8, 9] -var _inventory_index = 0 +var _inventory := [1, 2, 3, 4, 5, 6, 7, 8, 9] +var _inventory_index := 0 func _ready(): @@ -25,8 +24,8 @@ func select_slot(i: int): var block_id = _inventory[_inventory_index] if block_id != -1: - var block = Blocks.get_block(block_id) - print("Inventory select ", block.name) + var block = _block_types.get_block(block_id) + print("Inventory select ", block.base_info.name) _selected_frame.get_parent().remove_child(_selected_frame) var slot = _slot_container.get_child(i) diff --git a/project/blocky_game/gui/hotbar/hotbar_slot.gd b/project/blocky_game/gui/hotbar/hotbar_slot.gd index 092d268..3f03078 100644 --- a/project/blocky_game/gui/hotbar/hotbar_slot.gd +++ b/project/blocky_game/gui/hotbar/hotbar_slot.gd @@ -1,15 +1,14 @@ extends Control -const Blocks = preload("../../blocks/blocks.tres") - onready var _texture_rect = $TextureRect +onready var _block_types = get_node("/root/Main/Blocks") func set_block_id(id: int): if id == -1: _texture_rect.texture = null else: - var block = Blocks.get_block(id) - _texture_rect.texture = block.sprite_texture + var block = _block_types.get_block(id) + _texture_rect.texture = block.base_info.sprite_texture diff --git a/project/blocky_game/player/avatar_interaction.gd b/project/blocky_game/player/avatar_interaction.gd index 0eb9053..e4bc1c9 100644 --- a/project/blocky_game/player/avatar_interaction.gd +++ b/project/blocky_game/player/avatar_interaction.gd @@ -1,7 +1,7 @@ extends Node const Util = preload("res://common/util.gd") -const Blocks = preload("../blocks/blocks.tres") +const Blocks = preload("../blocks/blocks.gd") const COLLISION_LAYER_AVATAR = 2 @@ -23,6 +23,8 @@ export(Material) var cursor_material = null # TODO Eventually invert these dependencies onready var _head : Camera = get_parent().get_node("Camera") onready var _hotbar = get_node("../HotBar") +onready var _block_types : Blocks = get_node("/root/Main/Blocks") +onready var _water_updater = get_node("../../Water") var _terrain = null var _terrain_tool = null @@ -94,7 +96,7 @@ func _physics_process(delta): print("Can't place here!") elif _action_pick: - var rm := Blocks.get_raw_mapping(hit_raw_id) + var rm := _block_types.get_raw_mapping(hit_raw_id) _hotbar.try_select_slot_by_block_id(rm.block_id) _action_place = false @@ -139,33 +141,32 @@ func _can_place_voxel_at(pos: Vector3): func _place_single_block(pos: Vector3, block_id: int): - var block := Blocks.get_block(block_id) + var block := _block_types.get_block(block_id) var voxel_id := 0 var look_dir := -_head.get_transform().basis.z - match block.rotation_type: + match block.base_info.rotation_type: Blocks.ROTATION_TYPE_NONE: - voxel_id = block.voxels[0] + voxel_id = block.base_info.voxels[0] Blocks.ROTATION_TYPE_AXIAL: var axis := Util.get_longest_axis(look_dir) - voxel_id = block.voxels[axis] + voxel_id = block.base_info.voxels[axis] Blocks.ROTATION_TYPE_Y: var rot := Blocks.get_y_rotation_from_look_dir(look_dir) - voxel_id = block.voxels[rot] + voxel_id = block.base_info.voxels[rot] Blocks.ROTATION_TYPE_CUSTOM_BEHAVIOR: - block.behavior.place(_terrain_tool, pos, look_dir) + block.place(_terrain_tool, pos, look_dir) _: # Unknown value assert(false) - if block.rotation_type != Blocks.ROTATION_TYPE_CUSTOM_BEHAVIOR: + if block.base_info.rotation_type != Blocks.ROTATION_TYPE_CUSTOM_BEHAVIOR: _place_single_voxel(pos, voxel_id) - var updater = get_node("../../Water") - updater.schedule(pos) + _water_updater.schedule(pos) func _place_single_voxel(pos: Vector3, type: int): diff --git a/project/blocky_game/water.gd b/project/blocky_game/water.gd index c8e0e7a..ca14c23 100644 --- a/project/blocky_game/water.gd +++ b/project/blocky_game/water.gd @@ -1,6 +1,6 @@ extends Node -const Blocks = preload("./blocks/blocks.tres") +const Blocks = preload("./blocks/blocks.gd") const MAX_UPDATES_PER_FRAME = 64 const INTERVAL_SECONDS = 0.2 @@ -15,8 +15,9 @@ const _spread_directions = [ onready var _terrain : VoxelTerrain = get_node("../VoxelTerrain") onready var _terrain_tool := _terrain.get_voxel_tool() +onready var _blocks : Blocks = get_node("../Blocks") -# TODO An efficient Queue data structure would ne NICE +# TODO An efficient Queue data structure would be NICE var _update_queue := [] var _process_queue := [] var _process_index := 0 @@ -29,7 +30,7 @@ var _time_before_next_process := 0.0 func _ready(): _terrain_tool.set_channel(VoxelBuffer.CHANNEL_TYPE) - var water = Blocks.get_block_by_name("water") + var water = _blocks.get_block_by_name("water").base_info _water_id = water.id _water_full = water.voxels[0] _water_top = water.voxels[1] @@ -84,7 +85,7 @@ func _swap_queues(): func _process_cell(pos: Vector3): var v := _terrain_tool.get_voxel(pos) - var rm := Blocks.get_raw_mapping(v) + var rm := _blocks.get_raw_mapping(v) if rm.block_id != _water_id: # Water got removed in the meantime @@ -107,7 +108,7 @@ func _fill_with_water(pos: Vector3): var below := pos - Vector3(0, 1, 0) var above_v := _terrain_tool.get_voxel(above) var below_v := _terrain_tool.get_voxel(below) - var above_rm := Blocks.get_raw_mapping(above_v) + var above_rm := _blocks.get_raw_mapping(above_v) # Make sure the top has the surface model if above_rm.block_id == _water_id: _terrain_tool.set_voxel(pos, _water_full)