Changed Focus Handling to que-based pattern, WIP

This commit is contained in:
betalars 2023-04-22 15:11:10 +02:00
parent 04937e270e
commit e1c93f7be0
10 changed files with 434 additions and 63 deletions

View File

@ -0,0 +1,21 @@
extends Area3D
var has_mouse: bool = false
var has_focus: bool = false:
set(focus):
_on_mouse_entered()
$UiWrapper/UiSprite/SubViewport/Collectable_ui.has_focus = focus
has_focus = focus
# Called when the node enters the scene tree for the first time.
func _ready():
connect("mouse_entered", Callable(self, "_on_mouse_entered"))
func _on_mouse_entered():
input_ray_pickable = false
$UiWrapper.show()
$UiWrapper/UiSprite/SubViewport/Collectable_ui.show()
has_mouse = true
func _on_mouse_exited():
assert(false)

View File

@ -38,5 +38,10 @@ func _update_scene(new_mode) -> int:
emit_signal("freeze")
return new_mode
func _unhandled_input(event):
if event is InputEventMouseButton:
if event.pressed:
print("passed")
#State.pass_focus_to($PlayerController)

View File

@ -1,8 +1,7 @@
extends Area3D
@onready var s: Sprite3D = $Sprite3D
@onready var v: SubViewport = $Sprite3D/SubViewport
@onready var sprite: Sprite3D = $UiSprite
@onready var viewport: SubViewport = $UiSprite/SubViewport
func _process(delta):
var camera = get_viewport().get_camera_3d()
@ -16,24 +15,26 @@ func _process(delta):
func _unhandled_input(event):
if event is InputEventMouse:
# Handled via _on_input_event.
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_RIGHT:
assert(false)
return
v.push_input(event)
viewport.push_input(event)
func _on_input_event(_camera: Camera3D, event: InputEvent, pos: Vector3, _normal: Vector3, _shape_idx: int):
# Position of the event in Sprite3D local coordinates.
var texture_3d_position = s.get_global_transform().affine_inverse() * pos
var texture_3d_position = sprite.get_global_transform().affine_inverse() * pos
#if !is_zero_approx(texture_3d_position.z):
# # Discard event because event didn't happen on the side of the Sprite3D.
# return
# Position of the event relative to the texture.
var texture_position: Vector2 = Vector2(texture_3d_position.x, -texture_3d_position.y) / s.pixel_size - s.get_item_rect().position
var texture_position: Vector2 = Vector2(texture_3d_position.x, -texture_3d_position.y) / sprite.pixel_size - sprite.get_item_rect().position
# Send mouse event.
var e: InputEvent = event.duplicate()
if e is InputEventMouse:
e.set_position(texture_position)
e.set_global_position(texture_position)
v.push_input(e)
viewport.push_input(e)
func _on_button_pressed():
print("Button pressed")

View File

@ -0,0 +1,27 @@
@tool
extends Button
func hide():
if visible == true:
var tween:Tween = create_tween()
custom_minimum_size = get_minimum_size()
var tmp = text
text = ""
tween.tween_property(self, "custom_minimum_size", Vector2(size.x, 0), 0.2)
update_minimum_size()
await tween.finished
visible = false
text = tmp
update_minimum_size()
func show():
if visible == false:
var tmp = text
var tween:Tween = create_tween()
tween.tween_property(self, "custom_minimum_size", get_minimum_size(), 0.2)
text = ""
update_minimum_size()
visible = true
await tween.finished
text = tmp

View File

@ -0,0 +1,26 @@
[gd_scene format=3 uid="uid://52mr50b01ibd"]
[node name="Control" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Button" type="Button" parent="."]
layout_mode = 0
offset_left = 495.0
offset_top = 163.0
offset_right = 634.0
offset_bottom = 235.0
text = "small boi"
[node name="Button2" type="Button" parent="."]
visible = false
layout_mode = 0
offset_left = 441.0
offset_top = 119.0
offset_right = 694.0
offset_bottom = 320.0
text = "big boi"

View File

@ -0,0 +1,105 @@
@tool
extends CenterContainer
class_name Collectable_Ui
@export var collapsed = true:
set(collapse):
if is_inside_tree():
if State.reduce_motion:
collapsed = false
return
if collapse and not collapsed:
if is_inside_tree():
_hide_buttons()
collapsed = collapse
elif not collapse and collapsed:
if is_inside_tree():
_show_buttons()
collapsed = collapse
@export var is_story: bool = false
@export var has_focus: bool = false:
set(focused):
if has_focus == focused: return
if focused:
has_focus = State.request_focus(self)
if has_focus:
collapsed = false
if collected:
$Panel/Content/Buttons/VBoxContainer/put_back.grab_focus()
else:
$Panel/Content/Buttons/VBoxContainer/collect_or_listen.grab_focus()
elif has_focus:
has_focus = false
State.drop_own_focus(self)
get_viewport().gui_release_focus()
hide()
if not visible:
show()
@export var collected: bool = false:
set(set_collected):
collected = set_collected
if set_collected:
$Panel/Content/Buttons/VBoxContainer/put_back.show()
if is_story:
$Content/Buttons/VBoxContainer/put_back.disabled = true
$Content/Buttons/VBoxContainer/collect_or_listen.text = "listen again"
if State.allow_skipping:
$Content/Buttons/VBoxContainer/skip.text = "discard cards (skip)"
else:
$Content/Buttons/VBoxContainer/collect_or_listen.disabled = true
$Content/Buttons/VBoxContainer/put_back.show()
else:
$Content/Buttons/VBoxContainer/collect_or_listen.disabled = false
@export var skipped: bool = false
@export var item_name: String = "":
set(new_name):
item_name = new_name
$Content/Name.text = new_name
@export var content_notes: String = "":
set(new_notes):
content_notes = new_notes
$Content/Name.text = new_notes
# Called when the node enters the scene tree for the first time.
func _ready():
#$Panel/Content/ContentNotes.visible = State.show_content_notes
#$Panel/Content/Buttons/VBoxContainer/Summary.visible = State.provide_summaries
#$Panel/Content/Buttons/VBoxContainer/skip.visible = State.allow_skipping
if visible and not collapsed: _show_buttons()
func _hide_buttons():
if not State.reduce_motion: $AnimationPlayer.play_backwards("show_buttons")
func _show_buttons():
if State.reduce_motion:
$AnimationPlayer.play("show_buttons")
else:
$AnimationPlayer.play("RESET")
func hide():
if visible:
_hide_buttons()
var tween = create_tween()
tween.tween_property(self, "modulate", 0, 0.4)
_hide_buttons()
await tween.finished
visible = false
func show():
if not collapsed:
_show_buttons()
modulate = Color()
visible = true
var tween = create_tween()
tween.tween_property(self, "modulate", Color(1, 1, 1), 0.4)
func _yoink_focus():
State.request_focus(self, true)

View File

@ -1,22 +1,120 @@
[gd_scene format=3 uid="uid://cceyp2yd6o3sq"]
[gd_scene load_steps=9 format=3 uid="uid://cceyp2yd6o3sq"]
[node name="CenterContainer" type="CenterContainer"]
[ext_resource type="Theme" uid="uid://b056fn288p8ha" path="res://logic-scenes/themes/messy.theme" id="1_2apkb"]
[ext_resource type="Script" path="res://logic-scenes/collectable/collectable_ui.gd" id="1_tgjc2"]
[ext_resource type="Texture2D" uid="uid://d0ucjqi8tx6vt" path="res://import/interface-elements/frame.png" id="3_63j61"]
[sub_resource type="GDScript" id="GDScript_g0qhf"]
script/source = "@tool
extends Button
func hide():
if visible == true and not State.reduce_motion:
var tween:Tween = create_tween()
custom_minimum_size = get_minimum_size()
var tmp = text
text = \"\"
tween.tween_property(self, \"custom_minimum_size\", Vector2(size.x, 0), 0.2)
update_minimum_size()
await tween.finished
visible = false
text = tmp
update_minimum_size()
else:
visible = false
func show():
if visible == false and not State.reduce_motion:
var tmp = text
var tween:Tween = create_tween()
tween.tween_property(self, \"custom_minimum_size\", get_minimum_size(), 0.2)
text = \"\"
update_minimum_size()
visible = true
await tween.finished
text = tmp
else:
visible = true
"
[sub_resource type="Animation" id="Animation_rhsmi"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Panel/Content/Buttons:custom_minimum_size")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(0, 256)]
}
[sub_resource type="Animation" id="Animation_bq4rh"]
resource_name = "invisible"
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Panel/Content/Buttons:custom_minimum_size")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(0, 0)]
}
[sub_resource type="Animation" id="Animation_rx43a"]
resource_name = "show_buttons"
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Panel/Content/Buttons:custom_minimum_size")
tracks/0/interp = 2
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.4, 0.6),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 0,
"values": [Vector2(0, 0), Vector2(0, 135), Vector2(0, 130)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_jad23"]
_data = {
"RESET": SubResource("Animation_rhsmi"),
"invisible": SubResource("Animation_bq4rh"),
"show_buttons": SubResource("Animation_rx43a")
}
[node name="CollectableUi" type="CenterContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_2apkb")
script = ExtResource("1_tgjc2")
collapsed = null
[node name="VBoxContainer" type="VBoxContainer" parent="."]
[node name="Panel" type="PanelContainer" parent="."]
layout_mode = 2
[node name="Name" type="Label" parent="VBoxContainer"]
[node name="Content" type="VBoxContainer" parent="Panel"]
layout_mode = 2
[node name="Name" type="Label" parent="Panel/Content"]
layout_mode = 2
theme_type_variation = &"HeaderLarge"
text = "old Mask"
horizontal_alignment = 1
[node name="Content Notes" type="RichTextLabel" parent="VBoxContainer"]
[node name="ContentNotes" type="RichTextLabel" parent="Panel/Content"]
visible = false
custom_minimum_size = Vector2(256, 0)
layout_mode = 2
bbcode_enabled = true
@ -25,21 +123,55 @@ Food, Blood, Gore, Whatever, I need a second line.
[/center]"
fit_content = true
[node name="collect_or_listen" type="Button" parent="VBoxContainer"]
[node name="Buttons" type="ScrollContainer" parent="Panel/Content"]
custom_minimum_size = Vector2(0, 256)
layout_mode = 2
horizontal_scroll_mode = 0
vertical_scroll_mode = 3
[node name="VBoxContainer" type="VBoxContainer" parent="Panel/Content/Buttons"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="collect_or_listen" type="Button" parent="Panel/Content/Buttons/VBoxContainer"]
layout_mode = 2
tooltip_text = "Take this with you, and listen to it's story."
text = "Collect"
script = SubResource("GDScript_g0qhf")
[node name="Summary" type="Button" parent="VBoxContainer"]
[node name="Summary" type="Button" parent="Panel/Content/Buttons/VBoxContainer"]
layout_mode = 2
tooltip_text = "Take this with you, but get a neutral description of it's story."
text = "get neutral summary"
script = SubResource("GDScript_g0qhf")
[node name="skip" type="Button" parent="VBoxContainer"]
[node name="skip" type="Button" parent="Panel/Content/Buttons/VBoxContainer"]
layout_mode = 2
tooltip_text = "Choose this to entirely skip this Item without being unable to progress in the story. Skipped Segments can still be interacted with via the Pause Screen, if you decide to change your mind."
text = "skip"
script = SubResource("GDScript_g0qhf")
[node name="put_back" type="Button" parent="VBoxContainer"]
[node name="put_back" type="Button" parent="Panel/Content/Buttons/VBoxContainer"]
layout_mode = 2
text = "put back"
script = SubResource("GDScript_g0qhf")
[node name="StartFrame" type="TextureRect" parent="Panel"]
layout_mode = 2
mouse_filter = 2
texture = ExtResource("3_63j61")
expand_mode = 2
stretch_mode = 4
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
autoplay = "invisible"
libraries = {
"": SubResource("AnimationLibrary_jad23")
}
[connection signal="resized" from="Panel/Content/Buttons/VBoxContainer" to="Panel/Content/Buttons" method="_on_v_box_container_resized"]
[connection signal="focus_entered" from="Panel/Content/Buttons/VBoxContainer/collect_or_listen" to="." method="_yoink_focus"]
[connection signal="focus_entered" from="Panel/Content/Buttons/VBoxContainer/Summary" to="." method="_yoink_focus"]
[connection signal="focus_entered" from="Panel/Content/Buttons/VBoxContainer/skip" to="." method="_yoink_focus"]
[connection signal="focus_entered" from="Panel/Content/Buttons/VBoxContainer/put_back" to="." method="_yoink_focus"]

View File

@ -1,6 +1,33 @@
extends RigidBody3D
@export var active: bool = true : set = set_active
@export var has_focus: bool = true :
set(focused):
if has_focus != focused:
if focused:
has_focus = State.request_focus(self)
if is_inside_tree() and has_focus:
camera.make_current()
get_viewport().gui_release_focus()
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
var jitter_tween: Tween = create_tween()
jitter_tween.tween_property(self, "jitter_strength", 1, 1)
if has_entered: emit_signal("ui_entered")
elif has_focus:
camera.current = true
jitter_strength = 1
else:
if is_inside_tree() and has_focus:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
var jitter_tween: Tween = create_tween()
jitter_tween.tween_property(self, "jitter_strength", 0, 0.5)
if has_entered: emit_signal("ui_exited")
else:
jitter_strength = 0
has_focus = false
State.drop_own_focus(self)
sleeping = has_focus
@export_range (0, 10) var max_speed: float = 3
@export_range (0, 10) var max_acceleration: float = 5
@export_range (0, 20) var damp: float = 10
@ -30,34 +57,30 @@ var on_crouch_cooldown:bool = false
@onready var pitch:Node3D = $Yaw/Pitch
@onready var mount:Node3D = $Yaw/Pitch/Mount
@onready var camera:Camera3D = $Yaw/Pitch/Mount/Camera3D
@onready var focus_ray: RayCast3D = $Yaw/Pitch/Mount/Camera3D/RayCast3D
func set_active(activate):
active = activate
if !is_inside_tree(): return
if activate:
camera.make_current()
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
var jitter_tween: Tween = create_tween()
jitter_tween.tween_property(self, "jitter_strength", 1, 1)
else:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
var jitter_tween: Tween = create_tween()
jitter_tween.tween_property(self, "jitter_strength", 0, 0.5)
sleeping = active
signal ui_entered
var has_entered:bool = false
signal ui_exited
func _ready():
if active:
set_active(active)
jitter_strength = 1
_handle_jitter(0)
func _process(delta):
if Input.is_action_just_pressed("ui_cancel"):
set_active(!active)
if focus_ray.get_collider() != null:
emit_signal("ui_entered")
has_entered = true
if has_entered:
if focus_ray.get_collider() == null:
emit_signal("ui_exited")
has_entered = false
if Input.is_action_just_pressed("ui_accept"):
State.pass_focus_to(focus_ray.get_collider())
func _physics_process(delta:float):
if has_focus:
_handle_movement(delta)
_handle_rotation(delta)
if jitter_strength > 0: _handle_jitter(delta)
@ -122,9 +145,19 @@ func _handle_mouse_input(event:InputEventMouseMotion):
current_mouse_rotation = event.relative
func _unhandled_input(event:InputEvent):
if active:
if has_focus:
if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
_handle_mouse_input(event)
get_viewport().set_input_as_handled()
if event is InputEventMouseButton and has_entered:
has_focus = false
get_viewport().set_input_as_handled()
if event.is_action("ui_accept"):
State.pass_focus_to(focus_ray.get_collider())
get_viewport().set_input_as_handled()
func _on_empty_click():
State.pass_focus_to(self)
func _on_bed_enter(_body):
if not (crouched or on_crouch_cooldown):

View File

@ -120,6 +120,12 @@ transform = Transform3D(1, 0, 0, 0, 0.5, -0.866025, 0, 0.866025, 0.5, 0, 0.25649
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.202, 0.157)
current = true
[node name="RayCast3D" type="RayCast3D" parent="Yaw/Pitch/Mount/Camera3D"]
target_position = Vector3(0, 0, -1.3)
collision_mask = 17
collide_with_areas = true
collide_with_bodies = false
[node name="PlayerCollision" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0.322255, 0)
shape = SubResource("8")

View File

@ -9,33 +9,48 @@ var show_content_notes: bool = false
var provide_summaries: bool = false
var allow_skipping: bool = false
var current_focus: Node
var focus_list:Array = []
var lock_focus: bool = false
func request_focus(new_focus: Node, yoink:bool = false) -> bool:
if (current_focus == null or yoink) and new_focus != null:
current_focus = new_focus
func request_focus(new_focus: Node, reclaim_focus: bool = false) -> bool:
assert(is_instance_valid(new_focus))
if not focus_list.size() == 0: _pass_focus_of(focus_list[0])
if not lock_focus or get_tree().paused:
push_warning(new_focus.name, " attempted to get focus while tree was paused or fokus had been locked.")
return false
elif reclaim_focus:
if focus_list.has(new_focus):
while not focus_list[0] == new_focus: focus_list.pop_front()
return true
else:
focus_list.push_front(new_focus)
push_warning(new_focus.name, " attempted to reclaim focus it did not previousely have.")
return true
else:
focus_list.append(new_focus)
return true
else: return false
func pass_focus_to(to: Node) -> bool:
if "has_focus" in to:
if not current_focus == null:
drop_focus_of(current_focus)
to.has_focus = true
func assign_focus_to(focusable: Node) -> bool:
if "has_focus" in focusable:
if not focus_list.size() == 0:
_pass_focus_of(focus_list[0])
focusable.has_focus = true
return true
else: return false
func drop_own_focus(node: Node):
if current_focus == node:
current_focus = null
if focus_list[0] == node:
focus_list.pop_front().has_focus = false
assert(focus_list.size() > 0)
focus_list[0].has_focus = true
else:
push_error(node.name + " attempted to drop focus while not owning it!")
push_warning(node.name + " attempted to drop focus while not owning it.")
func drop_focus_of(node: Node):
if current_focus == node:
current_focus = null
if node.has_focus: node.has_focus = false
else:
node.has_focus = false
push_warning("Attempted to drop Focus for " + node.name + " while it wasn't focused!")
func clear_focus_with(focus: Node):
assert(is_instance_valid(focus))
if focus_list.size() > 0: focus_list[0].has_focus = false
focus_list = [focus]
func _pass_focus_of(previous_focus: Node):
previous_focus.has_focus = false