mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-18 22:23:46 +00:00
Camera 2D
This commit is contained in:
parent
bee29ba9ea
commit
4cb902053d
18 changed files with 791 additions and 24 deletions
104
Scenes/CameraController.gd
Normal file
104
Scenes/CameraController.gd
Normal 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
8
Scenes/CameraTarget.gd
Normal 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)
|
||||||
7
Scenes/PixelPerfectRendering.gd
Normal file
7
Scenes/PixelPerfectRendering.gd
Normal 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)
|
||||||
18
Scenes/SubViewportSprite.gd
Normal file
18
Scenes/SubViewportSprite.gd
Normal 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
12
Scenes/game.gd
Normal 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
32
Scenes/game.tscn
Normal 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")
|
||||||
|
|
@ -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="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="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="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"]
|
[sub_resource type="AtlasTexture" id="AtlasTexture_omx2u"]
|
||||||
atlas = ExtResource("2_bwf6x")
|
atlas = ExtResource("2_bwf6x")
|
||||||
region = Rect2(0, 0, 8, 16)
|
region = Rect2(0, 0, 8, 16)
|
||||||
|
|
||||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_ai4rh"]
|
|
||||||
size = Vector2(6, 8)
|
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id="AtlasTexture_pdst4"]
|
[sub_resource type="AtlasTexture" id="AtlasTexture_pdst4"]
|
||||||
atlas = ExtResource("2_bwf6x")
|
atlas = ExtResource("2_bwf6x")
|
||||||
region = Rect2(0, 0, 8, 16)
|
region = Rect2(0, 0, 8, 16)
|
||||||
|
|
@ -139,7 +141,19 @@ BulletScene = ExtResource("2_ov36d")
|
||||||
Muzzle = NodePath("Muzzle")
|
Muzzle = NodePath("Muzzle")
|
||||||
metadata/_edit_group_ = true
|
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
|
visible = false
|
||||||
scale = Vector2(2.5, 1.75)
|
scale = Vector2(2.5, 1.75)
|
||||||
texture = SubResource("AtlasTexture_omx2u")
|
texture = SubResource("AtlasTexture_omx2u")
|
||||||
|
|
@ -147,15 +161,10 @@ hframes = 3
|
||||||
vframes = 4
|
vframes = 4
|
||||||
frame = 1
|
frame = 1
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
[node name="CameraTarget" type="Node2D" parent="Smoothing2D"]
|
||||||
shape = SubResource("RectangleShape2D_ai4rh")
|
script = ExtResource("5_cxvyt")
|
||||||
|
|
||||||
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
|
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="Smoothing2D"]
|
||||||
y_sort_enabled = true
|
y_sort_enabled = true
|
||||||
sprite_frames = SubResource("SpriteFrames_q0rt3")
|
sprite_frames = SubResource("SpriteFrames_q0rt3")
|
||||||
animation = &"walk_left"
|
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
21
addons/smoothing/LICENSE
Normal 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.
|
||||||
7
addons/smoothing/plugin.cfg
Normal file
7
addons/smoothing/plugin.cfg
Normal 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"
|
||||||
235
addons/smoothing/smoothing.gd
Normal file
235
addons/smoothing/smoothing.gd
Normal 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
BIN
addons/smoothing/smoothing.png
(Stored with Git LFS)
Normal file
Binary file not shown.
34
addons/smoothing/smoothing.png.import
Normal file
34
addons/smoothing/smoothing.png.import
Normal 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
|
||||||
218
addons/smoothing/smoothing_2d.gd
Normal file
218
addons/smoothing/smoothing_2d.gd
Normal 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
BIN
addons/smoothing/smoothing_2d.png
(Stored with Git LFS)
Normal file
Binary file not shown.
34
addons/smoothing/smoothing_2d.png.import
Normal file
34
addons/smoothing/smoothing_2d.png.import
Normal 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
|
||||||
17
addons/smoothing/smoothing_plugin.gd
Normal file
17
addons/smoothing/smoothing_plugin.gd
Normal 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")
|
||||||
|
|
@ -17,16 +17,17 @@ config/icon="res://icon.svg"
|
||||||
|
|
||||||
[display]
|
[display]
|
||||||
|
|
||||||
window/size/viewport_width=640
|
window/size/viewport_width=1920
|
||||||
window/size/viewport_height=480
|
window/size/viewport_height=1080
|
||||||
window/stretch/mode="viewport"
|
|
||||||
window/stretch/scale=4.0
|
|
||||||
window/stretch/scale_mode="integer"
|
|
||||||
|
|
||||||
[dotnet]
|
[dotnet]
|
||||||
|
|
||||||
project/assembly_name="Cirno"
|
project/assembly_name="Cirno"
|
||||||
|
|
||||||
|
[editor_plugins]
|
||||||
|
|
||||||
|
enabled=PackedStringArray("res://addons/smoothing/plugin.cfg")
|
||||||
|
|
||||||
[input]
|
[input]
|
||||||
|
|
||||||
left={
|
left={
|
||||||
|
|
@ -70,10 +71,12 @@ shoot={
|
||||||
2d_physics/layer_2="player"
|
2d_physics/layer_2="player"
|
||||||
2d_physics/layer_3="items"
|
2d_physics/layer_3="items"
|
||||||
|
|
||||||
|
[physics]
|
||||||
|
|
||||||
|
common/physics_jitter_fix=0.0
|
||||||
|
|
||||||
[rendering]
|
[rendering]
|
||||||
|
|
||||||
textures/canvas_textures/default_texture_filter=0
|
textures/canvas_textures/default_texture_filter=0
|
||||||
renderer/rendering_method="gl_compatibility"
|
renderer/rendering_method="gl_compatibility"
|
||||||
renderer/rendering_method.mobile="gl_compatibility"
|
renderer/rendering_method.mobile="gl_compatibility"
|
||||||
2d/snap/snap_2d_transforms_to_pixel=true
|
|
||||||
2d/snap/snap_2d_vertices_to_pixel=true
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue