mirror of
https://github.com/godotengine/godot-demo-projects.git
synced 2025-12-16 05:20:06 +01:00
Improve dynamic split screen demo (#815)
This commit is contained in:
@@ -3,12 +3,9 @@
|
||||
This sample project showcases an implementation of dynamic
|
||||
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
|
||||
|
||||
Note: An HTML5 export is testable
|
||||
[here](https://benjaminnavarro.github.io/godot_dynamic_split_screen/index.html).
|
||||
Renderer: Forward Mobile
|
||||
|
||||
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
|
||||
|
||||
Open and launch the project inside the Godot engine and then
|
||||
you can use WASD keys to move the first player and IJKL keys
|
||||
to move the second one.
|
||||
Open and launch the project inside the Godot engine, then
|
||||
use WASD to move the first player (in red) and IJKL (or arrow keys)
|
||||
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
|
||||
the splitting line.
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -32,6 +32,7 @@ extends Node3D
|
||||
@onready var camera1 = viewport1.get_node(^"Camera1")
|
||||
@onready var camera2 = viewport2.get_node(^"Camera2")
|
||||
|
||||
var viewport_base_height = ProjectSettings.get_setting("display/window/size/viewport_height")
|
||||
|
||||
func _ready():
|
||||
_on_size_changed()
|
||||
|
||||
@@ -3,7 +3,7 @@ extends CharacterBody3D
|
||||
# Moves the player
|
||||
|
||||
@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):
|
||||
|
||||
@@ -82,6 +82,7 @@ common/physics_ticks_per_second=120
|
||||
|
||||
[rendering]
|
||||
|
||||
anti_aliasing/quality/msaa_3d=2
|
||||
renderer/rendering_method="mobile"
|
||||
environment/defaults/default_clear_color=Color(1, 1, 1, 1)
|
||||
anti_aliasing/quality/msaa_3d=2
|
||||
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 |
@@ -1,18 +1,17 @@
|
||||
shader_type canvas_item;
|
||||
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 viewport2 : source_color;
|
||||
uniform bool split_active; // true: split screen, false: use view1
|
||||
uniform vec2 player1_position; // position of player 1 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 vec4 split_line_color; // color of the split border
|
||||
|
||||
uniform bool split_active; // true: split screen, false: use view1
|
||||
uniform vec2 player1_position; // position of player 1 un UV coordinates
|
||||
uniform vec2 player2_position; // position of player 2 un UV coordinates
|
||||
uniform float split_line_thickness : hint_range(0, 10, 0.1); // width of the split boder
|
||||
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
|
||||
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 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);
|
||||
@@ -27,8 +26,8 @@ void fragment() {
|
||||
|
||||
if (split_active) {
|
||||
vec2 dx = player2_position - player1_position;
|
||||
float split_slope;
|
||||
|
||||
float split_slope;
|
||||
if (dx.y != 0.0) {
|
||||
split_slope = dx.x / dx.y;
|
||||
} else {
|
||||
@@ -38,29 +37,29 @@ void fragment() {
|
||||
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_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
|
||||
if (distance_to_split_line < split_line_thickness) {
|
||||
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;
|
||||
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
|
||||
if (UV.y > split_current_y) {
|
||||
if (player1_position.y > split_player1_position_y) {
|
||||
COLOR = vec4(view1, 1.0);
|
||||
} else {
|
||||
COLOR = vec4(view2, 1.0);
|
||||
}
|
||||
// Check on which side of the split UV is and select the proper view.
|
||||
if (UV.y > split_current_y) {
|
||||
if (player1_position.y > split_player1_position_y) {
|
||||
COLOR = vec4(view1, 1.0);
|
||||
} else {
|
||||
if (player1_position.y < split_player1_position_y) {
|
||||
COLOR = vec4(view1, 1.0);
|
||||
} else {
|
||||
COLOR = vec4(view2, 1.0);
|
||||
}
|
||||
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 {
|
||||
COLOR = vec4(view1, 1.0);
|
||||
|
||||
@@ -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="Shader" path="res://split_screen.gdshader" id="3"]
|
||||
@@ -15,21 +15,26 @@ sky_material = SubResource("ProceduralSkyMaterial_16la2")
|
||||
[sub_resource type="Environment" id="Environment_vdrvu"]
|
||||
background_mode = 2
|
||||
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
|
||||
glow_enabled = true
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="1"]
|
||||
shader = ExtResource("3")
|
||||
shader_parameter/viewport_size = null
|
||||
shader_parameter/split_active = false
|
||||
shader_parameter/player1_position = null
|
||||
shader_parameter/player2_position = null
|
||||
shader_parameter/split_active = null
|
||||
shader_parameter/split_line_color = null
|
||||
shader_parameter/split_line_thickness = null
|
||||
shader_parameter/viewport_size = null
|
||||
shader_parameter/split_line_thickness = 10.0
|
||||
shader_parameter/split_line_color = Vector3(0, 1, 0)
|
||||
|
||||
[sub_resource type="CapsuleMesh" id="2"]
|
||||
radius = 0.375
|
||||
height = 1.75
|
||||
rings = 4
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="3"]
|
||||
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
|
||||
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"]
|
||||
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="PlaneMesh" id="7"]
|
||||
@@ -183,12 +203,15 @@ albedo_color = Color(0.791675, 0.946163, 0.317723, 1)
|
||||
|
||||
[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)
|
||||
light_energy = 0.75
|
||||
shadow_enabled = true
|
||||
shadow_bias = 0.03
|
||||
shadow_blur = 2.0
|
||||
directional_shadow_mode = 0
|
||||
directional_shadow_split_3 = 0.25
|
||||
directional_shadow_blend_splits = true
|
||||
directional_shadow_fade_start = 1.0
|
||||
directional_shadow_max_distance = 25.0
|
||||
directional_shadow_max_distance = 12.0
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||
environment = SubResource("Environment_vdrvu")
|
||||
@@ -235,6 +258,14 @@ surface_material_override/0 = SubResource("3")
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Player1"]
|
||||
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="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4.184, 0.875, 3.019)
|
||||
script = ExtResource("5")
|
||||
@@ -247,6 +278,15 @@ surface_material_override/0 = SubResource("5")
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Player2"]
|
||||
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="Mesh" type="MeshInstance3D" parent="Ground"]
|
||||
|
||||
Reference in New Issue
Block a user