Improved component system

This commit is contained in:
Eduard Urbach 2024-02-15 16:28:27 +01:00
parent 8de91eaba1
commit b439919ad0
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
19 changed files with 89 additions and 99 deletions

View File

@ -16,8 +16,21 @@ Therefore the `client/assets` directory needs to be downloaded separately. Downl
- Cloth attachments
- Grass shader
- Water shader
- Loading UI
- Settings UI
## Components
This project makes heavy use of composition via components.
They are implemented with the following rules:
- Components can have **read-only** references to other components.
- Components must not **write** data to another component.
- Components that produce some output must use signals to communicate messages to other components.
In other words, it's allowed for components to have dependencies, but a component can not know where its output is going to be used in the future.
Therefore, outputs must use a signal to communicate the output to an unknown number of receivers.
## License
Please see the [license documentation](https://akyoto.dev/license).

View File

@ -1,18 +1,22 @@
extends Node
func _ready():
# %Logout.success.emit()
pause(true)
%Login.success.connect(on_login)
%Logout.success.connect(on_logout)
# %Login.send_login()
Global.username = "Local"
Global.account_id = "test"
Global.auth_token = "test"
%Login.success.emit()
Global.player = %PlayerAdd.spawn_player(Global.account_id)
var args := OS.get_cmdline_args()
var offline := args.has("--offline")
if offline:
Global.username = "Local"
Global.account_id = "test"
Global.auth_token = "test"
Global.player = %PlayerAdd.spawn_player(Global.account_id)
%Login.success.emit()
else:
%Login.send_login()
func _input(event):
if event.is_action_pressed("toggle_fullscreen"):

View File

@ -1,7 +1,7 @@
class_name Enemy
extends CharacterBody3D
extends Character
signal direction_changed
func set_direction(direction: Vector3):
direction_changed.emit(direction)
func _init():
controller = EnemyController.new()
add_child(controller)
controller.owner = self

View File

@ -3,9 +3,12 @@ extends Controller
var enemy: Enemy
func _init():
name = "Controller"
func _ready():
enemy = owner
func _process(_delta):
var direction := (Global.player.global_position - enemy.global_position).normalized()
enemy.set_direction(direction)
direction_changed.emit(direction)

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=15 format=3 uid="uid://cb2t7bvvf3gwh"]
[gd_scene load_steps=14 format=3 uid="uid://cb2t7bvvf3gwh"]
[ext_resource type="PackedScene" uid="uid://b358op5h1y83m" path="res://assets/slime/Slime.blend" id="1_1h1hj"]
[ext_resource type="Script" path="res://enemy/Enemy.gd" id="1_r5888"]
@ -7,7 +7,6 @@
[ext_resource type="PackedScene" uid="uid://6jpnl6c4fdvo" path="res://player/hud/HUDComponent.tscn" id="3_4jtio"]
[ext_resource type="PackedScene" uid="uid://bfvudkup0xsq4" path="res://enemy/death/DeathComponent.tscn" id="6_68kgw"]
[ext_resource type="PackedScene" uid="uid://dl4vcp04t8iyr" path="res://item/soul/Soul.tscn" id="7_i0rtb"]
[ext_resource type="Script" path="res://enemy/EnemyController.gd" id="8_b7r6l"]
[ext_resource type="PackedScene" uid="uid://c8lifxuvpchu3" path="res://player/visibility/VisibilityComponent.tscn" id="9_ssc7p"]
[sub_resource type="SphereShape3D" id="SphereShape3D_s5ct7"]
@ -95,6 +94,3 @@ hud = NodePath("../HUD")
hud_distance = 50.0
physics_distance = 50.0
render_distance = 100.0
[node name="EnemyController" type="Node" parent="."]
script = ExtResource("8_b7r6l")

View File

@ -23,7 +23,7 @@ func handle_packet(data: PackedByteArray):
var player := spawn_player(player_id)
player.position = server_position
player.set_character_name(player_name)
player.set_player_name(player_name)
func spawn_player(id: String) -> Player:
var player: Player

View File

@ -4,7 +4,7 @@ func _ready():
%PlayerAdd.main_player_spawned.connect(on_main_player_spawned)
func on_main_player_spawned(player: Player):
player.jumped.connect(on_jump)
player.controller.jumped.connect(on_jump)
func on_jump():
var buffer := StreamPeerBuffer.new()
@ -14,4 +14,4 @@ func on_jump():
func handle_packet(data: PackedByteArray):
var player_id := data.get_string_from_ascii()
var player := Global.players.get_player(player_id)
player.jump()
player.controller.jumped.emit()

View File

@ -0,0 +1,4 @@
class_name Character
extends CharacterBody3D
var controller: Controller

View File

@ -1,29 +1,10 @@
class_name Player
extends CharacterBody3D
extends Character
signal skill_used(skill: Skill)
signal dashed
signal jumped
signal name_changed(new_name: String)
signal direction_changed
@export var skills: Array[Skill]
var id: String
var controller: Controller
func use_skill(slot: int):
if get_node("State").current == StateComponent.State.Skill:
return
skill_used.emit(skills[slot])
func jump():
jumped.emit()
func set_direction(direction: Vector3):
direction_changed.emit(direction)
func set_player_name(new_name: String):
name = new_name
name_changed.emit(new_name)
name_changed.emit(new_name)

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=31 format=3 uid="uid://2lcnu3dy54lx"]
[gd_scene load_steps=29 format=3 uid="uid://2lcnu3dy54lx"]
[ext_resource type="Script" path="res://player/Player.gd" id="1_8gebs"]
[ext_resource type="PackedScene" uid="uid://c8j7t4yg7anb0" path="res://assets/female/Female.blend" id="2_8nah6"]
@ -6,9 +6,7 @@
[ext_resource type="Resource" uid="uid://yaq8ui3f6fwa" path="res://skill/slash/slash.tres" id="2_x58e1"]
[ext_resource type="Resource" uid="uid://ba1filjaldakv" path="res://skill/spin/spin.tres" id="3_l76ly"]
[ext_resource type="PackedScene" uid="uid://cgqbkj8wbcatv" path="res://assets/hair/PonyTail.blend" id="3_umw6q"]
[ext_resource type="FontFile" uid="uid://b7mov13kwi8u8" path="res://assets/font/ubuntu_nf_regular.ttf" id="4_76ehj"]
[ext_resource type="Skin" uid="uid://bbqyiue1vj37f" path="res://assets/hoodie/Hoodie_Skin.tres" id="4_b1tg1"]
[ext_resource type="Resource" uid="uid://1vmnijk8ap6b" path="res://skill/thrust/thrust.tres" id="4_s8cf2"]
[ext_resource type="ArrayMesh" uid="uid://dbmluwi2atit" path="res://assets/hoodie/Hoodie_Mesh.res" id="5_mkrgn"]
[ext_resource type="Resource" uid="uid://cnusbw23jf672" path="res://skill/dash/dash.tres" id="5_pnues"]
[ext_resource type="PackedScene" uid="uid://6jpnl6c4fdvo" path="res://player/hud/HUDComponent.tscn" id="7_fwgtd"]
@ -38,7 +36,6 @@ height = 1.6
collision_layer = 512
collision_mask = 769
script = ExtResource("1_8gebs")
skills = Array[Resource("res://skill/Skill.gd")]([ExtResource("2_x58e1"), ExtResource("3_l76ly"), ExtResource("4_s8cf2"), ExtResource("5_pnues")])
[node name="Model" type="Node3D" parent="."]
@ -77,19 +74,6 @@ transform = Transform3D(-4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, 0, 1, 0.1976
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.8, 0)
shape = SubResource("CapsuleShape3D_2f50n")
[node name="Label" type="Label3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0)
visible = false
pixel_size = 0.0005
billboard = 1
double_sided = false
alpha_antialiasing_mode = 1
outline_modulate = Color(0.0627451, 0.0627451, 0.0627451, 0.498039)
text = "Player"
font = ExtResource("4_76ehj")
font_size = 256
outline_size = 32
[node name="HUD" parent="." node_paths=PackedStringArray("health") instance=ExtResource("7_fwgtd")]
layers = 512
health = NodePath("../Health")
@ -121,6 +105,7 @@ libraries = {
}
[node name="Skills" parent="." instance=ExtResource("14_6idcf")]
skills = Array[Resource("res://skill/Skill.gd")]([ExtResource("2_x58e1"), ExtResource("3_l76ly"), null, ExtResource("5_pnues")])
[node name="State" parent="." instance=ExtResource("28_0i0of")]

View File

@ -5,7 +5,7 @@ var player: AnimationPlayer
var state: StateComponent
func _ready():
state = owner.find_child("State")
state = owner.get_node("State")
state.transitioned.connect(on_transition)
player = $AnimationPlayer

View File

@ -1,2 +1,6 @@
class_name Controller
extends Node
extends Node
signal direction_changed(direction: Vector3)
signal jumped
signal used_skill(slot: int)

View File

@ -1,32 +1,34 @@
class_name PlayerController
extends Controller
## The character that we're controlling.
var player: Player
## We need the movement component to check if we can perform certain actions.
var movement: MovementComponent
var skills: SkillsComponent
func _init(new_player: Player):
player = new_player
movement = player.find_child("Movement")
movement = player.get_node("Movement")
skills = player.get_node("Skills")
name = "Controller"
func _unhandled_input(event):
func _unhandled_input(event: InputEvent):
if Global.interacting_with_ui:
return
# Calculate the direction
update_direction()
update_actions(event)
func update_direction():
var move := Input.get_vector("move_left", "move_right", "move_forward", "move_backward")
var direction := (Global.camera.global_basis * Vector3(move.x, 0, move.y))
direction.y = 0
direction = direction.normalized()
# Notify components
player.set_direction(direction)
direction_changed.emit(direction)
func update_actions(event: InputEvent):
if event.is_action_pressed("jump") && movement && movement.can_jump():
player.jump()
jumped.emit()
for i in range(4):
if event.is_action_pressed("skill_%d" % (i + 1)):
player.use_skill(i)
used_skill.emit(i)

View File

@ -9,6 +9,7 @@ var server_position: Vector3
func _init(new_player: Player):
player = new_player
name = "Controller"
func _ready():
server_position = player.position
@ -17,9 +18,9 @@ func _process(_delta):
var move := server_position - player.position
move.y = 0.0
if move.length_squared() < 0.01:
player.set_direction(Vector3.ZERO)
if move.length_squared() < 0.02:
direction_changed.emit(Vector3.ZERO)
return
var direction := Vector3(move.x, 0, move.z).normalized()
player.set_direction(direction)
direction_changed.emit(direction)

View File

@ -9,7 +9,7 @@ var health: HealthComponent
func _ready():
health = owner.health
health.value_changed.connect(on_health_changed)
func on_health_changed():
value = health.value / health.max_value

View File

@ -5,18 +5,14 @@ extends Node
@export var jump_velocity := 4.5
@export var deceleration := 0.75
var body: CharacterBody3D
var body: Character
var direction: Vector3
var gravity: float
func _ready():
if owner.has_signal("direction_changed"):
owner.direction_changed.connect(on_direction_changed)
if owner.has_signal("jumped"):
owner.jumped.connect(jump)
body = owner as CharacterBody3D
body = owner as Character
body.controller.direction_changed.connect(on_direction_changed)
body.controller.jumped.connect(jump)
gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
func _physics_process(delta):

View File

@ -9,17 +9,13 @@ var angle: float
func _ready():
assert(root, "Rotation root needs to be set")
if owner.has_signal("direction_changed"):
owner.direction_changed.connect(on_direction_changed)
update_configuration_warnings()
owner.controller.direction_changed.connect(on_direction_changed)
func _process(delta):
if direction != Vector3.ZERO:
angle = atan2(direction.x, direction.z)
root.rotation.y = lerp_angle(root.rotation.y, angle, rotation_speed * delta)
func on_direction_changed(new_direction: Vector3):
direction = new_direction
direction = new_direction
if direction != Vector3.ZERO:
angle = atan2(direction.x, direction.z)

View File

@ -1,15 +1,20 @@
class_name SkillsComponent
extends Node
var player: Player
@export var skills: Array[Skill]
func _ready():
player = owner
player.skill_used.connect(use_skill)
var character := owner as Character
character.controller.used_skill.connect(use_skill)
func use_skill(slot: int):
if slot < 0 || slot >= skills.size():
return
var skill := skills[slot]
func use_skill(skill: Skill):
if !skill:
return
var scene := skill.scene.instantiate()
player.add_child(scene)
owner.add_child(scene)

View File

@ -17,7 +17,7 @@ var _current: State
func _ready():
current = State.Idle
movement = owner.find_child("Movement")
movement = owner.get_node("Movement")
func _process(_delta):
if current == State.Skill: