Merge Kenny's City Builder Starter Kit

This commit is contained in:
Tony Bark 2023-12-14 01:41:21 -05:00
parent dd04a01651
commit 36edaaf17b
510 changed files with 2236 additions and 7925 deletions

156
scripts/3d/builder3d.gd Normal file
View file

@ -0,0 +1,156 @@
extends Node3D
@export var structures: Array[Structure] = []
var map:DataMap
var index:int = 0 # Index of structure being built
@export var selector:Node3D # The 'cursor'
@export var selector_container:Node3D # Node that holds a preview of the structure
@export var view_camera:Camera3D # Used for raycasting mouse
@export var gridmap:GridMap
@export var cash_display:Label
var plane:Plane # Used for raycasting mouse
func _ready():
map = DataMap.new()
plane = Plane(Vector3.UP, Vector3.ZERO)
# Create new MeshLibrary dynamically, can also be done in the editor
# See: https://docs.godotengine.org/en/stable/tutorials/3d/using_gridmaps.html
var mesh_library = MeshLibrary.new()
for structure in structures:
var id = mesh_library.get_last_unused_item_id()
mesh_library.create_item(id)
mesh_library.set_item_mesh(id, get_mesh(structure.model))
mesh_library.set_item_mesh_transform(id, Transform3D())
gridmap.mesh_library = mesh_library
update_structure()
update_cash()
func _process(delta):
# Controls
action_rotate() # Rotates selection 90 degrees
action_structure_toggle() # Toggles between structures
action_save() # Saving
action_load() # Loading
# Map position based on mouse
var world_position = plane.intersects_ray(
view_camera.project_ray_origin(get_viewport().get_mouse_position()),
view_camera.project_ray_normal(get_viewport().get_mouse_position()))
var gridmap_position = Vector3(round(world_position.x), 0, round(world_position.z))
selector.position = lerp(selector.position, gridmap_position, delta * 40)
action_build(gridmap_position)
action_demolish(gridmap_position)
# Retrieve the mesh from a PackedScene, used for dynamically creating a MeshLibrary
func get_mesh(packed_scene):
var scene_state:SceneState = packed_scene.get_state()
for i in range(scene_state.get_node_count()):
if(scene_state.get_node_type(i) == "MeshInstance3D"):
for j in scene_state.get_node_property_count(i):
var prop_name = scene_state.get_node_property_name(i, j)
if prop_name == "mesh":
var prop_value = scene_state.get_node_property_value(i, j)
return prop_value.duplicate()
# Build (place) a structure
func action_build(gridmap_position):
if Input.is_action_just_pressed("build"):
var previous_tile = gridmap.get_cell_item(gridmap_position)
gridmap.set_cell_item(gridmap_position, index, gridmap.get_orthogonal_index_from_basis(selector.basis))
if previous_tile != index:
map.cash -= structures[index].price
update_cash()
# Demolish (remove) a structure
func action_demolish(gridmap_position):
if Input.is_action_just_pressed("demolish"):
gridmap.set_cell_item(gridmap_position, -1)
# Rotates the 'cursor' 90 degrees
func action_rotate():
if Input.is_action_just_pressed("rotate"):
selector.rotate_y(deg_to_rad(90))
# Toggle between structures to build
func action_structure_toggle():
if Input.is_action_just_pressed("structure_next"):
index = wrap(index + 1, 0, structures.size())
if Input.is_action_just_pressed("structure_previous"):
index = wrap(index - 1, 0, structures.size())
update_structure()
# Update the structure visual in the 'cursor'
func update_structure():
# Clear previous structure preview in selector
for n in selector_container.get_children():
selector_container.remove_child(n)
# Create new structure preview in selector
var _model = structures[index].model.instantiate()
selector_container.add_child(_model)
_model.position.y += 0.25
func update_cash():
cash_display.text = "$" + str(map.cash)
# Saving/load
func action_save():
if Input.is_action_just_pressed("save"):
print("Saving map...")
map.structures.clear()
for cell in gridmap.get_used_cells():
var data_structure:DataStructure = DataStructure.new()
data_structure.position = Vector2i(cell.x, cell.z)
data_structure.orientation = gridmap.get_cell_item_orientation(cell)
data_structure.structure = gridmap.get_cell_item(cell)
map.structures.append(data_structure)
ResourceSaver.save(map, "user://map.res")
func action_load():
if Input.is_action_just_pressed("load"):
print("Loading map...")
gridmap.clear()
map = ResourceLoader.load("user://map.res")
if not map:
map = DataMap.new()
for cell in map.structures:
gridmap.set_cell_item(Vector3i(cell.position.x, 0, cell.position.y), cell.structure, cell.orientation)
update_cash()

5
scripts/3d/data_map.gd Normal file
View file

@ -0,0 +1,5 @@
extends Resource
class_name DataMap
@export var cash:int = 10000
@export var structures:Array[DataStructure]

View file

@ -0,0 +1,6 @@
extends Resource
class_name DataStructure
@export var position:Vector2i
@export var orientation:int
@export var structure:int

8
scripts/3d/structure.gd Normal file
View file

@ -0,0 +1,8 @@
extends Resource
class_name Structure
@export_subgroup("Model")
@export var model:PackedScene # Model of the structure
@export_subgroup("Gameplay")
@export var price:int # Price of the structure when building

49
scripts/3d/view.gd Normal file
View file

@ -0,0 +1,49 @@
extends Node3D
var camera_position:Vector3
var camera_rotation:Vector3
@onready var camera = $Camera
func _ready():
camera_rotation = rotation_degrees # Initial rotation
pass
func _process(delta):
# Set position and rotation to targets
position = position.lerp(camera_position, delta * 8)
rotation_degrees = rotation_degrees.lerp(camera_rotation, delta * 6)
handle_input(delta)
# Handle input
func handle_input(_delta):
# Rotation
var input := Vector3.ZERO
input.x = Input.get_axis("camera_left", "camera_right")
input.z = Input.get_axis("camera_forward", "camera_back")
input = input.rotated(Vector3.UP, rotation.y).normalized()
camera_position += input / 4
# Back to center
if Input.is_action_pressed("camera_center"):
camera_position = Vector3()
func _input(event):
# Rotate camera using mouse (hold 'middle' mouse button)
if event is InputEventMouseMotion:
if Input.is_action_pressed("camera_rotate"):
camera_rotation += Vector3(0, -event.relative.x / 10, 0)