camera_controller.gd 4.0 KB

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