Skip to content

Added zooming and offset to collision #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func set_action(action: String, config_key: String):

This logic can be used to load key bindings from a configuration file.

## Docummentation:
## Documentation:

### Settings available via Editor/GDscript:

Expand All @@ -71,6 +71,7 @@ This logic can be used to load key bindings from a configuration file.
- float `distance` - The distance between the camera and the privot object. Minimum value is 0. Default value is 5.0
- bool `rotate_privot` - Rotate privot object with the mouselook. Default is false.
- bool `collision` - The camera avoid it to go through/behind objects. Default is true.
- float 'collision_buffer' - When colliding with objects offsets camera towards privot to prevent visual clipping.

#### Movement
- bool `movement` - Enable/disable camera movement (flying). Default is true.
Expand All @@ -93,6 +94,13 @@ This logic can be used to load key bindings from a configuration file.
- String `right_action` - Input Action for Right movement. Default action is "ui_right".
- String `up_action` - Input Action for upward movement. Default action is "ui_page_up".
- String `down_action` - Input Action for downward movement. Default action is "ui_page_down".
- String 'zoom_in_action' - Input Action for the zoom in movement. Default action is "".
- String 'zoom_out_action' - Input Action for the zoom out movement. Default action is "".

##### Zoom
- float 'zoom_min' - Minimum percentage of 'distance' to keep the camera away from the privot. Default is 25%
- float 'zoom_percent' - Percentage of current camera zoom between 'zoom_min' and 'distance'. Default is 0%
- float 'zoom_step' - Percentage to increase or decrease zoom levels on an input event. Default is 1%

#### Ingame Gui
- String `gui_action` - Input Action to show/hide the ingame control gui. Default action is "ui_cancel".
Expand Down
2 changes: 1 addition & 1 deletion assets/maujoe.camera_control/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func set_action(action: String, config_key: String):

This logic can be used to load key bindings from a configuration file.

## Docummentation:
## Documentation:

### Settings available via Editor/GDscript:

Expand Down
3 changes: 2 additions & 1 deletion assets/maujoe.camera_control/demo/demo.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ rotate_left_action = "ui_rotate_left"
rotate_right_action = "ui_rotate_right"
rotate_up_action = "ui_rotate_up"
rotate_down_action = "ui-rotate_down"
trigger_action = ""
zoom_in_action = "ui_zoom_in"
zoom_out_action = "ui_zoom_out"

[node name="DirectionalLight" type="DirectionalLight" parent="."]
transform = Transform( 1, 0, 0, 0, 0.866025, 0.5, 0, -0.5, 0.866025, 0, 8, 0 )
Expand Down
79 changes: 65 additions & 14 deletions assets/maujoe.camera_control/scripts/camera_control.gd
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export(NodePath) var privot setget set_privot
export var distance = 5.0 setget set_distance
export var rotate_privot = false
export var collisions = true setget set_collisions
export(float) var collision_buffer = 0.05 #Offsets the camera closer to privot to prevent clipping of camera through geometry at odd angles

# Movement settings
export var movement = true
Expand All @@ -45,6 +46,14 @@ export var right_action = "ui_right"
export var up_action = "ui_page_up"
export var down_action = "ui_page_down"
export var trigger_action = ""
export var zoom_in_action = ""
export var zoom_out_action = ""

#Zoom settings
export(float, 0.0, 1.0) var zoom_min = 0.25 setget set_zoom_min #Percentage of total distance to be used as minimum distance to maintain
export(float, 0.0, 1.0) var zoom_percent = 0.0 setget set_zoom #Percentage value of Current Zoom Level (1.0 is completely zoomed in)
#Note: This can be smaller than zoom_min its the percent of the available range of distance.
export(float, 0.0, 1.0) var zoom_step = 0.01 setget set_zoom_step #On each event how much to change zoom in or out(Can also disable zoom having this at 0.0)

# Gui settings
export var use_gui = true
Expand Down Expand Up @@ -78,7 +87,9 @@ func _ready():
rotate_left_action,
rotate_right_action,
rotate_up_action,
rotate_down_action
rotate_down_action,
zoom_in_action,
zoom_out_action
])

if privot:
Expand All @@ -104,14 +115,22 @@ func _input(event):
if freelook and _triggered:
if event is InputEventMouseMotion:
_mouse_offset = event.relative

_rotation_offset.x = Input.get_action_strength(rotate_right_action) - Input.get_action_strength(rotate_left_action)
_rotation_offset.y = Input.get_action_strength(rotate_down_action) - Input.get_action_strength(rotate_up_action)

if movement and _triggered:
_direction.x = Input.get_action_strength(right_action) - Input.get_action_strength(left_action)
_direction.y = Input.get_action_strength(up_action) - Input.get_action_strength(down_action)
_direction.z = Input.get_action_strength(backward_action) - Input.get_action_strength(forward_action)
if zoom_step > 0.0 and _triggered:
if len(zoom_in_action)!=0 and len(zoom_out_action)!=0:
if event.is_action_pressed(zoom_in_action):
#_zoomDir = 1#TODO allow for zoom smoothing over time(?)
zoom_percent += zoom_step
elif event.is_action_pressed(zoom_out_action):
#_zoomDir = -1#TODO allow for zoom smoothing over time(?)
zoom_percent -= zoom_step
set_zoom(zoom_percent)#Apply/update zoom limit

func _process(delta):
if _triggered:
Expand All @@ -129,6 +148,20 @@ func _physics_process(delta):
if _triggered:
_update_views_physics(delta)

func _force_update( delta ) -> void:
#Set Trigger as modes require this boolean as true to run. and we want them to run regardless of trigger when forced.
#We backup the current value of the boolean.
var tmp = _triggered
_triggered = true
#Depending on mode of operation call respective function
if collisions and privot:
_physics_process( delta )
else:
_process( delta )
#Restore the previous value of _triggered
#This should be safe as the called functions don't modify the triggered boolean value.
_triggered = tmp

func _update_views_physics(delta):
# Called when collision are enabled
_update_distance()
Expand All @@ -138,23 +171,23 @@ func _update_views_physics(delta):
var space_state = get_world().get_direct_space_state()
var obstacle = space_state.intersect_ray(privot.get_translation(), get_translation())
if not obstacle.empty():
set_translation(obstacle.position)
set_translation(obstacle.position.move_toward(translation, -collision_buffer))

func _update_movement(delta):
var offset = max_speed * acceleration * _direction

_speed.x = clamp(_speed.x + offset.x, -max_speed.x, max_speed.x)
_speed.y = clamp(_speed.y + offset.y, -max_speed.y, max_speed.y)
_speed.z = clamp(_speed.z + offset.z, -max_speed.z, max_speed.z)

# Apply deceleration if no input
if _direction.x == 0:
_speed.x *= (1.0 - deceleration)
if _direction.y == 0:
_speed.y *= (1.0 - deceleration)
if _direction.z == 0:
_speed.z *= (1.0 - deceleration)

if local:
translate(_speed * delta)
else:
Expand All @@ -169,36 +202,43 @@ func _update_rotation(delta):
offset += _rotation_offset * sensitivity * ROTATION_MULTIPLIER * delta

_mouse_offset = Vector2()

_yaw = _yaw * smoothness + offset.x * (1.0 - smoothness)
_pitch = _pitch * smoothness + offset.y * (1.0 - smoothness)

if yaw_limit < 360:
_yaw = clamp(_yaw, -yaw_limit - _total_yaw, yaw_limit - _total_yaw)
if pitch_limit < 360:
_pitch = clamp(_pitch, -pitch_limit - _total_pitch, pitch_limit - _total_pitch)

_total_yaw += _yaw
_total_pitch += _pitch

if privot:
var target = privot.get_translation()
var dist = get_translation().distance_to(target)

set_translation(target)
rotate_y(deg2rad(-_yaw))
rotate_object_local(Vector3(1,0,0), deg2rad(-_pitch))
translate(Vector3(0.0, 0.0, dist))

if rotate_privot:
privot.rotate_y(deg2rad(-_yaw))
else:
rotate_y(deg2rad(-_yaw))
rotate_object_local(Vector3(1,0,0), deg2rad(-_pitch))

func _calculated_zoomed_distance() -> float:
#Uses distance for distMax
var distMin = (distance * zoom_min)
var delta = (distance - distMin)
#Removing the '1.0 -' below will invert zoom value where 0.0 is fully zoomed in.
return distMin + ((1.0 - zoom_percent) * delta)

func _update_distance():
var t = privot.get_translation()
t.z -= distance
t.z -= _calculated_zoomed_distance()
set_translation(t)

func _update_process_func():
Expand Down Expand Up @@ -232,6 +272,10 @@ func set_enabled(value):
Input.set_mouse_mode(mouse_mode)
set_process_input(true)
_update_process_func()
#When scene starts with the camera not initially in the correct position and awaiting input with a privot defined,
#Until that first input event is reveived, camera would remain in the wrong initial location
#unless we force an initial update here. So we don't have to wait for the first input event(s) to define the _triggered bool.
_force_update( 0 )
else:
set_process(false)
set_process_input(false)
Expand All @@ -242,3 +286,10 @@ func set_smoothness(value):

func set_distance(value):
distance = max(0, value)

func set_zoom_min(value):
zoom_min = clamp(value, 0.0, 1.0)
func set_zoom(value):
zoom_percent = clamp(value, 0.0, 1.0)
func set_zoom_step(value):
zoom_step = clamp(value, 0.0, 1.0)
48 changes: 48 additions & 0 deletions assets/maujoe.camera_control/scripts/camera_control_gui.gd
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ var panel
var mouse_over = false
var mouse_pressed = false

#warning-ignore:SHADOWED_VARIABLE
#warning-ignore:SHADOWED_VARIABLE
func _init(camera, shortcut):
self.camera = camera
self.shortcut = shortcut
Expand Down Expand Up @@ -124,6 +126,14 @@ func _ready():
collisions.set_pressed(camera.collisions)
collisions.connect("toggled",self,"_on_btn_collisions_toggled")

var lbl_collision_buffer = Label.new()
lbl_collision_buffer.set_text("Collision Buffer")

var collision_buffer = HScrollBar.new()
collision_buffer.set_max(max(camera.distance - camera.zoom_min, 0.0))
collision_buffer.set_value(camera.collision_buffer)
collision_buffer.connect("value_changed", self, "_in_hsb_collision_buffer_value_changed")

# Movement
var lbl_movement = Label.new()
lbl_movement.set_text("Movement")
Expand Down Expand Up @@ -156,6 +166,25 @@ func _ready():
deceleration.set_value(camera.deceleration)
deceleration.connect("value_changed", self, "_in_hsb_deceleration_value_changed")

var lbl_zoom_min = Label.new()
var lbl_zoom_percent = Label.new()
var lbl_zoom_step = Label.new()
lbl_zoom_min.set_text("Minimum Zoom %")
lbl_zoom_percent.set_text("Current Zoom %")
lbl_zoom_step.set_text("Zoom Step Amount")

var zoom_min = HScrollBar.new()
zoom_min.set_max(1.0)
zoom_min.set_value(camera.zoom_min)
zoom_min.connect("value_changed", self, "_in_hsb_zoom_min_value_changed")
var zoom_percent = HScrollBar.new()
zoom_percent.set_max(1.0)
zoom_percent.set_value(camera.zoom_percent)
zoom_percent.connect("value_changed", self, "_in_hsb_zoom_percent_value_changed")
var zoom_step = SpinBox.new()
zoom_step.set_value(camera.zoom_step)
zoom_step.connect("value_changed",self,"_on_box_zoom_step_value_changed")

add_child(panel)
panel.add_child(container)
container.add_child(lbl_mouse)
Expand All @@ -175,6 +204,8 @@ func _ready():
container.add_child(lbl_pitch)
container.add_child(pitch)
container.add_child(collisions)
container.add_child(lbl_collision_buffer)
container.add_child(collision_buffer)
container.add_child(lbl_movement)
container.add_child(movement)
container.add_child(lbl_speed)
Expand All @@ -183,6 +214,12 @@ func _ready():
container.add_child(acceleration)
container.add_child(lbl_deceleration)
container.add_child(deceleration)
container.add_child(lbl_zoom_min)
container.add_child(zoom_min)
container.add_child(lbl_zoom_percent)
container.add_child(zoom_percent)
container.add_child(lbl_zoom_step)
container.add_child(zoom_step)

if DRAGGABLE:
panel.connect("mouse_entered", self, "_panel_entered")
Expand Down Expand Up @@ -211,6 +248,7 @@ func _input(event):
elif event is InputEventMouseMotion and mouse_over and mouse_pressed:
panel.set_begin(panel.get_begin() + event.relative)

#warning-ignore:SHADOWED_VARIABLE
func _update_privots(privot):
privot.clear()
privot.add_item("None")
Expand Down Expand Up @@ -281,6 +319,9 @@ func _on_box_pitch_value_changed(value):
func _on_btn_collisions_toggled(pressed):
camera.collisions = pressed

func _in_hsb_collision_buffer_value_changed(value):
camera.collision_buffer = value

func _on_btn_movement_toggled(pressed):
camera.movement = pressed

Expand All @@ -294,3 +335,10 @@ func _in_hsb_acceleration_value_changed(value):

func _in_hsb_deceleration_value_changed(value):
camera.deceleration = value

func _in_hsb_zoom_min_value_changed(value):
camera.zoom_min = value
func _in_hsb_zoom_percent_value_changed(value):
camera.zoom_percent = value
func _on_box_zoom_step_value_changed(value):
camera.zoom_step = value
15 changes: 14 additions & 1 deletion project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ config/icon="res://assets/maujoe.camera_control/icon.png"

[display]

window/size/height=740
window/size/test_width=1024
window/size/test_height=600
window/size/test_height=740

[input]

Expand Down Expand Up @@ -85,6 +86,18 @@ camera_control={
"events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":2,"pressed":false,"doubleclick":false,"script":null)
]
}
ui_zoom_in={
"deadzone": 0.5,
"events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":4,"pressed":false,"doubleclick":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":true,"control":false,"meta":false,"command":false,"pressed":false,"scancode":61,"unicode":0,"echo":false,"script":null)
]
}
ui_zoom_out={
"deadzone": 0.5,
"events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":5,"pressed":false,"doubleclick":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":45,"unicode":0,"echo":false,"script":null)
]
}

[rendering]

Expand Down