Improve scrolling
This commit is contained in:
@@ -22,6 +22,7 @@ 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
|
||||||
@@ -44,20 +45,23 @@ 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.
|
||||||
_bodyLabel.text = line.text
|
_bodyLabel.text = line.text
|
||||||
_bodyLabel.visible_characters = 0
|
_bodyLabel.visible_characters = -1
|
||||||
_bodyLabel.scroll_to_line(0)
|
_bodyLabel.scroll_to_line(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 = false
|
||||||
_isWaitingForInput = false
|
_isWaitingForInput = false
|
||||||
_hasLetGoOfInteract = !Input.is_action_pressed("interact")
|
_hasLetGoOfInteract = !Input.is_action_pressed("interact")
|
||||||
size.x = MAX_WIDTH
|
size.x = MAX_WIDTH
|
||||||
|
modulate.a = 0.0
|
||||||
visible = true
|
visible = true
|
||||||
_parsedText = _bodyLabel.get_parsed_text()
|
|
||||||
_isRevealing = true
|
|
||||||
|
|
||||||
func _process(delta:float) -> void:
|
func _process(delta:float) -> void:
|
||||||
if not visible:
|
if not visible:
|
||||||
@@ -68,6 +72,10 @@ 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
|
||||||
@@ -79,6 +87,40 @@ 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:
|
||||||
|
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:
|
func _processReveal(delta:float) -> void:
|
||||||
if _pauseTimer > 0.0:
|
if _pauseTimer > 0.0:
|
||||||
var speedMult:float = SPEEDUP_MULTIPLIER if Input.is_action_pressed("interact") else 1.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
|
_bodyLabel.visible_characters += 1
|
||||||
var idx:int = _bodyLabel.visible_characters - 1
|
var idx:int = _bodyLabel.visible_characters - 1
|
||||||
|
|
||||||
# Stop if this character has wrapped onto the next page.
|
# Count newlines up to idx to get the current line — no Godot layout call needed.
|
||||||
if idx > 0:
|
var charLine:int = _parsedText.left(idx + 1).count("\n")
|
||||||
var charLine:int = _bodyLabel.get_character_line(idx)
|
if charLine >= _startLine + _linesPerPage:
|
||||||
if charLine >= _startLine + _linesPerPage:
|
_bodyLabel.visible_characters -= 1
|
||||||
_bodyLabel.visible_characters -= 1
|
_onPageFull()
|
||||||
_onPageFull()
|
return false
|
||||||
return false
|
|
||||||
|
|
||||||
if idx < len(_parsedText):
|
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
|
return true
|
||||||
|
|
||||||
@@ -157,7 +203,8 @@ func _processAutoAdvance(delta:float) -> void:
|
|||||||
func _advance() -> void:
|
func _advance() -> void:
|
||||||
_advanceIndicator.visible = false
|
_advanceIndicator.visible = false
|
||||||
_advanceIndicator.modulate.a = 1.0
|
_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:
|
if hasMorePages:
|
||||||
_startLine += _linesPerPage
|
_startLine += _linesPerPage
|
||||||
_bodyLabel.scroll_to_line(_startLine)
|
_bodyLabel.scroll_to_line(_startLine)
|
||||||
|
|||||||
Reference in New Issue
Block a user