mirror of
https://github.com/tonytins/citylimits
synced 2025-06-26 01:44:42 -04:00
Lots of stuff I forgot to commit because Holidays
- D&D dice engine (see README) - Markdown support - Phantom camera
This commit is contained in:
parent
9589acd877
commit
2b41f84b05
125 changed files with 13170 additions and 23 deletions
5
addons/phantom_camera/scripts/group_names.gd
Normal file
5
addons/phantom_camera/scripts/group_names.gd
Normal file
|
@ -0,0 +1,5 @@
|
|||
@tool
|
||||
extends RefCounted
|
||||
|
||||
const PCAM_GROUP_NAME: StringName = "phantom_camera_group"
|
||||
const PCAM_HOST_GROUP_NAME: StringName = "phantom_camera_host_group"
|
|
@ -0,0 +1,477 @@
|
|||
@tool
|
||||
@icon("res://addons/phantom_camera/icons/PhantomCameraIcon2D.svg")
|
||||
class_name PhantomCamera2D
|
||||
extends Node2D
|
||||
|
||||
const Constants = preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd")
|
||||
var Properties = preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_properties.gd").new()
|
||||
|
||||
const FOLLOW_GROUP_ZOOM_AUTO: StringName = Constants.FOLLOW_PARAMETERS_NAME + "auto_zoom"
|
||||
const FOLLOW_GROUP_ZOOM_MIN: StringName = Constants.FOLLOW_PARAMETERS_NAME + "min_zoom"
|
||||
const FOLLOW_GROUP_ZOOM_MAX: StringName = Constants.FOLLOW_PARAMETERS_NAME + "max_zoom"
|
||||
const FOLLOW_GROUP_ZOOM_MARGIN: StringName = Constants.FOLLOW_PARAMETERS_NAME + "zoom_margin"
|
||||
var follow_group_zoom_auto: bool
|
||||
var follow_group_zoom_min: float = 1
|
||||
var follow_group_zoom_max: float = 5
|
||||
var follow_group_zoom_margin: Vector4
|
||||
|
||||
var _camera_offset: Vector2
|
||||
|
||||
func _get_property_list() -> Array:
|
||||
var property_list: Array[Dictionary]
|
||||
property_list.append_array(Properties.add_priority_properties())
|
||||
|
||||
property_list.append({
|
||||
"name": Constants.ZOOM_PROPERTY_NAME,
|
||||
"type": TYPE_VECTOR2,
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
|
||||
property_list.append_array(Properties.add_follow_mode_property())
|
||||
|
||||
if Properties.follow_mode != Constants.FollowMode.NONE:
|
||||
property_list.append_array(Properties.add_follow_target_property())
|
||||
|
||||
if Properties.follow_mode == Constants.FollowMode.GROUP:
|
||||
property_list.append({
|
||||
"name": FOLLOW_GROUP_ZOOM_AUTO,
|
||||
"type": TYPE_BOOL,
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
if follow_group_zoom_auto:
|
||||
property_list.append({
|
||||
"name": FOLLOW_GROUP_ZOOM_MIN,
|
||||
"type": TYPE_FLOAT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0.01, 100, 0.01,",
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
|
||||
property_list.append({
|
||||
"name": FOLLOW_GROUP_ZOOM_MAX,
|
||||
"type": TYPE_FLOAT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0.01, 100, 0.01,",
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
|
||||
property_list.append({
|
||||
"name": FOLLOW_GROUP_ZOOM_MARGIN,
|
||||
"type": TYPE_VECTOR4,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0, 100, 0.01,",
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
|
||||
if Properties.follow_has_target || Properties.has_follow_group:
|
||||
property_list.append_array(Properties.add_follow_properties())
|
||||
property_list.append_array(Properties.add_follow_framed())
|
||||
|
||||
property_list.append_array(Properties.add_tween_properties())
|
||||
|
||||
property_list.append_array(Properties.add_secondary_properties())
|
||||
|
||||
return property_list
|
||||
|
||||
|
||||
func _set(property: StringName, value) -> bool:
|
||||
Properties.set_priority_property(property, value, self)
|
||||
|
||||
# ZOOM
|
||||
if property == Constants.ZOOM_PROPERTY_NAME:
|
||||
if value.x == 0:
|
||||
Properties.zoom.x = 0.001
|
||||
else:
|
||||
Properties.zoom.x = value.x
|
||||
|
||||
if value.y == 0:
|
||||
Properties.zoom.y = 0.001
|
||||
else:
|
||||
Properties.zoom.y = value.y
|
||||
|
||||
# ZOOM CLAMP
|
||||
if property == FOLLOW_GROUP_ZOOM_AUTO:
|
||||
follow_group_zoom_auto = value
|
||||
notify_property_list_changed()
|
||||
|
||||
if property == FOLLOW_GROUP_ZOOM_MIN:
|
||||
if value > 0:
|
||||
follow_group_zoom_min = value
|
||||
else:
|
||||
follow_group_zoom_min = 0
|
||||
|
||||
if property == FOLLOW_GROUP_ZOOM_MAX:
|
||||
if value > 0:
|
||||
follow_group_zoom_max = value
|
||||
else:
|
||||
follow_group_zoom_max = 0
|
||||
|
||||
if property == FOLLOW_GROUP_ZOOM_MARGIN:
|
||||
follow_group_zoom_margin = value
|
||||
|
||||
Properties.set_follow_properties(property, value, self)
|
||||
Properties.set_tween_properties(property, value, self)
|
||||
Properties.set_secondary_properties(property, value, self)
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func _get(property: StringName):
|
||||
if property == Constants.PRIORITY_PROPERTY_NAME: return Properties.priority
|
||||
|
||||
if property == Constants.ZOOM_PROPERTY_NAME: return Properties.zoom
|
||||
|
||||
if property == Constants.FOLLOW_MODE_PROPERTY_NAME: return Properties.follow_mode
|
||||
if property == Constants.FOLLOW_TARGET_OFFSET_PROPERTY_NAME: return Properties.follow_target_offset_2D
|
||||
if property == Constants.FOLLOW_TARGET_PROPERTY_NAME: return Properties.follow_target_path
|
||||
if property == Constants.FOLLOW_GROUP_PROPERTY_NAME: return Properties.follow_group_paths
|
||||
|
||||
if property == Constants.FOLLOW_PATH_PROPERTY_NAME: return Properties.follow_path_path
|
||||
|
||||
if property == Constants.FOLLOW_FRAMED_DEAD_ZONE_HORIZONTAL_NAME: return Properties.follow_framed_dead_zone_width
|
||||
if property == Constants.FOLLOW_FRAMED_DEAD_ZONE_VERTICAL_NAME: return Properties.follow_framed_dead_zone_height
|
||||
if property == Constants.FOLLOW_VIEWFINDER_IN_PLAY_NAME: return Properties.show_viewfinder_in_play
|
||||
|
||||
if property == FOLLOW_GROUP_ZOOM_AUTO: return follow_group_zoom_auto
|
||||
if property == FOLLOW_GROUP_ZOOM_MIN: return follow_group_zoom_min
|
||||
if property == FOLLOW_GROUP_ZOOM_MAX: return follow_group_zoom_max
|
||||
if property == FOLLOW_GROUP_ZOOM_MARGIN: return follow_group_zoom_margin
|
||||
|
||||
if property == Constants.FOLLOW_DAMPING_NAME: return Properties.follow_has_damping
|
||||
if property == Constants.FOLLOW_DAMPING_VALUE_NAME: return Properties.follow_damping_value
|
||||
|
||||
if property == Constants.TWEEN_RESOURCE_PROPERTY_NAME: return Properties.tween_resource
|
||||
|
||||
if property == Constants.INACTIVE_UPDATE_MODE_PROPERTY_NAME: return Properties.inactive_update_mode
|
||||
if property == Constants.TWEEN_ONLOAD_NAME: return Properties.tween_onload
|
||||
|
||||
|
||||
###################
|
||||
# Private Functions
|
||||
###################
|
||||
func _enter_tree() -> void:
|
||||
Properties.is_2D = true
|
||||
Properties.camera_enter_tree(self)
|
||||
Properties.assign_pcam_host(self)
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
if Properties.pcam_host_owner:
|
||||
Properties.pcam_host_owner.pcam_removed_from_scene(self)
|
||||
|
||||
Properties.pcam_exit_tree(self)
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
# print(follow_group_zoom_margin)
|
||||
if not Properties.is_active:
|
||||
match Properties.inactive_update_mode:
|
||||
Constants.InactiveUpdateMode.NEVER:
|
||||
return
|
||||
# Constants.InactiveUpdateMode.EXPONENTIALLY:
|
||||
# TODO
|
||||
|
||||
if not Properties.should_follow: return
|
||||
|
||||
match Properties.follow_mode:
|
||||
Constants.FollowMode.GLUED:
|
||||
if Properties.follow_target_node:
|
||||
_interpolate_position(Properties.follow_target_node.position, delta)
|
||||
Constants.FollowMode.SIMPLE:
|
||||
if Properties.follow_target_node:
|
||||
_interpolate_position(_target_position_with_offset(), delta)
|
||||
Constants.FollowMode.GROUP:
|
||||
if Properties.has_follow_group:
|
||||
if Properties.follow_group_nodes_2D.size() == 1:
|
||||
_interpolate_position(Properties.follow_group_nodes_2D[0].get_global_position(), delta)
|
||||
else:
|
||||
var rect: Rect2 = Rect2(Properties.follow_group_nodes_2D[0].get_global_position(), Vector2.ZERO)
|
||||
for node in Properties.follow_group_nodes_2D:
|
||||
rect = rect.expand(node.get_global_position())
|
||||
if follow_group_zoom_auto:
|
||||
rect = rect.grow_individual(
|
||||
follow_group_zoom_margin.x,
|
||||
follow_group_zoom_margin.y,
|
||||
follow_group_zoom_margin.z,
|
||||
follow_group_zoom_margin.w)
|
||||
# else:
|
||||
# rect = rect.grow_individual(-80, 0, 0, 0)
|
||||
if follow_group_zoom_auto:
|
||||
var screen_size: Vector2 = get_viewport_rect().size
|
||||
if rect.size.x > rect.size.y * screen_size.aspect():
|
||||
Properties.zoom = clamp(screen_size.x / rect.size.x, follow_group_zoom_min, follow_group_zoom_max) * Vector2.ONE
|
||||
else:
|
||||
Properties.zoom = clamp(screen_size.y / rect.size.y, follow_group_zoom_min, follow_group_zoom_max) * Vector2.ONE
|
||||
_interpolate_position(rect.get_center(), delta)
|
||||
Constants.FollowMode.PATH:
|
||||
if Properties.follow_target_node and Properties.follow_path_node:
|
||||
var path_position: Vector2 = Properties.follow_path_node.get_global_position()
|
||||
_interpolate_position(
|
||||
Properties.follow_path_node.curve.get_closest_point(
|
||||
Properties.follow_target_node.get_global_position() - path_position
|
||||
) + path_position, \
|
||||
delta)
|
||||
Constants.FollowMode.FRAMED:
|
||||
if Properties.follow_target_node:
|
||||
if not Engine.is_editor_hint():
|
||||
Properties.viewport_position = (get_follow_target_node().get_global_transform_with_canvas().get_origin() + Properties.follow_target_offset_2D) / get_viewport_rect().size
|
||||
|
||||
if Properties.get_framed_side_offset() != Vector2.ZERO:
|
||||
var glo_pos: Vector2
|
||||
|
||||
var target_position: Vector2 = _target_position_with_offset() + _camera_offset
|
||||
var dead_zone_width: float = Properties.follow_framed_dead_zone_width
|
||||
var dead_zone_height: float = Properties.follow_framed_dead_zone_height
|
||||
|
||||
if dead_zone_width == 0 || dead_zone_height == 0:
|
||||
if dead_zone_width == 0 && dead_zone_height != 0:
|
||||
_interpolate_position(_target_position_with_offset(), delta)
|
||||
elif dead_zone_width != 0 && dead_zone_height == 0:
|
||||
glo_pos = _target_position_with_offset()
|
||||
glo_pos.x += target_position.x - global_position.x
|
||||
_interpolate_position(glo_pos, delta)
|
||||
else:
|
||||
_interpolate_position(_target_position_with_offset(), delta)
|
||||
else:
|
||||
_interpolate_position(target_position, delta)
|
||||
else:
|
||||
_camera_offset = global_position - _target_position_with_offset()
|
||||
else:
|
||||
set_global_position(_target_position_with_offset())
|
||||
|
||||
|
||||
func _target_position_with_offset() -> Vector2:
|
||||
return Properties.follow_target_node.get_global_position() + Properties.follow_target_offset_2D
|
||||
|
||||
|
||||
func _interpolate_position(position: Vector2, delta: float, target: Node2D = self) -> void:
|
||||
if Properties.follow_has_damping:
|
||||
target.set_global_position(
|
||||
target.get_global_position().lerp(
|
||||
position,
|
||||
delta * Properties.follow_damping_value
|
||||
)
|
||||
)
|
||||
else:
|
||||
target.set_global_position(position)
|
||||
|
||||
##################
|
||||
# Public Functions
|
||||
##################
|
||||
## Assigns the PhantomCamera2D to a new PhantomCameraHost.
|
||||
func assign_pcam_host() -> void:
|
||||
Properties.assign_pcam_host(self)
|
||||
## Gets the current PhantomCameraHost this PhantomCamera2D is assigned to.
|
||||
func get_pcam_host_owner() -> PhantomCameraHost:
|
||||
return Properties.pcam_host_owner
|
||||
|
||||
|
||||
## Assigns new Zoom value.
|
||||
func set_zoom(value: Vector2) -> void:
|
||||
Properties.zoom = value
|
||||
## Gets current Zoom value.
|
||||
func get_zoom() -> Vector2:
|
||||
return Properties.zoom
|
||||
|
||||
|
||||
## Assigns new Priority value.
|
||||
func set_priority(value: int) -> void:
|
||||
Properties.set_priority(value, self)
|
||||
## Gets current Priority value.
|
||||
func get_priority() -> int:
|
||||
return Properties.priority
|
||||
|
||||
|
||||
## Assigns a new PhantomCameraTween resource to the PhantomCamera2D
|
||||
func set_tween_resource(value: PhantomCameraTween) -> void:
|
||||
Properties.tween_resource = value
|
||||
## Gets the PhantomCameraTween resource assigned to the PhantomCamera2D
|
||||
## Returns null if there's nothing assigned to it.
|
||||
func get_tween_resource() -> PhantomCameraTween:
|
||||
return Properties.tween_resource
|
||||
|
||||
## Assigns a new Tween Duration value. The duration value is in seconds.
|
||||
## Note: This will override and make the Tween Resource unique to this PhantomCamera2D.
|
||||
func set_tween_duration(value: float) -> void:
|
||||
if get_tween_resource():
|
||||
Properties.tween_resource_default.duration = value
|
||||
Properties.tween_resource_default.transition = Properties.tween_resource.transition
|
||||
Properties.tween_resource_default.ease = Properties.tween_resource.ease
|
||||
set_tween_resource(null) # Clears resource from PCam instance
|
||||
else:
|
||||
Properties.tween_resource_default.duration = value
|
||||
## Gets the current Tween Duration value. The duration value is in seconds.
|
||||
func get_tween_duration() -> float:
|
||||
if get_tween_resource():
|
||||
return get_tween_resource().duration
|
||||
else:
|
||||
return Properties.tween_resource_default.duration
|
||||
|
||||
## Assigns a new Tween Transition value.
|
||||
## Note: This will override and make the Tween Resource unique to this PhantomCamera2D.
|
||||
func set_tween_transition(value: Constants.TweenTransitions) -> void:
|
||||
if get_tween_resource():
|
||||
Properties.tween_resource_default.duration = Properties.tween_resource.duration
|
||||
Properties.tween_resource_default.transition = value
|
||||
Properties.tween_resource_default.ease = Properties.tween_resource.ease
|
||||
set_tween_resource(null) # Clears resource from PCam instance
|
||||
else:
|
||||
Properties.tween_resource_default.transition = value
|
||||
## Gets the current Tween Transition value.
|
||||
func get_tween_transition() -> int:
|
||||
if get_tween_resource():
|
||||
return get_tween_resource().transition
|
||||
else:
|
||||
return Properties.tween_resource_default.transition
|
||||
|
||||
## Assigns a new Tween Ease value.
|
||||
## Note: This will override and make the Tween Resource unique to this PhantomCamera2D.
|
||||
func set_tween_ease(value: Constants.TweenEases) -> void:
|
||||
if get_tween_resource():
|
||||
Properties.tween_resource_default.duration = Properties.tween_resource.duration
|
||||
Properties.tween_resource_default.transition = Properties.tween_resource.ease
|
||||
Properties.tween_resource_default.ease = value
|
||||
set_tween_resource(null) # Clears resource from PCam instance
|
||||
else:
|
||||
Properties.tween_resource_default.ease = value
|
||||
## Gets the current Tween Ease value.
|
||||
func get_tween_ease() -> int:
|
||||
if get_tween_resource():
|
||||
return get_tween_resource().ease
|
||||
else:
|
||||
return Properties.tween_resource_default.ease
|
||||
|
||||
|
||||
## Gets current active state of the PhantomCamera2D.
|
||||
## If it returns true, it means the PhantomCamera2D is what the Camera2D is currently following.
|
||||
func is_active() -> bool:
|
||||
return Properties.is_active
|
||||
|
||||
|
||||
## Enables or disables the Tween on Load.
|
||||
func set_tween_on_load(value: bool) -> void:
|
||||
Properties.tween_onload = value
|
||||
## Gets the current Tween On Load value.
|
||||
func is_tween_on_load() -> bool:
|
||||
return Properties.tween_onload
|
||||
|
||||
|
||||
## Gets the current follow mode as an enum int based on Constants.FOLLOW_MODE enum.
|
||||
## Note: Setting Follow Mode purposely not added. A separate PCam should be used instead.
|
||||
func get_follow_mode() -> int:
|
||||
return Properties.follow_mode
|
||||
|
||||
## Assigns a new Node2D as the Follow Target property.
|
||||
func set_follow_target_node(value: Node2D) -> void:
|
||||
Properties.follow_target_node = value
|
||||
Properties.should_follow = true
|
||||
## Erases the current Node2D from the Follow Target property.
|
||||
func erase_follow_target_node() -> void:
|
||||
Properties.should_follow = false
|
||||
Properties.follow_target_node = null
|
||||
## Gets the current Node2D target property.
|
||||
func get_follow_target_node():
|
||||
if Properties.follow_target_node:
|
||||
return Properties.follow_target_node
|
||||
else:
|
||||
printerr("No Follow Target Node assigned")
|
||||
|
||||
|
||||
## Assigns a new Path2D to the Follow Path property.
|
||||
func set_follow_path(value: Path2D) -> void:
|
||||
Properties.follow_path_node = value
|
||||
## Erases the current Path2D from the Follow Path property.
|
||||
func erase_follow_path() -> void:
|
||||
Properties.follow_path_node = null
|
||||
## Gets the current Path2D from the Follow Path property.
|
||||
func get_follow_path():
|
||||
if Properties.follow_path_node:
|
||||
return Properties.follow_path_node
|
||||
else:
|
||||
printerr("No Follow Path assigned")
|
||||
|
||||
|
||||
## Assigns a new Vector2 for the Follow Target Offset property.
|
||||
func set_follow_target_offset(value: Vector2) -> void:
|
||||
Properties.follow_target_offset_2D = value
|
||||
## Gets the current Vector2 for the Follow Target Offset property.
|
||||
func get_follow_target_offset() -> Vector2:
|
||||
return Properties.follow_target_offset_2D
|
||||
|
||||
|
||||
## Enables or disables Follow Damping.
|
||||
func set_follow_has_damping(value: bool) -> void:
|
||||
Properties.follow_has_damping = value
|
||||
## Gets the currents Follow Damping property.
|
||||
func get_follow_has_damping() -> bool:
|
||||
return Properties.follow_has_damping
|
||||
|
||||
## Assigns new Damping value.
|
||||
func set_follow_damping_value(value: float) -> void:
|
||||
Properties.follow_damping_value = value
|
||||
## Gets the currents Follow Damping value.
|
||||
func get_follow_damping_value() -> float:
|
||||
return Properties.follow_damping_value
|
||||
|
||||
|
||||
## Adds a single Node2D to Follow Group array.
|
||||
func append_follow_group_node(value: Node2D) -> void:
|
||||
if not Properties.follow_group_nodes_2D.has(value):
|
||||
Properties.follow_group_nodes_2D.append(value)
|
||||
Properties.should_follow = true
|
||||
Properties.has_follow_group = true
|
||||
else:
|
||||
printerr(value, " is already part of Follow Group")
|
||||
## Adds an Array of type Node2D to Follow Group array.
|
||||
func append_follow_group_node_array(value: Array[Node2D]) -> void:
|
||||
for val in value:
|
||||
if not Properties.follow_group_nodes_2D.has(val):
|
||||
Properties.follow_group_nodes_2D.append(val)
|
||||
Properties.should_follow = true
|
||||
Properties.has_follow_group = true
|
||||
else:
|
||||
printerr(val, " is already part of Follow Group")
|
||||
## Removes Node2D from Follow Group array.
|
||||
func erase_follow_group_node(value: Node2D) -> void:
|
||||
Properties.follow_group_nodes_2D.erase(value)
|
||||
if Properties.follow_group_nodes_2D.size() < 1:
|
||||
Properties.should_follow = false
|
||||
Properties.has_follow_group = false
|
||||
## Gets all Node2D from Follow Group array.
|
||||
func get_follow_group_nodes() -> Array[Node2D]:
|
||||
return Properties.follow_group_nodes_2D
|
||||
|
||||
|
||||
## Enables or disables Auto zoom when using Group Follow.
|
||||
func set_auto_zoom(value: bool) -> void:
|
||||
follow_group_zoom_auto = value
|
||||
## Gets Auto Zoom state.
|
||||
func get_auto_zoom() -> bool:
|
||||
return follow_group_zoom_auto
|
||||
|
||||
## Assigns new Min Auto Zoom value.
|
||||
func set_min_auto_zoom(value: float) -> void:
|
||||
follow_group_zoom_min = value
|
||||
## Gets Min Auto Zoom value.
|
||||
func get_min_auto_zoom() -> float:
|
||||
return follow_group_zoom_min
|
||||
|
||||
## Assigns new Max Auto Zoom value.
|
||||
func set_max_auto_zoom(value: float) -> void:
|
||||
follow_group_zoom_max = value
|
||||
## Gets Max Auto Zoom value.
|
||||
func get_max_auto_zoom() -> float:
|
||||
return follow_group_zoom_max
|
||||
|
||||
## Assigns new Zoom Auto Margin value.
|
||||
func set_zoom_auto_margin(value: Vector4) -> void:
|
||||
follow_group_zoom_margin = value
|
||||
## Gets Zoom Auto Margin value.
|
||||
func get_zoom_auto_margin() -> Vector4:
|
||||
return follow_group_zoom_margin
|
||||
|
||||
|
||||
## Gets Interactive Update Mode property.
|
||||
func get_inactive_update_mode() -> String:
|
||||
return Constants.InactiveUpdateMode.keys()[Properties.inactive_update_mode].capitalize()
|
1014
addons/phantom_camera/scripts/phantom_camera/phantom_camera_3D.gd
Normal file
1014
addons/phantom_camera/scripts/phantom_camera/phantom_camera_3D.gd
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,87 @@
|
|||
@tool
|
||||
extends RefCounted
|
||||
|
||||
const PhantomCameraHost: Script = preload("res://addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd")
|
||||
|
||||
# Values
|
||||
const CAMERA_2D_NODE_NAME: StringName = "Camera2D"
|
||||
const CAMERA_3D_NODE_NAME: StringName = "Camera3D"
|
||||
const PCAM_HOST_NODE_NAME: StringName = "PhantomCameraHost"
|
||||
const PCAM_2D_NODE_NAME: StringName = "PhantomCamera2D"
|
||||
const PCAM_3D_NODE_NAME: StringName = "PhantomCamera3D"
|
||||
const COLOR_2D: Color = Color("8DA5F3")
|
||||
const COLOR_3D: Color = Color("FC7F7F")
|
||||
const COLOR_PCAM: Color = Color("3AB99A")
|
||||
const PCAM_HOST_COLOR: Color = Color("E0E0E0")
|
||||
|
||||
# Primary
|
||||
const PRIORITY_PROPERTY_NAME: StringName = "priority"
|
||||
const PRIORITY_OVERRIDE: StringName = "priority_override"
|
||||
const PCAM_HOST: StringName = "phantom_camera_host"
|
||||
|
||||
# Follow
|
||||
const FOLLOW_MODE_PROPERTY_NAME: StringName = "follow_mode"
|
||||
const FOLLOW_TARGET_PROPERTY_NAME: StringName = "follow_target"
|
||||
const FOLLOW_GROUP_PROPERTY_NAME: StringName = "follow_group"
|
||||
const FOLLOW_PATH_PROPERTY_NAME: StringName = "follow_path"
|
||||
const FOLLOW_PARAMETERS_NAME: StringName = "follow_parameters/"
|
||||
|
||||
# Follow Parameters
|
||||
const FOLLOW_DISTANCE_PROPERTY_NAME: StringName = FOLLOW_PARAMETERS_NAME + "distance"
|
||||
const FOLLOW_DAMPING_NAME: StringName = FOLLOW_PARAMETERS_NAME + "damping"
|
||||
const FOLLOW_DAMPING_VALUE_NAME: StringName = FOLLOW_PARAMETERS_NAME + "damping_value"
|
||||
const FOLLOW_TARGET_OFFSET_PROPERTY_NAME: StringName = FOLLOW_PARAMETERS_NAME + "target_offset"
|
||||
const FOLLOW_FRAMED_DEAD_ZONE_HORIZONTAL_NAME: StringName = FOLLOW_PARAMETERS_NAME + "dead_zone_horizontal"
|
||||
const FOLLOW_FRAMED_DEAD_ZONE_VERTICAL_NAME: StringName = FOLLOW_PARAMETERS_NAME + "dead_zone_vertical"
|
||||
const FOLLOW_VIEWFINDER_IN_PLAY_NAME: StringName = FOLLOW_PARAMETERS_NAME + "viewfinder_in_play"
|
||||
const DEAD_ZONE_CHANGED_SIGNAL: StringName = "dead_zone_changed"
|
||||
|
||||
#Zoom
|
||||
const ZOOM_PROPERTY_NAME: StringName = "zoom"
|
||||
|
||||
# Tween Resource
|
||||
const TWEEN_RESOURCE_PROPERTY_NAME: StringName = "tween_parameters"
|
||||
|
||||
# Secondary
|
||||
const TWEEN_ONLOAD_NAME: StringName = "tween_on_load"
|
||||
const INACTIVE_UPDATE_MODE_PROPERTY_NAME: StringName = "inactive_update_mode"
|
||||
|
||||
|
||||
enum FollowMode {
|
||||
NONE = 0,
|
||||
GLUED = 1,
|
||||
SIMPLE = 2,
|
||||
GROUP = 3,
|
||||
PATH = 4,
|
||||
FRAMED = 5,
|
||||
THIRD_PERSON = 6,
|
||||
}
|
||||
|
||||
enum TweenTransitions {
|
||||
LINEAR = 0,
|
||||
SINE = 1,
|
||||
QUINT = 2,
|
||||
QUART = 3,
|
||||
QUAD = 4,
|
||||
EXPO = 5,
|
||||
ELASTIC = 6,
|
||||
CUBIC = 7,
|
||||
CIRC = 8,
|
||||
BOUNCE = 9,
|
||||
BACK = 10,
|
||||
# CUSTOM = 11,
|
||||
# NONE = 12,
|
||||
}
|
||||
|
||||
enum TweenEases {
|
||||
EASE_IN = 0,
|
||||
EASE_OUT = 1,
|
||||
EASE_IN_OUT = 2,
|
||||
EASE_OUT_IN = 3,
|
||||
}
|
||||
|
||||
enum InactiveUpdateMode {
|
||||
ALWAYS,
|
||||
NEVER,
|
||||
# EXPONENTIALLY,
|
||||
}
|
|
@ -0,0 +1,488 @@
|
|||
@tool
|
||||
extends RefCounted
|
||||
|
||||
const Constants: Script = preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd")
|
||||
const PcamGroupNames: Script = preload("res://addons/phantom_camera/scripts/group_names.gd")
|
||||
|
||||
var is_2D: bool
|
||||
|
||||
var pcam_host_owner: PhantomCameraHost
|
||||
var scene_has_multiple_pcam_hosts: bool
|
||||
var pcam_host_group: Array[Node]
|
||||
|
||||
var is_active: bool
|
||||
|
||||
var priority_override: bool
|
||||
var priority: int = 0
|
||||
|
||||
var tween_onload: bool = true
|
||||
var has_tweened_onload: bool = true
|
||||
|
||||
# Follow
|
||||
var should_follow: bool
|
||||
var has_follow_group: bool
|
||||
var follow_target_node: Node
|
||||
var follow_target_path: NodePath
|
||||
var follow_has_target: bool
|
||||
var follow_has_path_target: bool
|
||||
var follow_path_node: Node
|
||||
var follow_path_path: NodePath
|
||||
var follow_mode: Constants.FollowMode = Constants.FollowMode.NONE
|
||||
var follow_target_offset_2D: Vector2
|
||||
var follow_target_offset_3D: Vector3
|
||||
var follow_has_damping: bool
|
||||
var follow_damping_value: float = 10
|
||||
|
||||
# Follow Group
|
||||
var follow_group_nodes_2D: Array[Node2D]
|
||||
var follow_group_nodes_3D: Array[Node3D]
|
||||
var follow_group_paths: Array[NodePath]
|
||||
|
||||
# Framed Follow
|
||||
signal dead_zone_changed
|
||||
var follow_framed_dead_zone_width: float
|
||||
var follow_framed_dead_zone_height: float
|
||||
var follow_framed_initial_set: bool
|
||||
var show_viewfinder_in_play: bool
|
||||
var viewport_position: Vector2
|
||||
|
||||
var zoom: Vector2 = Vector2.ONE
|
||||
|
||||
var tween_resource: PhantomCameraTween
|
||||
var tween_resource_default: PhantomCameraTween = PhantomCameraTween.new()
|
||||
|
||||
var inactive_update_mode: Constants.InactiveUpdateMode = Constants.InactiveUpdateMode.ALWAYS
|
||||
|
||||
|
||||
func camera_enter_tree(pcam: Node):
|
||||
pcam.add_to_group(PcamGroupNames.PCAM_GROUP_NAME)
|
||||
|
||||
if pcam.Properties.follow_target_path and \
|
||||
not pcam.get_parent() is SpringArm3D and \
|
||||
is_instance_valid(pcam.get_node(pcam.Properties.follow_target_path)):
|
||||
|
||||
pcam.Properties.follow_target_node = pcam.get_node(pcam.Properties.follow_target_path)
|
||||
elif follow_group_paths:
|
||||
if is_2D:
|
||||
follow_group_nodes_2D.clear()
|
||||
else:
|
||||
follow_group_nodes_3D.clear()
|
||||
|
||||
for path in follow_group_paths:
|
||||
if not path.is_empty() and pcam.get_node(path):
|
||||
should_follow = true
|
||||
has_follow_group = true
|
||||
if is_2D:
|
||||
follow_group_nodes_2D.append(pcam.get_node(path))
|
||||
else:
|
||||
follow_group_nodes_3D.append(pcam.get_node(path))
|
||||
|
||||
if pcam.Properties.follow_path_path:
|
||||
pcam.Properties.follow_path_node = pcam.get_node(pcam.Properties.follow_path_path)
|
||||
|
||||
func pcam_exit_tree(pcam: Node):
|
||||
pcam.remove_from_group(PcamGroupNames.PCAM_GROUP_NAME)
|
||||
|
||||
|
||||
#########################
|
||||
# Add Properties
|
||||
#########################
|
||||
func add_multiple_hosts_properties() -> Array:
|
||||
var _property_list: Array
|
||||
|
||||
if scene_has_multiple_pcam_hosts:
|
||||
_property_list.append({
|
||||
"name": Constants.PCAM_HOST,
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": ",".join(PackedStringArray(pcam_host_group)),
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
|
||||
return _property_list
|
||||
|
||||
|
||||
func add_priority_properties() -> Array:
|
||||
var _property_list: Array
|
||||
|
||||
_property_list.append({
|
||||
"name": Constants.PRIORITY_OVERRIDE,
|
||||
"type": TYPE_BOOL,
|
||||
})
|
||||
|
||||
_property_list.append({
|
||||
"name": Constants.PRIORITY_PROPERTY_NAME,
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
|
||||
return _property_list
|
||||
|
||||
|
||||
func add_follow_mode_property() -> Array:
|
||||
var _property_list: Array
|
||||
|
||||
var follow_mode_keys: Array = Constants.FollowMode.keys()
|
||||
if is_2D:
|
||||
follow_mode_keys.remove_at(Constants.FollowMode.THIRD_PERSON)
|
||||
|
||||
_property_list.append({
|
||||
"name": Constants.FOLLOW_MODE_PROPERTY_NAME,
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": ", ".join(PackedStringArray(follow_mode_keys)).capitalize(),
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
|
||||
return _property_list
|
||||
|
||||
|
||||
func add_follow_target_property() -> Array:
|
||||
var _property_list: Array
|
||||
|
||||
if follow_mode == Constants.FollowMode.GROUP:
|
||||
_property_list.append({
|
||||
"name": Constants.FOLLOW_GROUP_PROPERTY_NAME,
|
||||
"type": TYPE_ARRAY,
|
||||
"hint": PROPERTY_HINT_TYPE_STRING,
|
||||
"hint_string": TYPE_NODE_PATH,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
else:
|
||||
_property_list.append({
|
||||
"name": Constants.FOLLOW_TARGET_PROPERTY_NAME,
|
||||
"type": TYPE_NODE_PATH,
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
if follow_mode == Constants.FollowMode.PATH:
|
||||
_property_list.append({
|
||||
"name": Constants.FOLLOW_PATH_PROPERTY_NAME,
|
||||
"type": TYPE_NODE_PATH,
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
|
||||
return _property_list
|
||||
|
||||
|
||||
func add_follow_properties() -> Array:
|
||||
var _property_list: Array
|
||||
if follow_mode != Constants.FollowMode.NONE:
|
||||
if follow_mode == Constants.FollowMode.SIMPLE or \
|
||||
follow_mode == Constants.FollowMode.GROUP or \
|
||||
follow_mode == Constants.FollowMode.FRAMED or \
|
||||
follow_mode == Constants.FollowMode.THIRD_PERSON:
|
||||
if is_2D:
|
||||
_property_list.append({
|
||||
"name": Constants.FOLLOW_TARGET_OFFSET_PROPERTY_NAME,
|
||||
"type": TYPE_VECTOR2,
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
else:
|
||||
_property_list.append({
|
||||
"name": Constants.FOLLOW_TARGET_OFFSET_PROPERTY_NAME,
|
||||
"type": TYPE_VECTOR3,
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
|
||||
if follow_mode != Constants.FollowMode.NONE:
|
||||
_property_list.append({
|
||||
"name": Constants.FOLLOW_DAMPING_NAME,
|
||||
"type": TYPE_BOOL,
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
|
||||
if follow_has_damping:
|
||||
_property_list.append({
|
||||
"name": Constants.FOLLOW_DAMPING_VALUE_NAME,
|
||||
"type": TYPE_FLOAT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0.01, 100, 0.01,",
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
|
||||
return _property_list
|
||||
|
||||
|
||||
func add_follow_framed() -> Array:
|
||||
var _property_list: Array
|
||||
|
||||
if follow_mode == Constants.FollowMode.FRAMED:
|
||||
_property_list.append({
|
||||
"name": Constants.FOLLOW_FRAMED_DEAD_ZONE_HORIZONTAL_NAME,
|
||||
"type": TYPE_FLOAT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0, 1, 0.01,",
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
_property_list.append({
|
||||
"name": Constants.FOLLOW_FRAMED_DEAD_ZONE_VERTICAL_NAME,
|
||||
"type": TYPE_FLOAT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0, 1, 0.01,",
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
})
|
||||
|
||||
_property_list.append({
|
||||
"name": Constants.FOLLOW_VIEWFINDER_IN_PLAY_NAME,
|
||||
"type": TYPE_BOOL,
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
|
||||
return _property_list
|
||||
|
||||
|
||||
func add_tween_properties() -> Array:
|
||||
var _property_list: Array
|
||||
|
||||
_property_list.append({
|
||||
"name": Constants.TWEEN_RESOURCE_PROPERTY_NAME,
|
||||
"type": TYPE_OBJECT,
|
||||
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
||||
"hint_string": "PhantomCameraTween"
|
||||
})
|
||||
|
||||
return _property_list
|
||||
|
||||
|
||||
func add_secondary_properties() -> Array:
|
||||
var _property_list: Array
|
||||
|
||||
_property_list.append({
|
||||
"name": Constants.TWEEN_ONLOAD_NAME,
|
||||
"type": TYPE_BOOL,
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
|
||||
_property_list.append({
|
||||
"name": Constants.INACTIVE_UPDATE_MODE_PROPERTY_NAME,
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": ", ".join(PackedStringArray(Constants.InactiveUpdateMode.keys())).capitalize(),
|
||||
})
|
||||
|
||||
return _property_list
|
||||
|
||||
|
||||
#########################
|
||||
# Set Properties
|
||||
#########################
|
||||
func set_phantom_host_property(property: StringName, value, pcam: Node):
|
||||
if property == Constants.PCAM_HOST:
|
||||
if value != null && value is int:
|
||||
var host_node = instance_from_id(value)
|
||||
pcam_host_owner = host_node
|
||||
|
||||
|
||||
func set_priority_property(property: StringName, value, pcam: Node):
|
||||
if Engine.is_editor_hint() and is_instance_valid(pcam_host_owner):
|
||||
if property == Constants.PRIORITY_OVERRIDE:
|
||||
if value == true:
|
||||
priority_override = value
|
||||
pcam_host_owner.pcam_priority_override(pcam)
|
||||
else:
|
||||
priority_override = value
|
||||
pcam_host_owner.pcam_priority_updated(pcam)
|
||||
pcam_host_owner.pcam_priority_override_disabled()
|
||||
|
||||
if property == Constants.PRIORITY_PROPERTY_NAME:
|
||||
set_priority(value, pcam)
|
||||
|
||||
|
||||
func set_follow_properties(property: StringName, value, pcam: Node):
|
||||
if property == Constants.FOLLOW_MODE_PROPERTY_NAME:
|
||||
follow_mode = value
|
||||
|
||||
if follow_mode != Constants.FollowMode.GROUP:
|
||||
has_follow_group = false
|
||||
|
||||
if follow_mode == Constants.FollowMode.FRAMED:
|
||||
follow_framed_initial_set = true
|
||||
|
||||
pcam.notify_property_list_changed()
|
||||
|
||||
# match value:
|
||||
# Constants.FollowMode.NONE:
|
||||
# set_process(pcam, false)
|
||||
# _:
|
||||
# set_process(pcam, true)
|
||||
|
||||
if property == Constants.FOLLOW_TARGET_PROPERTY_NAME:
|
||||
if follow_mode != Constants.FollowMode.NONE:
|
||||
should_follow = true
|
||||
else:
|
||||
should_follow = false
|
||||
|
||||
follow_target_path = value
|
||||
var valueNodePath: NodePath = value as NodePath
|
||||
if not valueNodePath.is_empty():
|
||||
follow_has_target = true
|
||||
if pcam.has_node(follow_target_path):
|
||||
follow_target_node = pcam.get_node(follow_target_path)
|
||||
else:
|
||||
follow_has_target = false
|
||||
follow_target_node = null
|
||||
|
||||
pcam.notify_property_list_changed()
|
||||
|
||||
if property == Constants.FOLLOW_PATH_PROPERTY_NAME:
|
||||
follow_path_path = value
|
||||
|
||||
var valueNodePath: NodePath = value as NodePath
|
||||
if not valueNodePath.is_empty():
|
||||
follow_has_path_target = true
|
||||
if pcam.has_node(follow_path_path):
|
||||
follow_path_node = pcam.get_node(follow_path_path)
|
||||
else:
|
||||
follow_has_path_target = false
|
||||
follow_path_node = null
|
||||
pcam.notify_property_list_changed()
|
||||
|
||||
if property == Constants.FOLLOW_GROUP_PROPERTY_NAME:
|
||||
if value and value.size() > 0:
|
||||
# Clears the Array in case of reshuffling or updated Nodes
|
||||
if is_2D:
|
||||
follow_group_nodes_2D.clear()
|
||||
else:
|
||||
follow_group_nodes_3D.clear()
|
||||
follow_group_paths = value as Array[NodePath]
|
||||
|
||||
if not follow_group_paths.is_empty():
|
||||
for path in follow_group_paths:
|
||||
if pcam.has_node(path):
|
||||
should_follow = true
|
||||
has_follow_group = true
|
||||
var node: Node = pcam.get_node(path)
|
||||
if node is Node2D or node is Node3D:
|
||||
# Prevents duplicated nodes from being assigned to array
|
||||
if is_2D:
|
||||
if follow_group_nodes_2D.find(node):
|
||||
follow_group_nodes_2D.append(node)
|
||||
else:
|
||||
if follow_group_nodes_3D.find(node):
|
||||
follow_group_nodes_3D.append(node)
|
||||
else:
|
||||
printerr("Assigned non-Node3D to Follow Group")
|
||||
|
||||
pcam.notify_property_list_changed()
|
||||
|
||||
# Framed Follow
|
||||
if property == Constants.FOLLOW_FRAMED_DEAD_ZONE_HORIZONTAL_NAME:
|
||||
follow_framed_dead_zone_width = value
|
||||
dead_zone_changed.emit()
|
||||
if property == Constants.FOLLOW_FRAMED_DEAD_ZONE_VERTICAL_NAME:
|
||||
follow_framed_dead_zone_height = value
|
||||
dead_zone_changed.emit()
|
||||
if property == Constants.FOLLOW_VIEWFINDER_IN_PLAY_NAME:
|
||||
show_viewfinder_in_play = value
|
||||
|
||||
if property == Constants.FOLLOW_TARGET_OFFSET_PROPERTY_NAME:
|
||||
if value is Vector3:
|
||||
follow_target_offset_3D = value
|
||||
else:
|
||||
follow_target_offset_2D = value
|
||||
|
||||
if property == Constants.FOLLOW_DAMPING_NAME:
|
||||
follow_has_damping = value
|
||||
pcam.notify_property_list_changed()
|
||||
|
||||
if property == Constants.FOLLOW_DAMPING_VALUE_NAME:
|
||||
follow_damping_value = value
|
||||
|
||||
|
||||
func set_tween_properties(property: StringName, value, pcam: Node):
|
||||
if property == Constants.TWEEN_RESOURCE_PROPERTY_NAME:
|
||||
tween_resource = value
|
||||
|
||||
|
||||
func set_secondary_properties(property: StringName, value, pcam: Node):
|
||||
if property == Constants.TWEEN_ONLOAD_NAME:
|
||||
tween_onload = value
|
||||
if value == false:
|
||||
has_tweened_onload = false
|
||||
else:
|
||||
has_tweened_onload = true
|
||||
|
||||
if property == Constants.INACTIVE_UPDATE_MODE_PROPERTY_NAME:
|
||||
inactive_update_mode = value
|
||||
|
||||
|
||||
func set_priority(value: int, pcam: Node) -> void:
|
||||
if value < 0:
|
||||
printerr("Phantom Camera's priority cannot be less than 0")
|
||||
priority = 0
|
||||
else:
|
||||
priority = value
|
||||
|
||||
if pcam_host_owner:
|
||||
pcam_host_owner.pcam_priority_updated(pcam)
|
||||
# else:
|
||||
## TODO - Add logic to handle Phantom Camera Host in scene
|
||||
# printerr("Trying to change priority without a Phantom Camera Host - Please attached one to a Camera3D")
|
||||
# pass
|
||||
|
||||
|
||||
#########################
|
||||
# Other Functions
|
||||
#########################
|
||||
func assign_pcam_host(pcam: Node) -> void:
|
||||
pcam_host_group = pcam.get_tree().get_nodes_in_group(PcamGroupNames.PCAM_HOST_GROUP_NAME)
|
||||
|
||||
if pcam_host_group.size() == 1:
|
||||
pcam_host_owner = pcam.Properties.pcam_host_group[0]
|
||||
pcam_host_owner.pcam_added_to_scene(pcam)
|
||||
# else:
|
||||
# for camera_host in camera_host_group:
|
||||
# print("Multiple PhantomCameraBases in scene")
|
||||
# print(pcam_host_group)
|
||||
# print(pcam.get_tree().get_nodes_in_group(PhantomCameraGroupNames.PHANTOM_CAMERA_HOST_GROUP_NAME))
|
||||
# multiple_pcam_host_group.append(camera_host)
|
||||
# return null
|
||||
|
||||
|
||||
func toggle_priorty_override(pcam: Node) -> void:
|
||||
if pcam_host_owner:
|
||||
pcam_host_owner.pcam_priority_updated(pcam)
|
||||
|
||||
|
||||
func assign_specific_pcam_host(pcam: Node, pcam_host: PhantomCameraHost) -> void:
|
||||
pcam_host = pcam
|
||||
|
||||
|
||||
func check_multiple_pcam_host_property(pcam: Node, multiple_host: bool = false) -> void:
|
||||
if not multiple_host:
|
||||
scene_has_multiple_pcam_hosts = false
|
||||
else:
|
||||
scene_has_multiple_pcam_hosts = true
|
||||
|
||||
pcam.notify_property_list_changed()
|
||||
# pcam_host_group.append_array(host_group)
|
||||
|
||||
|
||||
func get_framed_side_offset() -> Vector2:
|
||||
var frame_out_bounds: Vector2
|
||||
|
||||
if viewport_position.x < 0.5 - follow_framed_dead_zone_width / 2:
|
||||
# Is outside left edge
|
||||
frame_out_bounds.x = -1
|
||||
|
||||
if viewport_position.y < 0.5 - follow_framed_dead_zone_height / 2:
|
||||
# Is outside top edge
|
||||
frame_out_bounds.y = 1
|
||||
|
||||
if viewport_position.x > 0.5 + follow_framed_dead_zone_width / 2:
|
||||
# Is outside right edge
|
||||
frame_out_bounds.x = 1
|
||||
|
||||
if viewport_position.y > 0.5001 + follow_framed_dead_zone_height / 2: # 0.501 to resolve an issue where the bottom vertical Dead Zone never becoming 0 when the Dead Zone Vertical parameter is set to 0
|
||||
# Is outside bottom edge
|
||||
frame_out_bounds.y = -1
|
||||
|
||||
return frame_out_bounds
|
|
@ -0,0 +1,366 @@
|
|||
@tool
|
||||
@icon("res://addons/phantom_camera/icons/PhantomCameraHostIcon.svg")
|
||||
class_name PhantomCameraHost
|
||||
extends Node
|
||||
|
||||
const PcamGroupNames = preload("res://addons/phantom_camera/scripts/group_names.gd")
|
||||
|
||||
var _pcam_tween: Tween
|
||||
var _tween_default_ease: Tween.EaseType
|
||||
var _easing: Tween.TransitionType
|
||||
|
||||
var camera_2D: Camera2D
|
||||
var camera_3D: Camera3D
|
||||
var _pcam_list: Array[Node]
|
||||
|
||||
var _active_pcam: Node
|
||||
var _active_pcam_priority: int = -1
|
||||
var _active_pcam_missing: bool = true
|
||||
var _active_pcam_has_damping: bool
|
||||
|
||||
var _prev_active_pcam_2D_transform: Transform2D
|
||||
var _prev_active_pcam_3D_transform: Transform3D
|
||||
|
||||
var trigger_pcam_tween: bool
|
||||
var tween_duration: float
|
||||
|
||||
var multiple_pcam_hosts: bool
|
||||
|
||||
var is_child_of_camera: bool = false
|
||||
var _is_2D: bool
|
||||
|
||||
signal update_editor_viewfinder
|
||||
|
||||
var framed_viewfinder_scene = load("res://addons/phantom_camera/framed_viewfinder/framed_viewfinder_panel.tscn")
|
||||
var framed_viewfinder_node: Control
|
||||
var viewfinder_needed_check: bool = true
|
||||
|
||||
var camera_zoom: Vector2
|
||||
|
||||
var _prev_camera_h_offset: float
|
||||
var _prev_camera_v_offset: float
|
||||
var _prev_camera_fov: float
|
||||
|
||||
var _should_refresh_transform: bool
|
||||
var _active_pcam_2D_glob_transform: Transform2D
|
||||
var _active_pcam_3D_glob_transform: Transform3D
|
||||
|
||||
###################
|
||||
# Private Functions
|
||||
###################
|
||||
func _enter_tree() -> void:
|
||||
# camera = get_parent()
|
||||
var parent = get_parent()
|
||||
|
||||
if parent is Camera2D or parent is Camera3D:
|
||||
is_child_of_camera = true
|
||||
if parent is Camera2D:
|
||||
_is_2D = true
|
||||
camera_2D = parent
|
||||
else:
|
||||
_is_2D = false
|
||||
camera_3D = parent
|
||||
|
||||
add_to_group(PcamGroupNames.PCAM_HOST_GROUP_NAME)
|
||||
# var already_multi_hosts: bool = multiple_pcam_hosts
|
||||
|
||||
_check_camera_host_amount()
|
||||
|
||||
if multiple_pcam_hosts:
|
||||
printerr(
|
||||
"Only one PhantomCameraHost can exist in a scene",
|
||||
"\n",
|
||||
"Multiple PhantomCameraHosts will be supported in https://github.com/MarcusSkov/phantom-camera/issues/26"
|
||||
)
|
||||
queue_free()
|
||||
|
||||
for pcam in _get_pcam_node_group():
|
||||
if not multiple_pcam_hosts:
|
||||
pcam_added_to_scene(pcam)
|
||||
pcam.assign_pcam_host()
|
||||
# else:
|
||||
# pcam.Properties.check_multiple_pcam_host_property(pcam, pca,_host_group, true)
|
||||
else:
|
||||
printerr(name, " is not a child of a Camera2D or Camera3D")
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
remove_from_group(PcamGroupNames.PCAM_HOST_GROUP_NAME)
|
||||
_check_camera_host_amount()
|
||||
|
||||
for pcam in _get_pcam_node_group():
|
||||
if not multiple_pcam_hosts:
|
||||
pcam.Properties.check_multiple_pcam_host_property(pcam)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not is_instance_valid(_active_pcam): return
|
||||
|
||||
if _is_2D:
|
||||
_active_pcam_2D_glob_transform = _active_pcam.get_global_transform()
|
||||
else:
|
||||
_active_pcam_3D_glob_transform = _active_pcam.get_global_transform()
|
||||
|
||||
|
||||
func _check_camera_host_amount():
|
||||
if _get_pcam_host_group().size() > 1:
|
||||
multiple_pcam_hosts = true
|
||||
else:
|
||||
multiple_pcam_hosts = false
|
||||
|
||||
|
||||
func _assign_new_active_pcam(pcam: Node) -> void:
|
||||
var no_previous_pcam: bool
|
||||
|
||||
if _active_pcam:
|
||||
if _is_2D:
|
||||
_prev_active_pcam_2D_transform = camera_2D.get_transform()
|
||||
else:
|
||||
_prev_active_pcam_3D_transform = camera_3D.get_transform()
|
||||
_prev_camera_fov = camera_3D.get_fov()
|
||||
_prev_camera_h_offset = camera_3D.get_h_offset()
|
||||
_prev_camera_v_offset = camera_3D.get_v_offset()
|
||||
|
||||
_active_pcam.Properties.is_active = false
|
||||
else:
|
||||
no_previous_pcam = true
|
||||
|
||||
_active_pcam = pcam
|
||||
_active_pcam_priority = pcam.get_priority()
|
||||
_active_pcam_has_damping = pcam.Properties.follow_has_damping
|
||||
|
||||
_active_pcam.Properties.is_active = true
|
||||
|
||||
if _is_2D:
|
||||
camera_zoom = camera_2D.get_zoom()
|
||||
else:
|
||||
if _active_pcam.get_camera_3D_resource():
|
||||
camera_3D.set_cull_mask(_active_pcam.get_camera_cull_mask())
|
||||
|
||||
if no_previous_pcam:
|
||||
if _is_2D:
|
||||
_prev_active_pcam_2D_transform = _active_pcam.get_transform()
|
||||
else:
|
||||
_prev_active_pcam_3D_transform = _active_pcam.get_transform()
|
||||
|
||||
tween_duration = 0
|
||||
trigger_pcam_tween = true
|
||||
|
||||
|
||||
func _find_pcam_with_highest_priority() -> void:
|
||||
for pcam in _pcam_list:
|
||||
if pcam.get_priority() > _active_pcam_priority:
|
||||
_assign_new_active_pcam(pcam)
|
||||
|
||||
_active_pcam_missing = false
|
||||
|
||||
|
||||
func _tween_pcam(delta: float) -> void:
|
||||
if _active_pcam.Properties.tween_onload == false && _active_pcam.Properties.has_tweened_onload == false:
|
||||
trigger_pcam_tween = false
|
||||
_reset_tween_on_load()
|
||||
return
|
||||
else:
|
||||
_reset_tween_on_load()
|
||||
|
||||
tween_duration += delta
|
||||
|
||||
if _is_2D:
|
||||
camera_2D.set_global_position(
|
||||
_tween_interpolate_value(_prev_active_pcam_2D_transform.origin, _active_pcam_2D_glob_transform.origin)
|
||||
)
|
||||
|
||||
camera_2D.set_zoom(
|
||||
_tween_interpolate_value(camera_zoom, _active_pcam.Properties.zoom)
|
||||
)
|
||||
else:
|
||||
camera_3D.set_global_position(
|
||||
_tween_interpolate_value(_prev_active_pcam_3D_transform.origin, _active_pcam_3D_glob_transform.origin)
|
||||
)
|
||||
|
||||
var prev_active_pcam_3D_basis = Quaternion(_prev_active_pcam_3D_transform.basis.orthonormalized())
|
||||
camera_3D.set_quaternion(
|
||||
Tween.interpolate_value(
|
||||
prev_active_pcam_3D_basis, \
|
||||
prev_active_pcam_3D_basis.inverse() * Quaternion(_active_pcam_3D_glob_transform.basis.orthonormalized()),
|
||||
tween_duration, \
|
||||
_active_pcam.get_tween_duration(), \
|
||||
_active_pcam.get_tween_transition(),
|
||||
_active_pcam.get_tween_ease(),
|
||||
)
|
||||
)
|
||||
|
||||
if _prev_camera_fov != _active_pcam.get_camera_fov() and _active_pcam.get_camera_3D_resource():
|
||||
camera_3D.set_fov(
|
||||
_tween_interpolate_value(_prev_camera_fov, _active_pcam.get_camera_fov())
|
||||
)
|
||||
|
||||
if _prev_camera_h_offset != _active_pcam.get_camera_h_offset() and _active_pcam.get_camera_3D_resource():
|
||||
camera_3D.set_h_offset(
|
||||
_tween_interpolate_value(_prev_camera_h_offset, _active_pcam.get_camera_h_offset())
|
||||
)
|
||||
|
||||
if _prev_camera_v_offset != _active_pcam.get_camera_v_offset() and _active_pcam.get_camera_3D_resource():
|
||||
camera_3D.set_v_offset(
|
||||
_tween_interpolate_value(_prev_camera_v_offset, _active_pcam.get_camera_v_offset())
|
||||
)
|
||||
|
||||
|
||||
func _tween_interpolate_value(from: Variant, to: Variant) -> Variant:
|
||||
return Tween.interpolate_value(
|
||||
from, \
|
||||
to - from,
|
||||
tween_duration, \
|
||||
_active_pcam.get_tween_duration(), \
|
||||
_active_pcam.get_tween_transition(),
|
||||
_active_pcam.get_tween_ease(),
|
||||
)
|
||||
|
||||
|
||||
func _reset_tween_on_load() -> void:
|
||||
for pcam in _get_pcam_node_group():
|
||||
pcam.Properties.has_tweened_onload = true
|
||||
|
||||
if not _is_2D:
|
||||
if _active_pcam.get_camera_3D_resource():
|
||||
camera_3D.set_fov(_active_pcam.get_camera_fov())
|
||||
camera_3D.set_h_offset(_active_pcam.get_camera_h_offset())
|
||||
camera_3D.set_v_offset(_active_pcam.get_camera_v_offset())
|
||||
|
||||
|
||||
func _pcam_follow(delta: float) -> void:
|
||||
if not _active_pcam: return
|
||||
|
||||
if _is_2D:
|
||||
camera_2D.set_global_transform(_active_pcam_2D_glob_transform)
|
||||
if _active_pcam.Properties.has_follow_group:
|
||||
if _active_pcam.Properties.follow_has_damping:
|
||||
camera_2D.zoom = camera_2D.zoom.lerp(_active_pcam.Properties.zoom, delta * _active_pcam.Properties.follow_damping_value)
|
||||
else:
|
||||
camera_2D.set_zoom(_active_pcam.zoom)
|
||||
else:
|
||||
camera_2D.set_zoom(_active_pcam.Properties.zoom)
|
||||
else:
|
||||
camera_3D.set_global_transform(_active_pcam_3D_glob_transform)
|
||||
|
||||
|
||||
func _refresh_transform() -> void:
|
||||
if _is_2D:
|
||||
_active_pcam_2D_glob_transform = _active_pcam.get_global_transform()
|
||||
else:
|
||||
_active_pcam_3D_glob_transform = _active_pcam.get_global_transform()
|
||||
|
||||
|
||||
func _process_pcam(delta: float) -> void:
|
||||
if _active_pcam_missing or not is_child_of_camera: return
|
||||
|
||||
if not trigger_pcam_tween:
|
||||
_pcam_follow(delta)
|
||||
|
||||
if viewfinder_needed_check:
|
||||
show_viewfinder_in_play()
|
||||
viewfinder_needed_check = false
|
||||
|
||||
if Engine.is_editor_hint():
|
||||
if not _is_2D:
|
||||
if _active_pcam.get_camera_3D_resource():
|
||||
camera_3D.set_fov(_active_pcam.get_camera_fov())
|
||||
camera_3D.set_h_offset(_active_pcam.get_camera_h_offset())
|
||||
camera_3D.set_v_offset(_active_pcam.get_camera_v_offset())
|
||||
|
||||
else:
|
||||
if tween_duration < _active_pcam.get_tween_duration():
|
||||
_tween_pcam(delta)
|
||||
else:
|
||||
tween_duration = 0
|
||||
trigger_pcam_tween = false
|
||||
show_viewfinder_in_play()
|
||||
_pcam_follow(delta)
|
||||
|
||||
|
||||
func show_viewfinder_in_play() -> void:
|
||||
if _active_pcam.Properties.show_viewfinder_in_play:
|
||||
if not Engine.is_editor_hint() && OS.has_feature("editor"): # Only appears when running in the editor
|
||||
var canvas_layer: CanvasLayer = CanvasLayer.new()
|
||||
get_tree().get_root().get_child(0).add_child(canvas_layer)
|
||||
|
||||
framed_viewfinder_node = framed_viewfinder_scene.instantiate()
|
||||
canvas_layer.add_child(framed_viewfinder_node)
|
||||
else:
|
||||
if framed_viewfinder_node:
|
||||
framed_viewfinder_node.queue_free()
|
||||
|
||||
|
||||
func _get_pcam_node_group() -> Array[Node]:
|
||||
return get_tree().get_nodes_in_group(PcamGroupNames.PCAM_GROUP_NAME)
|
||||
|
||||
|
||||
func _get_pcam_host_group() -> Array[Node]:
|
||||
return get_tree().get_nodes_in_group(PcamGroupNames.PCAM_HOST_GROUP_NAME)
|
||||
|
||||
|
||||
func _process(delta):
|
||||
if not is_instance_valid(_active_pcam): return
|
||||
|
||||
if _should_refresh_transform:
|
||||
# _refresh_transform()
|
||||
if _is_2D:
|
||||
_active_pcam_2D_glob_transform = _active_pcam.get_global_transform()
|
||||
else:
|
||||
_active_pcam_3D_glob_transform = _active_pcam.get_global_transform()
|
||||
|
||||
_should_refresh_transform = false
|
||||
|
||||
_process_pcam(delta)
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
_should_refresh_transform = true
|
||||
|
||||
|
||||
##################
|
||||
# Public Functions
|
||||
##################
|
||||
func pcam_added_to_scene(pcam: Node) -> void:
|
||||
_pcam_list.append(pcam)
|
||||
_find_pcam_with_highest_priority()
|
||||
|
||||
|
||||
func pcam_removed_from_scene(pcam) -> void:
|
||||
_pcam_list.erase(pcam)
|
||||
if pcam == _active_pcam:
|
||||
_active_pcam_missing = true
|
||||
_active_pcam_priority = -1
|
||||
_find_pcam_with_highest_priority()
|
||||
|
||||
|
||||
func pcam_priority_updated(pcam: Node) -> void:
|
||||
if Engine.is_editor_hint() and _active_pcam.Properties.priority_override: return
|
||||
|
||||
if not is_instance_valid(pcam): return
|
||||
|
||||
var current_pcam_priority: int = pcam.get_priority()
|
||||
|
||||
if current_pcam_priority >= _active_pcam_priority and pcam != _active_pcam:
|
||||
_assign_new_active_pcam(pcam)
|
||||
elif pcam == _active_pcam:
|
||||
if current_pcam_priority <= _active_pcam_priority:
|
||||
_active_pcam_priority = current_pcam_priority
|
||||
_find_pcam_with_highest_priority()
|
||||
else:
|
||||
_active_pcam_priority = current_pcam_priority
|
||||
|
||||
|
||||
func pcam_priority_override(pcam: Node) -> void:
|
||||
if Engine.is_editor_hint() and _active_pcam.Properties.priority_override:
|
||||
_active_pcam.Properties.priority_override = false
|
||||
|
||||
_assign_new_active_pcam(pcam)
|
||||
update_editor_viewfinder.emit()
|
||||
|
||||
func pcam_priority_override_disabled() -> void:
|
||||
update_editor_viewfinder.emit()
|
||||
|
||||
|
||||
func get_active_pcam() -> Node:
|
||||
return _active_pcam
|
|
@ -0,0 +1,14 @@
|
|||
class_name Camera3DResource
|
||||
extends Resource
|
||||
|
||||
## The time it takes to tween to this property
|
||||
@export_flags_3d_physics var cull_mask: int = 1048575
|
||||
|
||||
## Horizontally offsets the Camera3D
|
||||
@export var h_offset: float = 0
|
||||
|
||||
## Vertically offsets the Camera3D
|
||||
@export var v_offset: float = 0
|
||||
|
||||
## Adjusts Camera3D FOV
|
||||
@export var fov: float = 75
|
13
addons/phantom_camera/scripts/resources/tween_resource.gd
Normal file
13
addons/phantom_camera/scripts/resources/tween_resource.gd
Normal file
|
@ -0,0 +1,13 @@
|
|||
class_name PhantomCameraTween
|
||||
extends Resource
|
||||
|
||||
const Constants = preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd")
|
||||
|
||||
## The time it takes to tween to this property
|
||||
@export var duration: float = 1
|
||||
|
||||
## The transition bezier type for the tween
|
||||
@export var transition: Constants.TweenTransitions = Constants.TweenTransitions.LINEAR
|
||||
|
||||
## The ease type for the tween
|
||||
@export var ease: Constants.TweenEases = Constants.TweenEases.EASE_IN_OUT
|
439
addons/phantom_camera/scripts/viewfinder/viewfinder.gd
Normal file
439
addons/phantom_camera/scripts/viewfinder/viewfinder.gd
Normal file
|
@ -0,0 +1,439 @@
|
|||
@tool
|
||||
extends Control
|
||||
|
||||
const PcamGroupNames = preload("res://addons/phantom_camera/scripts/group_names.gd")
|
||||
const Constants = preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd")
|
||||
|
||||
var _selected_camera: Node
|
||||
var _active_pcam_camera
|
||||
var pcam_host_group: Array[Node]
|
||||
|
||||
var editor_interface: EditorInterface
|
||||
|
||||
####################
|
||||
# Dead Zone Controls
|
||||
####################
|
||||
@onready var dead_zone_center_hbox: VBoxContainer = %DeadZoneCenterHBoxContainer
|
||||
@onready var dead_zone_center_center_panel: Panel = %DeadZoneCenterCenterPanel
|
||||
@onready var dead_zone_left_center_panel: Panel = %DeadZoneLeftCenterPanel
|
||||
@onready var dead_zone_right_center_panel: Panel = %DeadZoneRightCenterPanel
|
||||
@onready var target_point: Panel = %TargetPoint
|
||||
|
||||
var aspect_ratio_container: AspectRatioContainer
|
||||
@onready var aspect_ratio_containers: AspectRatioContainer = %AspectRatioContainer
|
||||
@onready var camera_viewport_panel: Panel = aspect_ratio_containers.get_child(0)
|
||||
@onready var _framed_viewfinder: Control = %FramedViewfinder
|
||||
@onready var _dead_zone_h_box_container: Control = %DeadZoneHBoxContainer
|
||||
@onready var sub_viewport: SubViewport = %SubViewport
|
||||
|
||||
###########################
|
||||
# Viewfinder Empty Controls
|
||||
###########################
|
||||
@onready var _empty_state_control: Control = %EmptyStateControl
|
||||
@onready var _empty_state_icon: Control = %EmptyStateIcon
|
||||
@onready var _empty_state_text: RichTextLabel = %EmptyStateText
|
||||
@onready var _add_node_button: Button = %AddNodeButton
|
||||
@onready var _add_node_button_text: RichTextLabel = %AddNodeTypeText
|
||||
|
||||
|
||||
###################
|
||||
# Priority Override
|
||||
###################
|
||||
@onready var _priority_override_button: Button = %PriorityOverrideButton
|
||||
@onready var _priority_override_name_label: Label = %PriorityOverrideNameLabel
|
||||
|
||||
|
||||
# TODO - Should be in a central location
|
||||
const _camera_2d_icon: CompressedTexture2D = preload("res://addons/phantom_camera/icons/viewfinder/Camera2DIcon.svg")
|
||||
const _camera_3d_icon: CompressedTexture2D = preload("res://addons/phantom_camera/icons/viewfinder/Camera3DIcon.svg")
|
||||
const _pcam_host_icon: CompressedTexture2D = preload("res://addons/phantom_camera/icons/PhantomCameraHostIcon.svg")
|
||||
const _pcam_2D_icon: CompressedTexture2D = preload("res://addons/phantom_camera/icons/PhantomCameraGizmoIcon2D.svg")
|
||||
const _pcam_3D_icon: CompressedTexture2D = preload("res://addons/phantom_camera/icons/PhantomCameraGizmoIcon3D.svg")
|
||||
|
||||
const _overlay_color_alpha: float = 0.3
|
||||
|
||||
var _no_open_scene_icon: CompressedTexture2D = preload("res://addons/phantom_camera/icons/viewfinder/SceneTypesIcon.svg")
|
||||
var _no_open_scene_string: String = "[b]2D[/b] or [b]3D[/b] scene open"
|
||||
|
||||
var is_2D: bool
|
||||
var is_scene: bool
|
||||
|
||||
var has_camera_viewport_panel_size: bool = true
|
||||
|
||||
var min_horizontal: float
|
||||
var max_horizontal: float
|
||||
var min_vertical: float
|
||||
var max_vertical: float
|
||||
|
||||
|
||||
func _ready():
|
||||
visibility_changed.connect(_visibility_check)
|
||||
set_process(false)
|
||||
|
||||
aspect_ratio_containers.set_ratio(get_viewport_rect().size.x / get_viewport_rect().size.y)
|
||||
|
||||
# TODO - Don't think this is needed / does anything?
|
||||
var root_node = get_tree().get_root().get_child(0)
|
||||
if root_node is Node3D || root_node is Node2D:
|
||||
%SubViewportContainer.set_visible(false)
|
||||
|
||||
if root_node is Node2D:
|
||||
is_2D = true
|
||||
else:
|
||||
is_2D = false
|
||||
|
||||
_set_viewfinder(root_node, false)
|
||||
|
||||
if Engine.is_editor_hint():
|
||||
get_tree().node_added.connect(_node_added)
|
||||
get_tree().node_removed.connect(_node_added)
|
||||
else:
|
||||
_empty_state_control.set_visible(false)
|
||||
|
||||
_priority_override_button.set_visible(false)
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
if Engine.is_editor_hint():
|
||||
if get_tree().node_added.is_connected(_node_added):
|
||||
get_tree().node_added.disconnect(_node_added)
|
||||
get_tree().node_removed.disconnect(_node_added)
|
||||
|
||||
if aspect_ratio_containers.resized.is_connected(_resized):
|
||||
aspect_ratio_containers.resized.disconnect(_resized)
|
||||
|
||||
if _add_node_button.pressed.is_connected(_add_node):
|
||||
_add_node_button.pressed.disconnect(_add_node)
|
||||
|
||||
if is_instance_valid(_active_pcam_camera):
|
||||
if _active_pcam_camera.Properties.is_connected(Constants.DEAD_ZONE_CHANGED_SIGNAL, _on_dead_zone_changed):
|
||||
_active_pcam_camera.Properties.disconnect(Constants.DEAD_ZONE_CHANGED_SIGNAL, _on_dead_zone_changed)
|
||||
|
||||
if _priority_override_button.pressed.is_connected(_select_override_pcam):
|
||||
_priority_override_button.pressed.disconnect(_select_override_pcam)
|
||||
|
||||
|
||||
func _process(_delta: float):
|
||||
if not visible or not is_instance_valid(_active_pcam_camera): return
|
||||
|
||||
var unprojected_position_clamped: Vector2 = Vector2(
|
||||
clamp(_active_pcam_camera.Properties.viewport_position.x, min_horizontal, max_horizontal),
|
||||
clamp(_active_pcam_camera.Properties.viewport_position.y, min_vertical, max_vertical)
|
||||
)
|
||||
target_point.position = camera_viewport_panel.size * unprojected_position_clamped - target_point.size / 2
|
||||
|
||||
if not has_camera_viewport_panel_size:
|
||||
_on_dead_zone_changed()
|
||||
|
||||
|
||||
func _node_added(node: Node) -> void:
|
||||
if editor_interface == null: return
|
||||
_visibility_check()
|
||||
|
||||
|
||||
func scene_changed(scene_root: Node) -> void:
|
||||
if scene_root is Node2D:
|
||||
# print("Is 2D node")
|
||||
is_2D = true
|
||||
is_scene = true
|
||||
|
||||
_add_node_button.set_visible(true)
|
||||
# var camera: Camera2D = scene_root.get_viewport().get_camera_2d()
|
||||
var camera: Camera2D = _get_camera_2D()
|
||||
|
||||
_check_camera(scene_root, camera, true)
|
||||
elif scene_root is Node3D:
|
||||
# print("Is 3D node")
|
||||
# Is 3D scene
|
||||
is_2D = false
|
||||
is_scene = true
|
||||
|
||||
_add_node_button.set_visible(true)
|
||||
var camera: Camera3D = scene_root.get_viewport().get_camera_3d()
|
||||
_check_camera(scene_root, camera, false)
|
||||
else:
|
||||
# print("Not a 2D or 3D scene")
|
||||
is_scene = false
|
||||
# Is not a 2D or 3D scene
|
||||
_set_empty_viewfinder_state(_no_open_scene_string, _no_open_scene_icon)
|
||||
_add_node_button.set_visible(false)
|
||||
|
||||
|
||||
func _visibility_check():
|
||||
if not editor_interface or not visible: return
|
||||
|
||||
if not is_instance_valid(editor_interface):
|
||||
is_scene = false
|
||||
# Is not a 2D or 3D scene
|
||||
_set_empty_viewfinder_state(_no_open_scene_string, _no_open_scene_icon)
|
||||
_add_node_button.set_visible(false)
|
||||
return
|
||||
|
||||
var root: Node = editor_interface.get_edited_scene_root()
|
||||
if root is Node2D:
|
||||
# print("Is a 2D scene")
|
||||
is_2D = true
|
||||
is_scene = true
|
||||
|
||||
_add_node_button.set_visible(true)
|
||||
# TODO: Figure out why the line below doesn't work...
|
||||
# var camera: Camera2D = root.get_viewport().get_camera_2d()
|
||||
|
||||
var camera: Camera2D = _get_camera_2D()
|
||||
_check_camera(root, camera, true)
|
||||
elif root is Node3D:
|
||||
# Is 3D scene
|
||||
is_2D = false
|
||||
is_scene = true
|
||||
|
||||
_add_node_button.set_visible(true)
|
||||
var camera: Camera3D = root.get_viewport().get_camera_3d()
|
||||
_check_camera(root, camera, false)
|
||||
# editor_interface.get_selection().clear()
|
||||
# editor_interface.get_selection().add_node(pcam_host_group[0].get_active_pcam())
|
||||
else:
|
||||
is_scene = false
|
||||
# Is not a 2D or 3D scene
|
||||
_set_empty_viewfinder_state(_no_open_scene_string, _no_open_scene_icon)
|
||||
_add_node_button.set_visible(false)
|
||||
|
||||
if not _priority_override_button.pressed.is_connected(_select_override_pcam):
|
||||
_priority_override_button.pressed.connect(_select_override_pcam)
|
||||
|
||||
|
||||
func _get_camera_2D() -> Camera2D:
|
||||
var camerasGroupName = "__cameras_%d" % editor_interface.get_edited_scene_root().get_viewport().get_viewport_rid().get_id()
|
||||
var cameras = get_tree().get_nodes_in_group(camerasGroupName)
|
||||
|
||||
for camera in cameras:
|
||||
if camera is Camera2D and camera.is_current:
|
||||
return camera
|
||||
|
||||
return null
|
||||
|
||||
|
||||
func _check_camera(root: Node, camera: Node, is_2D: bool) -> void:
|
||||
var camera_string: String
|
||||
var pcam_string: String
|
||||
var color: Color
|
||||
var color_alpha: Color
|
||||
var camera_icon: CompressedTexture2D
|
||||
var pcam_icon: CompressedTexture2D
|
||||
|
||||
if is_2D:
|
||||
camera_string = Constants.CAMERA_2D_NODE_NAME
|
||||
pcam_string = Constants.PCAM_2D_NODE_NAME
|
||||
color = Constants.COLOR_2D
|
||||
camera_icon = _camera_2d_icon
|
||||
pcam_icon = _pcam_2D_icon
|
||||
else:
|
||||
camera_string = Constants.CAMERA_3D_NODE_NAME
|
||||
pcam_string = Constants.PCAM_3D_NODE_NAME
|
||||
color = Constants.COLOR_3D
|
||||
camera_icon = _camera_3d_icon
|
||||
pcam_icon = _pcam_3D_icon
|
||||
|
||||
if camera:
|
||||
# Has Camera
|
||||
var pcam_host: PhantomCameraHost
|
||||
if camera.get_children().size() > 0:
|
||||
for cam_child in camera.get_children():
|
||||
if cam_child is PhantomCameraHost:
|
||||
pcam_host = cam_child
|
||||
|
||||
if pcam_host:
|
||||
if get_tree().get_nodes_in_group(PcamGroupNames.PCAM_GROUP_NAME):
|
||||
# Pcam exists in tree
|
||||
_set_viewfinder(root, true)
|
||||
# if pcam_host.get_active_pcam().get_get_follow_mode():
|
||||
# _on_dead_zone_changed()
|
||||
|
||||
_set_viewfinder_state()
|
||||
|
||||
# Related to: https://github.com/ramokz/phantom-camera/issues/105
|
||||
# REMOVE BELOW WHEN 2D VIEWFINDER IS SUPPORTED
|
||||
if not is_2D:
|
||||
%NoSupportMsg.set_visible(false)
|
||||
elif is_2D:
|
||||
%NoSupportMsg.set_visible(true)
|
||||
### REMOVAL END
|
||||
|
||||
else:
|
||||
# No PCam in scene
|
||||
_update_button(pcam_string, pcam_icon, color)
|
||||
_set_empty_viewfinder_state(pcam_string, pcam_icon)
|
||||
else:
|
||||
# No PCamHost in scene
|
||||
_update_button(Constants.PCAM_HOST_NODE_NAME, _pcam_host_icon, Constants.PCAM_HOST_COLOR)
|
||||
_set_empty_viewfinder_state(Constants.PCAM_HOST_NODE_NAME, _pcam_host_icon)
|
||||
else:
|
||||
# No PCamHost in scene
|
||||
_update_button(Constants.PCAM_HOST_NODE_NAME, _pcam_host_icon, Constants.PCAM_HOST_COLOR)
|
||||
_set_empty_viewfinder_state(Constants.PCAM_HOST_NODE_NAME, _pcam_host_icon)
|
||||
else:
|
||||
# No Camera
|
||||
_update_button(camera_string, camera_icon, color)
|
||||
_set_empty_viewfinder_state(camera_string, camera_icon)
|
||||
|
||||
|
||||
func _update_button(text: String, icon: CompressedTexture2D, color: Color) -> void:
|
||||
_add_node_button_text.set_text("[center]Add [img=32]" + icon.resource_path + "[/img] [b]"+ text + "[/b][/center]");
|
||||
var button_theme_hover: StyleBoxFlat = _add_node_button.get_theme_stylebox("hover")
|
||||
button_theme_hover.border_color = color
|
||||
_add_node_button.add_theme_stylebox_override("hover", button_theme_hover)
|
||||
|
||||
|
||||
func _set_viewfinder_state() -> void:
|
||||
_empty_state_control.set_visible(false)
|
||||
|
||||
_framed_viewfinder.set_visible(true)
|
||||
target_point.set_visible(true)
|
||||
|
||||
if is_instance_valid(_active_pcam_camera):
|
||||
if _active_pcam_camera.get_follow_mode() == Constants.FollowMode.FRAMED:
|
||||
_dead_zone_h_box_container.set_visible(true)
|
||||
else:
|
||||
_dead_zone_h_box_container.set_visible(false)
|
||||
|
||||
|
||||
func _set_empty_viewfinder_state(text: String, icon: CompressedTexture2D) -> void:
|
||||
_framed_viewfinder.set_visible(false)
|
||||
target_point.set_visible(false)
|
||||
|
||||
_empty_state_control.set_visible(true)
|
||||
_empty_state_icon.set_texture(icon)
|
||||
if icon == _no_open_scene_icon:
|
||||
_empty_state_text.set_text("[center]No " + text + "[/center]")
|
||||
else:
|
||||
_empty_state_text.set_text("[center]No [b]" + text + "[/b] in scene[/center]")
|
||||
|
||||
if _add_node_button.pressed.is_connected(_add_node):
|
||||
_add_node_button.pressed.disconnect(_add_node)
|
||||
|
||||
_add_node_button.pressed.connect(_add_node.bind(text))
|
||||
|
||||
|
||||
func _add_node(node_type: String) -> void:
|
||||
if not editor_interface: return
|
||||
|
||||
var root: Node = editor_interface.get_edited_scene_root()
|
||||
|
||||
match node_type:
|
||||
_no_open_scene_string:
|
||||
pass
|
||||
Constants.CAMERA_2D_NODE_NAME:
|
||||
var camera: Camera2D = Camera2D.new()
|
||||
_instantiate_node(root, camera, Constants.CAMERA_2D_NODE_NAME)
|
||||
Constants.CAMERA_3D_NODE_NAME:
|
||||
var camera: Camera3D = Camera3D.new()
|
||||
_instantiate_node(root, camera, Constants.CAMERA_3D_NODE_NAME)
|
||||
Constants.PCAM_HOST_NODE_NAME:
|
||||
var pcam_host: PhantomCameraHost = PhantomCameraHost.new()
|
||||
pcam_host.set_name(Constants.PCAM_HOST_NODE_NAME)
|
||||
if is_2D:
|
||||
# get_tree().get_edited_scene_root().get_viewport().get_camera_2d().add_child(pcam_host)
|
||||
_get_camera_2D().add_child(pcam_host)
|
||||
pcam_host.set_owner(get_tree().get_edited_scene_root())
|
||||
else:
|
||||
# var pcam_3D := get_tree().get_edited_scene_root().get_viewport().get_camera_3d()
|
||||
get_tree().get_edited_scene_root().get_viewport().get_camera_3d().add_child(pcam_host)
|
||||
pcam_host.set_owner(get_tree().get_edited_scene_root())
|
||||
Constants.PCAM_2D_NODE_NAME:
|
||||
var pcam_2D: PhantomCamera2D = PhantomCamera2D.new()
|
||||
_instantiate_node(root, pcam_2D, Constants.PCAM_2D_NODE_NAME)
|
||||
Constants.PCAM_3D_NODE_NAME:
|
||||
var pcam_3D: PhantomCamera3D = PhantomCamera3D.new()
|
||||
_instantiate_node(root, pcam_3D, Constants.PCAM_3D_NODE_NAME)
|
||||
|
||||
|
||||
func _instantiate_node(root: Node, node: Node, name: String) -> void:
|
||||
node.set_name(name)
|
||||
root.add_child(node)
|
||||
node.set_owner(get_tree().get_edited_scene_root())
|
||||
|
||||
|
||||
func _set_viewfinder(root: Node, editor: bool):
|
||||
pcam_host_group = root.get_tree().get_nodes_in_group(PcamGroupNames.PCAM_HOST_GROUP_NAME)
|
||||
if pcam_host_group.size() != 0:
|
||||
if pcam_host_group.size() == 1:
|
||||
var pcam_host: PhantomCameraHost = pcam_host_group[0]
|
||||
if is_2D:
|
||||
_selected_camera = pcam_host.camera_2D
|
||||
_active_pcam_camera = _selected_camera.get_child(0).get_active_pcam() as PhantomCamera2D
|
||||
if editor:
|
||||
var camera_2D_rid: RID = _selected_camera.get_canvas_item()
|
||||
# TODO - Missing 2D viewport support - https://github.com/ramokz/phantom-camera/issues/105
|
||||
RenderingServer.viewport_attach_camera(sub_viewport.get_viewport_rid(), camera_2D_rid)
|
||||
else:
|
||||
_selected_camera = pcam_host.camera_3D
|
||||
_active_pcam_camera = _selected_camera.get_child(0).get_active_pcam() as PhantomCamera3D
|
||||
if editor:
|
||||
var camera_3D_rid: RID = _selected_camera.get_camera_rid()
|
||||
RenderingServer.viewport_attach_camera(sub_viewport.get_viewport_rid(), camera_3D_rid)
|
||||
|
||||
if _selected_camera.keep_aspect == Camera3D.KeepAspect.KEEP_HEIGHT:
|
||||
aspect_ratio_containers.set_stretch_mode(AspectRatioContainer.STRETCH_HEIGHT_CONTROLS_WIDTH)
|
||||
else:
|
||||
aspect_ratio_containers.set_stretch_mode(AspectRatioContainer.STRETCH_WIDTH_CONTROLS_HEIGHT)
|
||||
|
||||
_on_dead_zone_changed()
|
||||
set_process(true)
|
||||
|
||||
if not pcam_host.update_editor_viewfinder.is_connected(_on_update_editor_viewfinder):
|
||||
pcam_host.update_editor_viewfinder.connect(_on_update_editor_viewfinder.bind(pcam_host))
|
||||
|
||||
if not aspect_ratio_containers.resized.is_connected(_resized):
|
||||
aspect_ratio_containers.resized.connect(_resized)
|
||||
|
||||
if not _active_pcam_camera.Properties.is_connected(_active_pcam_camera.Constants.DEAD_ZONE_CHANGED_SIGNAL, _on_dead_zone_changed):
|
||||
_active_pcam_camera.Properties.connect(_active_pcam_camera.Constants.DEAD_ZONE_CHANGED_SIGNAL, _on_dead_zone_changed)
|
||||
|
||||
# aspect_ratio_container
|
||||
# TODO - Might not be needed
|
||||
# _active_pcam_camera.Properties.disconnect(_on_dead_zone_changed)
|
||||
else:
|
||||
for pcam_host in pcam_host_group:
|
||||
print(pcam_host, " is in a scene")
|
||||
|
||||
|
||||
func _resized() -> void:
|
||||
_on_dead_zone_changed()
|
||||
|
||||
func _on_dead_zone_changed() -> void:
|
||||
if not is_instance_valid(_active_pcam_camera): return
|
||||
|
||||
if camera_viewport_panel.size == Vector2.ZERO:
|
||||
has_camera_viewport_panel_size = false
|
||||
return
|
||||
else:
|
||||
has_camera_viewport_panel_size = true
|
||||
|
||||
var dead_zone_width: float = _active_pcam_camera.Properties.follow_framed_dead_zone_width * camera_viewport_panel.size.x
|
||||
var dead_zone_height: float = _active_pcam_camera.Properties.follow_framed_dead_zone_height * camera_viewport_panel.size.y
|
||||
dead_zone_center_hbox.set_custom_minimum_size(Vector2(dead_zone_width, 0))
|
||||
dead_zone_center_center_panel.set_custom_minimum_size(Vector2(0, dead_zone_height))
|
||||
dead_zone_left_center_panel.set_custom_minimum_size(Vector2(0, dead_zone_height))
|
||||
dead_zone_right_center_panel.set_custom_minimum_size(Vector2(0, dead_zone_height))
|
||||
|
||||
min_horizontal = 0.5 - _active_pcam_camera.Properties.follow_framed_dead_zone_width / 2
|
||||
max_horizontal = 0.5 + _active_pcam_camera.Properties.follow_framed_dead_zone_width / 2
|
||||
min_vertical = 0.5 - _active_pcam_camera.Properties.follow_framed_dead_zone_height / 2
|
||||
max_vertical = 0.5 + _active_pcam_camera.Properties.follow_framed_dead_zone_height / 2
|
||||
|
||||
# target_point.position = Vector2(viewport_width / 2, viewport_height / 2)
|
||||
|
||||
####################
|
||||
## Priority Override
|
||||
####################
|
||||
func _on_update_editor_viewfinder(pcam_host: PhantomCameraHost) -> void:
|
||||
if pcam_host.get_active_pcam().Properties.priority_override:
|
||||
_active_pcam_camera = pcam_host.get_active_pcam()
|
||||
_priority_override_button.set_visible(true)
|
||||
_priority_override_name_label.set_text(_active_pcam_camera.name)
|
||||
_priority_override_button.set_tooltip_text(_active_pcam_camera.name)
|
||||
else:
|
||||
_priority_override_button.set_visible(false)
|
||||
|
||||
func _select_override_pcam() -> void:
|
||||
editor_interface.get_selection().clear()
|
||||
editor_interface.get_selection().add_node(_active_pcam_camera)
|
Loading…
Add table
Add a link
Reference in a new issue