From ebc435074b8cae46fe9cf8d5b6582da1f142f2a8 Mon Sep 17 00:00:00 2001 From: Aaron Franke Date: Fri, 6 Mar 2020 01:27:29 -0500 Subject: [PATCH] [2D Platformer] Add a splitscreen mode --- 2d/platformer/project.godot | 60 +++++++++++++++ 2d/platformer/src/Actors/Player.gd | 23 ++++-- 2d/platformer/src/Level/Level.tscn | 4 +- 2d/platformer/src/Level/Music.gd | 16 ++++ 2d/platformer/src/Main/Game.gd | 19 +++++ 2d/platformer/src/Main/Splitscreen.tscn | 80 ++++++++++++++++++++ 2d/platformer/src/UserInterface/PauseMenu.gd | 3 + 7 files changed, 199 insertions(+), 6 deletions(-) create mode 100644 2d/platformer/src/Level/Music.gd create mode 100644 2d/platformer/src/Main/Splitscreen.tscn diff --git a/2d/platformer/project.godot b/2d/platformer/project.godot index f1ba6af5..f17170c5 100644 --- a/2d/platformer/project.godot +++ b/2d/platformer/project.godot @@ -148,6 +148,66 @@ toggle_pause={ , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":11,"pressure":0.0,"pressed":false,"script":null) ] } +jump_p1={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":87,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null) + ] +} +move_left_p1={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":65,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":-1.0,"script":null) + ] +} +move_right_p1={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":68,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":15,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":1.0,"script":null) + ] +} +shoot_p1={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":90,"unicode":0,"echo":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":32,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":2,"pressure":0.0,"pressed":false,"script":null) + ] +} +jump_p2={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777232,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":0,"pressure":0.0,"pressed":false,"script":null) + ] +} +move_left_p2={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777231,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":14,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":1,"axis":0,"axis_value":-1.0,"script":null) + ] +} +move_right_p2={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777233,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":15,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":1,"axis":0,"axis_value":1.0,"script":null) + ] +} +shoot_p2={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777350,"unicode":0,"echo":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":16777238,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":2,"pressure":0.0,"pressed":false,"script":null) + ] +} +splitscreen={ +"deadzone": 0.5, +"events": [ Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":10,"pressure":0.0,"pressed":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":16777218,"unicode":0,"echo":false,"script":null) + ] +} [layer_names] diff --git a/2d/platformer/src/Actors/Player.gd b/2d/platformer/src/Actors/Player.gd index 1cfac327..3b978f22 100644 --- a/2d/platformer/src/Actors/Player.gd +++ b/2d/platformer/src/Actors/Player.gd @@ -4,6 +4,8 @@ extends Actor const FLOOR_DETECT_DISTANCE = 20.0 +export(String) var action_suffix = "" + onready var platform_detector = $PlatformDetector onready var sprite = $Sprite onready var animation_player = $AnimationPlayer @@ -11,6 +13,17 @@ onready var shoot_timer = $ShootAnimation onready var gun = $Sprite/Gun +func _ready(): + # Static types are necessary to avoid + var camera: Camera2D = $Camera + if action_suffix == "_p1": + camera.custom_viewport = $"../.." + elif action_suffix == "_p2": + var viewport: Viewport = $"../../../../ViewportContainer2/Viewport" + viewport.world_2d = ($"../.." as Viewport).world_2d + camera.custom_viewport = viewport + + # Physics process is a built-in loop in Godot. # If you define _physics_process on a node, Godot will call it every frame. @@ -32,7 +45,7 @@ onready var gun = $Sprite/Gun func _physics_process(_delta): var direction = get_direction() - var is_jump_interrupted = Input.is_action_just_released("jump") and _velocity.y < 0.0 + var is_jump_interrupted = Input.is_action_just_released("jump" + action_suffix) and _velocity.y < 0.0 _velocity = calculate_move_velocity(_velocity, direction, speed, is_jump_interrupted) var snap_vector = Vector2.DOWN * FLOOR_DETECT_DISTANCE if direction.y == 0.0 else Vector2.ZERO @@ -44,14 +57,14 @@ func _physics_process(_delta): # When the character’s direction changes, we want to to scale the Sprite accordingly to flip it. # This will make Robi face left or right depending on the direction you move. if direction.x != 0: - sprite.scale.x = direction.x + sprite.scale.x = 1 if direction.x > 0 else -1 # We use the sprite's scale to store Robi’s look direction which allows us to shoot # bullets forward. # There are many situations like these where you can reuse existing properties instead of # creating new variables. var is_shooting = false - if Input.is_action_just_pressed("shoot"): + if Input.is_action_just_pressed("shoot" + action_suffix): is_shooting = gun.shoot(sprite.scale.x) var animation = get_new_animation(is_shooting) @@ -63,8 +76,8 @@ func _physics_process(_delta): func get_direction(): return Vector2( - Input.get_action_strength("move_right") - Input.get_action_strength("move_left"), - -Input.get_action_strength("jump") if is_on_floor() and Input.is_action_just_pressed("jump") else 0.0 + Input.get_action_strength("move_right" + action_suffix) - Input.get_action_strength("move_left" + action_suffix), + -1 if is_on_floor() and Input.is_action_just_pressed("jump" + action_suffix) else 0 ) diff --git a/2d/platformer/src/Level/Level.tscn b/2d/platformer/src/Level/Level.tscn index bf56438f..c1757f42 100644 --- a/2d/platformer/src/Level/Level.tscn +++ b/2d/platformer/src/Level/Level.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=11 format=2] +[gd_scene load_steps=12 format=2] [ext_resource path="res://assets/art/tileset/tileset.tres" type="TileSet" id=1] [ext_resource path="res://src/Actors/Enemy.tscn" type="PackedScene" id=2] @@ -8,6 +8,7 @@ [ext_resource path="res://assets/art/platforms/moving_platform.png" type="Texture" id=6] [ext_resource path="res://src/Level/ParallaxBackground.tscn" type="PackedScene" id=7] [ext_resource path="res://assets/audio/music/music.ogg" type="AudioStream" id=8] +[ext_resource path="res://src/Level/Music.gd" type="Script" id=9] [sub_resource type="Animation" id=1] resource_name = "move" @@ -263,3 +264,4 @@ position = Vector2( 828.515, 77.262 ) [node name="Music" type="AudioStreamPlayer" parent="."] stream = ExtResource( 8 ) autoplay = true +script = ExtResource( 9 ) diff --git a/2d/platformer/src/Level/Music.gd b/2d/platformer/src/Level/Music.gd new file mode 100644 index 00000000..9e3c7e52 --- /dev/null +++ b/2d/platformer/src/Level/Music.gd @@ -0,0 +1,16 @@ +extends AudioStreamPlayer + +const DOUBLE_VOLUME_DB = 6 # Do not change. Represents doubling of sound pressure. + +export(int) var base_volume_db = -4 + +func _ready(): + # To avoid AudioStreamPlayer2D sounds playing on top of each other and + # being very loud, let's decrease the volume for splitscreen mode, but + # increase the music volume to keep the music at the same volume. + if get_parent().get_owner().name == "Splitscreen": + AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Master"), base_volume_db - DOUBLE_VOLUME_DB) + volume_db = DOUBLE_VOLUME_DB + else: + AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Master"), base_volume_db) + volume_db = 0 diff --git a/2d/platformer/src/Main/Game.gd b/2d/platformer/src/Main/Game.gd index 568fe27d..7d09f0d8 100644 --- a/2d/platformer/src/Main/Game.gd +++ b/2d/platformer/src/Main/Game.gd @@ -13,6 +13,14 @@ func _init(): OS.max_window_size = OS.get_screen_size() +func _notification(what): + if what == NOTIFICATION_WM_QUIT_REQUEST: + # We need to clean up a little bit first to avoid Viewport errors. + if name == "Splitscreen": + $Black/SplitContainer/ViewportContainer1.free() + $Black.queue_free() + + func _input(event): if event.is_action_pressed("toggle_fullscreen"): OS.window_fullscreen = not OS.window_fullscreen @@ -29,3 +37,14 @@ func _input(event): else: _pause_menu.close() get_tree().set_input_as_handled() + + elif event.is_action_pressed("splitscreen"): + if name == "Splitscreen": + # We need to clean up a little bit first to avoid Viewport errors. + $Black/SplitContainer/ViewportContainer1.free() + $Black.queue_free() + # warning-ignore:return_value_discarded + get_tree().change_scene("res://src/Main/Game.tscn") + else: + # warning-ignore:return_value_discarded + get_tree().change_scene("res://src/Main/Splitscreen.tscn") diff --git a/2d/platformer/src/Main/Splitscreen.tscn b/2d/platformer/src/Main/Splitscreen.tscn new file mode 100644 index 00000000..29009127 --- /dev/null +++ b/2d/platformer/src/Main/Splitscreen.tscn @@ -0,0 +1,80 @@ +[gd_scene load_steps=6 format=2] + +[ext_resource path="res://src/UserInterface/PauseMenu.tscn" type="PackedScene" id=1] +[ext_resource path="res://src/Main/Game.gd" type="Script" id=2] +[ext_resource path="res://src/Level/Level.tscn" type="PackedScene" id=3] +[ext_resource path="res://src/Actors/Player.tscn" type="PackedScene" id=4] +[ext_resource path="res://src/Level/ParallaxBackground.tscn" type="PackedScene" id=5] + +[node name="Splitscreen" type="Node"] +pause_mode = 2 +script = ExtResource( 2 ) + +[node name="InterfaceLayer" type="CanvasLayer" parent="."] +layer = 100 + +[node name="PauseMenu" parent="InterfaceLayer" instance=ExtResource( 1 )] + +[node name="Black" type="ColorRect" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_right = 6.10352e-05 +margin_bottom = 3.05176e-05 +color = Color( 0, 0, 0, 1 ) + +[node name="SplitContainer" type="HSplitContainer" parent="Black"] +anchor_right = 1.0 +anchor_bottom = 1.0 +dragger_visibility = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ViewportContainer1" type="ViewportContainer" parent="Black/SplitContainer"] +margin_right = 394.0 +margin_bottom = 480.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +stretch = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Viewport" type="Viewport" parent="Black/SplitContainer/ViewportContainer1"] +size = Vector2( 394, 480 ) +size_override_stretch = true +handle_input_locally = false +usage = 0 +render_target_update_mode = 3 +audio_listener_enable_2d = true + +[node name="Level" parent="Black/SplitContainer/ViewportContainer1/Viewport" instance=ExtResource( 3 )] + +[node name="Player1" parent="Black/SplitContainer/ViewportContainer1/Viewport/Level" instance=ExtResource( 4 )] +position = Vector2( 90, 546 ) +action_suffix = "_p1" + +[node name="Player2" parent="Black/SplitContainer/ViewportContainer1/Viewport/Level" instance=ExtResource( 4 )] +position = Vector2( 120, 546 ) +action_suffix = "_p2" + +[node name="ViewportContainer2" type="ViewportContainer" parent="Black/SplitContainer"] +margin_left = 406.0 +margin_right = 800.0 +margin_bottom = 480.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +stretch = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Viewport" type="Viewport" parent="Black/SplitContainer/ViewportContainer2"] +size = Vector2( 394, 480 ) +size_override_stretch = true +handle_input_locally = false +usage = 0 +render_target_update_mode = 3 +audio_listener_enable_2d = true + +[node name="ParallaxBackground" parent="Black/SplitContainer/ViewportContainer2/Viewport" instance=ExtResource( 5 )] diff --git a/2d/platformer/src/UserInterface/PauseMenu.gd b/2d/platformer/src/UserInterface/PauseMenu.gd index f8e98dad..7b2f4c2d 100644 --- a/2d/platformer/src/UserInterface/PauseMenu.gd +++ b/2d/platformer/src/UserInterface/PauseMenu.gd @@ -23,4 +23,7 @@ func _on_ResumeButton_pressed(): func _on_QuitButton_pressed(): + if get_parent().get_parent().name == "Splitscreen": + # We need to clean up a little bit first to avoid Viewport errors. + $"../../Black/SplitContainer/ViewportContainer1".free() get_tree().quit()