feat: Optimized ESCCamera (#434)

Co-authored-by: Dennis Ploeger <develop@dieploegers.de>
This commit is contained in:
Dennis Ploeger
2021-11-12 16:55:26 +01:00
committed by GitHub
parent c325c7f66d
commit 15b3e30e28
8 changed files with 342 additions and 267 deletions

View File

@@ -39,6 +39,6 @@ func run(command_params: Array) -> int:
.push(
escoria.object_manager.get_object(command_params[0]).node,
command_params[1],
command_params[2]
Tween.new().get("TRANS_%s" % command_params[2])
)
return ESCExecution.RC_OK

View File

@@ -13,7 +13,12 @@ class_name CameraShiftCommand
func configure() -> ESCCommandArgumentDescriptor:
return ESCCommandArgumentDescriptor.new(
2,
[TYPE_INT, TYPE_INT, [TYPE_INT, TYPE_REAL], TYPE_STRING],
[
[TYPE_INT, TYPE_REAL],
[TYPE_INT, TYPE_REAL],
[TYPE_INT, TYPE_REAL],
TYPE_STRING
],
[null, null, 1, "QUAD"]
)
@@ -22,9 +27,11 @@ func configure() -> ESCCommandArgumentDescriptor:
func run(command_params: Array) -> int:
(escoria.object_manager.get_object("_camera").node as ESCCamera)\
.shift(
command_params[0],
command_params[1],
Vector2(
command_params[0],
command_params[1]
),
command_params[2],
command_params[3]
Tween.new().get("TRANS_%s" % command_params[3])
)
return ESCExecution.RC_OK

View File

@@ -124,6 +124,10 @@ export(float) var v_speed_damp: float = 1.0
export(NodePath) var animation_player_node: NodePath = "" \
setget _set_animation_player_node
# The node that references the camera position and zoom if this item is used
# as a camera target
export(NodePath) var camera_node
# ESCAnimationsResource (for walking, idling...)
var animations: ESCAnimationResource
@@ -456,6 +460,15 @@ func stop_talking():
)
# Return the camera position if a camera_position_node exists or the
# global position of the player
func get_camera_node():
if camera_node and get_node(camera_node):
return get_node(camera_node)
return self
# Detect the child nodes and set respective references
func _detect_children() -> void:
# Initialize collision variable.

View File

@@ -4,8 +4,7 @@ extends ESCItem
class_name ESCPlayer, "res://addons/escoria-core/design/esc_player.svg"
# The node that references the camera position
export(NodePath) var camera_position_node
# Wether the player can be selected like an item
export(bool) var selectable = false
@@ -23,11 +22,3 @@ func _ready():
else:
tooltip_name = ""
disconnect("input_event", self, "manage_input")
# Return the camera position if a camera_position_node exists or the
# global position of the player
func get_camera_pos():
if camera_position_node and get_node(camera_position_node):
return get_node(camera_position_node).global_position
return global_position

View File

@@ -4,23 +4,36 @@ class_name ESCCamera
# Reference to the tween node for animating camera movements
var tween
var _tween: Tween
# Target position of the camera
var target: Vector2 = Vector2()
var _target: Vector2 = Vector2()
# The object to follow
var follow_target: Node = null
var _follow_target: Node = null
# Target zoom of the camera
var zoom_target: Vector2
# Time of zoom
var zoom_time
var _zoom_target: Vector2
# This is needed to adjust dialog positions and such, see dialog_instance.gd
var zoom_transform
# Prepare the tween
func _ready():
_tween = Tween.new()
add_child(_tween)
_tween.connect("tween_all_completed", self, "_target_reached")
escoria.object_manager.register_object(
ESCObject.new(
"_camera",
self
),
true
)
# Update the position if the followed target is moving
func _process(_delta):
if is_instance_valid(_follow_target) and not _tween.is_active() and _follow_target.has_moved():
self.global_position = _follow_target.global_position
# Sets camera limits so it doesn't go out of the scene
@@ -35,30 +48,34 @@ func set_limits(limits: ESCCameraLimits):
self.limit_bottom = limits.limit_bottom
# Resolve the correct position and zoom of the target object
#
# #### Parameters
# - p_target: The target to resolve
func _resolve_target_and_zoom(p_target) -> void:
target = Vector2()
zoom_target = Vector2()
follow_target = null
_target = Vector2()
_zoom_target = Vector2()
_follow_target = null
if p_target is Node and "is_movable" in p_target and p_target.is_movable:
_follow_target = p_target
if p_target is Vector2:
target = p_target
elif p_target is Array:
_target = p_target
elif p_target is Array and p_target.size() > 0:
var target_pos = Vector2()
for obj in p_target:
target_pos += obj.get_camera_pos()
# Let the error in if an empty array was passed (divzero)
target = target_pos / p_target.size()
elif p_target is Node and p_target.has_node("camera_pos") and \
p_target.get_node("camera_pos") is Camera2D:
target = p_target.get_node("camera_pos").global_position
zoom_target = p_target.get_node("camera_pos").zoom
elif p_target is Node and "is_movable" in p_target and p_target.is_movable:
follow_target = p_target
elif p_target.has_method("get_camera_pos"):
target = p_target.get_camera_pos()
_target = target_pos / p_target.size()
elif p_target.has_method("get_camera_node"):
if "global_position" in p_target.get_camera_node():
_target = p_target.get_camera_node().global_position
if "zoom" in p_target.get_camera_node():
_zoom_target = p_target.get_camera_node().zoom
else:
target = p_target.global_position
_target = p_target.global_position
func set_drag_margin_enabled(p_dm_h_enabled, p_dm_v_enabled):
@@ -66,147 +83,192 @@ func set_drag_margin_enabled(p_dm_h_enabled, p_dm_v_enabled):
self.drag_margin_v_enabled = p_dm_v_enabled
# Set the target for the camera
#
# #### Parameters
# - p_target: Object to target
# - p_speed: Number of seconds for the camera to reach the target
func set_target(p_target, p_speed : float = 0.0):
var speed = p_speed
_resolve_target_and_zoom(p_target)
if not follow_target == null:
target = follow_target.global_position
escoria.logger.info("Current camera position = " + str(self.global_position))
escoria.logger.info(
"Current camera position = %s " % str(self.global_position)
)
if speed == 0.0:
self.global_position = target
self.global_position = _target
else:
var time = self.global_position.distance_to(target) / speed
var time = self.global_position.distance_to(_target) / speed
if tween.is_active():
var tweenstat = String(tween.tell()) + "/" + String(tween.get_runtime())
escoria.logger.report_warnings("camera.gd:set_target()",
["Tween still active running camera_set_target: " + tweenstat])
tween.emit_signal("tween_completed")
if _tween.is_active():
escoria.logger.report_warnings(
"esc_camera.gd:set_target()",
[
"Tween is still active: %f/%f" % [
_tween.tell(),
_tween.get_runtime()
]
]
)
_tween.emit_signal("tween_completed")
tween.interpolate_property(
_tween.interpolate_property(
self,
"global_position",
self.global_position,
target,
_target,
time,
Tween.TRANS_LINEAR,
Tween.EASE_IN_OUT
)
tween.start()
_tween.start()
func set_camera_zoom(p_zoom_level, p_time):
# Set the camera zoom level
#
# #### Parameters
# - p_zoom_level: Zoom level to set
# - p_time: Number of seconds for the camera to reach the zoom level
func set_camera_zoom(p_zoom_level: float, p_time: float):
if p_zoom_level <= 0.0:
escoria.logger.report_errors("camera.gd:set_camera_zoom()",
["Tried to set negative or zero zoom level"])
escoria.logger.report_errors(
"camera.gd:set_camera_zoom()",
["Tried to set negative or zero zoom level"]
)
zoom_time = p_time
zoom_target = Vector2(1, 1) * p_zoom_level
_zoom_target = Vector2(1, 1) * p_zoom_level
if zoom_time == 0:
self.zoom = zoom_target
if p_time == 0:
self.zoom = _zoom_target
else:
if tween.is_active():
var tweenstat = String(tween.tell()) + "/" + String(tween.get_runtime())
escoria.logger.report_warnings("camera",
["Tween still active running camera_set_zoom: " + tweenstat])
tween.emit_signal("tween_completed")
if _tween.is_active():
escoria.logger.report_warnings(
"esc_camera.gd:set_camera_zoom()",
[
"Tween is still active: %f/%f" % [
_tween.tell(),
_tween.get_runtime()
]
]
)
_tween.emit_signal("tween_completed")
tween.interpolate_property(self, "zoom", self.zoom, zoom_target,
zoom_time, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
tween.start()
_tween.interpolate_property(
self,
"zoom",
self.zoom,
_zoom_target,
p_time,
Tween.TRANS_LINEAR,
Tween.EASE_IN_OUT
)
_tween.start()
func push(p_target, p_time, p_type):
var time = float(p_time)
var type = "TRANS_" + p_type
# Push the camera towards the target in terms of position and zoom level
# using a given transition type and time.
# See
# https://docs.godotengine.org/en/stable/classes/class_tween.html#enumerations
#
# #### Parameters
# - p_target: Target to push to
# - p_time: Number of seconds for the transition to take
# - p_type: Tween transition type
func push(p_target, p_time: float = 0.0, p_type: int = 0):
_resolve_target_and_zoom(p_target)
var push_target = null
if follow_target != null:
if _follow_target != null:
push_target = p_target.position
else:
push_target = target
push_target = _target
if time == 0:
if p_time == 0:
self.global_position = push_target
if zoom_target != Vector2():
self.zoom = zoom_target
if _zoom_target != Vector2():
self.zoom = _zoom_target
else:
if tween.is_active():
var tweenstat = String(tween.tell()) + "/" + String(tween.get_runtime())
escoria.logger.report_warnings("camera",
["Tween still active running camera_push: " + tweenstat])
tween.emit_signal("tween_completed", null, null)
if _tween.is_active():
escoria.logger.report_warnings(
"esc_camera.gd:push()",
[
"Tween is still active:" % [
_tween.tell(),
_tween.get_runtime()
]
]
)
_tween.emit_signal("tween_completed", null, null)
if zoom_target != Vector2():
tween.interpolate_property(
if _zoom_target != Vector2():
_tween.interpolate_property(
self,
"zoom",
self.zoom,
zoom_target,
time,
tween.get(type),
_zoom_target,
p_time,
p_type,
Tween.EASE_IN_OUT
)
tween.interpolate_property(
_tween.interpolate_property(
self,
"global_position",
self.global_position,
push_target,
time,
tween.get(type),
p_time,
p_type,
Tween.EASE_IN_OUT
)
tween.start()
_tween.start()
func shift(p_x, p_y, p_time, p_type):
follow_target = null
var x = int(p_x)
var y = int(p_y)
var time = float(p_time)
var type = "TRANS_" + p_type
# Shift the camera by the given vector in a given time and using a specific
# Tween transition type.
#
# See
# https://docs.godotengine.org/en/stable/classes/class_tween.html#enumerations
#
# #### Parameters
# - p_target: Vector to shift the camera by
# - p_time: Number of seconds for the transition to take
# - p_type: Tween transition type
func shift(p_target: Vector2, p_time: float, p_type: int):
_follow_target = null
var new_pos = self.global_position + Vector2(x, y)
target = new_pos
var new_pos = self.global_position + p_target
_target = new_pos
if tween.is_active():
var tweenstat = String(tween.tell()) + "/" + String(tween.get_runtime())
escoria.logger.report_warnings("camera",
["Tween still active running camera_shift: " + tweenstat])
tween.emit_signal("tween_completed")
if _tween.is_active():
escoria.logger.report_warnings(
"esc_camera.gd:set_camera_zoom()",
[
"Tween is still active: %f/%f" % [
_tween.tell(),
_tween.get_runtime()
]
]
)
_tween.emit_signal("tween_completed")
tween.interpolate_property(self, "global_position", self.global_position,
new_pos, float(time), tween.get(type), Tween.EASE_IN_OUT)
tween.start()
func target_reached():
tween.stop_all()
func _process(_delta):
zoom_transform = self.get_canvas_transform()
if is_instance_valid(follow_target) and not tween.is_active() and follow_target.has_moved():
self.global_position = follow_target.global_position
func _ready():
tween = Tween.new()
add_child(tween)
tween.connect("tween_all_completed", self, "target_reached")
escoria.object_manager.register_object(
ESCObject.new(
"_camera",
self
),
true
_tween.interpolate_property(
self,
"global_position",
self.global_position,
new_pos,
p_time,
p_type,
Tween.EASE_IN_OUT
)
_tween.start()
func _target_reached():
_tween.stop_all()