├── .gitignore ├── DemoScene.tscn ├── LICENSE ├── README.md ├── icon.png ├── icon.png.import ├── platformer_controller └── platformer_controller.gd └── project.godot /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Godot-specific ignores 3 | .godot/ 4 | .import/ 5 | export.cfg 6 | export_presets.cfg 7 | 8 | # Mono-specific ignores 9 | .mono/ 10 | data_*/ 11 | -------------------------------------------------------------------------------- /DemoScene.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=3 uid="uid://cqfhnpb7xftpo"] 2 | 3 | [ext_resource type="Script" path="res://platformer_controller/platformer_controller.gd" id="1"] 4 | [ext_resource type="Texture2D" uid="uid://di5a1pv5iuup3" path="res://icon.png" id="2"] 5 | 6 | [sub_resource type="RectangleShape2D" id="1"] 7 | size = Vector2(1024, 112) 8 | 9 | [sub_resource type="RectangleShape2D" id="2"] 10 | size = Vector2(416, 112) 11 | 12 | [sub_resource type="RectangleShape2D" id="3"] 13 | size = Vector2(64, 608) 14 | 15 | [sub_resource type="RectangleShape2D" id="4"] 16 | size = Vector2(64, 64) 17 | 18 | [node name="Main" type="Node2D"] 19 | 20 | [node name="StaticBody2D" type="StaticBody2D" parent="."] 21 | position = Vector2(520, 544) 22 | 23 | [node name="ColorRect" type="ColorRect" parent="StaticBody2D"] 24 | offset_left = -520.0 25 | offset_top = -56.0 26 | offset_right = 504.0 27 | offset_bottom = 56.0 28 | color = Color(0, 0.443137, 0.027451, 1) 29 | 30 | [node name="CollisionShape2D" type="CollisionShape2D" parent="StaticBody2D"] 31 | position = Vector2(-8, 0) 32 | shape = SubResource("1") 33 | 34 | [node name="StaticBody2D2" type="StaticBody2D" parent="."] 35 | position = Vector2(520, 360) 36 | 37 | [node name="ColorRect" type="ColorRect" parent="StaticBody2D2"] 38 | offset_left = -520.0 39 | offset_top = 16.0 40 | offset_right = -104.0 41 | offset_bottom = 128.0 42 | color = Color(0, 0.443137, 0.027451, 1) 43 | 44 | [node name="CollisionShape2D" type="CollisionShape2D" parent="StaticBody2D2"] 45 | position = Vector2(-312, 72) 46 | shape = SubResource("2") 47 | 48 | [node name="StaticBody2D3" type="StaticBody2D" parent="."] 49 | position = Vector2(520, 360) 50 | 51 | [node name="CollisionShape2D" type="CollisionShape2D" parent="StaticBody2D3"] 52 | position = Vector2(-552, -64) 53 | shape = SubResource("3") 54 | 55 | [node name="CollisionShape2D2" type="CollisionShape2D" parent="StaticBody2D3"] 56 | position = Vector2(536, -64) 57 | shape = SubResource("3") 58 | 59 | [node name="PlatformerController2D" type="CharacterBody2D" parent="."] 60 | position = Vector2(288, 320) 61 | script = ExtResource("1") 62 | 63 | [node name="icon" type="Sprite2D" parent="PlatformerController2D"] 64 | texture = ExtResource("2") 65 | 66 | [node name="CollisionShape2D" type="CollisionShape2D" parent="PlatformerController2D"] 67 | shape = SubResource("4") 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ev01 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PlatformerController2D 2 | 3 | A 2D platformer class for godot. 4 | 5 | ## Changelog 6 | 7 | ### Version 2.0.2 8 | 9 | - Fixed bug where falling off a cliff would give an extra jump 10 | 11 | - Fixed bug where setting coyote_time or jump_buffer to 0 would raise an error 12 | 13 | - Added ground_jump and double_jump functions 14 | 15 | - Added separate functions for starting the jump_buffer or coyote timers and checking if they are running 16 | 17 | ### Version 2.0.1 18 | 19 | - Removed the .godot and .import folders from the repository 20 | 21 | ### Version 2.0 22 | 23 | - Updated to Godot 4.0 24 | 25 | - Removed set and get functions in favour of properties 26 | 27 | - Can now have negative jump height, which will reverse gravity 28 | 29 | - Gravity is now positive when pointing down, and negative when pointing up 30 | 31 | - Added 'jumped' and 'hit_ground' signals 32 | 33 | - Split the large `_physics_process` function into multiple functions 34 | 35 | ### Version 1.0.1 36 | 37 | - Updated to Godot 3.4 38 | - Fixed division by zero error when changing min jump height 39 | - Other minor fixes 40 | 41 | ## Installation 42 | 43 | 1. Add platformer_controller.gd to your project 44 | 2. Type `extends PlatformerController2D` to the top of your script 45 | 3. Add these input mappings in your project settings (or you can change the input variables in the inspector) 46 | - "move_left" 47 | - "move_right" 48 | - "jump" 49 | 50 | ## Features 51 | 52 | - Double jump 53 | - Coyote time 54 | - Jump buffer 55 | - Hold jump to go higher 56 | - Defining jump height and duration (as opposed to setting gravity and jump velocity) 57 | - Assymetrical jumps (falling faster than rising) 58 | 59 | | | | | 60 | | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | 61 | 62 | ## Customization / Export variables 63 | 64 | Here are the values that you can change in the inspector: 65 | 66 | ### max_jump_height 67 | 68 | The max jump height in pixels. You reach this when you hold down jump. 69 | 70 | ### Min Jump Height 71 | 72 | The minimum jump height (tapping jump). 73 | 74 | ### Double Jump Height 75 | 76 | The height of your jump in the air (i.e. double jump, triple jump etc.). 77 | 78 | ### Jump Duration 79 | 80 | How long it takes to get to the peak of the jump (in seconds). 81 | 82 | ### Falling Gravity Multiplier 83 | 84 | Multiplies the gravity by this while falling. 85 | 86 | ### Max Jump Amount 87 | 88 | How many times you can jump before hitting the ground. Set this to 2 for a double jump. 89 | 90 | ### Max Acceleration 91 | 92 | How much you accelerate when you hold left or right (in pixels/sec^2). 93 | 94 | ### Friction 95 | 96 | The higher this number, the more friction is on your character. 97 | 98 | ### Can Hold Jump 99 | 100 | If this is off, you have to press jump down every time you land. If its on you can keep it held. 101 | 102 | ### Coyote Time 103 | 104 | You can still jump this many seconds after falling off a ledge. 105 | 106 | ### Jump Buffer 107 | 108 | Pressing jump this many seconds before hitting the ground will still make you jump.\ 109 | Note: This is only needed when can_hold_jump is off. 110 | 111 | ### Input Variables 112 | 113 | `input_left`\ 114 | `input_right`\ 115 | `input_jump`\ 116 | Set these to the names of your actions in the Input Map 117 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ev01/PlatformerController2D/178b1df40dd9ec40841893807d5ea3da819931d0/icon.png -------------------------------------------------------------------------------- /icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://di5a1pv5iuup3" 6 | path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.png" 14 | dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /platformer_controller/platformer_controller.gd: -------------------------------------------------------------------------------- 1 | extends CharacterBody2D 2 | 3 | class_name PlatformerController2D 4 | 5 | signal jumped(is_ground_jump: bool) 6 | signal hit_ground() 7 | 8 | 9 | # Set these to the name of your action (in the Input Map) 10 | ## Name of input action to move left. 11 | @export var input_left : String = "move_left" 12 | ## Name of input action to move right. 13 | @export var input_right : String = "move_right" 14 | ## Name of input action to jump. 15 | @export var input_jump : String = "jump" 16 | 17 | 18 | const DEFAULT_MAX_JUMP_HEIGHT = 150 19 | const DEFAULT_MIN_JUMP_HEIGHT = 60 20 | const DEFAULT_DOUBLE_JUMP_HEIGHT = 100 21 | const DEFAULT_JUMP_DURATION = 0.3 22 | 23 | var _max_jump_height: float = DEFAULT_MAX_JUMP_HEIGHT 24 | ## The max jump height in pixels (holding jump). 25 | @export var max_jump_height: float = DEFAULT_MAX_JUMP_HEIGHT: 26 | get: 27 | return _max_jump_height 28 | set(value): 29 | _max_jump_height = value 30 | 31 | default_gravity = calculate_gravity(_max_jump_height, jump_duration) 32 | jump_velocity = calculate_jump_velocity(_max_jump_height, jump_duration) 33 | double_jump_velocity = calculate_jump_velocity2(double_jump_height, default_gravity) 34 | release_gravity_multiplier = calculate_release_gravity_multiplier( 35 | jump_velocity, min_jump_height, default_gravity) 36 | 37 | 38 | var _min_jump_height: float = DEFAULT_MIN_JUMP_HEIGHT 39 | ## The minimum jump height (tapping jump). 40 | @export var min_jump_height: float = DEFAULT_MIN_JUMP_HEIGHT: 41 | get: 42 | return _min_jump_height 43 | set(value): 44 | _min_jump_height = value 45 | release_gravity_multiplier = calculate_release_gravity_multiplier( 46 | jump_velocity, min_jump_height, default_gravity) 47 | 48 | 49 | 50 | var _double_jump_height: float = DEFAULT_DOUBLE_JUMP_HEIGHT 51 | ## The height of your jump in the air. 52 | @export var double_jump_height: float = DEFAULT_DOUBLE_JUMP_HEIGHT: 53 | get: 54 | return _double_jump_height 55 | set(value): 56 | _double_jump_height = value 57 | double_jump_velocity = calculate_jump_velocity2(double_jump_height, default_gravity) 58 | 59 | 60 | var _jump_duration: float = DEFAULT_JUMP_DURATION 61 | ## How long it takes to get to the peak of the jump in seconds. 62 | @export var jump_duration: float = DEFAULT_JUMP_DURATION: 63 | get: 64 | return _jump_duration 65 | set(value): 66 | _jump_duration = value 67 | 68 | default_gravity = calculate_gravity(max_jump_height, jump_duration) 69 | jump_velocity = calculate_jump_velocity(max_jump_height, jump_duration) 70 | double_jump_velocity = calculate_jump_velocity2(double_jump_height, default_gravity) 71 | release_gravity_multiplier = calculate_release_gravity_multiplier( 72 | jump_velocity, min_jump_height, default_gravity) 73 | 74 | ## Multiplies the gravity by this while falling. 75 | @export var falling_gravity_multiplier = 1.5 76 | ## Amount of jumps allowed before needing to touch the ground again. Set to 2 for double jump. 77 | @export var max_jump_amount = 1 78 | @export var max_acceleration = 10000 79 | @export var friction = 20 80 | @export var can_hold_jump : bool = false 81 | ## You can still jump this many seconds after falling off a ledge. 82 | @export var coyote_time : float = 0.1 83 | ## Pressing jump this many seconds before hitting the ground will still make you jump. 84 | ## Only neccessary when can_hold_jump is unchecked. 85 | @export var jump_buffer : float = 0.1 86 | 87 | 88 | # These will be calcualted automatically 89 | # Gravity will be positive if it's going down, and negative if it's going up 90 | var default_gravity : float 91 | var jump_velocity : float 92 | var double_jump_velocity : float 93 | # Multiplies the gravity by this when we release jump 94 | var release_gravity_multiplier : float 95 | 96 | 97 | var jumps_left : int 98 | var holding_jump := false 99 | 100 | enum JumpType {NONE, GROUND, AIR} 101 | ## The type of jump the player is performing. Is JumpType.NONE if they player is on the ground. 102 | var current_jump_type: JumpType = JumpType.NONE 103 | 104 | # Used to detect if player just hit the ground 105 | var _was_on_ground: bool 106 | 107 | var acc = Vector2() 108 | 109 | # coyote_time and jump_buffer must be above zero to work. Otherwise, godot will throw an error. 110 | @onready var is_coyote_time_enabled = coyote_time > 0 111 | @onready var is_jump_buffer_enabled = jump_buffer > 0 112 | @onready var coyote_timer = Timer.new() 113 | @onready var jump_buffer_timer = Timer.new() 114 | 115 | 116 | func _init(): 117 | default_gravity = calculate_gravity(max_jump_height, jump_duration) 118 | jump_velocity = calculate_jump_velocity(max_jump_height, jump_duration) 119 | double_jump_velocity = calculate_jump_velocity2(double_jump_height, default_gravity) 120 | release_gravity_multiplier = calculate_release_gravity_multiplier( 121 | jump_velocity, min_jump_height, default_gravity) 122 | 123 | 124 | func _ready(): 125 | if is_coyote_time_enabled: 126 | add_child(coyote_timer) 127 | coyote_timer.wait_time = coyote_time 128 | coyote_timer.one_shot = true 129 | 130 | if is_jump_buffer_enabled: 131 | add_child(jump_buffer_timer) 132 | jump_buffer_timer.wait_time = jump_buffer 133 | jump_buffer_timer.one_shot = true 134 | 135 | 136 | func _input(_event): 137 | acc.x = 0 138 | if Input.is_action_pressed(input_left): 139 | acc.x = -max_acceleration 140 | 141 | if Input.is_action_pressed(input_right): 142 | acc.x = max_acceleration 143 | 144 | if Input.is_action_just_pressed(input_jump): 145 | holding_jump = true 146 | start_jump_buffer_timer() 147 | if (not can_hold_jump and can_ground_jump()) or can_double_jump(): 148 | jump() 149 | 150 | if Input.is_action_just_released(input_jump): 151 | holding_jump = false 152 | 153 | 154 | func _physics_process(delta): 155 | if is_coyote_timer_running() or (is_feet_on_ground() and current_jump_type == JumpType.NONE): 156 | jumps_left = max_jump_amount 157 | if is_feet_on_ground() and current_jump_type == JumpType.NONE: 158 | start_coyote_timer() 159 | 160 | # Check if we just hit the ground this frame 161 | if not _was_on_ground and is_feet_on_ground(): 162 | current_jump_type = JumpType.NONE 163 | jumps_left = max_jump_amount 164 | if is_jump_buffer_timer_running() and not can_hold_jump: 165 | jump() 166 | 167 | hit_ground.emit() 168 | 169 | 170 | # Cannot do this in _input because it needs to be checked every frame 171 | if Input.is_action_pressed(input_jump): 172 | if can_ground_jump() and can_hold_jump: 173 | jump() 174 | 175 | var gravity = apply_gravity_multipliers_to(default_gravity) 176 | acc.y = gravity 177 | 178 | # Apply friction 179 | velocity.x *= 1 / (1 + (delta * friction)) 180 | velocity += acc * delta 181 | 182 | 183 | _was_on_ground = is_feet_on_ground() 184 | move_and_slide() 185 | 186 | 187 | ## Use this instead of coyote_timer.start() to check if the coyote_timer is enabled first 188 | func start_coyote_timer(): 189 | if is_coyote_time_enabled: 190 | coyote_timer.start() 191 | 192 | ## Use this instead of jump_buffer_timer.start() to check if the jump_buffer is enabled first 193 | func start_jump_buffer_timer(): 194 | if is_jump_buffer_enabled: 195 | jump_buffer_timer.start() 196 | 197 | ## Use this instead of `not coyote_timer.is_stopped()`. This will always return false if 198 | ## the coyote_timer is disabled 199 | func is_coyote_timer_running(): 200 | if (is_coyote_time_enabled and not coyote_timer.is_stopped()): 201 | return true 202 | 203 | return false 204 | 205 | ## Use this instead of `not jump_buffer_timer.is_stopped()`. This will always return false if 206 | ## the jump_buffer_timer is disabled 207 | func is_jump_buffer_timer_running(): 208 | if is_jump_buffer_enabled and not jump_buffer_timer.is_stopped(): 209 | return true 210 | 211 | return false 212 | 213 | 214 | func can_ground_jump() -> bool: 215 | if jumps_left <= 0: 216 | return false 217 | 218 | if (current_jump_type == JumpType.NONE and is_feet_on_ground()) or is_coyote_timer_running(): 219 | return true 220 | 221 | return false 222 | 223 | 224 | func can_double_jump(): 225 | if jumps_left <= 1 and jumps_left == max_jump_amount: 226 | # Special case where you've fallen off a cliff and only have 1 jump. You cannot use your 227 | # first jump in the air 228 | return false 229 | 230 | if jumps_left > 0 and not is_feet_on_ground() and coyote_timer.is_stopped(): 231 | return true 232 | 233 | return false 234 | 235 | 236 | ## Same as is_on_floor(), but also returns true if gravity is reversed and you are on the ceiling 237 | func is_feet_on_ground(): 238 | if is_on_floor() and default_gravity >= 0: 239 | return true 240 | if is_on_ceiling() and default_gravity <= 0: 241 | return true 242 | 243 | return false 244 | 245 | 246 | ## Perform a ground jump, or a double jump if the character is in the air. 247 | func jump(): 248 | if can_double_jump(): 249 | double_jump() 250 | else: 251 | ground_jump() 252 | 253 | 254 | ## Perform a double jump without checking if the player is able to. 255 | func double_jump(): 256 | if jumps_left == max_jump_amount: 257 | # Your first jump must be used when on the ground. 258 | # If your first jump is used in the air, an additional jump will be taken away. 259 | jumps_left -= 1 260 | 261 | velocity.y = -double_jump_velocity 262 | current_jump_type = JumpType.AIR 263 | jumps_left -= 1 264 | jumped.emit(false) 265 | 266 | 267 | ## Perform a ground jump without checking if the player is able to. 268 | func ground_jump(): 269 | velocity.y = -jump_velocity 270 | current_jump_type = JumpType.GROUND 271 | jumps_left -= 1 272 | coyote_timer.stop() 273 | jumped.emit(true) 274 | 275 | 276 | func apply_gravity_multipliers_to(gravity) -> float: 277 | if velocity.y * sign(default_gravity) > 0: # If we are falling 278 | gravity *= falling_gravity_multiplier 279 | 280 | # if we released jump and are still rising 281 | elif velocity.y * sign(default_gravity) < 0: 282 | if not holding_jump: 283 | if not current_jump_type == JumpType.AIR: # Always jump to max height when we are using a double jump 284 | gravity *= release_gravity_multiplier # multiply the gravity so we have a lower jump 285 | 286 | 287 | return gravity 288 | 289 | 290 | ## Calculates the desired gravity from jump height and jump duration. [br] 291 | ## Formula is from [url=https://www.youtube.com/watch?v=hG9SzQxaCm8]this video[/url] 292 | func calculate_gravity(p_max_jump_height, p_jump_duration): 293 | return (2 * p_max_jump_height) / pow(p_jump_duration, 2) 294 | 295 | 296 | ## Calculates the desired jump velocity from jump height and jump duration. 297 | func calculate_jump_velocity(p_max_jump_height, p_jump_duration): 298 | return (2 * p_max_jump_height) / (p_jump_duration) 299 | 300 | 301 | ## Calculates jump velocity from jump height and gravity. [br] 302 | ## Formula from 303 | ## [url]https://sciencing.com/acceleration-velocity-distance-7779124.html#:~:text=in%20every%20step.-,Starting%20from%3A,-v%5E2%3Du[/url] 304 | func calculate_jump_velocity2(p_max_jump_height, p_gravity): 305 | return sqrt(abs(2 * p_gravity * p_max_jump_height)) * sign(p_max_jump_height) 306 | 307 | 308 | ## Calculates the gravity when the key is released based off the minimum jump height and jump velocity. [br] 309 | ## Formula is from [url]https://sciencing.com/acceleration-velocity-distance-7779124.html[/url] 310 | func calculate_release_gravity_multiplier(p_jump_velocity, p_min_jump_height, p_gravity): 311 | var release_gravity = pow(p_jump_velocity, 2) / (2 * p_min_jump_height) 312 | return release_gravity / p_gravity 313 | 314 | 315 | ## Returns a value for friction that will hit the max speed after 90% of time_to_max seconds. [br] 316 | ## Formula from [url]https://www.reddit.com/r/gamedev/comments/bdbery/comment/ekxw9g4/?utm_source=share&utm_medium=web2x&context=3[/url] 317 | func calculate_friction(time_to_max): 318 | return 1 - (2.30259 / time_to_max) 319 | 320 | 321 | ## Formula from [url]https://www.reddit.com/r/gamedev/comments/bdbery/comment/ekxw9g4/?utm_source=share&utm_medium=web2x&context=3[/url] 322 | func calculate_speed(p_max_speed, p_friction): 323 | return (p_max_speed / p_friction) - p_max_speed 324 | 325 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="PlatformerController2D" 14 | run/main_scene="res://DemoScene.tscn" 15 | config/features=PackedStringArray("4.0") 16 | config/icon="res://icon.png" 17 | 18 | [display] 19 | 20 | window/size/viewport_width=1024 21 | window/size/viewport_height=600 22 | window/stretch/mode="canvas_items" 23 | 24 | [input] 25 | 26 | move_left={ 27 | "deadzone": 0.5, 28 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) 29 | , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"echo":false,"script":null) 30 | ] 31 | } 32 | move_right={ 33 | "deadzone": 0.5, 34 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) 35 | , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"echo":false,"script":null) 36 | ] 37 | } 38 | jump={ 39 | "deadzone": 0.5, 40 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) 41 | , null, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"echo":false,"script":null) 42 | ] 43 | } 44 | 45 | [physics] 46 | 47 | common/enable_pause_aware_picking=true 48 | 49 | [rendering] 50 | 51 | environment/default_environment="res://default_env.tres" 52 | --------------------------------------------------------------------------------