Fixed flicker

This commit is contained in:
2026-06-12 13:57:22 -05:00
parent b2e8cd2c30
commit aae68a60d5
2 changed files with 38 additions and 31 deletions
+13 -3
View File
@@ -32,6 +32,7 @@ func setup(responses:Array[DialogueResponse], entity:Entity) -> void:
_list.add_child(label) _list.add_child(label)
size.x = MAX_WIDTH size.x = MAX_WIDTH
modulate.a = 0.0
visible = true visible = true
_updateSelection() _updateSelection()
@@ -79,9 +80,18 @@ func _updateWorldPosition() -> void:
var camera:Camera3D = get_viewport().get_camera_3d() var camera:Camera3D = get_viewport().get_camera_3d()
if camera == null: if camera == null:
return return
if size.y == 0:
return
var worldPos:Vector3 = _entity.global_position + Vector3(0, 2.5, 0) var worldPos:Vector3 = _entity.global_position + Vector3(0, 2.5, 0)
if camera.is_position_behind(worldPos):
modulate.a = 0.0
return
modulate.a = 1.0
var screenPos:Vector2 = camera.unproject_position(worldPos) var screenPos:Vector2 = camera.unproject_position(worldPos)
var viewportSize:Vector2 = get_viewport().get_visible_rect().size var viewportSize:Vector2 = get_viewport().get_visible_rect().size
position = screenPos - Vector2(size.x * 0.5, size.y) position.x = clamp(screenPos.x - size.x * 0.5, 0.0, viewportSize.x - size.x)
position.x = clamp(position.x, 0.0, viewportSize.x - size.x) var yAbove:float = screenPos.y - size.y
position.y = clamp(position.y, 0.0, viewportSize.y - size.y) if yAbove >= 0.0:
position.y = yAbove
else:
position.y = clamp(screenPos.y + 10.0, 0.0, viewportSize.y - size.y)
+25 -28
View File
@@ -22,7 +22,6 @@ var _linesPerPage:int = 0
var _revealTimer:float = 0.0 var _revealTimer:float = 0.0
var _pauseTimer:float = 0.0 var _pauseTimer:float = 0.0
var _autoAdvanceTimer:float = 0.0 var _autoAdvanceTimer:float = 0.0
var _layoutReady:bool = false
var _isRevealing:bool = false var _isRevealing:bool = false
var _isWaitingForInput:bool = false var _isWaitingForInput:bool = false
var _hasLetGoOfInteract:bool = true var _hasLetGoOfInteract:bool = true
@@ -45,23 +44,41 @@ func setup(line:DialogueLine, entity:Entity, mode:AdvancementMode = AdvancementM
_advancementMode = mode _advancementMode = mode
_speakerLabel.text = entity.displayName if entity else "" _speakerLabel.text = entity.displayName if entity else ""
_speakerLabel.visible = _speakerLabel.text != "" _speakerLabel.visible = _speakerLabel.text != ""
# Show all characters so Godot shapes the full text and computes line-break
# positions before reveal starts. The box is transparent this frame. # Set width explicitly before text so get_character_line() uses correct metrics.
# The Container layout pass hasn't run yet, so we derive inner width from the
# panel StyleBox margins rather than waiting for a deferred layout frame.
size.x = MAX_WIDTH
_bodyLabel.size.x = _bodyLabelWidth()
_bodyLabel.text = line.text _bodyLabel.text = line.text
_bodyLabel.visible_characters = -1 _bodyLabel.visible_characters = -1
_bodyLabel.scroll_to_line(0) _bodyLabel.scroll_to_line(0)
# get_character_line() forces synchronous text shaping via _validate_line_caches(),
# so we can compute pre-wrapped text right now without any pre-pass frame.
_parsedText = _buildPreWrappedText(_bodyLabel.get_parsed_text())
_bodyLabel.text = _parsedText
_bodyLabel.autowrap_mode = TextServer.AUTOWRAP_OFF
_bodyLabel.visible_characters = 0
_startLine = 0 _startLine = 0
_linesPerPage = LINES_PER_PAGE _linesPerPage = LINES_PER_PAGE
_revealTimer = 0.0 _revealTimer = 0.0
_pauseTimer = 0.0 _pauseTimer = 0.0
_autoAdvanceTimer = 0.0 _autoAdvanceTimer = 0.0
_layoutReady = false _isRevealing = true
_isRevealing = false
_isWaitingForInput = false _isWaitingForInput = false
_hasLetGoOfInteract = !Input.is_action_pressed("interact") _hasLetGoOfInteract = !Input.is_action_pressed("interact")
size.x = MAX_WIDTH
modulate.a = 0.0 _updateWorldPosition()
visible = true visible = true
_revealNextChar()
func _bodyLabelWidth() -> float:
var style:StyleBox = get_theme_stylebox("panel")
if style == null:
return MAX_WIDTH
return MAX_WIDTH - style.get_margin(SIDE_LEFT) - style.get_margin(SIDE_RIGHT)
func _process(delta:float) -> void: func _process(delta:float) -> void:
if not visible: if not visible:
@@ -72,10 +89,6 @@ func _process(delta:float) -> void:
if _isWaitingForInput: if _isWaitingForInput:
_advanceIndicator.modulate.a = 0.5 + 0.5 * sin(Time.get_ticks_msec() / 300.0) _advanceIndicator.modulate.a = 0.5 + 0.5 * sin(Time.get_ticks_msec() / 300.0)
if not _layoutReady:
_finishLayout()
return
if _isRevealing: if _isRevealing:
_processReveal(delta) _processReveal(delta)
return return
@@ -87,20 +100,6 @@ func _process(delta:float) -> void:
if _advancementMode == AdvancementMode.TIMED: if _advancementMode == AdvancementMode.TIMED:
_processAutoAdvance(delta) _processAutoAdvance(delta)
# Called on the first _process frame after setup(). By this point Godot has
# shaped the full text, so get_character_line() returns stable positions.
# We bake explicit \n characters into the text at every wrap boundary, then
# disable autowrap so the layout never changes during reveal.
func _finishLayout() -> void:
var raw:String = _bodyLabel.get_parsed_text()
_parsedText = _buildPreWrappedText(raw)
_bodyLabel.text = _parsedText
_bodyLabel.autowrap_mode = TextServer.AUTOWRAP_OFF
_bodyLabel.visible_characters = 0
modulate.a = 1.0
_layoutReady = true
_isRevealing = true
func _buildPreWrappedText(parsed:String) -> String: func _buildPreWrappedText(parsed:String) -> String:
if parsed.is_empty(): if parsed.is_empty():
return parsed return parsed
@@ -108,8 +107,6 @@ func _buildPreWrappedText(parsed:String) -> String:
var prevLine:int = 0 var prevLine:int = 0
for i in range(len(parsed)): for i in range(len(parsed)):
var ch:String = parsed[i] var ch:String = parsed[i]
# Source-level newlines advance the expected line counter directly;
# skip the charLine check so we never double-insert.
if ch == "\n": if ch == "\n":
result += "\n" result += "\n"
prevLine += 1 prevLine += 1
@@ -147,7 +144,6 @@ func _revealNextChar() -> bool:
_bodyLabel.visible_characters += 1 _bodyLabel.visible_characters += 1
var idx:int = _bodyLabel.visible_characters - 1 var idx:int = _bodyLabel.visible_characters - 1
# Count newlines up to idx to get the current line — no Godot layout call needed.
var charLine:int = _parsedText.left(idx + 1).count("\n") var charLine:int = _parsedText.left(idx + 1).count("\n")
if charLine >= _startLine + _linesPerPage: if charLine >= _startLine + _linesPerPage:
_bodyLabel.visible_characters -= 1 _bodyLabel.visible_characters -= 1
@@ -215,6 +211,7 @@ func _advance() -> void:
_revealTimer = 0.0 _revealTimer = 0.0
_pauseTimer = 0.0 _pauseTimer = 0.0
_isRevealing = true _isRevealing = true
_revealNextChar()
else: else:
dismissed.emit() dismissed.emit()
visible = false visible = false