mirror of
https://github.com/litruv/PNGTuber-Plus.git
synced 2026-07-24 02:26:02 +10:00
Compare commits
7 Commits
v2025.07.2
...
v2025.07.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 56e76c61e6 | |||
| 5dfe68cb81 | |||
| 6e02a67114 | |||
| 32a67a6617 | |||
| fb0c7a9e3b | |||
| f0ce00b23b | |||
| d4a9f5a0b0 |
14
README.md
14
README.md
@@ -29,12 +29,24 @@ Supports combos like Ctrl+Shift+Alt+Cmd. For people with more keybinds than fing
|
||||

|
||||
|
||||
|
||||
### ↗️ Middle Mouse Button Panning
|
||||
### ↗️ Editor Middle Mouse Button Panning
|
||||
|
||||
Hold MMB in the editor to pan around the scene, it'll reset when you go back to live.
|
||||
|
||||

|
||||
|
||||
### ↗️ View Mode MMB Shake
|
||||
|
||||
Hold MMB in view mode to shake your character, Show off them physics!
|
||||
|
||||

|
||||
|
||||
|
||||
🎞️ Delta Time Physics
|
||||
|
||||
Physics are now framerate-independent. Low FPS won’t make your character move in slow motion anymore.
|
||||
Just smooth, consistent jank if that’s your thing. Great for stop-motion vibes.
|
||||
|
||||
|
||||
### 🎤 Microphone Fixes
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ extends Node2D
|
||||
@onready var editButton = $Edit/Button
|
||||
|
||||
func _process(delta):
|
||||
var uiAnimSpeed = Global.main.physicsTimeMultiplier * 20.0 if Global.main else 120.0
|
||||
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),0.2)
|
||||
editSprite.scale = lerp(editSprite.scale, Vector2(1.2,1.2), delta * uiAnimSpeed)
|
||||
else:
|
||||
editSprite.scale = lerp(editSprite.scale,Vector2(1.0,1.0),0.2)
|
||||
editSprite.scale = lerp(editSprite.scale, Vector2(1.0,1.0), delta * uiAnimSpeed)
|
||||
|
||||
@@ -30,7 +30,8 @@ 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()):
|
||||
sprites[s].scale = lerp(sprites[s].scale,Vector2(1.2,1.2),0.2)
|
||||
var uiAnimSpeed = Global.main.physicsTimeMultiplier * 20.0 if Global.main else 120.0
|
||||
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)
|
||||
match b:
|
||||
@@ -50,7 +51,8 @@ func _process(delta):
|
||||
Global.mouse.text = "Duplicate sprite"
|
||||
|
||||
else:
|
||||
sprites[s].scale = lerp(sprites[s].scale,Vector2(1.0,1.0),0.2)
|
||||
var uiAnimSpeed = Global.main.physicsTimeMultiplier * 20.0 if Global.main else 120.0
|
||||
sprites[s].scale = lerp(sprites[s].scale, Vector2(1.0,1.0), delta * uiAnimSpeed)
|
||||
s += 1
|
||||
|
||||
var newColor = Color.DARK_SLATE_GRAY if Global.heldSprite == null else Color.WHITE
|
||||
|
||||
@@ -36,6 +36,10 @@ var yVel = 0
|
||||
var bounceSlider = 250
|
||||
var bounceGravity = 1000
|
||||
|
||||
# Physics timing multiplier - adjust this to fine-tune physics speed
|
||||
# 1.0 = very slow, 10.0 = reasonable, 60.0 = old frame-based speed
|
||||
var physicsTimeMultiplier = 6.0
|
||||
|
||||
#Costumes
|
||||
var costume = 1
|
||||
var bounceOnCostumeChange = false
|
||||
@@ -49,6 +53,12 @@ var initialPanPosition = Vector2.ZERO
|
||||
var initialCameraOffset = Vector2.ZERO
|
||||
var cameraOffset = Vector2.ZERO
|
||||
|
||||
#View Mode Panning
|
||||
var viewModePanning = false
|
||||
var initialViewPanPosition = Vector2.ZERO
|
||||
var viewPanOffset = Vector2.ZERO
|
||||
var viewPanLerpSpeed = 3.0
|
||||
|
||||
var bounceChange = 0.0
|
||||
|
||||
#IMPORTANT
|
||||
@@ -141,18 +151,28 @@ func _ready():
|
||||
func _process(delta):
|
||||
var hold = origin.get_parent().position.y
|
||||
|
||||
origin.get_parent().position.y += yVel * 0.0166
|
||||
origin.get_parent().position.y += yVel * delta * physicsTimeMultiplier
|
||||
if origin.get_parent().position.y > 0:
|
||||
origin.get_parent().position.y = 0
|
||||
bounceChange = hold - origin.get_parent().position.y
|
||||
|
||||
yVel += bounceGravity*0.0166
|
||||
yVel += bounceGravity * delta * physicsTimeMultiplier
|
||||
|
||||
# Handle view mode panning lerp back to center
|
||||
if !editMode:
|
||||
if !viewModePanning:
|
||||
# Lerp back to center when not actively panning
|
||||
viewPanOffset = viewPanOffset.lerp(Vector2.ZERO, viewPanLerpSpeed * delta)
|
||||
|
||||
# Apply view pan offset to the root character (affects physics)
|
||||
origin.position = (get_viewport().get_visible_rect().size * 0.5) + viewPanOffset
|
||||
updateCameraPosition()
|
||||
|
||||
if Input.is_action_just_pressed("openFolder"):
|
||||
OS.shell_open(ProjectSettings.globalize_path("user://"))
|
||||
|
||||
moveSpriteMenu(delta)
|
||||
zoomScene()
|
||||
zoomScene(delta)
|
||||
|
||||
fileSystemOpen = isFileSystemOpen()
|
||||
|
||||
@@ -173,15 +193,24 @@ func _input(event):
|
||||
isPanning = false
|
||||
else:
|
||||
if event.pressed:
|
||||
# Reset camera in view mode
|
||||
resetCameraPosition()
|
||||
# Start view mode panning
|
||||
viewModePanning = true
|
||||
initialViewPanPosition = event.global_position
|
||||
else:
|
||||
# Stop view mode panning
|
||||
viewModePanning = false
|
||||
|
||||
# Handle mouse movement during panning
|
||||
# Handle mouse movement during edit mode panning
|
||||
elif editMode and event is InputEventMouseMotion and isPanning:
|
||||
var totalDelta = event.global_position - initialPanPosition
|
||||
|
||||
# Calculate new camera offset from initial position plus total movement
|
||||
cameraOffset = initialCameraOffset - totalDelta
|
||||
# Scale pan distance based on zoom level for more intuitive panning
|
||||
# Higher zoom (closer) = less movement, lower zoom (farther) = more movement
|
||||
var zoomScale = 1.0 / camera.zoom.x
|
||||
var scaledDelta = totalDelta * zoomScale
|
||||
|
||||
# Calculate new camera offset from initial position plus scaled movement
|
||||
cameraOffset = initialCameraOffset - scaledDelta
|
||||
|
||||
# Update camera position immediately
|
||||
var s = get_viewport().get_visible_rect().size
|
||||
@@ -194,6 +223,15 @@ func _input(event):
|
||||
viewerArrows.position = editControls.position
|
||||
pushUpdates.position.y = controlPanel.position.y
|
||||
pushUpdates.position.x = editControls.position.x
|
||||
|
||||
# Handle mouse movement during view mode panning
|
||||
elif !editMode and event is InputEventMouseMotion and viewModePanning:
|
||||
var totalDelta = event.global_position - initialViewPanPosition
|
||||
|
||||
# Scale pan distance for view mode (less sensitive than edit mode)
|
||||
# Invert the movement so dragging right moves character right (not left)
|
||||
var panSensitivity = 0.5
|
||||
viewPanOffset = -totalDelta * panSensitivity
|
||||
|
||||
func followShadow():
|
||||
# Shadow disabled for selected sprites
|
||||
@@ -235,7 +273,7 @@ func onWindowSizeChange():
|
||||
updateCameraPosition()
|
||||
spriteList.position.x = s.x - 233
|
||||
|
||||
func zoomScene():
|
||||
func zoomScene(delta):
|
||||
#Handles Zooming
|
||||
if Input.is_action_pressed("control"):
|
||||
if Input.is_action_just_pressed("scrollUp"):
|
||||
@@ -249,7 +287,7 @@ func zoomScene():
|
||||
scaleOverall -= 10
|
||||
changeZoom()
|
||||
|
||||
$ControlPanel/ZoomLabel.modulate.a = lerp($ControlPanel/ZoomLabel.modulate.a,0.0,0.02)
|
||||
$ControlPanel/ZoomLabel.modulate.a = lerp($ControlPanel/ZoomLabel.modulate.a, 0.0, delta * (physicsTimeMultiplier * 1.2))
|
||||
|
||||
func changeZoom():
|
||||
var newZoom = Vector2(1.0,1.0) / camera.zoom
|
||||
@@ -296,6 +334,8 @@ func updateCameraPosition():
|
||||
func resetCameraPosition():
|
||||
cameraOffset = Vector2.ZERO
|
||||
isPanning = false
|
||||
viewPanOffset = Vector2.ZERO
|
||||
viewModePanning = false
|
||||
updateCameraPosition()
|
||||
Global.pushUpdate("Camera reset to center.")
|
||||
|
||||
@@ -315,6 +355,10 @@ func swapMode():
|
||||
# Reset camera when entering view mode
|
||||
if !editMode:
|
||||
resetCameraPosition()
|
||||
else:
|
||||
# Reset view mode panning when entering edit mode
|
||||
viewPanOffset = Vector2.ZERO
|
||||
viewModePanning = false
|
||||
|
||||
get_viewport().transparent_bg = !editMode
|
||||
if Global.backgroundColor.a != 0.0:
|
||||
@@ -660,9 +704,9 @@ func moveSpriteMenu(delta):
|
||||
|
||||
|
||||
if $EditControls/MoveMenuUp.overlaps_area(Global.mouse.area):
|
||||
Global.spriteEdit.position.y += (delta*432.0)
|
||||
Global.spriteEdit.position.y += (delta * 432.0 * physicsTimeMultiplier)
|
||||
elif $EditControls/MoveMenuDown.overlaps_area(Global.mouse.area):
|
||||
Global.spriteEdit.position.y -= (delta*432.0)
|
||||
Global.spriteEdit.position.y -= (delta * 432.0 * physicsTimeMultiplier)
|
||||
|
||||
if Global.spriteEdit.position.y > 66:
|
||||
Global.spriteEdit.position.y = 66
|
||||
|
||||
@@ -14358,6 +14358,7 @@ text = "Zoom : 100%"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="VersionLabels" type="Node2D" parent="ControlPanel"]
|
||||
position = Vector2(-284, 59)
|
||||
|
||||
[node name="Label" type="Label" parent="ControlPanel/VersionLabels"]
|
||||
offset_left = -713.0
|
||||
@@ -14377,13 +14378,21 @@ label_settings = SubResource("LabelSettings_qg0do")
|
||||
|
||||
[node name="versionNo" type="Label" parent="ControlPanel/VersionLabels/Label2"]
|
||||
layout_mode = 0
|
||||
offset_left = 144.0
|
||||
offset_top = 40.0
|
||||
offset_right = 282.0
|
||||
offset_bottom = 74.0
|
||||
offset_left = 104.0
|
||||
offset_top = 11.0
|
||||
offset_right = 242.0
|
||||
offset_bottom = 54.0
|
||||
text = "v1.4.5"
|
||||
label_settings = SubResource("LabelSettings_xvf50")
|
||||
|
||||
[node name="Label3" type="Label" parent="ControlPanel/VersionLabels"]
|
||||
offset_left = -566.0
|
||||
offset_top = -93.0
|
||||
offset_right = -428.0
|
||||
offset_bottom = -59.0
|
||||
text = "Litruv Edition"
|
||||
label_settings = SubResource("LabelSettings_qg0do")
|
||||
|
||||
[node name="EditControls" type="Node2D" parent="."]
|
||||
z_index = 4090
|
||||
script = ExtResource("17_bf1ic")
|
||||
|
||||
@@ -79,12 +79,13 @@ var ignoreBounce = false
|
||||
#Animation
|
||||
var frames = 1
|
||||
var animSpeed = 0
|
||||
var animTime = 0.0 # Time accumulator for animations
|
||||
|
||||
var remadePolygon = false
|
||||
|
||||
var clipped = false
|
||||
|
||||
var tick = 0
|
||||
var time = 0.0 # Time accumulator for physics calculations
|
||||
|
||||
#Opacity
|
||||
var spriteOpacity = 1.0
|
||||
@@ -224,7 +225,7 @@ func replaceSprite(pathNew):
|
||||
remakePolygon()
|
||||
|
||||
func _process(delta):
|
||||
tick += 1
|
||||
time += delta
|
||||
if Global.heldSprite == self:
|
||||
|
||||
grabArea.visible = true
|
||||
@@ -251,13 +252,17 @@ func _process(delta):
|
||||
|
||||
talkBlink()
|
||||
|
||||
animation()
|
||||
animation(delta)
|
||||
|
||||
func animation():
|
||||
|
||||
var speed = max(float(animSpeed),Engine.max_fps*6.0)
|
||||
func animation(delta):
|
||||
if animSpeed > 0 and frames > 1:
|
||||
if Global.animationTick % int((speed)/float(animSpeed)) == 0:
|
||||
animTime += delta
|
||||
# Convert animSpeed to frames per second - no additional scaling needed since this controls frame rate
|
||||
var frameRate = animSpeed / 60.0 # Normalize from old frame-based system
|
||||
var frameInterval = 1.0 / frameRate
|
||||
|
||||
if animTime >= frameInterval:
|
||||
animTime -= frameInterval
|
||||
if sprite.frame == frames - 1:
|
||||
sprite.frame = 0
|
||||
else:
|
||||
@@ -348,31 +353,46 @@ func drag(delta):
|
||||
if dragSpeed == 0:
|
||||
dragger.global_position = wob.global_position
|
||||
else:
|
||||
dragger.global_position = lerp(dragger.global_position,wob.global_position,1/dragSpeed)
|
||||
# Convert dragSpeed to a proper delta-time lerp factor
|
||||
# Higher dragSpeed = slower movement, so we invert it
|
||||
# Use Global.main.physicsTimeMultiplier for consistent physics timing
|
||||
var physicsMultiplier = Global.main.physicsTimeMultiplier if Global.main else 10.0
|
||||
var lerpFactor = clamp(delta * (physicsMultiplier * 10.0 / max(dragSpeed, 0.1)), 0.0, 1.0)
|
||||
dragger.global_position = lerp(dragger.global_position, wob.global_position, lerpFactor)
|
||||
dragOrigin.global_position = dragger.global_position
|
||||
|
||||
func wobble():
|
||||
wob.position.x = sin(tick*xFrq)*xAmp
|
||||
wob.position.y = sin(tick*yFrq)*yAmp
|
||||
# Use Global.main.physicsTimeMultiplier for consistent physics timing
|
||||
var physicsMultiplier = Global.main.physicsTimeMultiplier if Global.main else 10.0
|
||||
var scaledTime = time * physicsMultiplier
|
||||
wob.position.x = sin(scaledTime * xFrq) * xAmp
|
||||
wob.position.y = sin(scaledTime * yFrq) * yAmp
|
||||
|
||||
func rotationalDrag(length,delta):
|
||||
func rotationalDrag(length, delta):
|
||||
var yvel = (length * rdragStr)
|
||||
|
||||
#Calculate Max angle
|
||||
yvel = clamp(yvel,rLimitMin,rLimitMax)
|
||||
yvel = clamp(yvel, rLimitMin, rLimitMax)
|
||||
|
||||
# Add rotation wobble as percentage of rotational limits range
|
||||
var rotationRange = rLimitMax - rLimitMin
|
||||
var rotationWobble = sin(tick*rFrq) * (rotationRange * rAmp * 0.01)
|
||||
var physicsMultiplier = Global.main.physicsTimeMultiplier if Global.main else 10.0
|
||||
var scaledTime = time * physicsMultiplier
|
||||
var rotationWobble = sin(scaledTime * rFrq) * (rotationRange * rAmp * 0.01)
|
||||
var totalRotation = yvel + rotationWobble
|
||||
|
||||
sprite.rotation = lerp_angle(sprite.rotation,deg_to_rad(totalRotation),0.25)
|
||||
# Use delta-based lerp for smooth rotation with slower speed
|
||||
var lerpFactor = clamp(delta * (physicsMultiplier * 1.0), 0.0, 1.0) # Reduced from 15.0 to 4.0
|
||||
sprite.rotation = lerp_angle(sprite.rotation, deg_to_rad(totalRotation), lerpFactor)
|
||||
|
||||
func stretch(length,delta):
|
||||
func stretch(length, delta):
|
||||
var yvel = (length * stretchAmount * 0.01)
|
||||
var target = Vector2(1.0-yvel,1.0+yvel)
|
||||
var target = Vector2(1.0 - yvel, 1.0 + yvel)
|
||||
|
||||
sprite.scale = lerp(sprite.scale,target,0.5)
|
||||
# Use delta-based lerp for smooth stretching
|
||||
var physicsMultiplier = Global.main.physicsTimeMultiplier if Global.main else 10.0
|
||||
var lerpFactor = clamp(delta * (physicsMultiplier * 30.0), 0.0, 1.0)
|
||||
sprite.scale = lerp(sprite.scale, target, lerpFactor)
|
||||
|
||||
func changeCollision(enable):
|
||||
grabArea.monitorable = enable
|
||||
|
||||
Reference in New Issue
Block a user