|
| 1 | +extends Spatial |
| 2 | + |
| 3 | +# Handle the motion of both players' camera as well as communication with the |
| 4 | +# SplitScreen shader to achieve the dynamic split screen effet |
| 5 | +# |
| 6 | +# Cameras are place on the segment joining the two players, either in the middle |
| 7 | +# if players are close enough or at a fixed distance if they are not. |
| 8 | +# In the first case, both cameras being at the same location, only the view of |
| 9 | +# the first one is used for the entire screen thus allowing the players to play |
| 10 | +# on a unsplit screen. |
| 11 | +# In the second case, the screen is split in two with a line perpendicular to the |
| 12 | +# segement joining the two players. |
| 13 | +# |
| 14 | +# The points of customization are: |
| 15 | +# max_separation: the distance between players at which the view starts to split |
| 16 | +# split_line_thickness: the thickness of the split line in pixels |
| 17 | +# split_line_color: color of the split line |
| 18 | +# adaptive_split_line_thickness: if true, the split line thickness will vary |
| 19 | +# depending on the distance between players. If false, the thickness will |
| 20 | +# be constant and equal to split_line_thickness |
| 21 | + |
| 22 | +export(float) var max_separation = 20.0 |
| 23 | +export(float) var split_line_thickness = 3.0 |
| 24 | +export(Color, RGBA) var split_line_color = Color.black |
| 25 | +export(bool) var adaptive_split_line_thickness = true |
| 26 | + |
| 27 | +onready var player1 = $'../Player1' |
| 28 | +onready var player2 = $'../Player2' |
| 29 | +onready var camera1: Camera = $'Viewport1/Camera1' |
| 30 | +onready var camera2: Camera = $'Viewport2/Camera2' |
| 31 | +onready var view: TextureRect = $'View' |
| 32 | + |
| 33 | + |
| 34 | +func _ready(): |
| 35 | + _on_size_changed() |
| 36 | + _update_splitscreen() |
| 37 | + |
| 38 | + get_viewport().connect("size_changed", self, "_on_size_changed") |
| 39 | + |
| 40 | + view.material.set_shader_param('viewport1', $Viewport1.get_texture()) |
| 41 | + view.material.set_shader_param('viewport2', $Viewport2.get_texture()) |
| 42 | + |
| 43 | + |
| 44 | +func _process(_delta): |
| 45 | + _move_cameras() |
| 46 | + _update_splitscreen() |
| 47 | + |
| 48 | + |
| 49 | +func _move_cameras(): |
| 50 | + var position_difference = _compute_position_difference_in_world() |
| 51 | + |
| 52 | + var distance = clamp(_compute_horizontal_length(position_difference), 0, max_separation) |
| 53 | + |
| 54 | + position_difference = position_difference.normalized() * distance |
| 55 | + |
| 56 | + camera1.translation.x = player1.translation.x + position_difference.x / 2.0 |
| 57 | + camera1.translation.z = player1.translation.z + position_difference.z / 2.0 |
| 58 | + |
| 59 | + camera2.translation.x = player2.translation.x - position_difference.x / 2.0 |
| 60 | + camera2.translation.z = player2.translation.z - position_difference.z / 2.0 |
| 61 | + |
| 62 | + |
| 63 | +func _update_splitscreen(): |
| 64 | + var screen_size = get_viewport().get_visible_rect().size |
| 65 | + var player1_position = camera1.unproject_position(player1.translation) / screen_size |
| 66 | + var player2_position = camera2.unproject_position(player2.translation) / screen_size |
| 67 | + |
| 68 | + var thickness |
| 69 | + if adaptive_split_line_thickness: |
| 70 | + var position_difference = _compute_position_difference_in_world() |
| 71 | + var distance = _compute_horizontal_length(position_difference) |
| 72 | + thickness = lerp(0, split_line_thickness, (distance - max_separation) / max_separation) |
| 73 | + thickness = clamp(thickness, 0, split_line_thickness) |
| 74 | + else: |
| 75 | + thickness = split_line_thickness |
| 76 | + |
| 77 | + view.material.set_shader_param('split_active', _get_split_state()) |
| 78 | + view.material.set_shader_param('player1_position', player1_position) |
| 79 | + view.material.set_shader_param('player2_position', player2_position) |
| 80 | + view.material.set_shader_param('split_line_thickness', thickness) |
| 81 | + view.material.set_shader_param('split_line_color', split_line_color) |
| 82 | + |
| 83 | + |
| 84 | +# Split screen is active if players are too far apart from each other. |
| 85 | +# Only the horizontal components (x, z) are used for distance computation |
| 86 | +func _get_split_state(): |
| 87 | + var position_difference = _compute_position_difference_in_world() |
| 88 | + var separation_distance = _compute_horizontal_length(position_difference) |
| 89 | + return separation_distance > max_separation |
| 90 | + |
| 91 | + |
| 92 | +func _on_size_changed(): |
| 93 | + var screen_size = get_viewport().get_visible_rect().size |
| 94 | + |
| 95 | + $Viewport1.size = screen_size |
| 96 | + $Viewport2.size = screen_size |
| 97 | + view.rect_size = screen_size |
| 98 | + |
| 99 | + view.material.set_shader_param('viewport_size', screen_size) |
| 100 | + |
| 101 | + |
| 102 | +func _compute_position_difference_in_world(): |
| 103 | + return player2.translation - player1.translation |
| 104 | + |
| 105 | + |
| 106 | +func _compute_horizontal_length(vec): |
| 107 | + return Vector2(vec.x, vec.z).length() |
0 commit comments