Improve dynamic split screen demo (#815)

This commit is contained in:
Hugo Locurcio
2023-01-17 21:41:02 +01:00
committed by GitHub
parent ee5862a782
commit 682b933dac
8 changed files with 84 additions and 46 deletions

View File

@@ -3,12 +3,9 @@
This sample project showcases an implementation of dynamic This sample project showcases an implementation of dynamic
split screen, also called Voronoi split screen. split screen, also called Voronoi split screen.
Language: [GDSL](https://docs.godotengine.org/en/latest/tutorials/shaders/shader_reference/shading_language.html) and GDScript Language: [Godot shader language](https://docs.godotengine.org/en/latest/tutorials/shaders/shader_reference/shading_language.html) and GDScript
Renderer: GLES 2 Renderer: Forward Mobile
Note: An HTML5 export is testable
[here](https://benjaminnavarro.github.io/godot_dynamic_split_screen/index.html).
Check out this demo on the asset library: https://godotengine.org/asset-library/asset/541 Check out this demo on the asset library: https://godotengine.org/asset-library/asset/541
@@ -38,14 +35,14 @@ distance otherwise.
## How to use it ## How to use it
Open and launch the project inside the Godot engine and then Open and launch the project inside the Godot engine, then
you can use WASD keys to move the first player and IJKL keys use WASD to move the first player (in red) and IJKL (or arrow keys)
to move the second one. to move the second player (in blue).
The `Cameras` node has parameters to tune the distance at The `camera_controller.gd` script sets parameters to tune the distance at
which the screen splits and also the width and color of which the screen splits and also the width and color of
the splitting line. the splitting line.
## Screenshots ## Screenshots
![Screenshots](screenshots/splitscreen.png) ![Screenshots](screenshots/dynamic_split_screen.webp)

View File

@@ -32,6 +32,7 @@ extends Node3D
@onready var camera1 = viewport1.get_node(^"Camera1") @onready var camera1 = viewport1.get_node(^"Camera1")
@onready var camera2 = viewport2.get_node(^"Camera2") @onready var camera2 = viewport2.get_node(^"Camera2")
var viewport_base_height = ProjectSettings.get_setting("display/window/size/viewport_height")
func _ready(): func _ready():
_on_size_changed() _on_size_changed()

View File

@@ -3,7 +3,7 @@ extends CharacterBody3D
# Moves the player # Moves the player
@export_range(1, 2) var player_id: int = 1 @export_range(1, 2) var player_id: int = 1
@export var walk_speed: float = 2.5 @export var walk_speed: float = 2
func _physics_process(_delta): func _physics_process(_delta):

View File

@@ -82,6 +82,7 @@ common/physics_ticks_per_second=120
[rendering] [rendering]
anti_aliasing/quality/msaa_3d=2 renderer/rendering_method="mobile"
environment/defaults/default_clear_color=Color(1, 1, 1, 1) environment/defaults/default_clear_color=Color(1, 1, 1, 1)
anti_aliasing/quality/msaa_3d=2
environment/default_environment="res://default_env.tres" environment/default_environment="res://default_env.tres"

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,18 +1,17 @@
shader_type canvas_item; shader_type canvas_item;
render_mode unshaded; render_mode unshaded;
uniform vec2 viewport_size; // size in pixels of the viewport. Cannot be access from the shader in GLES2 uniform vec2 viewport_size; // size in pixels of the viewport
uniform sampler2D viewport1 : source_color; uniform sampler2D viewport1 : source_color;
uniform sampler2D viewport2 : source_color; uniform sampler2D viewport2 : source_color;
uniform bool split_active; // true: split screen, false: use view1 uniform bool split_active; // true: split screen, false: use view1
uniform vec2 player1_position; // position of player 1 un UV coordinates uniform vec2 player1_position; // position of player 1 un UV coordinates
uniform vec2 player2_position; // position of player 2 un UV coordinates uniform vec2 player2_position; // position of player 2 un UV coordinates
uniform float split_line_thickness; // width of the split boder uniform float split_line_thickness : hint_range(0, 10, 0.1); // width of the split boder
uniform vec4 split_line_color; // color of the split border uniform vec3 split_line_color : source_color; // color of the split border
// from https://stackoverflow.com/questions/15276454/is-it-possible-to-draw-line-thickness-in-a-fragment-shader // from https://stackoverflow.com/questions/15276454/is-it-possible-to-draw-line-thickness-in-a-fragment-shader
float distanceToLine(vec2 p1, vec2 p2, vec2 point) { float distance_to_line(vec2 p1, vec2 p2, vec2 point) {
float a = p1.y - p2.y; float a = p1.y - p2.y;
float b = p2.x - p1.x; float b = p2.x - p1.x;
return abs(a * point.x + b * point.y + p1.x * p2.y - p2.x * p1.y) / sqrt(a * a + b * b); return abs(a * point.x + b * point.y + p1.x * p2.y - p2.x * p1.y) / sqrt(a * a + b * b);
@@ -27,8 +26,8 @@ void fragment() {
if (split_active) { if (split_active) {
vec2 dx = player2_position - player1_position; vec2 dx = player2_position - player1_position;
float split_slope;
float split_slope;
if (dx.y != 0.0) { if (dx.y != 0.0) {
split_slope = dx.x / dx.y; split_slope = dx.x / dx.y;
} else { } else {
@@ -38,29 +37,29 @@ void fragment() {
vec2 split_origin = vec2(0.5, 0.5); vec2 split_origin = vec2(0.5, 0.5);
vec2 split_line_start = vec2(0.0, height * ((split_origin.x - 0.0) * split_slope + split_origin.y)); vec2 split_line_start = vec2(0.0, height * ((split_origin.x - 0.0) * split_slope + split_origin.y));
vec2 split_line_end = vec2(width, height * ((split_origin.x - 1.0) * split_slope + split_origin.y)); vec2 split_line_end = vec2(width, height * ((split_origin.x - 1.0) * split_slope + split_origin.y));
float distance_to_split_line = distanceToLine(split_line_start, split_line_end, vec2(UV.x * width, UV.y * height));
// Draw split border if close enough float split_current_y = (split_origin.x - UV.x) * split_slope + split_origin.y;
if (distance_to_split_line < split_line_thickness) { float split_player1_position_y = (split_origin.x - player1_position.x) * split_slope + split_origin.y;
COLOR = split_line_color;
} else {
float split_current_y = (split_origin.x - UV.x) * split_slope + split_origin.y;
float split_player1_position_y = (split_origin.x - player1_position.x) * split_slope + split_origin.y;
// Check on which side of the split UV is and select the proper view // Check on which side of the split UV is and select the proper view.
if (UV.y > split_current_y) { if (UV.y > split_current_y) {
if (player1_position.y > split_player1_position_y) { if (player1_position.y > split_player1_position_y) {
COLOR = vec4(view1, 1.0); COLOR = vec4(view1, 1.0);
} else {
COLOR = vec4(view2, 1.0);
}
} else { } else {
if (player1_position.y < split_player1_position_y) { COLOR = vec4(view2, 1.0);
COLOR = vec4(view1, 1.0);
} else {
COLOR = vec4(view2, 1.0);
}
} }
} else {
if (player1_position.y < split_player1_position_y) {
COLOR = vec4(view1, 1.0);
} else {
COLOR = vec4(view2, 1.0);
}
}
float distance_to_split_line = distance_to_line(split_line_start, split_line_end, vec2(UV.x * width, UV.y * height));
if (distance_to_split_line < split_line_thickness) {
// Draw antialiased split line.
COLOR.rgb = mix(split_line_color, COLOR.rgb, distance_to_split_line / split_line_thickness);
} }
} else { } else {
COLOR = vec4(view1, 1.0); COLOR = vec4(view1, 1.0);

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=60 format=3 uid="uid://dksa68cph6y4b"] [gd_scene load_steps=63 format=3 uid="uid://dksa68cph6y4b"]
[ext_resource type="Script" path="res://camera_controller.gd" id="2"] [ext_resource type="Script" path="res://camera_controller.gd" id="2"]
[ext_resource type="Shader" path="res://split_screen.gdshader" id="3"] [ext_resource type="Shader" path="res://split_screen.gdshader" id="3"]
@@ -15,21 +15,26 @@ sky_material = SubResource("ProceduralSkyMaterial_16la2")
[sub_resource type="Environment" id="Environment_vdrvu"] [sub_resource type="Environment" id="Environment_vdrvu"]
background_mode = 2 background_mode = 2
sky = SubResource("Sky_i64ko") sky = SubResource("Sky_i64ko")
ambient_light_source = 2
ambient_light_color = Color(0.79, 0.8775, 1, 1)
ambient_light_sky_contribution = 0.0
ambient_light_energy = 0.33
tonemap_mode = 2 tonemap_mode = 2
glow_enabled = true glow_enabled = true
[sub_resource type="ShaderMaterial" id="1"] [sub_resource type="ShaderMaterial" id="1"]
shader = ExtResource("3") shader = ExtResource("3")
shader_parameter/viewport_size = null
shader_parameter/split_active = false
shader_parameter/player1_position = null shader_parameter/player1_position = null
shader_parameter/player2_position = null shader_parameter/player2_position = null
shader_parameter/split_active = null shader_parameter/split_line_thickness = 10.0
shader_parameter/split_line_color = null shader_parameter/split_line_color = Vector3(0, 1, 0)
shader_parameter/split_line_thickness = null
shader_parameter/viewport_size = null
[sub_resource type="CapsuleMesh" id="2"] [sub_resource type="CapsuleMesh" id="2"]
radius = 0.375 radius = 0.375
height = 1.75 height = 1.75
rings = 4
[sub_resource type="StandardMaterial3D" id="3"] [sub_resource type="StandardMaterial3D" id="3"]
albedo_color = Color(0.933333, 0.0784314, 0.0784314, 1) albedo_color = Color(0.933333, 0.0784314, 0.0784314, 1)
@@ -38,9 +43,24 @@ albedo_color = Color(0.933333, 0.0784314, 0.0784314, 1)
radius = 0.375 radius = 0.375
height = 1.75 height = 1.75
[sub_resource type="TorusMesh" id="TorusMesh_abtrc"]
inner_radius = 0.4
outer_radius = 0.6
ring_segments = 6
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_63nwq"]
albedo_color = Color(0.5, 0.5, 0.5, 1)
emission_enabled = true
emission = Color(1, 0, 0, 1)
[sub_resource type="StandardMaterial3D" id="5"] [sub_resource type="StandardMaterial3D" id="5"]
albedo_color = Color(0.0784314, 0.411765, 0.933333, 1) albedo_color = Color(0.0784314, 0.411765, 0.933333, 1)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_wi7e2"]
albedo_color = Color(0.5, 0.5, 0.5, 1)
emission_enabled = true
emission = Color(0.12549, 0.501961, 1, 1)
[sub_resource type="StandardMaterial3D" id="6"] [sub_resource type="StandardMaterial3D" id="6"]
[sub_resource type="PlaneMesh" id="7"] [sub_resource type="PlaneMesh" id="7"]
@@ -183,12 +203,15 @@ albedo_color = Color(0.791675, 0.946163, 0.317723, 1)
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(-0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, 0.75, -0.433013, 0, 0, 0) transform = Transform3D(-0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, 0.75, -0.433013, 0, 0, 0)
light_energy = 0.75
shadow_enabled = true shadow_enabled = true
shadow_bias = 0.03
shadow_blur = 2.0
directional_shadow_mode = 0 directional_shadow_mode = 0
directional_shadow_split_3 = 0.25 directional_shadow_split_3 = 0.25
directional_shadow_blend_splits = true directional_shadow_blend_splits = true
directional_shadow_fade_start = 1.0 directional_shadow_fade_start = 1.0
directional_shadow_max_distance = 25.0 directional_shadow_max_distance = 12.0
[node name="WorldEnvironment" type="WorldEnvironment" parent="."] [node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_vdrvu") environment = SubResource("Environment_vdrvu")
@@ -235,6 +258,14 @@ surface_material_override/0 = SubResource("3")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Player1"] [node name="CollisionShape3D" type="CollisionShape3D" parent="Player1"]
shape = SubResource("4") shape = SubResource("4")
[node name="MeshInstance3D" type="MeshInstance3D" parent="Player1"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.6, 0)
mesh = SubResource("TorusMesh_abtrc")
surface_material_override/0 = SubResource("StandardMaterial3D_63nwq")
[node name="OmniLight3D" type="OmniLight3D" parent="Player1"]
light_color = Color(1, 0, 0, 1)
[node name="Player2" type="CharacterBody3D" parent="."] [node name="Player2" type="CharacterBody3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4.184, 0.875, 3.019) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4.184, 0.875, 3.019)
script = ExtResource("5") script = ExtResource("5")
@@ -247,6 +278,15 @@ surface_material_override/0 = SubResource("5")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Player2"] [node name="CollisionShape3D" type="CollisionShape3D" parent="Player2"]
shape = SubResource("4") shape = SubResource("4")
[node name="MeshInstance3D2" type="MeshInstance3D" parent="Player2"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.7, 0)
mesh = SubResource("TorusMesh_abtrc")
skeleton = NodePath("../../Player1")
surface_material_override/0 = SubResource("StandardMaterial3D_wi7e2")
[node name="OmniLight3D" type="OmniLight3D" parent="Player2"]
light_color = Color(0.12549, 0.501961, 1, 1)
[node name="Ground" type="StaticBody3D" parent="."] [node name="Ground" type="StaticBody3D" parent="."]
[node name="Mesh" type="MeshInstance3D" parent="Ground"] [node name="Mesh" type="MeshInstance3D" parent="Ground"]