Improve scrolling
This commit is contained in:
@@ -22,6 +22,7 @@ var _linesPerPage:int = 0
|
||||
var _revealTimer:float = 0.0
|
||||
var _pauseTimer:float = 0.0
|
||||
var _autoAdvanceTimer:float = 0.0
|
||||
var _layoutReady:bool = false
|
||||
var _isRevealing:bool = false
|
||||
var _isWaitingForInput:bool = false
|
||||
var _hasLetGoOfInteract:bool = true
|
||||
@@ -44,20 +45,23 @@ func setup(line:DialogueLine, entity:Entity, mode:AdvancementMode = AdvancementM
|
||||
_advancementMode = mode
|
||||
_speakerLabel.text = entity.displayName if entity else ""
|
||||
_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.
|
||||
_bodyLabel.text = line.text
|
||||
_bodyLabel.visible_characters = 0
|
||||
_bodyLabel.visible_characters = -1
|
||||
_bodyLabel.scroll_to_line(0)
|
||||
_startLine = 0
|
||||
_linesPerPage = LINES_PER_PAGE
|
||||
_revealTimer = 0.0
|
||||
_pauseTimer = 0.0
|
||||
_autoAdvanceTimer = 0.0
|
||||
_layoutReady = false
|
||||
_isRevealing = false
|
||||
_isWaitingForInput = false
|
||||
_hasLetGoOfInteract = !Input.is_action_pressed("interact")
|
||||
size.x = MAX_WIDTH
|
||||
modulate.a = 0.0
|
||||
visible = true
|
||||
_parsedText = _bodyLabel.get_parsed_text()
|
||||
_isRevealing = true
|
||||
|
||||
func _process(delta:float) -> void:
|
||||
if not visible:
|
||||
@@ -68,6 +72,10 @@ func _process(delta:float) -> void:
|
||||
if _isWaitingForInput:
|
||||
_advanceIndicator.modulate.a = 0.5 + 0.5 * sin(Time.get_ticks_msec() / 300.0)
|
||||
|
||||
if not _layoutReady:
|
||||
_finishLayout()
|
||||
return
|
||||
|
||||
if _isRevealing:
|
||||
_processReveal(delta)
|
||||
return
|
||||
@@ -79,6 +87,40 @@ func _process(delta:float) -> void:
|
||||
if _advancementMode == AdvancementMode.TIMED:
|
||||
_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:
|
||||
if parsed.is_empty():
|
||||
return parsed
|
||||
var result:String = ""
|
||||
var prevLine:int = 0
|
||||
for i in range(len(parsed)):
|
||||
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":
|
||||
result += "\n"
|
||||
prevLine += 1
|
||||
continue
|
||||
var charLine:int = _bodyLabel.get_character_line(i)
|
||||
if charLine > prevLine:
|
||||
result += "\n"
|
||||
prevLine = charLine
|
||||
result += ch
|
||||
return result
|
||||
|
||||
func _processReveal(delta:float) -> void:
|
||||
if _pauseTimer > 0.0:
|
||||
var speedMult:float = SPEEDUP_MULTIPLIER if Input.is_action_pressed("interact") else 1.0
|
||||
@@ -102,16 +144,20 @@ func _revealNextChar() -> bool:
|
||||
_bodyLabel.visible_characters += 1
|
||||
var idx:int = _bodyLabel.visible_characters - 1
|
||||
|
||||
# Stop if this character has wrapped onto the next page.
|
||||
if idx > 0:
|
||||
var charLine:int = _bodyLabel.get_character_line(idx)
|
||||
if charLine >= _startLine + _linesPerPage:
|
||||
_bodyLabel.visible_characters -= 1
|
||||
_onPageFull()
|
||||
return false
|
||||
# Count newlines up to idx to get the current line — no Godot layout call needed.
|
||||
var charLine:int = _parsedText.left(idx + 1).count("\n")
|
||||
if charLine >= _startLine + _linesPerPage:
|
||||
_bodyLabel.visible_characters -= 1
|
||||
_onPageFull()
|
||||
return false
|
||||
|
||||
if idx < len(_parsedText):
|
||||
_pauseTimer = _getPauseForChar(idx)
|
||||
var isLastVisible:bool = idx >= len(_parsedText) - 1
|
||||
if not isLastVisible:
|
||||
var nextCharLine:int = _parsedText.left(idx + 2).count("\n")
|
||||
isLastVisible = nextCharLine >= _startLine + _linesPerPage
|
||||
if not isLastVisible:
|
||||
_pauseTimer = _getPauseForChar(idx)
|
||||
|
||||
return true
|
||||
|
||||
@@ -157,7 +203,8 @@ func _processAutoAdvance(delta:float) -> void:
|
||||
func _advance() -> void:
|
||||
_advanceIndicator.visible = false
|
||||
_advanceIndicator.modulate.a = 1.0
|
||||
var hasMorePages:bool = _startLine + _linesPerPage < _bodyLabel.get_line_count()
|
||||
var totalLines:int = _parsedText.count("\n") + 1
|
||||
var hasMorePages:bool = _startLine + _linesPerPage < totalLines
|
||||
if hasMorePages:
|
||||
_startLine += _linesPerPage
|
||||
_bodyLabel.scroll_to_line(_startLine)
|
||||
|
||||
Reference in New Issue
Block a user