fixed the UI shaking at higher framerates

This commit is contained in:
2025-07-23 05:56:01 +10:00
parent 760ffb8b80
commit 773867bd2c
15 changed files with 241 additions and 7 deletions

View File

@@ -4,7 +4,7 @@ extends Node2D
@onready var editButton = $Edit/Button
func _process(delta):
var uiAnimSpeed = Global.main.physicsTimeMultiplier * 20.0 if Global.main else 120.0
var uiAnimSpeed = 8.0 # Fixed UI animation speed
if Rect2(editButton.get_parent().position-Vector2(24,24),editButton.size).has_point(get_local_mouse_position()):
editSprite.scale = lerp(editSprite.scale, Vector2(1.2,1.2), delta * uiAnimSpeed)
else:

View File

@@ -30,7 +30,7 @@ func _process(delta):
if buttons[b] == null:
continue
if Rect2(buttons[b].get_parent().position-Vector2(24,24),buttons[b].size).has_point(get_local_mouse_position()):
var uiAnimSpeed = Global.main.physicsTimeMultiplier * 20.0 if Global.main else 120.0
var uiAnimSpeed = 8.0 # Fixed UI animation speed
sprites[s].scale = lerp(sprites[s].scale, Vector2(1.2,1.2), delta * uiAnimSpeed)
if Input.is_action_pressed("mouse_left"):
sprites[s].scale = Vector2(0.6,0.6)
@@ -51,7 +51,7 @@ func _process(delta):
Global.mouse.text = "Duplicate sprite"
else:
var uiAnimSpeed = Global.main.physicsTimeMultiplier * 20.0 if Global.main else 120.0
var uiAnimSpeed = 8.0 # Fixed UI animation speed
sprites[s].scale = lerp(sprites[s].scale, Vector2(1.0,1.0), delta * uiAnimSpeed)
s += 1

View File

@@ -0,0 +1,144 @@
extends SubViewport
## NDI Avatar Viewport Controller
## Handles avatar-only NDI streaming with synchronized states
#Node Reference
@onready var origin = $OriginMotion/Origin
@onready var ndi_output = $NDIOutput
## Initialize viewport settings for transparent background and avatar mirroring
func _ready():
# Set transparent background
transparent_bg = true
# Connect to the main scene for state sync
if Global.main:
Global.main.connect("spriteVisToggles", Callable(self, "_on_sprite_visibility_toggle"))
Global.main.connect("bounceChange", Callable(self, "_sync_bounce"))
## Sync avatar sprites from main scene to this viewport
func sync_avatar():
# Clear existing sprites
for child in origin.get_children():
child.queue_free()
# Wait for cleanup
await get_tree().process_frame
# Get sprites from main scene
var main_sprites = get_tree().get_nodes_in_group("saved")
for sprite in main_sprites:
clone_sprite(sprite)
## Clone a sprite from main scene to this viewport
func clone_sprite(original_sprite):
# Create new instance
var sprite_scene = preload("res://ui_scenes/selectedSprite/spriteObject.tscn")
var cloned_sprite = sprite_scene.instantiate()
# Copy all properties
cloned_sprite.path = original_sprite.path
cloned_sprite.id = original_sprite.id
cloned_sprite.parentId = original_sprite.parentId
cloned_sprite.displayName = original_sprite.displayName
# Visual properties
cloned_sprite.offset = original_sprite.offset
cloned_sprite.z = original_sprite.z
cloned_sprite.position = original_sprite.position
# Physics properties
cloned_sprite.dragSpeed = original_sprite.dragSpeed
cloned_sprite.xFrq = original_sprite.xFrq
cloned_sprite.xAmp = original_sprite.xAmp
cloned_sprite.yFrq = original_sprite.yFrq
cloned_sprite.yAmp = original_sprite.yAmp
cloned_sprite.rFrq = original_sprite.rFrq
cloned_sprite.rAmp = original_sprite.rAmp
cloned_sprite.rdragStr = original_sprite.rdragStr
cloned_sprite.rLimitMin = original_sprite.rLimitMin
cloned_sprite.rLimitMax = original_sprite.rLimitMax
cloned_sprite.stretchAmount = original_sprite.stretchAmount
cloned_sprite.ignoreBounce = original_sprite.ignoreBounce
# Animation properties
cloned_sprite.frames = original_sprite.frames
cloned_sprite.animSpeed = original_sprite.animSpeed
# Visibility properties
cloned_sprite.showOnTalk = original_sprite.showOnTalk
cloned_sprite.showOnBlink = original_sprite.showOnBlink
cloned_sprite.costumeLayers = original_sprite.costumeLayers.duplicate()
cloned_sprite.spriteOpacity = original_sprite.spriteOpacity
cloned_sprite.affectChildrenOpacity = original_sprite.affectChildrenOpacity
cloned_sprite.blendMode = original_sprite.blendMode
cloned_sprite.toggle = original_sprite.toggle
cloned_sprite.clipped = original_sprite.clipped
# Transform properties
cloned_sprite.staticRotation = original_sprite.staticRotation
cloned_sprite.mirrorHorizontal = original_sprite.mirrorHorizontal
cloned_sprite.mirrorVertical = original_sprite.mirrorVertical
# Wobble sync
cloned_sprite.wobbleSyncGroup = original_sprite.wobbleSyncGroup
# Add to origin
origin.add_child(cloned_sprite)
# Apply current costume visibility
if Global.main:
var current_costume = Global.main.costume
if cloned_sprite.costumeLayers[current_costume - 1] == 1:
cloned_sprite.visible = true
else:
cloned_sprite.visible = false
## Process function to keep sprites in sync with main scene
func _process(delta):
if !Global.main:
return
# Sync origin position with bounce from main scene
$OriginMotion.position.y = 360 + Global.main.bounceChange
# Sync sprites with main scene
var main_sprites = get_tree().get_nodes_in_group("saved")
var avatar_sprites = origin.get_children()
for i in range(min(main_sprites.size(), avatar_sprites.size())):
sync_sprite_state(main_sprites[i], avatar_sprites[i])
## Sync individual sprite state
func sync_sprite_state(main_sprite, avatar_sprite):
if !is_instance_valid(main_sprite) or !is_instance_valid(avatar_sprite):
return
# Sync position and transform
avatar_sprite.position = main_sprite.position
avatar_sprite.rotation = main_sprite.rotation
avatar_sprite.scale = main_sprite.scale
# Sync visibility based on costume
if Global.main:
var current_costume = Global.main.costume
if avatar_sprite.costumeLayers[current_costume - 1] == 1:
avatar_sprite.visible = main_sprite.visible
else:
avatar_sprite.visible = false
# Sync animation frame
if avatar_sprite.has_method("get_sprite") and main_sprite.has_method("get_sprite"):
var avatar_spr = avatar_sprite.get_sprite()
var main_spr = main_sprite.get_sprite()
if avatar_spr and main_spr:
avatar_spr.frame = main_spr.frame
## Handle sprite visibility toggles
func _on_sprite_visibility_toggle():
sync_avatar()
## Sync bounce changes
func _sync_bounce(bounce_change):
$OriginMotion.position.y = 360 + bounce_change

View File

@@ -0,0 +1 @@
uid://dkomsdn2pvjwp

View File

@@ -0,0 +1,18 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://main_scenes/NDIAvatarViewport.gd" id="1"]
[node name="SubViewport" type="SubViewport"]
handle_input_locally = false
size = Vector2i(1280, 720)
render_target_update_mode = 4
script = ExtResource("1")
[node name="NDIOutput" type="NDIOutput"]
name = "PNGTuber Avatar"
enable_editor_output = true
[node name="OriginMotion" type="Node2D"]
position = Vector2(640, 360)
[node name="Origin" type="Node2D" parent="OriginMotion"]

View File

@@ -0,0 +1 @@
uid://c1wo8w4ac848b

View File

@@ -8,7 +8,7 @@ var editMode = true
@onready var controlPanel = $ControlPanel
@onready var editControls = $EditControls
@onready var tutorial = $Tutorial
@onready var spriteViewer = $EditControls/SpriteViewer
@onready var spriteViewer = $EditControls/Container/SpriteViewer
@onready var viewerArrows = $ViewerArrows
@onready var spriteList = $EditControls/SpriteList
@@ -26,6 +26,7 @@ var editMode = true
@onready var shadow = $shadowSprite
@onready var ndi_output = $NDIOutput
@onready var ndi_avatar_viewport = $NDIAvatarViewport
#Scene Reference
@@ -156,6 +157,10 @@ func _ready():
origin.position = s*0.5
updateCameraPosition()
# Initialize avatar viewport
if ndi_avatar_viewport:
ndi_avatar_viewport.visible = false # Start disabled
# Test NDI output on startup
call_deferred("toggle_ndi")
@@ -431,8 +436,17 @@ func add_image(path):
Global.spriteList.updateData()
# Sync avatar to NDI viewport
if ndi_avatar_viewport:
call_deferred("sync_avatar_viewport")
Global.pushUpdate("Added new sprite.")
## Sync avatar viewport with current sprite state
func sync_avatar_viewport():
if ndi_avatar_viewport:
ndi_avatar_viewport.sync_avatar()
#Opens File Dialog
func _on_add_button_pressed():
fileDialog.visible = true
@@ -575,6 +589,10 @@ func _on_load_dialog_file_selected(path):
if sprite.has_method("updateOpacity"):
sprite.updateOpacity()
# Sync avatar to NDI viewport
if ndi_avatar_viewport:
ndi_avatar_viewport.sync_avatar()
Global.pushUpdate("Loaded avatar at: " + path)
onWindowSizeChange()

View File

@@ -1,6 +1,7 @@
[gd_scene load_steps=80 format=4 uid="uid://bakgnwotiysu1"]
[gd_scene load_steps=81 format=4 uid="uid://bakgnwotiysu1"]
[ext_resource type="Script" uid="uid://c4g7pgn40uv4q" path="res://main_scenes/main.gd" id="1_06alx"]
[ext_resource type="Script" uid="uid://dkomsdn2pvjwp" path="res://main_scenes/NDIAvatarViewport.gd" id="1_avatar_viewport"]
[ext_resource type="Script" uid="uid://18kfbk72spvo" path="res://main_scenes/ControlPanel.gd" id="4_5ygs2"]
[ext_resource type="Texture2D" uid="uid://goff8ioh380e" path="res://ui_scenes/button sprites/buttonBack.png" id="4_xenui"]
[ext_resource type="Texture2D" uid="uid://da4jthat2oas4" path="res://ui_scenes/button sprites/fancy.png" id="5_75a7k"]
@@ -14755,7 +14756,21 @@ z_index = -4096
[node name="NDIOutput" type="NDIOutput" parent="."]
name = "PNGTuber-Plus"
audio_bus = &"Master"
[node name="NDIAvatarViewport" type="SubViewport" parent="."]
handle_input_locally = false
size = Vector2i(1280, 720)
render_target_update_mode = 4
script = ExtResource("1_avatar_viewport")
[node name="NDIOutput" type="NDIOutput" parent="NDIAvatarViewport"]
name = "PNGTuber Avatar"
enable_editor_output = true
[node name="OriginMotion" type="Node2D" parent="NDIAvatarViewport"]
position = Vector2(640, 360)
[node name="Origin" type="Node2D" parent="NDIAvatarViewport/OriginMotion"]
[connection signal="pressed" from="ControlPanel/MicButtong/Button" to="." method="_on_button_pressed"]
[connection signal="pressed" from="ControlPanel/SettingsButton/settingsButtons" to="." method="_on_settings_buttons_pressed"]