Advertisement
TyrannicGoat

Co-op platformer camera controller

Aug 16th, 2022
2,219
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. extends Node
  2. #NOTE: this script was originally meant to allow more than 2 avatars but I simplified it, as it is currently it will only function with 0 avatars or 2 avatars
  3. var avatars = [null, null]
  4. var target_zoom = Vector2(1,1)
  5.  
  6. export var avatar_padding = Vector2(25, 25)
  7. export var prefer_zoom= Vector2(1,1)
  8. export var limit_zoom = Vector2(4, 4)
  9.  
  10. onready var camera = $Camera2D
  11.  
  12.  
  13. func _init():
  14.     Players.connect("avatar_was_assigned", self, "_on_avatar_assigned_to_player")
  15.     Players.connect("avatar_was_unassigned", self, "_on_avatar_unassigned_to_player")
  16.    
  17. func _ready():
  18.     camera.current = true
  19.     target_zoom = camera.zoom
  20.     var viewport = get_viewport()
  21.  
  22. func _process(delta):
  23.     var current_camera_position = camera.position
  24.     camera.current = true
  25.    
  26.     var target_location = _get_target_location()
  27.     var target_zoom = _get_target_zoom()
  28.    
  29.     camera.position = target_location
  30.     camera.zoom = target_zoom
  31.  
  32. #-----------------------------------------------
  33. #helpers
  34.  
  35. func _get_avatar_position(avatar_index :int):
  36.     if avatar_index > avatars.size():
  37.         return camera.position
  38.     var avatar = avatars[avatar_index] as Node2D
  39.     if avatar == null:
  40.         return camera.position
  41.     return avatar.position
  42.  
  43. func _get_target_location():
  44.     var target_position = _calculate_target_position()
  45.     if target_position == null: #edge case: null if avatar count = 0
  46.         return camera.position
  47.     return target_position
  48.  
  49.  
  50. func _get_required_size():#to ensure all player avatars are visible
  51.     var p1 = _get_avatar_position(0) as Vector2
  52.     var p2 = _get_avatar_position(1) as Vector2
  53.    
  54.     var avatar_min_pos = Vector2(min(p1.x, p2.x),  min(p1.y, p2.y))
  55.     var avatar_max_pos = Vector2(max(p1.x, p2.x),  max(p1.y, p2.y))
  56.     var dif = avatar_max_pos - avatar_min_pos
  57.     dif += avatar_padding
  58.     return dif
  59.  
  60. func _get_target_zoom():
  61.     assert(limit_zoom.x >= prefer_zoom.x)
  62.     assert(limit_zoom.y >= prefer_zoom.y)
  63.    
  64.     var viewport_size = get_viewport().size
  65.    
  66.     var p1 = _get_avatar_position(0) as Vector2
  67.     var p2 = _get_avatar_position(1) as Vector2
  68.    
  69.     var avatar_min_pos = Vector2(min(p1.x, p2.x),  min(p1.y, p2.y))
  70.     var avatar_max_pos = Vector2(max(p1.x, p2.x),  max(p1.y, p2.y))
  71.    
  72.    
  73.     var required_size = _get_required_size() #required size to ensure all player avatars are visible
  74.     var current_size  = viewport_size * camera.zoom #current size of viewport in worldspace (after considering zoom)
  75.     var prefer_size = viewport_size * prefer_zoom #prefered size is the most zoomed in allowed
  76.     var limit_size = viewport_size *  limit_zoom #limit size is the most zoomed out allowed
  77.    
  78.     var target_size = Vector2(max(required_size.x, current_size.x), max(required_size.y, current_size.y))
  79.    
  80.     target_size.x = max(target_size.x, prefer_size.x)
  81.     target_size.x = min(target_size.x, limit_size.x)
  82.     target_size.y = max(target_size.y, prefer_size.y)
  83.     target_size.y = min(target_size.y, limit_size.y)
  84.    
  85.     var target_zoom = target_size/viewport_size
  86.     var zoom_max = max(target_zoom.x, target_zoom.y)
  87.     return Vector2(zoom_max, zoom_max)
  88.  
  89. func _calculate_target_position():
  90.     var target_position = Vector2.ZERO #initialize to zero
  91.     var count = 0
  92.    
  93.     for avatar in avatars:
  94.         assert(avatar != null)
  95.         if avatar != null:
  96.             count += 1
  97.             target_position += avatar.global_position
  98.    
  99.     if count == 0:
  100.         camera.position = Vector2.ZERO
  101.         return null
  102.    
  103.     #this is a simple average, can be turned into a weighted average with some modifications
  104.     return Vector2(target_position.x / float(count), target_position.y / float(count))
  105.  
  106. #-----------------------------------------------
  107. #signals
  108.  
  109. func _on_avatar_assigned_to_player(player, avatar):
  110.     assert(player != null)
  111.     assert(avatar != null)
  112.     avatars[player.player_number-1] = avatar
  113.     print("P%d avatar assigned "%player.player_number, avatar)
  114.     assert(avatars[player.player_number-1] != null)
  115.    
  116. func _on_avatar_unassigned_to_player(player, avatar):
  117.     avatars[player.player_number-1] = null
  118.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement