Camera 2D

This commit is contained in:
MaddoScientisto 2024-03-04 08:25:41 +01:00
commit 4cb902053d
18 changed files with 791 additions and 24 deletions

104
Scenes/CameraController.gd Normal file
View file

@ -0,0 +1,104 @@
class_name CameraController
extends Camera2D
## Whether to use pixel snap.
@export var pixel_snap: bool = true
## Whether camera movement should be smooth.
@export var enable_smoothing: bool = true
## The current target being followed.
var _active_target: CameraTarget
var _previous_pixel_snap_delta := Vector2.ZERO
## The current camera velocity, for smooth damping.
var _current_velocity := Vector2.ZERO
## The current exact position of the camera.
var _current_position: Vector2
func _ready() -> void:
# Add to group so we can look it up later.
add_to_group("camera_controllers")
## Critically damped spring, based on Game Programming Gems 4 Chapter 1.10. https://archive.org/details/game-programming-gems-4/page/95/mode/2up
## Returns a 2-tuple of [next_position, next_velocity].
func smooth_damp(current: float, target: float, current_velocity: float, smooth_time: float, max_speed: float, delta: float) -> Array[float]:
smooth_time = max(smooth_time, 0.0001)
var omega := 2.0 / smooth_time
var x := omega * delta
var x_exp := 1.0 / (1.0 + x + 0.48 * x * x + 0.235 * x * x * x)
var change := current - target
var original_target := target
# Clamp max speed.
var max_change := max_speed * smooth_time
change = clamp(change, -max_change, max_change)
target = current - change
var temp := (current_velocity + omega * change) * delta
current_velocity = (current_velocity - omega * temp) * x_exp
var output := target + (change + temp) * x_exp
# Prevent overshooting.
if (original_target - current > 0.0) == (output > original_target):
output = original_target
current_velocity = (output - original_target) / delta
return [output, current_velocity]
func _unhandled_input(_event: InputEvent) -> void:
if Input.is_key_pressed(KEY_1):
pixel_snap = not pixel_snap
print("Camera pixel snap: ", pixel_snap)
if Input.is_key_pressed(KEY_2):
enable_smoothing = not enable_smoothing
print("Camera smoothing: ", enable_smoothing)
# It's important that the camera position gets updated in _process instead of _physics_process,
# since it needs to be dependent on frame rate.
func _process(delta: float) -> void:
# Update position.
var next_position: Vector2
var target: Vector2 = _active_target.global_position
if enable_smoothing:
# Handle target movement with smooth_damp.
# Replace this with any smooth follow / lerp of your choosing, but be careful to use `delta`
# properly -- improper use can result in jitter. Don't do lerp(current, target, delta).
var res_x := smooth_damp(_current_position.x, target.x, _current_velocity.x, 0.2, INF, delta)
var res_y := smooth_damp(_current_position.y, target.y, _current_velocity.y, 0.2, INF, delta)
next_position.x = res_x[0]
next_position.y = res_y[0]
_current_velocity.x = res_x[1]
_current_velocity.y = res_y[1]
else:
# Set next camera position to the exact target.
next_position = target
_current_position = next_position
if pixel_snap:
# IMPORTANT: perform pixel snap so that the camera movement doesn't interfere with the
# sub-pixel "smooth movement" we do in the shader.
var snapped_position = (next_position + Vector2(0.5, 0.5)).floor()
_previous_pixel_snap_delta = snapped_position - next_position
next_position = snapped_position
else:
_previous_pixel_snap_delta = Vector2.ZERO
# Set the camera position.
global_position = next_position
# IMPORTANT: Work around godot bug where camera doesn't update immediately: https://github.com/godotengine/godot/issues/74203
force_update_scroll()
## Returns the position delta between the pixel-snapped position and the actual controlled camera position.
## If pixel snap is off, returns Zero.
func get_pixel_snap_delta() -> Vector2:
return _previous_pixel_snap_delta
## Registers a camera target to follow.
func register_target(target: CameraTarget) -> void:
assert(not _active_target)
_active_target = target
_current_position = _active_target.global_position

8
Scenes/CameraTarget.gd Normal file
View file

@ -0,0 +1,8 @@
class_name CameraTarget
extends Node2D
func _ready() -> void:
# Attempt to register with controller.
var result: Node = get_tree().get_first_node_in_group("camera_controllers")
if result and result is CameraController:
result.register_target(self)

View file

@ -0,0 +1,7 @@
extends Node2D
@onready var sub_viewport: SubViewport = $SubViewport
func _unhandled_input(event: InputEvent) -> void:
# SubViewports don't receive input by default, so we have to propagate it.
sub_viewport.push_input(event)

View file

@ -0,0 +1,18 @@
extends Sprite2D
@onready var orig_pos := position
func _ready() -> void:
# It's important that this script gets processed _after_ the "game scene", which includes the camera controller.
# That is why we have placed it after the SubViewport. While we're at it, we can
# use a larger priority number so that this node gets processed later in the process graph.
process_priority = 1000
func _process(_delta: float) -> void:
# Set the pixel snap delta from the camera so that we can have smooth camera movement.
var pixel_snap_delta := Vector2.ZERO
var result: Node = get_tree().get_first_node_in_group("camera_controllers")
if result and result is CameraController:
pixel_snap_delta = result.get_pixel_snap_delta()
# Hard-code scaling ratio of 6 (you'd want to calculate this in a real game).
position = orig_pos + pixel_snap_delta * 6

12
Scenes/game.gd Normal file
View file

@ -0,0 +1,12 @@
extends Node2D
## Whether to use the debug player camera for testing. Otherwise, use the CameraController with non-pixel-perfect settings (hard-coded zoom).
@export var use_debug_player_camera: bool = false
func _ready() -> void:
var camera_controller: CameraController = get_tree().get_first_node_in_group("camera_controllers")
if use_debug_player_camera:
var debug_player_camera: Camera2D = get_tree().get_first_node_in_group("debug_player_camera")
camera_controller.enabled = false
debug_player_camera.zoom = Vector2(1, 1)
debug_player_camera.enabled = true

32
Scenes/game.tscn Normal file
View file

@ -0,0 +1,32 @@
[gd_scene load_steps=6 format=3 uid="uid://cwfaxgr8pgfga"]
[ext_resource type="Script" path="res://Scenes/game.gd" id="1_s0gpj"]
[ext_resource type="Script" path="res://Scenes/PixelPerfectRendering.gd" id="2_gdejn"]
[ext_resource type="PackedScene" uid="uid://bv451a8wgty4u" path="res://Scenes/test.tscn" id="3_l2ygy"]
[ext_resource type="Script" path="res://Scenes/SubViewportSprite.gd" id="4_adb7a"]
[sub_resource type="ViewportTexture" id="ViewportTexture_m5h5j"]
viewport_path = NodePath("PixelPerfectRendering/SubViewport")
[node name="Game" type="Node2D"]
script = ExtResource("1_s0gpj")
[node name="PixelPerfectRendering" type="Node2D" parent="."]
editor_description = "The children of this node are responsible for handling pixel-perfect rendering in the game world."
script = ExtResource("2_gdejn")
[node name="SubViewport" type="SubViewport" parent="PixelPerfectRendering"]
handle_input_locally = false
snap_2d_vertices_to_pixel = true
canvas_item_default_texture_filter = 0
size = Vector2i(322, 182)
render_target_update_mode = 4
[node name="GameScene" parent="PixelPerfectRendering/SubViewport" instance=ExtResource("3_l2ygy")]
[node name="SubViewportSprite" type="Sprite2D" parent="."]
texture_filter = 1
position = Vector2(960, 540)
scale = Vector2(6, 6)
texture = SubResource("ViewportTexture_m5h5j")
script = ExtResource("4_adb7a")

View file

@ -1,16 +1,18 @@
[gd_scene load_steps=20 format=3 uid="uid://bghghp5ep4w2j"]
[gd_scene load_steps=22 format=3 uid="uid://bghghp5ep4w2j"]
[ext_resource type="Script" path="res://Scripts/PlayerMovement.cs" id="1_m27vu"]
[ext_resource type="Texture2D" uid="uid://la06powu57hu" path="res://Sprites/Cirno_Big.png" id="2_bwf6x"]
[ext_resource type="PackedScene" uid="uid://b1qnfiuokpvsr" path="res://Scenes/bullet.tscn" id="2_ov36d"]
[ext_resource type="Script" path="res://addons/smoothing/smoothing_2d.gd" id="4_j4xhu"]
[ext_resource type="Script" path="res://Scenes/CameraTarget.gd" id="5_cxvyt"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_ai4rh"]
size = Vector2(6, 8)
[sub_resource type="AtlasTexture" id="AtlasTexture_omx2u"]
atlas = ExtResource("2_bwf6x")
region = Rect2(0, 0, 8, 16)
[sub_resource type="RectangleShape2D" id="RectangleShape2D_ai4rh"]
size = Vector2(6, 8)
[sub_resource type="AtlasTexture" id="AtlasTexture_pdst4"]
atlas = ExtResource("2_bwf6x")
region = Rect2(0, 0, 8, 16)
@ -139,7 +141,19 @@ BulletScene = ExtResource("2_ov36d")
Muzzle = NodePath("Muzzle")
metadata/_edit_group_ = true
[node name="Sprite2D" type="Sprite2D" parent="."]
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("RectangleShape2D_ai4rh")
[node name="Muzzle" type="Marker2D" parent="."]
position = Vector2(5, 0)
[node name="Node2D" type="Node2D" parent="."]
[node name="Smoothing2D" type="Node2D" parent="."]
script = ExtResource("4_j4xhu")
flags = 55
[node name="Sprite2D" type="Sprite2D" parent="Smoothing2D"]
visible = false
scale = Vector2(2.5, 1.75)
texture = SubResource("AtlasTexture_omx2u")
@ -147,15 +161,10 @@ hframes = 3
vframes = 4
frame = 1
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("RectangleShape2D_ai4rh")
[node name="CameraTarget" type="Node2D" parent="Smoothing2D"]
script = ExtResource("5_cxvyt")
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="Smoothing2D"]
y_sort_enabled = true
sprite_frames = SubResource("SpriteFrames_q0rt3")
animation = &"walk_left"
[node name="Muzzle" type="Marker2D" parent="."]
position = Vector2(5, 0)
[node name="Node2D" type="Node2D" parent="."]

File diff suppressed because one or more lines are too long

21
addons/smoothing/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Lawnjelly
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,7 @@
[plugin]
name="Smoothing"
description="Smoothing nodes for fixed timestep interpolation."
author="Lawnjelly"
version="1.2.1"
script="smoothing_plugin.gd"

View file

@ -0,0 +1,235 @@
# Copyright (c) 2019 Lawnjelly
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
extends Node3D
@export
var target: NodePath:
get:
return target
set(v):
target = v
set_target()
var _m_Target: Node3D
var _m_trCurr: Transform3D
var _m_trPrev: Transform3D
const SF_ENABLED = 1 << 0
const SF_TRANSLATE = 1 << 1
const SF_BASIS = 1 << 2
const SF_SLERP = 1 << 3
const SF_INVISIBLE = 1 << 4
@export_flags("enabled", "translate", "basis", "slerp") var flags: int = SF_ENABLED | SF_TRANSLATE | SF_BASIS:
set(v):
flags = v
# we may have enabled or disabled
_SetProcessing()
get:
return flags
##########################################################################################
# USER FUNCS
# call this checked e.g. starting a level, AFTER moving the target
# so we can update both the previous and current values
func teleport():
var temp_flags = flags
_SetFlags(SF_TRANSLATE | SF_BASIS)
_RefreshTransform()
_m_trPrev = _m_trCurr
# do one frame update to make sure all components are updated
_process(0)
# resume old flags
flags = temp_flags
func set_enabled(bEnable: bool):
_ChangeFlags(SF_ENABLED, bEnable)
_SetProcessing()
func is_enabled():
return _TestFlags(SF_ENABLED)
##########################################################################################
func _ready():
_m_trCurr = Transform3D()
_m_trPrev = Transform3D()
set_process_priority(100)
set_as_top_level(true)
Engine.set_physics_jitter_fix(0.0)
func set_target():
if is_inside_tree():
_FindTarget()
func _set_flags(new_value):
flags = new_value
# we may have enabled or disabled
_SetProcessing()
func _get_flags():
return flags
func _SetProcessing():
var bEnable = _TestFlags(SF_ENABLED)
if _TestFlags(SF_INVISIBLE):
bEnable = false
set_process(bEnable)
set_physics_process(bEnable)
pass
func _enter_tree():
# might have been moved
_FindTarget()
pass
func _notification(what):
match what:
# invisible turns unchecked processing
NOTIFICATION_VISIBILITY_CHANGED:
_ChangeFlags(SF_INVISIBLE, is_visible_in_tree() == false)
_SetProcessing()
func _RefreshTransform():
if _HasTarget() == false:
return
_m_trPrev = _m_trCurr
_m_trCurr = _m_Target.global_transform
func _FindTarget():
_m_Target = null
# If no target has been assigned in the property,
# default to using the parent as the target.
if target.is_empty():
var parent = get_parent_node_3d()
if parent:
_m_Target = parent
return
var targ = get_node(target)
if ! targ:
printerr("ERROR SmoothingNode : Target " + str(target) + " not found")
return
if not targ is Node3D:
printerr("ERROR SmoothingNode : Target " + str(target) + " is not node 3D")
target = ""
return
# if we got to here targ is a spatial
_m_Target = targ
# certain targets are disallowed
if _m_Target == self:
var msg = str(_m_Target.get_name()) + " assigned to " + str(self.get_name()) + "]"
printerr("ERROR SmoothingNode : Target should not be self [", msg)
# error message
#OS.alert("Target cannot be a parent or grandparent in the scene tree.", "SmoothingNode")
_m_Target = null
target = ""
return
func _HasTarget() -> bool:
if _m_Target == null:
return false
# has not been deleted?
if is_instance_valid(_m_Target):
return true
_m_Target = null
return false
func _process(_delta):
var f = Engine.get_physics_interpolation_fraction()
var tr: Transform3D = Transform3D()
# translate
if _TestFlags(SF_TRANSLATE):
var ptDiff = _m_trCurr.origin - _m_trPrev.origin
tr.origin = _m_trPrev.origin + (ptDiff * f)
# rotate
if _TestFlags(SF_BASIS):
if _TestFlags(SF_SLERP):
tr.basis = _m_trPrev.basis.slerp(_m_trCurr.basis, f)
else:
tr.basis = _LerpBasis(_m_trPrev.basis, _m_trCurr.basis, f)
transform = tr
func _physics_process(_delta):
_RefreshTransform()
func _LerpBasis(from: Basis, to: Basis, f: float) -> Basis:
var res: Basis = Basis()
res.x = from.x.lerp(to.x, f)
res.y = from.y.lerp(to.y, f)
res.z = from.z.lerp(to.z, f)
return res
func _SetFlags(f):
flags |= f
func _ClearFlags(f):
flags &= ~f
func _TestFlags(f):
return (flags & f) == f
func _ChangeFlags(f, bSet):
if bSet:
_SetFlags(f)
else:
_ClearFlags(f)

BIN
addons/smoothing/smoothing.png (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bm44ebnxqc82"
path="res://.godot/imported/smoothing.png-6b454a779e636eaa20b6c6ac618bf82a.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/smoothing/smoothing.png"
dest_files=["res://.godot/imported/smoothing.png-6b454a779e636eaa20b6c6ac618bf82a.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View file

@ -0,0 +1,218 @@
# Copyright (c) 2019 Lawnjelly
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
extends Node2D
@export
var target: NodePath:
get:
return target
set(v):
target = v
if is_inside_tree():
_FindTarget()
var _m_Target: Node2D
var _m_Flip:bool = false
var _m_Trans_curr: Transform2D = Transform2D()
var _m_Trans_prev: Transform2D = Transform2D()
const SF_ENABLED = 1 << 0
const SF_GLOBAL_IN = 1 << 1
const SF_GLOBAL_OUT = 1 << 2
const SF_TOP_LEVEL = 1 << 3
const SF_INVISIBLE = 1 << 4
# ADDED: Pixel snap support.
const SF_PIXELSNAP = 1 << 5
@export_flags("enabled", "global in", "global out", "top level", "pixel snap") var flags: int = SF_ENABLED | SF_GLOBAL_IN | SF_GLOBAL_OUT | SF_PIXELSNAP:
set(v):
flags = v
# we may have enabled or disabled
_SetProcessing()
get:
return flags
##########################################################################################
# USER FUNCS
# call this checked e.g. starting a level, AFTER moving the target
# so we can update both the previous and current values
func teleport():
_RefreshTransform()
_m_Trans_prev = _m_Trans_curr
# call frame upate to make sure all components of the node are set
_process(0)
func set_enabled(bEnable: bool):
_ChangeFlags(SF_ENABLED, bEnable)
_SetProcessing()
func is_enabled():
return _TestFlags(SF_ENABLED)
func set_pixel_snap(bPixelSnap: bool):
_ChangeFlags(SF_PIXELSNAP, bPixelSnap)
##########################################################################################
func _ready():
set_process_priority(100)
Engine.set_physics_jitter_fix(0.0)
set_as_top_level(_TestFlags(SF_TOP_LEVEL))
func _SetProcessing():
var bEnable = _TestFlags(SF_ENABLED)
if _TestFlags(SF_INVISIBLE):
bEnable = false
set_process(bEnable)
set_physics_process(bEnable)
set_as_top_level(_TestFlags(SF_TOP_LEVEL))
func _enter_tree():
# might have been moved
_FindTarget()
func _notification(what):
match what:
# invisible turns unchecked processing
NOTIFICATION_VISIBILITY_CHANGED:
_ChangeFlags(SF_INVISIBLE, is_visible_in_tree() == false)
_SetProcessing()
func _RefreshTransform():
if _HasTarget() == false:
return
_m_Trans_prev = _m_Trans_curr
if _TestFlags(SF_GLOBAL_IN):
_m_Trans_curr = _m_Target.get_global_transform()
else:
_m_Trans_curr = _m_Target.get_transform()
_m_Flip = false
# Ideally we would use determinant core function, as in commented line below, but we
# need to workaround for backward compat.
# if (_m_Trans_prev.determinant() < 0) != (_m_Trans_curr.determinant() < 0):
if (_Determinant_Sign(_m_Trans_prev) != _Determinant_Sign(_m_Trans_curr)):
_m_Flip = true
func _Determinant_Sign(t:Transform2D)->bool:
# Workaround Transform2D determinant function not being available
# until 3.6 / 4.1.
# We calculate determinant manually, slower but compatible to lower
# godot versions.
var d = (t.x.x * t.y.y) - (t.x.y * t.y.x)
return d >= 0.0
func _FindTarget():
_m_Target = null
# If no target has been assigned in the property,
# default to using the parent as the target.
if target.is_empty():
var parent = get_parent()
if parent and (parent is Node2D):
_m_Target = parent
return
var targ = get_node(target)
if ! targ:
printerr("ERROR SmoothingNode2D : Target " + str(target) + " not found")
return
if not targ is Node2D:
printerr("ERROR SmoothingNode2D : Target " + str(target) + " is not Node2D")
target = ""
return
# if we got to here targ is correct type
_m_Target = targ
func _HasTarget() -> bool:
if _m_Target == null:
return false
# has not been deleted?
if is_instance_valid(_m_Target):
return true
_m_Target = null
return false
func _process(_delta):
var f = Engine.get_physics_interpolation_fraction()
var tr = Transform2D()
tr.origin = lerp(_m_Trans_prev.origin, _m_Trans_curr.origin, f)
if _TestFlags(SF_PIXELSNAP):
tr.origin = (tr.origin + Vector2(0.5, 0.5)).floor()
tr.x = lerp(_m_Trans_prev.x, _m_Trans_curr.x, f)
tr.y = lerp(_m_Trans_prev.y, _m_Trans_curr.y, f)
# When a sprite flip is detected, turn off interpolation for that tick.
if _m_Flip:
tr = _m_Trans_curr
if _TestFlags(SF_GLOBAL_OUT) and not _TestFlags(SF_TOP_LEVEL):
set_global_transform(tr)
else:
set_transform(tr)
func _physics_process(_delta):
_RefreshTransform()
func _SetFlags(f):
flags |= f
func _ClearFlags(f):
flags &= ~f
func _TestFlags(f):
return (flags & f) == f
func _ChangeFlags(f, bSet):
if bSet:
_SetFlags(f)
else:
_ClearFlags(f)

BIN
addons/smoothing/smoothing_2d.png (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://i3h8ng5fyd1m"
path="res://.godot/imported/smoothing_2d.png-4942c58db397caab18506104d957cac1.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/smoothing/smoothing_2d.png"
dest_files=["res://.godot/imported/smoothing_2d.png-4942c58db397caab18506104d957cac1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View file

@ -0,0 +1,17 @@
@tool
extends EditorPlugin
func _enter_tree():
# Initialization of the plugin goes here
# Add the new type with a name, a parent type, a script and an icon
add_custom_type("Smoothing", "Node3D", preload("smoothing.gd"), preload("smoothing.png"))
add_custom_type("Smoothing2D", "Node2D", preload("smoothing_2d.gd"), preload("smoothing_2d.png"))
pass
func _exit_tree():
# Clean-up of the plugin goes here
# Always remember to remove_at it from the engine when deactivated
remove_custom_type("Smoothing")
remove_custom_type("Smoothing2D")

View file

@ -17,16 +17,17 @@ config/icon="res://icon.svg"
[display]
window/size/viewport_width=640
window/size/viewport_height=480
window/stretch/mode="viewport"
window/stretch/scale=4.0
window/stretch/scale_mode="integer"
window/size/viewport_width=1920
window/size/viewport_height=1080
[dotnet]
project/assembly_name="Cirno"
[editor_plugins]
enabled=PackedStringArray("res://addons/smoothing/plugin.cfg")
[input]
left={
@ -70,10 +71,12 @@ shoot={
2d_physics/layer_2="player"
2d_physics/layer_3="items"
[physics]
common/physics_jitter_fix=0.0
[rendering]
textures/canvas_textures/default_texture_filter=0
renderer/rendering_method="gl_compatibility"
renderer/rendering_method.mobile="gl_compatibility"
2d/snap/snap_2d_transforms_to_pixel=true
2d/snap/snap_2d_vertices_to_pixel=true