amélioration de la commande tactile

les clues sont dépendantes du contexte
elles affichent un bouton si on est au clavier
un doigt si on est à la souris ou au doigt
This commit is contained in:
Thomas
2025-03-29 12:51:15 +01:00
parent 2125236638
commit 6edefc58d0
13 changed files with 1673 additions and 16 deletions

View File

@@ -2,9 +2,14 @@ extends Sprite2D
class_name BubbleClue class_name BubbleClue
func setVisible(isV: bool): func setVisible(isV: bool):
$ButtonClue.setVisible(false)
$TouchClue.setVisible(false)
visible = isV visible = isV
if isV: if isV:
$AnimationPlayer.play("move") $AnimationPlayer.play("move")
if GameState.isUsingTouch:
$TouchClue.setVisible(true)
else:
$ButtonClue.setVisible(true)
else: else:
$AnimationPlayer.stop() $AnimationPlayer.stop()
$Sprite2D.setVisible(isV)

View File

@@ -1,8 +1,9 @@
[gd_scene load_steps=7 format=3 uid="uid://dn10ervwv15oo"] [gd_scene load_steps=8 format=3 uid="uid://dn10ervwv15oo"]
[ext_resource type="Texture2D" uid="uid://cwbhdira3w8qx" path="res://assest/ui/UI_48x48.png" id="1_o6ktm"] [ext_resource type="Texture2D" uid="uid://cwbhdira3w8qx" path="res://assest/ui/UI_48x48.png" id="1_o6ktm"]
[ext_resource type="Script" path="res://UI/clues/bubble_clue.gd" id="2_u6r66"] [ext_resource type="Script" path="res://UI/clues/bubble_clue.gd" id="2_u6r66"]
[ext_resource type="PackedScene" uid="uid://clqdxhwojbkwp" path="res://UI/clues/button_clue.tscn" id="3_p3oka"] [ext_resource type="PackedScene" uid="uid://clqdxhwojbkwp" path="res://UI/clues/button_clue.tscn" id="3_p3oka"]
[ext_resource type="PackedScene" uid="uid://dnauf5lsaj63n" path="res://UI/clues/touch_clue.tscn" id="4_ei01g"]
[sub_resource type="Animation" id="Animation_srt4p"] [sub_resource type="Animation" id="Animation_srt4p"]
loop_mode = 1 loop_mode = 1
@@ -21,7 +22,7 @@ tracks/0/keys = {
tracks/1/type = "value" tracks/1/type = "value"
tracks/1/imported = false tracks/1/imported = false
tracks/1/enabled = true tracks/1/enabled = true
tracks/1/path = NodePath("Sprite2D:position") tracks/1/path = NodePath("ButtonClue:position")
tracks/1/interp = 1 tracks/1/interp = 1
tracks/1/loop_wrap = true tracks/1/loop_wrap = true
tracks/1/keys = { tracks/1/keys = {
@@ -30,6 +31,18 @@ tracks/1/keys = {
"update": 0, "update": 0,
"values": [Vector2(1, -5)] "values": [Vector2(1, -5)]
} }
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("TouchClue:position")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(),
"transitions": PackedFloat32Array(),
"update": 0,
"values": []
}
[sub_resource type="Animation" id="Animation_puwvl"] [sub_resource type="Animation" id="Animation_puwvl"]
resource_name = "move" resource_name = "move"
@@ -49,7 +62,7 @@ tracks/0/keys = {
tracks/1/type = "value" tracks/1/type = "value"
tracks/1/imported = false tracks/1/imported = false
tracks/1/enabled = true tracks/1/enabled = true
tracks/1/path = NodePath("Sprite2D:position") tracks/1/path = NodePath("ButtonClue:position")
tracks/1/interp = 1 tracks/1/interp = 1
tracks/1/loop_wrap = true tracks/1/loop_wrap = true
tracks/1/keys = { tracks/1/keys = {
@@ -58,6 +71,18 @@ tracks/1/keys = {
"update": 0, "update": 0,
"values": [Vector2(0, -5), Vector2(0, -4), Vector2(0, -3), Vector2(0, -4)] "values": [Vector2(0, -5), Vector2(0, -4), Vector2(0, -3), Vector2(0, -4)]
} }
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("TouchClue:position")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0, 0.266667, 0.533333, 0.733333),
"transitions": PackedFloat32Array(1, 1, 1, 1),
"update": 0,
"values": [Vector2(-9.53674e-07, 4), Vector2(0, 5), Vector2(0, 6), Vector2(0, 5)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_kli7q"] [sub_resource type="AnimationLibrary" id="AnimationLibrary_kli7q"]
_data = { _data = {
@@ -72,10 +97,14 @@ vframes = 9
frame = 5 frame = 5
script = ExtResource("2_u6r66") script = ExtResource("2_u6r66")
[node name="Sprite2D" parent="." instance=ExtResource("3_p3oka")] [node name="ButtonClue" parent="." instance=ExtResource("3_p3oka")]
position = Vector2(1, -5) position = Vector2(1, -5)
scale = Vector2(0.401042, 0.401042) scale = Vector2(0.401042, 0.401042)
[node name="TouchClue" parent="." instance=ExtResource("4_ei01g")]
position = Vector2(0, 5.23667)
scale = Vector2(0.416667, 0.416667)
[node name="AnimationPlayer" type="AnimationPlayer" parent="."] [node name="AnimationPlayer" type="AnimationPlayer" parent="."]
libraries = { libraries = {
"": SubResource("AnimationLibrary_kli7q") "": SubResource("AnimationLibrary_kli7q")

36
UI/clues/touch_clue.tscn Normal file
View File

@@ -0,0 +1,36 @@
[gd_scene load_steps=5 format=3 uid="uid://dnauf5lsaj63n"]
[ext_resource type="Texture2D" uid="uid://cwbhdira3w8qx" path="res://assest/ui/UI_48x48.png" id="1_nlo3w"]
[ext_resource type="Script" path="res://UI/clues/button_clue.gd" id="2_4uok5"]
[sub_resource type="Animation" id="Animation_srt4p"]
loop_mode = 1
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:frame")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.233333, 0.5, 0.733333),
"transitions": PackedFloat32Array(1, 1, 1, 1),
"update": 1,
"values": [74, 92, 94, 76]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_kli7q"]
_data = {
"move": SubResource("Animation_srt4p")
}
[node name="Sprite2D" type="Sprite2D"]
texture = ExtResource("1_nlo3w")
hframes = 18
vframes = 16
frame = 74
script = ExtResource("2_4uok5")
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
libraries = {
"": SubResource("AnimationLibrary_kli7q")
}

View File

@@ -0,0 +1,176 @@
extends CanvasLayer
## A basic dialogue balloon for use with Dialogue Manager.
## The action to use for advancing the dialogue
@export var next_action: StringName = &"ui_accept"
## The action to use to skip typing the dialogue
@export var skip_action: StringName = &"ui_cancel"
## The dialogue resource
var resource: DialogueResource
## Temporary game states
var temporary_game_states: Array = []
## See if we are waiting for the player
var is_waiting_for_input: bool = false
## See if we are running a long mutation and should hide the balloon
var will_hide_balloon: bool = false
## A dictionary to store any ephemeral variables
var locals: Dictionary = {}
var _locale: String = TranslationServer.get_locale()
## The current line
var dialogue_line: DialogueLine:
set(value):
if value:
dialogue_line = value
apply_dialogue_line()
else:
# The dialogue has finished so close the balloon
queue_free()
get:
return dialogue_line
## A cooldown timer for delaying the balloon hide when encountering a mutation.
var mutation_cooldown: Timer = Timer.new()
## The base balloon anchor
@onready var balloon: Control = %Balloon
## The label showing the name of the currently speaking character
@onready var character_label: RichTextLabel = %CharacterLabel
## The label showing the currently spoken dialogue
@onready var dialogue_label: DialogueLabel = %DialogueLabel
## The menu of responses
@onready var responses_menu: DialogueResponsesMenu = %ResponsesMenu
func _ready() -> void:
balloon.hide()
Engine.get_singleton("DialogueManager").mutated.connect(_on_mutated)
# If the responses menu doesn't have a next action set, use this one
if responses_menu.next_action.is_empty():
responses_menu.next_action = next_action
mutation_cooldown.timeout.connect(_on_mutation_cooldown_timeout)
add_child(mutation_cooldown)
func _unhandled_input(_event: InputEvent) -> void:
# Only the balloon is allowed to handle input while it's showing
get_viewport().set_input_as_handled()
func _notification(what: int) -> void:
## Detect a change of locale and update the current dialogue line to show the new language
if what == NOTIFICATION_TRANSLATION_CHANGED and _locale != TranslationServer.get_locale() and is_instance_valid(dialogue_label):
_locale = TranslationServer.get_locale()
var visible_ratio = dialogue_label.visible_ratio
self.dialogue_line = await resource.get_next_dialogue_line(dialogue_line.id)
if visible_ratio < 1:
dialogue_label.skip_typing()
## Start some dialogue
func start(dialogue_resource: DialogueResource, title: String, extra_game_states: Array = []) -> void:
temporary_game_states = [self] + extra_game_states
is_waiting_for_input = false
resource = dialogue_resource
self.dialogue_line = await resource.get_next_dialogue_line(title, temporary_game_states)
## Apply any changes to the balloon given a new [DialogueLine].
func apply_dialogue_line() -> void:
mutation_cooldown.stop()
is_waiting_for_input = false
balloon.focus_mode = Control.FOCUS_ALL
balloon.grab_focus()
character_label.visible = not dialogue_line.character.is_empty()
character_label.text = tr(dialogue_line.character, "dialogue")
dialogue_label.hide()
dialogue_label.dialogue_line = dialogue_line
responses_menu.hide()
responses_menu.responses = dialogue_line.responses
# Show our balloon
balloon.show()
will_hide_balloon = false
dialogue_label.show()
if not dialogue_line.text.is_empty():
dialogue_label.type_out()
await dialogue_label.finished_typing
# Wait for input
if dialogue_line.responses.size() > 0:
balloon.focus_mode = Control.FOCUS_NONE
responses_menu.show()
elif dialogue_line.time != "":
var time = dialogue_line.text.length() * 0.02 if dialogue_line.time == "auto" else dialogue_line.time.to_float()
await get_tree().create_timer(time).timeout
next(dialogue_line.next_id)
else:
is_waiting_for_input = true
balloon.focus_mode = Control.FOCUS_ALL
balloon.grab_focus()
## Go to the next line
func next(next_id: String) -> void:
self.dialogue_line = await resource.get_next_dialogue_line(next_id, temporary_game_states)
#region Signals
func _on_mutation_cooldown_timeout() -> void:
if will_hide_balloon:
will_hide_balloon = false
balloon.hide()
func _on_mutated(_mutation: Dictionary) -> void:
is_waiting_for_input = false
will_hide_balloon = true
mutation_cooldown.start(0.1)
func _on_balloon_gui_input(event: InputEvent) -> void:
# See if we need to skip typing of the dialogue
if dialogue_label.is_typing:
var mouse_was_clicked: bool = event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.is_pressed()
var skip_button_was_pressed: bool = event.is_action_pressed(skip_action)
if mouse_was_clicked or skip_button_was_pressed:
get_viewport().set_input_as_handled()
dialogue_label.skip_typing()
return
if not is_waiting_for_input: return
if dialogue_line.responses.size() > 0: return
# When there are no response options the balloon itself is the clickable thing
get_viewport().set_input_as_handled()
if event is InputEventMouseButton and event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT:
next(dialogue_line.next_id)
elif event.is_action_pressed(next_action) and get_viewport().gui_get_focus_owner() == balloon:
next(dialogue_line.next_id)
func _on_responses_menu_response_selected(response: DialogueResponse) -> void:
next(response.next_id)
#endregion

View File

@@ -0,0 +1,151 @@
[gd_scene load_steps=10 format=3 uid="uid://cj4rsfngov4u5"]
[ext_resource type="Script" path="res://UI/dialogue_ballon/balloon.gd" id="1_36de5"]
[ext_resource type="FontFile" uid="uid://c1lnxul6k2adw" path="res://assest/font/vhs-gothic.ttf" id="2_17xl4"]
[ext_resource type="PackedScene" uid="uid://ckvgyvclnwggo" path="res://addons/dialogue_manager/dialogue_label.tscn" id="2_a8ve6"]
[ext_resource type="Script" path="res://addons/dialogue_manager/dialogue_responses_menu.gd" id="3_72ixx"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_spyqn"]
bg_color = Color(0.147672, 0.147672, 0.147672, 1)
border_width_left = 3
border_width_top = 3
border_width_right = 3
border_width_bottom = 3
border_color = Color(0.329412, 0.329412, 0.329412, 1)
corner_radius_top_left = 5
corner_radius_top_right = 5
corner_radius_bottom_right = 5
corner_radius_bottom_left = 5
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ri4m3"]
bg_color = Color(0.122348, 0.0253013, 0.0469849, 1)
border_width_left = 3
border_width_top = 3
border_width_right = 3
border_width_bottom = 3
border_color = Color(1, 1, 1, 1)
corner_radius_top_left = 5
corner_radius_top_right = 5
corner_radius_bottom_right = 5
corner_radius_bottom_left = 5
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_e0njw"]
bg_color = Color(0, 0, 0, 1)
border_width_left = 3
border_width_top = 3
border_width_right = 3
border_width_bottom = 3
border_color = Color(0.6, 0.6, 0.6, 1)
corner_radius_top_left = 5
corner_radius_top_right = 5
corner_radius_bottom_right = 5
corner_radius_bottom_left = 5
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_uy0d5"]
bg_color = Color(0, 0, 0, 1)
border_width_left = 3
border_width_top = 3
border_width_right = 3
border_width_bottom = 3
corner_radius_top_left = 5
corner_radius_top_right = 5
corner_radius_bottom_right = 5
corner_radius_bottom_left = 5
[sub_resource type="Theme" id="Theme_qq3yp"]
default_font = ExtResource("2_17xl4")
default_font_size = 30
Button/styles/disabled = SubResource("StyleBoxFlat_spyqn")
Button/styles/focus = SubResource("StyleBoxFlat_ri4m3")
Button/styles/hover = SubResource("StyleBoxFlat_e0njw")
Button/styles/normal = SubResource("StyleBoxFlat_e0njw")
MarginContainer/constants/margin_bottom = 15
MarginContainer/constants/margin_left = 30
MarginContainer/constants/margin_right = 30
MarginContainer/constants/margin_top = 15
Panel/styles/panel = SubResource("StyleBoxFlat_uy0d5")
[node name="ExampleBalloon" type="CanvasLayer"]
layer = 100
script = ExtResource("1_36de5")
[node name="Balloon" type="Control" parent="."]
unique_name_in_owner = true
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = SubResource("Theme_qq3yp")
[node name="Panel" type="Panel" parent="Balloon"]
clip_children = 2
layout_mode = 1
anchors_preset = 12
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 21.0
offset_top = -183.0
offset_right = -19.0
offset_bottom = -19.0
grow_horizontal = 2
grow_vertical = 0
mouse_filter = 1
[node name="Dialogue" type="MarginContainer" parent="Balloon/Panel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="Balloon/Panel/Dialogue"]
layout_mode = 2
[node name="CharacterLabel" type="RichTextLabel" parent="Balloon/Panel/Dialogue/VBoxContainer"]
unique_name_in_owner = true
modulate = Color(1, 1, 1, 0.501961)
layout_mode = 2
mouse_filter = 1
bbcode_enabled = true
text = "Character"
fit_content = true
scroll_active = false
[node name="DialogueLabel" parent="Balloon/Panel/Dialogue/VBoxContainer" instance=ExtResource("2_a8ve6")]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 3
text = "Dialogue..."
[node name="Responses" type="MarginContainer" parent="Balloon"]
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -147.0
offset_top = -558.0
offset_right = 494.0
offset_bottom = -154.0
grow_horizontal = 2
grow_vertical = 0
[node name="ResponsesMenu" type="VBoxContainer" parent="Balloon/Responses" node_paths=PackedStringArray("response_template")]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 8
theme_override_constants/separation = 2
script = ExtResource("3_72ixx")
response_template = NodePath("ResponseExample")
[node name="ResponseExample" type="Button" parent="Balloon/Responses/ResponsesMenu"]
layout_mode = 2
text = "Response example"
[connection signal="gui_input" from="Balloon" to="." method="_on_balloon_gui_input"]
[connection signal="response_selected" from="Balloon/Responses/ResponsesMenu" to="." method="_on_responses_menu_response_selected"]

BIN
assest/font/vhs-gothic.ttf Normal file

Binary file not shown.

View File

@@ -0,0 +1,34 @@
[remap]
importer="font_data_dynamic"
type="FontFile"
uid="uid://c1lnxul6k2adw"
path="res://.godot/imported/vhs-gothic.ttf-13d0d6385277df002ce582065337b5b3.fontdata"
[deps]
source_file="res://assest/font/vhs-gothic.ttf"
dest_files=["res://.godot/imported/vhs-gothic.ttf-13d0d6385277df002ce582065337b5b3.fontdata"]
[params]
Rendering=null
antialiasing=1
generate_mipmaps=false
disable_embedded_bitmaps=true
multichannel_signed_distance_field=false
msdf_pixel_range=8
msdf_size=48
allow_system_fallback=true
force_autohinter=false
hinting=1
subpixel_positioning=1
oversampling=0.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
preload=[]
language_support={}
script_support={}
opentype_features={}

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,8 @@ func _unhandled_input(event: InputEvent) -> void:
human.wants_to_grab = false human.wants_to_grab = false
human.wants_to_interact_with = null human.wants_to_interact_with = null
human.velocityVector = Input.get_vector("move_left", "move_right", "move_up", "move_down") human.velocityVector = Input.get_vector("move_left", "move_right", "move_up", "move_down")
if human.velocityVector != Vector2(0, 0):
GameState.isUsingTouch = false
if event.is_action_pressed("grab"): if event.is_action_pressed("grab"):
if can_interact_with: if can_interact_with:
@@ -23,6 +25,7 @@ func _unhandled_input(event: InputEvent) -> void:
# moving using either touch or mouse button # moving using either touch or mouse button
if event is InputEventMouseButton or event is InputEventScreenTouch: if event is InputEventMouseButton or event is InputEventScreenTouch:
GameState.isUsingTouch = true
var tile_pos = world.local_to_map(world.to_local(get_global_mouse_position())) var tile_pos = world.local_to_map(world.to_local(get_global_mouse_position()))
if event.pressed: if event.pressed:
pathFinder.destination = get_global_mouse_position() pathFinder.destination = get_global_mouse_position()

3
game_state.gd Normal file
View File

@@ -0,0 +1,3 @@
extends Node
var isUsingTouch = false

View File

@@ -4,12 +4,20 @@ class_name InteractionZone
@export var clue: BubbleClue @export var clue: BubbleClue
var clueEnabled = false var clueEnabled = false
var player: Human var player: Human
var hasMouseInside = false
func _unhandled_input(event: InputEvent) -> void: func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseButton or event is InputEventScreenTouch: if event is InputEventMouseButton or event is InputEventScreenTouch:
if event.pressed: if event.pressed:
if clueEnabled: if clueEnabled:
player.wants_to_interact_with = get_parent() if hasMouseInside:
player.wants_to_interact_with = get_parent()
func _mouse_enter() -> void:
hasMouseInside = true
func _mouse_exit() -> void:
hasMouseInside = false
func enable_interaction_clue(h: Human): func enable_interaction_clue(h: Human):
player = h player = h

File diff suppressed because one or more lines are too long

View File

@@ -18,6 +18,11 @@ config/icon="res://icon.svg"
[autoload] [autoload]
DialogueManager="*res://addons/dialogue_manager/dialogue_manager.gd" DialogueManager="*res://addons/dialogue_manager/dialogue_manager.gd"
GameState="*res://game_state.gd"
[dialogue_manager]
runtime/balloon_path="res://UI/dialogue_ballon/balloon.tscn"
[display] [display]