Improved component system
This commit is contained in:
parent
8de91eaba1
commit
b439919ad0
13
README.md
13
README.md
@ -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).
|
||||
|
@ -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"):
|
||||
|
@ -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
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
4
client/player/Character.gd
Normal file
4
client/player/Character.gd
Normal file
@ -0,0 +1,4 @@
|
||||
class_name Character
|
||||
extends CharacterBody3D
|
||||
|
||||
var controller: Controller
|
@ -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)
|
@ -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")]
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,2 +1,6 @@
|
||||
class_name Controller
|
||||
extends Node
|
||||
extends Node
|
||||
|
||||
signal direction_changed(direction: Vector3)
|
||||
signal jumped
|
||||
signal used_skill(slot: int)
|
@ -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)
|
||||
|
@ -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)
|
@ -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
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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)
|
@ -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)
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user