in-world ui textboxes
This commit is contained in:
+2
-1
@@ -1,9 +1,10 @@
|
||||
class_name RootUI extends Control
|
||||
|
||||
@export var debugMenu:DebugMenu
|
||||
@export var textBox:VNTextbox
|
||||
@export var mainChatBox:ChatBox
|
||||
@export var gameMenu:GameMenu
|
||||
@export var pauseMenu:PauseMenu
|
||||
@export var chatBoxContainer:Control
|
||||
|
||||
func _enter_tree() -> void:
|
||||
UI.rootUi = self
|
||||
|
||||
+13
-3
@@ -6,7 +6,7 @@
|
||||
[ext_resource type="PackedScene" uid="uid://b38dr0wkix76t" path="res://ui/debugmenu/DebugMenu.tscn" id="4_u132g"]
|
||||
[ext_resource type="PackedScene" uid="uid://bv5r2x9m4k7n1" path="res://ui/gamemenu/GameMenu.tscn" id="5_gmenu"]
|
||||
|
||||
[node name="RootUI" type="Control" node_paths=PackedStringArray("debugMenu", "textBox", "gameMenu", "pauseMenu")]
|
||||
[node name="RootUI" type="Control" node_paths=PackedStringArray("debugMenu", "mainChatBox", "gameMenu", "pauseMenu", "chatBoxContainer")]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
@@ -16,9 +16,10 @@ grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
script = ExtResource("1_son71")
|
||||
debugMenu = NodePath("DebugMenu")
|
||||
textBox = NodePath("VNTextbox")
|
||||
mainChatBox = NodePath("ChatBox")
|
||||
gameMenu = NodePath("GameMenu")
|
||||
pauseMenu = NodePath("PauseMenu")
|
||||
chatBoxContainer = NodePath("WorldChatBoxContainer")
|
||||
metadata/_custom_type_script = "uid://dq3qyyayugt5l"
|
||||
|
||||
[node name="DebugMenu" parent="." instance=ExtResource("4_u132g")]
|
||||
@@ -34,6 +35,15 @@ process_mode = 3
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
|
||||
[node name="VNTextbox" parent="." instance=ExtResource("1_1mtk3")]
|
||||
[node name="WorldChatBoxContainer" type="Control" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="ChatBox" parent="." instance=ExtResource("1_1mtk3")]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
|
||||
+24
-3
@@ -6,16 +6,18 @@ var rootUi:RootUI = null
|
||||
# between lines where the textbox is momentarily closed.
|
||||
var dialogueActive:bool = false
|
||||
|
||||
var _chatBoxes:Array = []
|
||||
|
||||
var DEBUG_MENU:
|
||||
get():
|
||||
if rootUi && rootUi.debugMenu:
|
||||
return rootUi.debugMenu
|
||||
return null
|
||||
|
||||
var TEXTBOX:
|
||||
var MAIN_CHATBOX:
|
||||
get():
|
||||
if rootUi && rootUi.textBox:
|
||||
return rootUi.textBox
|
||||
if rootUi && rootUi.mainChatBox:
|
||||
return rootUi.mainChatBox
|
||||
return null
|
||||
|
||||
var GAME_MENU:
|
||||
@@ -29,3 +31,22 @@ var PAUSE_MENU:
|
||||
if rootUi && rootUi.pauseMenu:
|
||||
return rootUi.pauseMenu
|
||||
return null
|
||||
|
||||
func addChatBox(box:WorldChatBox) -> void:
|
||||
_chatBoxes.append(box)
|
||||
|
||||
func removeChatBox(box:WorldChatBox) -> void:
|
||||
_chatBoxes.erase(box)
|
||||
|
||||
func hasAdvanceableChatBox() -> bool:
|
||||
for box in _chatBoxes:
|
||||
if box.mode == WorldChatBox.Mode.ADVANCEABLE:
|
||||
return true
|
||||
return false
|
||||
|
||||
func spawnWorldChatBox(target:Node3D = null) -> WorldChatBox:
|
||||
assert(rootUi != null)
|
||||
var chatbox:WorldChatBox = WorldChatBox.SCENE.instantiate()
|
||||
rootUi.chatBoxContainer.add_child(chatbox)
|
||||
chatbox.worldTarget = target
|
||||
return chatbox
|
||||
|
||||
+58
-10
@@ -1,6 +1,10 @@
|
||||
class_name VNTextbox extends PanelContainer
|
||||
class_name ChatBox extends PanelContainer
|
||||
|
||||
const VN_REVEAL_TIME = 0.01
|
||||
const _DEFAULT_ANCHOR_TOP = 1.0
|
||||
const _DEFAULT_ANCHOR_RIGHT = 1.0
|
||||
const _DEFAULT_ANCHOR_BOTTOM = 1.0
|
||||
const _DEFAULT_OFFSET_TOP = -58.0
|
||||
|
||||
var label:AdvancedRichText;
|
||||
var parsedOutText = ""
|
||||
@@ -12,6 +16,7 @@ var currentViewScrolled = true;
|
||||
var isSpeedupDown = false;
|
||||
|
||||
var hasLetGoOfInteract:bool = true;
|
||||
var worldTarget:Node3D = null
|
||||
|
||||
var isClosed:bool = false:
|
||||
get():
|
||||
@@ -19,20 +24,63 @@ var isClosed:bool = false:
|
||||
set(value):
|
||||
self.visible = !value;
|
||||
|
||||
signal textboxClosing
|
||||
signal chatboxClosing
|
||||
|
||||
func _ready() -> void:
|
||||
label = $MarginContainer/Label
|
||||
isClosed = true
|
||||
|
||||
func _setWorldLayout(hasTarget:bool) -> void:
|
||||
if hasTarget:
|
||||
anchor_left = 0.0
|
||||
anchor_top = 0.0
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
offset_left = 0.0
|
||||
offset_top = 0.0
|
||||
offset_right = 0.0
|
||||
offset_bottom = 0.0
|
||||
custom_minimum_size = Vector2(90, 0)
|
||||
else:
|
||||
if label != null:
|
||||
label.fit_content = false
|
||||
anchor_left = 0.0
|
||||
anchor_top = _DEFAULT_ANCHOR_TOP
|
||||
anchor_right = _DEFAULT_ANCHOR_RIGHT
|
||||
anchor_bottom = _DEFAULT_ANCHOR_BOTTOM
|
||||
offset_left = 0.0
|
||||
offset_top = _DEFAULT_OFFSET_TOP
|
||||
offset_right = 0.0
|
||||
offset_bottom = 0.0
|
||||
custom_minimum_size = Vector2.ZERO
|
||||
|
||||
func _updateWorldPosition() -> void:
|
||||
if worldTarget == null:
|
||||
return
|
||||
var camera:Camera3D = get_viewport().get_camera_3d()
|
||||
if camera == null:
|
||||
return
|
||||
var screenPos:Vector2 = camera.unproject_position(worldTarget.global_position + Vector3(0, 2.5, 0))
|
||||
var viewportSize:Vector2 = get_viewport().get_visible_rect().size
|
||||
position = screenPos - size * 0.5
|
||||
position.x = clamp(position.x, 0.0, viewportSize.x - size.x)
|
||||
position.y = clamp(position.y, 0.0, viewportSize.y - size.y)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if isClosed:
|
||||
return;
|
||||
|
||||
_updateWorldPosition()
|
||||
|
||||
# _recalcText always resets fit_content to false; re-apply so the
|
||||
# PanelContainer can grow to its content height in world-space mode.
|
||||
if worldTarget != null and !label.fit_content:
|
||||
label.fit_content = true
|
||||
|
||||
if label.getFinalText() == "":
|
||||
isClosed = true;
|
||||
return;
|
||||
|
||||
|
||||
if Input.is_action_just_released("interact") && !hasLetGoOfInteract:
|
||||
hasLetGoOfInteract = true;
|
||||
return
|
||||
@@ -51,7 +99,7 @@ func _process(delta: float) -> void:
|
||||
else:
|
||||
# On last page
|
||||
if Input.is_action_just_released("interact") && hasLetGoOfInteract:
|
||||
textboxClosing.emit();
|
||||
chatboxClosing.emit();
|
||||
isClosed = true;
|
||||
currentViewScrolled = true
|
||||
return;
|
||||
@@ -73,15 +121,15 @@ func _process(delta: float) -> void:
|
||||
revealTimer = 0;
|
||||
label.visible_characters += 1;
|
||||
|
||||
func setText(text:String) -> void:
|
||||
# Prepare textbox for scrolling
|
||||
func setText(text:String, target:Node3D = null) -> void:
|
||||
worldTarget = target
|
||||
_setWorldLayout(worldTarget != null)
|
||||
|
||||
# Resets scroll
|
||||
revealTimer = 0;
|
||||
currentViewScrolled = false;
|
||||
label.startLine = 0;
|
||||
|
||||
# I had a frame wait here before.
|
||||
label.text = text;
|
||||
label.visible_characters = 0;
|
||||
|
||||
@@ -90,7 +138,7 @@ func setText(text:String) -> void:
|
||||
isSpeedupDown = false
|
||||
isClosed = false;
|
||||
|
||||
func setTextAndWait(text:String) -> void:
|
||||
self.setText(text);
|
||||
await self.textboxClosing
|
||||
func setTextAndWait(text:String, target:Node3D = null) -> void:
|
||||
self.setText(text, target);
|
||||
await self.chatboxClosing
|
||||
await get_tree().process_frame
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[ext_resource type="Script" uid="uid://h8lw23ypcfty" path="res://ui/component/VNTextbox.gd" id="2_uo1gm"]
|
||||
[ext_resource type="Script" uid="uid://bjj6upgk1uvxd" path="res://ui/component/advancedrichtext/AdvancedRichText.gd" id="3_m60k3"]
|
||||
|
||||
[node name="VNTextbox" type="PanelContainer"]
|
||||
[node name="ChatBox" type="PanelContainer"]
|
||||
clip_contents = true
|
||||
anchors_preset = 12
|
||||
anchor_top = 1.0
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
class_name WorldChatBox extends PanelContainer
|
||||
|
||||
enum Mode { ADVANCEABLE, TIMED }
|
||||
|
||||
const SCENE = preload("res://ui/component/WorldChatBox.tscn")
|
||||
|
||||
signal chatboxClosed
|
||||
|
||||
var mode:Mode = Mode.TIMED
|
||||
var worldTarget:Node3D = null
|
||||
var _timer:float = 0.0
|
||||
var _label:Label
|
||||
|
||||
func _ready() -> void:
|
||||
_label = $MarginContainer/Label
|
||||
UI.addChatBox(self)
|
||||
visible = false
|
||||
|
||||
func showTimed(text:String, duration:float) -> void:
|
||||
mode = Mode.TIMED
|
||||
_timer = duration
|
||||
_label.text = text
|
||||
visible = true
|
||||
|
||||
func showAdvanceable(text:String) -> void:
|
||||
mode = Mode.ADVANCEABLE
|
||||
_label.text = text
|
||||
visible = true
|
||||
|
||||
func showAndWait(text:String) -> void:
|
||||
showAdvanceable(text)
|
||||
await chatboxClosed
|
||||
|
||||
func close() -> void:
|
||||
UI.removeChatBox(self)
|
||||
chatboxClosed.emit()
|
||||
queue_free()
|
||||
|
||||
func _updateWorldPosition() -> void:
|
||||
if worldTarget == null:
|
||||
return
|
||||
var camera:Camera3D = get_viewport().get_camera_3d()
|
||||
if camera == null:
|
||||
return
|
||||
var screenPos:Vector2 = camera.unproject_position(worldTarget.global_position + Vector3(0, 2.5, 0))
|
||||
var viewportSize:Vector2 = get_viewport().get_visible_rect().size
|
||||
position = screenPos - size * 0.5
|
||||
position.x = clamp(position.x, 0.0, viewportSize.x - size.x)
|
||||
position.y = clamp(position.y, 0.0, viewportSize.y - size.y)
|
||||
|
||||
func _process(delta:float) -> void:
|
||||
if !visible:
|
||||
return
|
||||
_updateWorldPosition()
|
||||
match mode:
|
||||
Mode.TIMED:
|
||||
_timer -= delta
|
||||
if _timer <= 0.0:
|
||||
close()
|
||||
Mode.ADVANCEABLE:
|
||||
if Input.is_action_just_pressed("interact"):
|
||||
close()
|
||||
@@ -0,0 +1 @@
|
||||
uid://m1keb2pw8bqq
|
||||
@@ -0,0 +1,21 @@
|
||||
[gd_scene load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dm7ee4aqjr2dl" path="res://ui/UI Theme.tres" id="1_wx4lp"]
|
||||
[ext_resource type="Script" path="res://ui/component/WorldChatBox.gd" id="2_wcb"]
|
||||
|
||||
[node name="WorldChatBox" type="PanelContainer"]
|
||||
custom_minimum_size = Vector2(90, 0)
|
||||
mouse_filter = 2
|
||||
theme = ExtResource("1_wx4lp")
|
||||
script = ExtResource("2_wcb")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 5
|
||||
theme_override_constants/margin_top = 3
|
||||
theme_override_constants/margin_right = 5
|
||||
theme_override_constants/margin_bottom = 3
|
||||
|
||||
[node name="Label" type="Label" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
autowrap_mode = 3
|
||||
@@ -41,7 +41,7 @@ func _unhandled_input(event:InputEvent) -> void:
|
||||
if event.is_action_pressed("menu"):
|
||||
if visible:
|
||||
close()
|
||||
elif !UI.dialogueActive && UI.TEXTBOX.isClosed:
|
||||
elif !UI.dialogueActive && UI.MAIN_CHATBOX.isClosed:
|
||||
open()
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user