Refactor the way Blocks are loaded. Now a node to avoid cyclic references.

master
Marc Gilleron 2020-08-16 02:09:37 +01:00
parent d01056015c
commit f9de1b5fcb
10 changed files with 101 additions and 90 deletions

View File

@ -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()

View File

@ -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):

View File

@ -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 )

View File

@ -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

View File

@ -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 )

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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)