├── .mono └── assemblies │ └── Debug │ ├── GodotSharp.dll │ ├── GodotSharp.pdb │ ├── GodotSharp.xml │ ├── GodotSharpEditor.dll │ ├── GodotSharpEditor.pdb │ ├── GodotSharpEditor.xml │ └── api_hash_cache.cfg ├── Godot_Rollback.exe ├── LICENSE ├── README.md ├── Scenes ├── Game.tscn ├── Gui.tscn └── Prefab │ ├── Arena.tscn │ ├── Player.tscn │ └── Wall.tscn ├── Scripts ├── Gui.gd ├── Player.gd └── Session.gd ├── export_presets.cfg └── project.godot /.mono/assemblies/Debug/GodotSharp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValorZard/Godot-Rollback-Netcode---Example/4e0b5f079a341b89de6620b9a5f8e816659fc15c/.mono/assemblies/Debug/GodotSharp.dll -------------------------------------------------------------------------------- /.mono/assemblies/Debug/GodotSharp.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValorZard/Godot-Rollback-Netcode---Example/4e0b5f079a341b89de6620b9a5f8e816659fc15c/.mono/assemblies/Debug/GodotSharp.pdb -------------------------------------------------------------------------------- /.mono/assemblies/Debug/GodotSharpEditor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValorZard/Godot-Rollback-Netcode---Example/4e0b5f079a341b89de6620b9a5f8e816659fc15c/.mono/assemblies/Debug/GodotSharpEditor.dll -------------------------------------------------------------------------------- /.mono/assemblies/Debug/GodotSharpEditor.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValorZard/Godot-Rollback-Netcode---Example/4e0b5f079a341b89de6620b9a5f8e816659fc15c/.mono/assemblies/Debug/GodotSharpEditor.pdb -------------------------------------------------------------------------------- /.mono/assemblies/Debug/GodotSharpEditor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | GodotSharpEditor 5 | 6 | 7 | 8 | 9 | Editor export plugins are automatically activated whenever the user exports the project. Their most common use is to determine what files are being included in the exported project. For each plugin, is called at the beginning of the export process and then is called for each exported file. 10 | 11 | 12 | 13 | 14 | Virtual method to be overridden by the user. It is called when the export starts and provides all information about the export. features is the list of features for the export, is_debug is true for debug builds, path is the target path for the exported project. flags is only used when running a runnable profile, e.g. when using native run on Android. 15 | 16 | 17 | 18 | 19 | Virtual method to be overridden by the user. Called when the export is finished. 20 | 21 | 22 | 23 | 24 | Virtual method to be overridden by the user. Called for each exported file, providing arguments that can be used to identify the file. path is the path of the file, type is the represented by the file (e.g. ) and features is the list of features for the export. 25 | Calling inside this callback will make the file not included in the export. 26 | 27 | 28 | 29 | 30 | Adds a shared object with the given tags and destination path. 31 | 32 | 33 | 34 | 35 | Adds a static lib from the given path to the iOS project. 36 | 37 | 38 | 39 | 40 | Adds a custom file to be exported. path is the virtual path that can be used to load the file, file is the binary data of the file. If remap is true, file will not be exported, but instead remapped to the given path. 41 | 42 | 43 | 44 | 45 | Adds a static library (*.a) or dynamic library (*.dylib, *.framework) to Linking Phase in iOS's Xcode project. 46 | 47 | 48 | 49 | 50 | Adds a dynamic library (*.dylib, *.framework) to Linking Phase in iOS's Xcode project and embeds it into resulting binary. 51 | Note: For static libraries (*.a) works in same way as . 52 | This method should not be used for System libraries as they are already present on the device. 53 | 54 | 55 | 56 | 57 | Adds content for iOS Property List files. 58 | 59 | 60 | 61 | 62 | Adds linker flags for the iOS export. 63 | 64 | 65 | 66 | 67 | Adds an iOS bundle file from the given path to the exported project. 68 | 69 | 70 | 71 | 72 | Adds a C++ code to the iOS export. The final code is created from the code appended by each active export plugin. 73 | 74 | 75 | 76 | 77 | To be called inside . Skips the current file, so it's not included in the export. 78 | 79 | 80 | 81 | 82 | An editor feature profile can be used to disable specific features of the Godot editor. When disabled, the features won't appear in the editor, which makes the editor less cluttered. This is useful in education settings to reduce confusion or when working in a team. For example, artists and level designers could use a feature profile that disables the script editor to avoid accidentally making changes to files they aren't supposed to edit. 83 | To manage editor feature profiles visually, use Editor > Manage Feature Profiles... at the top of the editor window. 84 | 85 | 86 | 87 | 88 | The 3D editor. If this feature is disabled, the 3D editor won't display but 3D nodes will still display in the Create New Node dialog. 89 | 90 | 91 | 92 | 93 | The Script tab, which contains the script editor and class reference browser. If this feature is disabled, the Script tab won't display. 94 | 95 | 96 | 97 | 98 | The AssetLib tab. If this feature is disabled, the AssetLib tab won't display. 99 | 100 | 101 | 102 | 103 | Scene tree editing. If this feature is disabled, the Scene tree dock will still be visible but will be read-only. 104 | 105 | 106 | 107 | 108 | The Node dock. If this feature is disabled, signals and groups won't be visible and modifiable from the editor. 109 | 110 | 111 | 112 | 113 | The FileSystem dock. If this feature is disabled, the FileSystem dock won't be visible. 114 | 115 | 116 | 117 | 118 | The Import dock. If this feature is disabled, the Import dock won't be visible. 119 | 120 | 121 | 122 | 123 | Represents the size of the enum. 124 | 125 | 126 | 127 | 128 | If disable is true, disables the class specified by class_name. When disabled, the class won't appear in the Create New Node dialog. 129 | 130 | 131 | 132 | 133 | Returns true if the class specified by class_name is disabled. When disabled, the class won't appear in the Create New Node dialog. 134 | 135 | 136 | 137 | 138 | If disable is true, disables editing for the class specified by class_name. When disabled, the class will still appear in the Create New Node dialog but the inspector will be read-only when selecting a node that extends the class. 139 | 140 | 141 | 142 | 143 | Returns true if editing for the class specified by class_name is disabled. When disabled, the class will still appear in the Create New Node dialog but the inspector will be read-only when selecting a node that extends the class. 144 | 145 | 146 | 147 | 148 | If disable is true, disables editing for property in the class specified by class_name. When a property is disabled, it won't appear in the inspector when selecting a node that extends the class specified by class_name. 149 | 150 | 151 | 152 | 153 | Returns true if property is disabled in the class specified by class_name. When a property is disabled, it won't appear in the inspector when selecting a node that extends the class specified by class_name. 154 | 155 | 156 | 157 | 158 | If disable is true, disables the editor feature specified in feature. When a feature is disabled, it will disappear from the editor entirely. 159 | 160 | 161 | 162 | 163 | Returns true if the feature is disabled. When a feature is disabled, it will disappear from the editor entirely. 164 | 165 | 166 | 167 | 168 | Returns the specified feature's human-readable name. 169 | 170 | 171 | 172 | 173 | Saves the editor feature profile to a file in JSON format. It can then be imported using the feature profile manager's Import button or the button. 174 | 175 | 176 | 177 | 178 | Loads an editor feature profile from a file. The file must follow the JSON format obtained by using the feature profile manager's Export button or the method. 179 | 180 | 181 | 182 | 183 | The displays resources as thumbnails. 184 | 185 | 186 | 187 | 188 | The displays resources as a list of filenames. 189 | 190 | 191 | 192 | 193 | The can select only one file. Accepting the window will open the file. 194 | 195 | 196 | 197 | 198 | The can select multiple files. Accepting the window will open all files. 199 | 200 | 201 | 202 | 203 | The can select only one directory. Accepting the window will open the directory. 204 | 205 | 206 | 207 | 208 | The can select a file or directory. Accepting the window will open it. 209 | 210 | 211 | 212 | 213 | The can select only one file. Accepting the window will save the file. 214 | 215 | 216 | 217 | 218 | The can only view res:// directory contents. 219 | 220 | 221 | 222 | 223 | The can only view user:// directory contents. 224 | 225 | 226 | 227 | 228 | The can view the entire local file system. 229 | 230 | 231 | 232 | 233 | The location from which the user may select a file, including res://, user://, and the local file system. 234 | 235 | 236 | 237 | 238 | The view format in which the displays resources to the user. 239 | 240 | 241 | 242 | 243 | The purpose of the , which defines the allowed behaviors. 244 | 245 | 246 | 247 | 248 | The currently occupied directory. 249 | 250 | 251 | 252 | 253 | The currently selected file. 254 | 255 | 256 | 257 | 258 | The file system path in the address bar. 259 | 260 | 261 | 262 | 263 | If true, hidden files and directories will be visible in the . 264 | 265 | 266 | 267 | 268 | If true, the will not warn the user before overwriting files. 269 | 270 | 271 | 272 | 273 | Removes all filters except for "All Files (*)". 274 | 275 | 276 | 277 | 278 | Adds a comma-delimited file extension filter option to the with an optional semi-colon-delimited label. 279 | For example, "*.tscn, *.scn; Scenes" results in filter text "Scenes (*.tscn, *.scn)". 280 | 281 | 282 | 283 | 284 | Returns the VBoxContainer used to display the file system. 285 | 286 | 287 | 288 | 289 | Notify the that its view of the data is no longer accurate. Updates the view contents on next view update. 290 | 291 | 292 | 293 | 294 | This object holds information of all resources in the filesystem, their types, etc. 295 | Note: This class shouldn't be instantiated directly. Instead, access the singleton using . 296 | 297 | 298 | 299 | 300 | Gets the root directory object. 301 | 302 | 303 | 304 | 305 | Returns true of the filesystem is being scanned. 306 | 307 | 308 | 309 | 310 | Returns the scan progress for 0 to 1 if the FS is being scanned. 311 | 312 | 313 | 314 | 315 | Scan the filesystem for changes. 316 | 317 | 318 | 319 | 320 | Check if the source of any imported resource changed. 321 | 322 | 323 | 324 | 325 | Update a file information. Call this if an external program (not Godot) modified the file. 326 | 327 | 328 | 329 | 330 | Returns a view into the filesystem at path. 331 | 332 | 333 | 334 | 335 | Gets the type of the file, given the full path. 336 | 337 | 338 | 339 | 340 | Scans the script files and updates the list of custom class names. 341 | 342 | 343 | 344 | 345 | A more generalized, low-level variation of the directory concept. 346 | 347 | 348 | 349 | 350 | Returns the number of subdirectories in this directory. 351 | 352 | 353 | 354 | 355 | Returns the subdirectory at index idx. 356 | 357 | 358 | 359 | 360 | Returns the number of files in this directory. 361 | 362 | 363 | 364 | 365 | Returns the name of the file at index idx. 366 | 367 | 368 | 369 | 370 | Returns the path to the file at index idx. 371 | 372 | 373 | 374 | 375 | Returns the file extension of the file at index idx. 376 | 377 | 378 | 379 | 380 | Returns the name of the script class defined in the file at index idx. If the file doesn't define a script class using the class_name syntax, this will return an empty string. 381 | 382 | 383 | 384 | 385 | Returns the base class of the script class defined in the file at index idx. If the file doesn't define a script class using the class_name syntax, this will return an empty string. 386 | 387 | 388 | 389 | 390 | Returns true if the file at index idx imported properly. 391 | 392 | 393 | 394 | 395 | Returns the name of this directory. 396 | 397 | 398 | 399 | 400 | Returns the path to this directory. 401 | 402 | 403 | 404 | 405 | Returns the parent directory for this directory or null if called on a directory at res:// or user://. 406 | 407 | 408 | 409 | 410 | Returns the index of the file with name name or -1 if not found. 411 | 412 | 413 | 414 | 415 | Returns the index of the directory with name name or -1 if not found. 416 | 417 | 418 | 419 | 420 | EditorImportPlugins provide a way to extend the editor's resource import functionality. Use them to import resources from custom files or to provide alternatives to the editor's existing importers. Register your with . 421 | EditorImportPlugins work by associating with specific file extensions and a resource type. See and . They may optionally specify some import presets that affect the import process. EditorImportPlugins are responsible for creating the resources and saving them in the .import directory. 422 | Below is an example EditorImportPlugin that imports a from a file with the extension ".special" or ".spec": 423 | 424 | tool 425 | extends EditorImportPlugin 426 | 427 | func get_importer_name(): 428 | return "my.special.plugin" 429 | 430 | func get_visible_name(): 431 | return "Special Mesh Importer" 432 | 433 | func get_recognized_extensions(): 434 | return ["special", "spec"] 435 | 436 | func get_save_extension(): 437 | return "mesh" 438 | 439 | func get_resource_type(): 440 | return "Mesh" 441 | 442 | func get_preset_count(): 443 | return 1 444 | 445 | func get_preset_name(i): 446 | return "Default" 447 | 448 | func get_import_options(i): 449 | return [{"name": "my_option", "default_value": false}] 450 | 451 | func import(source_file, save_path, options, platform_variants, gen_files): 452 | var file = File.new() 453 | if file.open(source_file, File.READ) != OK: 454 | return FAILED 455 | 456 | var mesh = Mesh.new() 457 | # Fill the Mesh with data read in "file", left as an exercise to the reader 458 | 459 | var filename = save_path + "." + get_save_extension() 460 | ResourceSaver.save(filename, mesh) 461 | return OK 462 | 463 | 464 | 465 | 466 | 467 | Gets the options and default values for the preset at this index. Returns an Array of Dictionaries with the following keys: name, default_value, property_hint (optional), hint_string (optional), usage (optional). 468 | 469 | 470 | 471 | 472 | Gets the order of this importer to be run when importing resources. Higher values will be called later. Use this to ensure the importer runs after the dependencies are already imported. 473 | 474 | 475 | 476 | 477 | Gets the unique name of the importer. 478 | 479 | 480 | 481 | 482 | This method can be overridden to hide specific import options if conditions are met. This is mainly useful for hiding options that depend on others if one of them is disabled. For example: 483 | 484 | func get_option_visibility(option, options): 485 | # Only show the lossy quality setting if the compression mode is set to "Lossy". 486 | if option == "compress/lossy_quality" and options.has("compress/mode"): 487 | return int(options["compress/mode"]) == COMPRESS_LOSSY 488 | 489 | return true 490 | 491 | Return true to make all options always visible. 492 | 493 | 494 | 495 | 496 | Gets the number of initial presets defined by the plugin. Use to get the default options for the preset and to get the name of the preset. 497 | 498 | 499 | 500 | 501 | Gets the name of the options preset at this index. 502 | 503 | 504 | 505 | 506 | Gets the priority of this plugin for the recognized extension. Higher priority plugins will be preferred. The default priority is 1.0. 507 | 508 | 509 | 510 | 511 | Gets the list of file extensions to associate with this loader (case-insensitive). e.g. ["obj"]. 512 | 513 | 514 | 515 | 516 | Gets the Godot resource type associated with this loader. e.g. "Mesh" or "Animation". 517 | 518 | 519 | 520 | 521 | Gets the extension used to save this resource in the .import directory. 522 | 523 | 524 | 525 | 526 | Gets the name to display in the import window. 527 | 528 | 529 | 530 | 531 | Imports source_file into save_path with the import options specified. The platform_variants and gen_files arrays will be modified by this function. 532 | This method must be overridden to do the actual importing work. See this class' description for an example of overriding this method. 533 | 534 | 535 | 536 | 537 | The editor inspector is by default located on the right-hand side of the editor. It's used to edit the properties of the selected node. For example, you can select a node such as then edit its transform through the inspector tool. The editor inspector is an essential tool in the game development workflow. 538 | Note: This class shouldn't be instantiated directly. Instead, access the singleton using . 539 | 540 | 541 | 542 | 543 | Refreshes the inspector. 544 | Note: To save on CPU resources, calling this method will do nothing if the time specified in docks/property_editor/auto_refresh_interval editor setting hasn't passed yet since this method was last called. (By default, this interval is set to 0.3 seconds.) 545 | 546 | 547 | 548 | 549 | This plugins allows adding custom property editors to . 550 | Plugins are registered via . 551 | When an object is edited, the function is called and must return true if the object type is supported. 552 | If supported, the function will be called, allowing to place custom controls at the beginning of the class. 553 | Subsequently, the and are called for every category and property. They offer the ability to add custom controls to the inspector too. 554 | Finally will be called. 555 | On each of these calls, the "add" functions can be called. 556 | 557 | 558 | 559 | 560 | Returns true if this object can be handled by this plugin. 561 | 562 | 563 | 564 | 565 | Called to allow adding controls at the beginning of the list. 566 | 567 | 568 | 569 | 570 | Called to allow adding controls at the beginning of the category. 571 | 572 | 573 | 574 | 575 | Called to allow adding controls at the end of the list. 576 | 577 | 578 | 579 | 580 | Called to allow adding property specific editors to the inspector. Usually these inherit . Returning true removes the built-in editor for this property, otherwise allows to insert a custom editor before the built-in one. 581 | 582 | 583 | 584 | 585 | Adds a custom control, not necessarily a property editor. 586 | 587 | 588 | 589 | 590 | Adds a property editor, this must inherit . 591 | 592 | 593 | 594 | 595 | Adds an editor that allows modifying multiple properties, this must inherit . 596 | 597 | 598 | 599 | 600 | EditorInterface gives you control over Godot editor's window. It allows customizing the window, saving and (re-)loading scenes, rendering mesh previews, inspecting and editing resources and objects, and provides access to , , , , the editor viewport, and information about scenes. 601 | Note: This class shouldn't be instantiated directly. Instead, access the singleton using . 602 | 603 | 604 | 605 | 606 | If true, enables distraction-free mode which hides side docks to increase the space available for the main view. 607 | 608 | 609 | 610 | 611 | Shows the given property on the given object in the editor's Inspector dock. If inspector_only is true, plugins will not attempt to edit object. 612 | 613 | 614 | 615 | 616 | Returns the editor's instance. 617 | 618 | 619 | 620 | 621 | Returns the editor's instance. 622 | 623 | 624 | 625 | 626 | Returns the editor's instance. 627 | 628 | 629 | 630 | 631 | Returns the main container of Godot editor's window. For example, you can use it to retrieve the size of the container and place your controls accordingly. 632 | 633 | 634 | 635 | 636 | Edits the given . 637 | 638 | 639 | 640 | 641 | Opens the scene at the given path. 642 | 643 | 644 | 645 | 646 | Reloads the scene at the given path. 647 | 648 | 649 | 650 | 651 | Plays the main scene. 652 | 653 | 654 | 655 | 656 | Plays the currently active scene. 657 | 658 | 659 | 660 | 661 | Plays the scene specified by its filepath. 662 | 663 | 664 | 665 | 666 | Stops the scene that is currently playing. 667 | 668 | 669 | 670 | 671 | Returns true if a scene is currently being played, false otherwise. Paused scenes are considered as being played. 672 | 673 | 674 | 675 | 676 | Returns the name of the scene that is being played. If no scene is currently being played, returns an empty string. 677 | 678 | 679 | 680 | 681 | Returns an with the file paths of the currently opened scenes. 682 | 683 | 684 | 685 | 686 | Returns the edited (current) scene's root . 687 | 688 | 689 | 690 | 691 | Returns the editor's instance. 692 | 693 | 694 | 695 | 696 | Returns the editor's instance. 697 | 698 | 699 | 700 | 701 | Returns the main editor control. Use this as a parent for main screens. 702 | Note: This returns the main editor control containing the whole editor, not the 2D or 3D viewports specifically. 703 | 704 | 705 | 706 | 707 | Returns mesh previews rendered at the given size as an of s. 708 | 709 | 710 | 711 | 712 | Selects the file, with the path provided by file, in the FileSystem dock. 713 | 714 | 715 | 716 | 717 | Returns the path of the directory currently selected in the . If a file is selected, its base directory will be returned using String.get_base_dir instead. 718 | 719 | 720 | 721 | 722 | Returns the current path being viewed in the . 723 | 724 | 725 | 726 | 727 | Returns the editor's instance. 728 | 729 | 730 | 731 | 732 | Sets the enabled status of a plugin. The plugin name is the same as its directory name. 733 | 734 | 735 | 736 | 737 | Returns true if the specified plugin is enabled. The plugin name is the same as its directory name. 738 | 739 | 740 | 741 | 742 | Returns the editor's instance. 743 | 744 | 745 | 746 | 747 | Saves the scene. Returns either OK or ERR_CANT_CREATE (see @GlobalScope constants). 748 | 749 | 750 | 751 | 752 | Saves the scene as a file at path. 753 | 754 | 755 | 756 | 757 | Sets the editor's current main screen to the one specified in name. name must match the text of the tab in question exactly (2D, 3D, Script, AssetLib). 758 | 759 | 760 | 761 | 762 | Plugins are used by the editor to extend functionality. The most common types of plugins are those which edit a given node or resource type, import plugins and export plugins. See also to add functions to the editor. 763 | 764 | 765 | 766 | 767 | Represents the size of the enum. 768 | 769 | 770 | 771 | 772 | This method is called when the editor is about to save the project, switch to another tab, etc. It asks the plugin to apply any pending state changes to ensure consistency. 773 | This is used, for example, in shader editors to let the plugin know that it must apply the shader code being written by the user to the object. 774 | 775 | 776 | 777 | 778 | Clear all the state and reset the object being edited to zero. This ensures your plugin does not keep editing a currently existing node, or a node from the wrong scene. 779 | 780 | 781 | 782 | 783 | Called by the engine when the user disables the in the Plugin tab of the project settings window. 784 | 785 | 786 | 787 | 788 | This function is used for plugins that edit specific object types (nodes or resources). It requests the editor to edit the given object. 789 | 790 | 791 | 792 | 793 | Called by the engine when the user enables the in the Plugin tab of the project settings window. 794 | 795 | 796 | 797 | 798 | Called by the engine when the 2D editor's viewport is updated. Use the overlay for drawing. You can update the viewport manually by calling . 799 | 800 | func forward_canvas_draw_over_viewport(overlay): 801 | # Draw a circle at cursor position. 802 | overlay.draw_circle(overlay.get_local_mouse_position(), 64) 803 | 804 | func forward_canvas_gui_input(event): 805 | if event is InputEventMouseMotion: 806 | # Redraw viewport when cursor is moved. 807 | update_overlays() 808 | return true 809 | return false 810 | 811 | 812 | 813 | 814 | 815 | This method is the same as , except it draws on top of everything. Useful when you need an extra layer that shows over anything else. 816 | You need to enable calling of this method by using . 817 | 818 | 819 | 820 | 821 | Called when there is a root node in the current edited scene, is implemented and an happens in the 2D viewport. Intercepts the , if return true consumes the event, otherwise forwards event to other Editor classes. Example: 822 | 823 | # Prevents the InputEvent to reach other Editor classes 824 | func forward_canvas_gui_input(event): 825 | var forward = true 826 | return forward 827 | 828 | Must return false in order to forward the to other Editor classes. Example: 829 | 830 | # Consumes InputEventMouseMotion and forwards other InputEvent types 831 | func forward_canvas_gui_input(event): 832 | var forward = false 833 | if event is InputEventMouseMotion: 834 | forward = true 835 | return forward 836 | 837 | 838 | 839 | 840 | 841 | Called by the engine when the 3D editor's viewport is updated. Use the overlay for drawing. You can update the viewport manually by calling . 842 | 843 | func forward_spatial_draw_over_viewport(overlay): 844 | # Draw a circle at cursor position. 845 | overlay.draw_circle(overlay.get_local_mouse_position(), 64) 846 | 847 | func forward_spatial_gui_input(camera, event): 848 | if event is InputEventMouseMotion: 849 | # Redraw viewport when cursor is moved. 850 | update_overlays() 851 | return true 852 | return false 853 | 854 | 855 | 856 | 857 | 858 | This method is the same as , except it draws on top of everything. Useful when you need an extra layer that shows over anything else. 859 | You need to enable calling of this method by using . 860 | 861 | 862 | 863 | 864 | Called when there is a root node in the current edited scene, is implemented and an happens in the 3D viewport. Intercepts the , if return true consumes the event, otherwise forwards event to other Editor classes. Example: 865 | 866 | # Prevents the InputEvent to reach other Editor classes 867 | func forward_spatial_gui_input(camera, event): 868 | var forward = true 869 | return forward 870 | 871 | Must return false in order to forward the to other Editor classes. Example: 872 | 873 | # Consumes InputEventMouseMotion and forwards other InputEvent types 874 | func forward_spatial_gui_input(camera, event): 875 | var forward = false 876 | if event is InputEventMouseMotion: 877 | forward = true 878 | return forward 879 | 880 | 881 | 882 | 883 | 884 | This is for editors that edit script-based objects. You can return a list of breakpoints in the format (script:line), for example: res://path_to_script.gd:25. 885 | 886 | 887 | 888 | 889 | Override this method in your plugin to return a in order to give it an icon. 890 | For main screen plugins, this appears at the top of the screen, to the right of the "2D", "3D", "Script", and "AssetLib" buttons. 891 | Ideally, the plugin icon should be white with a transparent background and 16x16 pixels in size. 892 | 893 | func get_plugin_icon(): 894 | # You can use a custom icon: 895 | return preload("res://addons/my_plugin/my_plugin_icon.svg") 896 | # Or use a built-in icon: 897 | return get_editor_interface().get_base_control().get_icon("Node", "EditorIcons") 898 | 899 | 900 | 901 | 902 | 903 | Override this method in your plugin to provide the name of the plugin when displayed in the Godot editor. 904 | For main screen plugins, this appears at the top of the screen, to the right of the "2D", "3D", "Script", and "AssetLib" buttons. 905 | 906 | 907 | 908 | 909 | Gets the state of your plugin editor. This is used when saving the scene (so state is kept when opening it again) and for switching tabs (so state can be restored when the tab returns). 910 | 911 | 912 | 913 | 914 | Gets the GUI layout of the plugin. This is used to save the project's editor layout when is called or the editor layout was changed(For example changing the position of a dock). 915 | 916 | 917 | 918 | 919 | Implement this function if your plugin edits a specific type of object (Resource or Node). If you return true, then you will get the functions and called when the editor requests them. If you have declared the methods and these will be called too. 920 | 921 | 922 | 923 | 924 | Returns true if this is a main screen editor plugin (it goes in the workspace selector together with 2D, 3D, Script and AssetLib). 925 | 926 | 927 | 928 | 929 | This function will be called when the editor is requested to become visible. It is used for plugins that edit a specific object type. 930 | Remember that you have to manage the visibility of all your editor controls manually. 931 | 932 | 933 | 934 | 935 | This method is called after the editor saves the project or when it's closed. It asks the plugin to save edited external scenes/resources. 936 | 937 | 938 | 939 | 940 | Restore the state saved by . 941 | 942 | 943 | 944 | 945 | Restore the plugin GUI layout saved by . 946 | 947 | 948 | 949 | 950 | Adds a custom control to a container (see ). There are many locations where custom controls can be added in the editor UI. 951 | Please remember that you have to manage the visibility of your custom controls yourself (and likely hide it after adding it). 952 | When your plugin is deactivated, make sure to remove your custom control with and free it with . 953 | 954 | 955 | 956 | 957 | Adds a control to the bottom panel (together with Output, Debug, Animation, etc). Returns a reference to the button added. It's up to you to hide/show the button when needed. When your plugin is deactivated, make sure to remove your custom control with and free it with . 958 | 959 | 960 | 961 | 962 | Adds the control to a specific dock slot (see for options). 963 | If the dock is repositioned and as long as the plugin is active, the editor will save the dock position on further sessions. 964 | When your plugin is deactivated, make sure to remove your custom control with and free it with . 965 | 966 | 967 | 968 | 969 | Removes the control from the dock. You have to manually the control. 970 | 971 | 972 | 973 | 974 | Removes the control from the bottom panel. You have to manually the control. 975 | 976 | 977 | 978 | 979 | Removes the control from the specified container. You have to manually the control. 980 | 981 | 982 | 983 | 984 | Adds a custom menu item to Project > Tools as name that calls callback on an instance of handler with a parameter ud when user activates it. 985 | 986 | 987 | 988 | 989 | Adds a custom submenu under Project > Tools > name. submenu should be an object of class . This submenu should be cleaned up using remove_tool_menu_item(name). 990 | 991 | 992 | 993 | 994 | Removes a menu name from Project > Tools. 995 | 996 | 997 | 998 | 999 | Adds a custom type, which will appear in the list of nodes or resources. An icon can be optionally passed. 1000 | When given node or resource is selected, the base type will be instanced (ie, "Spatial", "Control", "Resource"), then the script will be loaded and set to this object. 1001 | You can use the virtual method to check if your custom object is being edited by checking the script or using the is keyword. 1002 | During run-time, this will be a simple object with a script so this function does not need to be called then. 1003 | 1004 | 1005 | 1006 | 1007 | Removes a custom type added by . 1008 | 1009 | 1010 | 1011 | 1012 | Adds a script at path to the Autoload list as name. 1013 | 1014 | 1015 | 1016 | 1017 | Removes an Autoload name from the list. 1018 | 1019 | 1020 | 1021 | 1022 | Updates the overlays of the 2D and 3D editor viewport. Causes methods , , and to be called. 1023 | 1024 | 1025 | 1026 | 1027 | Gets the undo/redo object. Most actions in the editor can be undoable, so use this object to make sure this happens when it's worth it. 1028 | 1029 | 1030 | 1031 | 1032 | Queue save the project's editor layout. 1033 | 1034 | 1035 | 1036 | 1037 | Registers a new export plugin. Export plugins are used when the project is being exported. See for more information. 1038 | 1039 | 1040 | 1041 | 1042 | Use this method if you always want to receive inputs from 3D view screen inside . It might be especially usable if your plugin will want to use raycast in the scene. 1043 | 1044 | 1045 | 1046 | 1047 | Enables calling of for the 2D editor and for the 3D editor when their viewports are updated. You need to call this method only once and it will work permanently for this plugin. 1048 | 1049 | 1050 | 1051 | 1052 | Returns the object that gives you control over Godot editor's window and its functionalities. 1053 | 1054 | 1055 | 1056 | 1057 | Gets the Editor's dialogue used for making scripts. 1058 | Note: Users can configure it before use. 1059 | 1060 | 1061 | 1062 | 1063 | This control allows property editing for one or multiple properties into . It is added via . 1064 | 1065 | 1066 | 1067 | 1068 | Set this property to change the label (if you want to show one). 1069 | 1070 | 1071 | 1072 | 1073 | Used by the inspector, set to true when the property is read-only. 1074 | 1075 | 1076 | 1077 | 1078 | Used by the inspector, set to true when the property is checkable. 1079 | 1080 | 1081 | 1082 | 1083 | Used by the inspector, set to true when the property is checked. 1084 | 1085 | 1086 | 1087 | 1088 | Used by the inspector, set to true when the property must draw with error color. This is used for editable children's properties. 1089 | 1090 | 1091 | 1092 | 1093 | Used by the inspector, set to true when the property can add keys for animation. 1094 | 1095 | 1096 | 1097 | 1098 | When this virtual function is called, you must update your editor. 1099 | 1100 | 1101 | 1102 | 1103 | Gets the edited property. If your editor is for a single property (added via ), then this will return the property. 1104 | 1105 | 1106 | 1107 | 1108 | Gets the edited object. 1109 | 1110 | 1111 | 1112 | 1113 | Override if you want to allow a custom tooltip over your property. 1114 | 1115 | 1116 | 1117 | 1118 | If any of the controls added can gain keyboard focus, add it here. This ensures that focus will be restored if the inspector is refreshed. 1119 | 1120 | 1121 | 1122 | 1123 | Adds controls with this function if you want them on the bottom (below the label). 1124 | 1125 | 1126 | 1127 | 1128 | If one or several properties have changed, this must be called. field is used in case your editor can modify fields separately (as an example, Vector3.x). The changing argument avoids the editor requesting this property to be refreshed (leave as false if unsure). 1129 | 1130 | 1131 | 1132 | 1133 | This object is used to generate previews for resources of files. 1134 | Note: This class shouldn't be instantiated directly. Instead, access the singleton using . 1135 | 1136 | 1137 | 1138 | 1139 | Queue a resource file for preview (using a path). Once the preview is ready, your receiver.receiver_func will be called either containing the preview texture or an empty texture (if no preview was possible). Callback must have the format: (path,texture,userdata). Userdata can be anything. 1140 | 1141 | 1142 | 1143 | 1144 | Queue a resource being edited for preview (using an instance). Once the preview is ready, your receiver.receiver_func will be called either containing the preview texture or an empty texture (if no preview was possible). Callback must have the format: (path,texture,userdata). Userdata can be anything. 1145 | 1146 | 1147 | 1148 | 1149 | Create an own, custom preview generator. 1150 | 1151 | 1152 | 1153 | 1154 | Removes a custom preview generator. 1155 | 1156 | 1157 | 1158 | 1159 | Check if the resource changed, if so, it will be invalidated and the corresponding signal emitted. 1160 | 1161 | 1162 | 1163 | 1164 | Custom code to generate previews. Please check file_dialog/thumbnail_size in to find out the right size to do previews at. 1165 | 1166 | 1167 | 1168 | 1169 | If this function returns true, the generator will call or for small previews as well. 1170 | By default, it returns false. 1171 | 1172 | 1173 | 1174 | 1175 | Generate a preview from a given resource with the specified size. This must always be implemented. 1176 | Returning an empty texture is an OK way to fail and let another generator take care. 1177 | Care must be taken because this function is always called from a thread (not the main thread). 1178 | 1179 | 1180 | 1181 | 1182 | Generate a preview directly from a path with the specified size. Implementing this is optional, as default code will load and call . 1183 | Returning an empty texture is an OK way to fail and let another generator take care. 1184 | Care must be taken because this function is always called from a thread (not the main thread). 1185 | 1186 | 1187 | 1188 | 1189 | If this function returns true, the generator will automatically generate the small previews from the normal preview texture generated by the methods or . 1190 | By default, it returns false. 1191 | 1192 | 1193 | 1194 | 1195 | Returns true if your generator supports the resource of type type. 1196 | 1197 | 1198 | 1199 | 1200 | This is an FBX 3D asset importer with full support for most FBX features. 1201 | If exporting a FBX scene from Autodesk Maya, use these FBX export settings: 1202 | 1203 | - Smoothing Groups 1204 | - Smooth Mesh 1205 | - Triangluate (for meshes with blend shapes) 1206 | - Bake Animation 1207 | - Resample All 1208 | - Deformed Models 1209 | - Skins 1210 | - Blend Shapes 1211 | - Curve Filters 1212 | - Constant Key Reducer 1213 | - Auto Tangents Only 1214 | - *Do not check* Constraints (as it will break the file) 1215 | - Can check Embed Media (embeds textures into the exported FBX file) 1216 | - Note that when importing embedded media, the texture and mesh will be a single immutable file. 1217 | - You will have to re-export then re-import the FBX if the texture has changed. 1218 | - Units: Centimeters 1219 | - Up Axis: Y 1220 | - Binary format in FBX 2017 1221 | 1222 | 1223 | 1224 | 1225 | 1226 | Imported scenes can be automatically modified right after import by setting their Custom Script Import property to a tool script that inherits from this class. 1227 | The callback receives the imported scene's root node and returns the modified version of the scene. Usage example: 1228 | 1229 | tool # Needed so it runs in editor 1230 | extends EditorScenePostImport 1231 | 1232 | # This sample changes all node names 1233 | 1234 | # Called right after the scene is imported and gets the root node 1235 | func post_import(scene): 1236 | # Change all node names to "modified_[oldnodename]" 1237 | iterate(scene) 1238 | return scene # Remember to return the imported scene 1239 | 1240 | func iterate(node): 1241 | if node != null: 1242 | node.name = "modified_" + node.name 1243 | for child in node.get_children(): 1244 | iterate(child) 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | Called after the scene was imported. This method must return the modified version of the scene. 1251 | 1252 | 1253 | 1254 | 1255 | Returns the resource folder the imported scene file is located in. 1256 | 1257 | 1258 | 1259 | 1260 | Returns the source file path which got imported (e.g. res://scene.dae). 1261 | 1262 | 1263 | 1264 | 1265 | Scripts extending this class and implementing its method can be executed from the Script Editor's File > Run menu option (or by pressing Ctrl+Shift+X) while the editor is running. This is useful for adding custom in-editor functionality to Godot. For more complex additions, consider using s instead. 1266 | Note: Extending scripts need to have tool mode enabled. 1267 | Example script: 1268 | 1269 | tool 1270 | extends EditorScript 1271 | 1272 | func _run(): 1273 | print("Hello from the Godot Editor!") 1274 | 1275 | Note: The script is run in the Editor context, which means the output is visible in the console window started with the Editor (stdout) instead of the usual Godot Output dock. 1276 | 1277 | 1278 | 1279 | 1280 | This method is executed by the Editor when File > Run is used. 1281 | 1282 | 1283 | 1284 | 1285 | Adds node as a child of the root node in the editor context. 1286 | Warning: The implementation of this method is currently disabled. 1287 | 1288 | 1289 | 1290 | 1291 | Returns the Editor's currently active scene. 1292 | 1293 | 1294 | 1295 | 1296 | Returns the singleton instance. 1297 | 1298 | 1299 | 1300 | 1301 | This object manages the SceneTree selection in the editor. 1302 | Note: This class shouldn't be instantiated directly. Instead, access the singleton using . 1303 | 1304 | 1305 | 1306 | 1307 | Clear the selection. 1308 | 1309 | 1310 | 1311 | 1312 | Adds a node to the selection. 1313 | 1314 | 1315 | 1316 | 1317 | Removes a node from the selection. 1318 | 1319 | 1320 | 1321 | 1322 | Gets the list of selected nodes. 1323 | 1324 | 1325 | 1326 | 1327 | Gets the list of selected nodes, optimized for transform operations (i.e. moving them, rotating, etc). This list avoids situations where a node is selected and also child/grandchild. 1328 | 1329 | 1330 | 1331 | 1332 | Object that holds the project-independent editor settings. These settings are generally visible in the Editor > Editor Settings menu. 1333 | Property names use slash delimiters to distinguish sections. Setting values can be of any Variant type. It's recommended to use snake_case for editor settings to be consistent with the Godot editor itself. 1334 | Accessing the settings can be done using the following methods, such as: 1335 | 1336 | # `settings.set("some/property", value)` also works as this class overrides `_set()` internally. 1337 | settings.set_setting("some/property",value) 1338 | 1339 | # `settings.get("some/property", value)` also works as this class overrides `_get()` internally. 1340 | settings.get_setting("some/property") 1341 | 1342 | var list_of_settings = settings.get_property_list() 1343 | 1344 | Note: This class shouldn't be instantiated directly. Instead, access the singleton using . 1345 | 1346 | 1347 | 1348 | 1349 | Emitted after any editor setting has changed. It's used by various editor plugins to update their visuals on theme changes or logic on configuration changes. 1350 | 1351 | 1352 | 1353 | 1354 | Returns true if the setting specified by name exists, false otherwise. 1355 | 1356 | 1357 | 1358 | 1359 | Sets the value of the setting specified by name. This is equivalent to using on the EditorSettings instance. 1360 | 1361 | 1362 | 1363 | 1364 | Returns the value of the setting specified by name. This is equivalent to using on the EditorSettings instance. 1365 | 1366 | 1367 | 1368 | 1369 | Erases the setting whose name is specified by property. 1370 | 1371 | 1372 | 1373 | 1374 | Sets the initial value of the setting specified by name to value. This is used to provide a value for the Revert button in the Editor Settings. If update_current is true, the current value of the setting will be set to value as well. 1375 | 1376 | 1377 | 1378 | 1379 | Returns true if the setting specified by name can have its value reverted to the default value, false otherwise. When this method returns true, a Revert button will display next to the setting in the Editor Settings. 1380 | 1381 | 1382 | 1383 | 1384 | Returns the default value of the setting specified by name. This is the value that would be applied when clicking the Revert button in the Editor Settings. 1385 | 1386 | 1387 | 1388 | 1389 | Adds a custom property info to a property. The dictionary must contain: 1390 | - name: (the name of the property) 1391 | - type: (see ) 1392 | - optionally hint: (see ) and hint_string: 1393 | Example: 1394 | 1395 | editor_settings.set("category/property_name", 0) 1396 | 1397 | var property_info = { 1398 | "name": "category/property_name", 1399 | "type": TYPE_INT, 1400 | "hint": PROPERTY_HINT_ENUM, 1401 | "hint_string": "one,two,three" 1402 | } 1403 | 1404 | editor_settings.add_property_info(property_info) 1405 | 1406 | 1407 | 1408 | 1409 | 1410 | Gets the global settings path for the engine. Inside this path, you can find some standard paths such as: 1411 | settings/tmp - Used for temporary storage of files 1412 | settings/templates - Where export templates are located 1413 | 1414 | 1415 | 1416 | 1417 | Returns the project-specific settings path. Projects all have a unique subdirectory inside the settings path where project-specific settings are saved. 1418 | 1419 | 1420 | 1421 | 1422 | Sets project-specific metadata with the section, key and data specified. This metadata is stored outside the project folder and therefore won't be checked into version control. See also . 1423 | 1424 | 1425 | 1426 | 1427 | Returns project-specific metadata for the section and key specified. If the metadata doesn't exist, default will be returned instead. See also . 1428 | 1429 | 1430 | 1431 | 1432 | Sets the list of favorite files and directories for this project. 1433 | 1434 | 1435 | 1436 | 1437 | Returns the list of favorite files and directories for this project. 1438 | 1439 | 1440 | 1441 | 1442 | Sets the list of recently visited folders in the file dialog for this project. 1443 | 1444 | 1445 | 1446 | 1447 | Returns the list of recently visited folders in the file dialog for this project. 1448 | 1449 | 1450 | 1451 | 1452 | Custom gizmo that is used for providing custom visualization and editing (handles) for 3D Spatial objects. See for more information. 1453 | 1454 | 1455 | 1456 | 1457 | Commit a handle being edited (handles must have been previously added by ). 1458 | If the cancel parameter is true, an option to restore the edited value to the original is provided. 1459 | 1460 | 1461 | 1462 | 1463 | Gets the name of an edited handle (handles must have been previously added by ). 1464 | Handles can be named for reference to the user when editing. 1465 | 1466 | 1467 | 1468 | 1469 | Gets actual value of a handle. This value can be anything and used for eventually undoing the motion when calling . 1470 | 1471 | 1472 | 1473 | 1474 | Returns true if the handle at index index is highlighted by being hovered with the mouse. 1475 | 1476 | 1477 | 1478 | 1479 | This function is called when the this gizmo refers to changes (the is called). 1480 | 1481 | 1482 | 1483 | 1484 | This function is used when the user drags a gizmo handle (previously added with ) in screen coordinates. 1485 | The is also provided so screen coordinates can be converted to raycasts. 1486 | 1487 | 1488 | 1489 | 1490 | Adds lines to the gizmo (as sets of 2 points), with a given material. The lines are used for visualizing the gizmo. Call this function during . 1491 | 1492 | If the parameter is null, then the default value is new Color(1, 1, 1, 1) 1493 | 1494 | 1495 | 1496 | Adds a mesh to the gizmo with the specified billboard state, skeleton and material. If billboard is true, the mesh will rotate to always face the camera. Call this function during . 1497 | 1498 | 1499 | 1500 | 1501 | Adds the specified segments to the gizmo's collision shape for picking. Call this function during . 1502 | 1503 | 1504 | 1505 | 1506 | Adds collision triangles to the gizmo for picking. A can be generated from a regular too. Call this function during . 1507 | 1508 | 1509 | 1510 | 1511 | Adds an unscaled billboard for visualization. Call this function during . 1512 | 1513 | If the parameter is null, then the default value is new Color(1, 1, 1, 1) 1514 | 1515 | 1516 | 1517 | Adds a list of handles (points) which can be used to deform the object being edited. 1518 | There are virtual functions which will be called upon editing of these handles. Call this function during . 1519 | 1520 | 1521 | 1522 | 1523 | Sets the reference node for the gizmo. node must inherit from . 1524 | 1525 | 1526 | 1527 | 1528 | Returns the Spatial node associated with this gizmo. 1529 | 1530 | 1531 | 1532 | 1533 | Returns the that owns this gizmo. It's useful to retrieve materials using . 1534 | 1535 | 1536 | 1537 | 1538 | Removes everything in the gizmo including meshes, collisions and handles. 1539 | 1540 | 1541 | 1542 | 1543 | Sets the gizmo's hidden state. If true, the gizmo will be hidden. If false, it will be shown. 1544 | 1545 | 1546 | 1547 | 1548 | EditorSpatialGizmoPlugin allows you to define a new type of Gizmo. There are two main ways to do so: extending for the simpler gizmos, or creating a new type. See the tutorial in the documentation for more info. 1549 | 1550 | 1551 | 1552 | 1553 | Override this method to define whether the gizmo can be hidden or not. Returns true if not overridden. 1554 | 1555 | 1556 | 1557 | 1558 | Override this method to commit gizmo handles. Called for this plugin's active gizmos. 1559 | 1560 | 1561 | 1562 | 1563 | Override this method to return a custom for the spatial nodes of your choice, return null for the rest of nodes. See also . 1564 | 1565 | 1566 | 1567 | 1568 | Override this method to provide gizmo's handle names. Called for this plugin's active gizmos. 1569 | 1570 | 1571 | 1572 | 1573 | Gets actual value of a handle from gizmo. Called for this plugin's active gizmos. 1574 | 1575 | 1576 | 1577 | 1578 | Override this method to provide the name that will appear in the gizmo visibility menu. 1579 | 1580 | 1581 | 1582 | 1583 | Override this method to set the gizmo's priority. Higher values correspond to higher priority. If a gizmo with higher priority conflicts with another gizmo, only the gizmo with higher priority will be used. 1584 | All built-in editor gizmos return a priority of -1. If not overridden, this method will return 0, which means custom gizmos will automatically override built-in gizmos. 1585 | 1586 | 1587 | 1588 | 1589 | Override this method to define which Spatial nodes have a gizmo from this plugin. Whenever a node is added to a scene this method is called, if it returns true the node gets a generic assigned and is added to this plugin's list of active gizmos. 1590 | 1591 | 1592 | 1593 | 1594 | Gets whether a handle is highlighted or not. Called for this plugin's active gizmos. 1595 | 1596 | 1597 | 1598 | 1599 | Override this method to define whether Spatial with this gizmo should be selecteble even when the gizmo is hidden. 1600 | 1601 | 1602 | 1603 | 1604 | Callback to redraw the provided gizmo. Called for this plugin's active gizmos. 1605 | 1606 | 1607 | 1608 | 1609 | Update the value of a handle after it has been updated. Called for this plugin's active gizmos. 1610 | 1611 | 1612 | 1613 | 1614 | Creates an unshaded material with its variants (selected and/or editable) and adds them to the internal material list. They can then be accessed with and used in and . Should not be overridden. 1615 | 1616 | 1617 | 1618 | 1619 | Creates an icon material with its variants (selected and/or editable) and adds them to the internal material list. They can then be accessed with and used in . Should not be overridden. 1620 | 1621 | If the parameter is null, then the default value is new Color(1, 1, 1, 1) 1622 | 1623 | 1624 | 1625 | Creates a handle material with its variants (selected and/or editable) and adds them to the internal material list. They can then be accessed with and used in . Should not be overridden. 1626 | 1627 | 1628 | 1629 | 1630 | Adds a new material to the internal material list for the plugin. It can then be accessed with . Should not be overridden. 1631 | 1632 | 1633 | 1634 | 1635 | Gets material from the internal list of materials. If an is provided, it will try to get the corresponding variant (selected and/or editable). 1636 | 1637 | 1638 | 1639 | 1640 | Used by the editor to display VCS extracted information in the editor. The implementation of this API is included in VCS addons, which are essentially GDNative plugins that need to be put into the project folder. These VCS addons are scripts which are attached (on demand) to the object instance of EditorVCSInterface. All the functions listed below, instead of performing the task themselves, they call the internally defined functions in the VCS addons to provide a plug-n-play experience. 1641 | 1642 | 1643 | 1644 | 1645 | Returns true if the addon is ready to respond to function calls, else returns false. 1646 | 1647 | 1648 | 1649 | 1650 | Initializes the VCS addon if not already. Uses the argument value as the path to the working directory of the project. Creates the initial commit if required. Returns true if no failure occurs, else returns false. 1651 | 1652 | 1653 | 1654 | 1655 | Returns true if the VCS addon has been initialized, else returns false. 1656 | 1657 | 1658 | 1659 | 1660 | Returns a containing the path of the detected file change mapped to an integer signifying what kind of a change the corresponding file has experienced. 1661 | The following integer values are being used to signify that the detected file is: 1662 | - 0: New to the VCS working directory 1663 | - 1: Modified 1664 | - 2: Renamed 1665 | - 3: Deleted 1666 | - 4: Typechanged 1667 | 1668 | 1669 | 1670 | 1671 | Stages the file which should be committed when is called. Argument should contain the absolute path. 1672 | 1673 | 1674 | 1675 | 1676 | Unstages the file which was staged previously to be committed, so that it is no longer committed when is called. Argument should contain the absolute path. 1677 | 1678 | 1679 | 1680 | 1681 | Creates a version commit if the addon is initialized, else returns without doing anything. Uses the files which have been staged previously, with the commit message set to a value as provided as in the argument. 1682 | 1683 | 1684 | 1685 | 1686 | Returns an of objects containing the diff output from the VCS in use, if a VCS addon is initialized, else returns an empty object. The diff contents also consist of some contextual lines which provide context to the observed line change in the file. 1687 | Each object has the line diff contents under the keys: 1688 | - "content" to store a containing the line contents 1689 | - "status" to store a which contains "+" in case the content is a line addition but it stores a "-" in case of deletion and an empty string in the case the line content is neither an addition nor a deletion. 1690 | - "new_line_number" to store an integer containing the new line number of the line content. 1691 | - "line_count" to store an integer containing the number of lines in the line content. 1692 | - "old_line_number" to store an integer containing the old line number of the line content. 1693 | - "offset" to store the offset of the line change since the first contextual line content. 1694 | 1695 | 1696 | 1697 | 1698 | Shuts down the VCS addon to allow cleanup code to run on call. Returns true is no failure occurs, else returns false. 1699 | 1700 | 1701 | 1702 | 1703 | Returns the project name of the VCS working directory. 1704 | 1705 | 1706 | 1707 | 1708 | Returns the name of the VCS if the VCS has been initialized, else return an empty string. 1709 | 1710 | 1711 | 1712 | 1713 | The creates script files according to a given template for a given scripting language. The standard use is to configure its fields prior to calling one of the methods. 1714 | 1715 | func _ready(): 1716 | dialog.config("Node", "res://new_node.gd") # For in-engine types 1717 | dialog.config("\"res://base_node.gd\"", "res://derived_node.gd") # For script types 1718 | dialog.popup_centered() 1719 | 1720 | 1721 | 1722 | 1723 | 1724 | Prefills required fields to configure the ScriptCreateDialog for use. 1725 | 1726 | 1727 | 1728 | 1729 | Note: This class shouldn't be instantiated directly. Instead, access the singleton using . 1730 | 1731 | 1732 | 1733 | 1734 | Goes to the specified line in the current script. 1735 | 1736 | 1737 | 1738 | 1739 | Returns a that is currently active in editor. 1740 | 1741 | 1742 | 1743 | 1744 | Returns an array with all objects which are currently open in editor. 1745 | 1746 | 1747 | 1748 | 1749 | Opens the script create dialog. The script will extend base_name. The file extension can be omitted from base_path. It will be added based on the selected scripting language. 1750 | 1751 | 1752 | 1753 | 1754 | Add a custom Visual Script node to the editor. It'll be placed under "Custom Nodes" with the category as the parameter. 1755 | 1756 | 1757 | 1758 | 1759 | Remove a custom Visual Script node from the editor. Custom nodes already placed on scripts won't be removed. 1760 | 1761 | 1762 | 1763 | 1764 | -------------------------------------------------------------------------------- /.mono/assemblies/Debug/api_hash_cache.cfg: -------------------------------------------------------------------------------- 1 | [core] 2 | 3 | modified_time=1612667064 4 | bindings_version=13 5 | cs_glue_version=1611786528 6 | api_hash=2774521799279745730 7 | 8 | [editor] 9 | 10 | modified_time=1612667064 11 | bindings_version=13 12 | cs_glue_version=1611786528 13 | api_hash=5227283776401907609 14 | -------------------------------------------------------------------------------- /Godot_Rollback.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValorZard/Godot-Rollback-Netcode---Example/4e0b5f079a341b89de6620b9a5f8e816659fc15c/Godot_Rollback.exe -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Srayan Jana 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 | # Godot Rollback Netcode 2 | 3 | This is an example of Rollback Netcode implemented in Godot with GDScript and Universal Plug and Play. 4 | No port forwarding required! 5 | 6 | If you want a more production ready version of this netcode, complete with WebRTC support and possible steam integration, then I suggest you check out tthis plugin by Snopek Games: https://gitlab.com/snopek-games/godot-rollback-netcode/ 7 | 8 | Major thanks to @SpiceyWolf for contributing to this project. 9 | 10 | **PULL REQUESTS WELCOME** 11 | 12 | Explanation of Rollback Netcode: https://ki.infil.net/w02-netcode.html 13 | 14 | Basic Rollback Netcode Algorithm: https://gist.github.com/rcmagic/f8d76bca32b5609e85ab156db38387e9 15 | 16 | Deeper Explanation (using code): http://blog.hypersect.com/rollback-networking-in-inversus/ 17 | ## Local Testing 18 | 19 | For testing on a single computer, you will want to have two different clients open at the same time. Make sure that they are both set to Local and make sure only **ONE** is set as Player One. 20 | 21 | ## Further Research 22 | 23 | - Figure out how to do this with WebRTC 24 | - Add more players 25 | - Integrate fixed point libraries 26 | - Current plan is to create a fork of Box2D with the C++ Library fpm to make it deterministic and then make that a Godot module. 27 | - Alternatively, we could use [this library](https://github.com/91Act/box2d_fixed) that's already a fork of Box2D thats determinisitic 28 | - Biggest problem is figuring out how to add custom phsyics to Godot 29 | 30 | # Resources 31 | 32 | ## General Game Stuff 33 | 34 | Introduction to State Machines:https://gameprogrammingpatterns.com/state.html 35 | 36 | ## General Networking: 37 | 38 | Networked Physics:https://www.gafferongames.com/post/introduction_to_networked_physics/ 39 | 40 | Also the full website is pretty cool (General Networking Bible)https://gafferongames.com/ 41 | 42 | Master list of resources: https://github.com/MFatihMAR/Game-Networking-Resources 43 | 44 | ## Godot Networking: 45 | 46 | Basic multiplayer documentation: https://docs.godotengine.org/en/stable/tutorials/networking/high_level_multiplayer.html 47 | 48 | Multiplayer Tutorials: Used to build the prototype[Files · master · menip / Godot Multiplayer Tutorials · GitLab](https://gitlab.com/menip/godot-multiplayer-tutorials/-/tree/master) 49 | 50 | First video in a series on how to do dedicated servers in Godot.[Godot Multiplayer Server-Client Tutorial | Godot Dedicated Server #1 - YouTube](https://www.youtube.com/watch?v=lnFN6YabFKg) 51 | 52 | Very minimalistic tutorial on how to set up a godot dedicated server.https://mrminimal.gitlab.io/2018/07/26/godot-dedicated-server-tutorial.html 53 | 54 | NAT Holepunching Plugin: https://github.com/SLGamesCregg/HolePuncher 55 | 56 | ## General Godot Stuff: 57 | 58 | How to format your gdscript code: https://github.com/Scony/godot-gdscript-toolkit 59 | 60 | How to do camera on multiple people:https://www.youtube.com/watch?v=W7WsL3qaPqg 61 | 62 | ## FPS Networking: 63 | 64 | Source Engine Networking (Valve):https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking 65 | 66 | Overwatch Networking:https://www.youtube.com/watch?v=W3aieHjyNvw 67 | 68 | Halo Reach Networking:https://www.youtube.com/watch?v=h47zZrqjgLc 69 | 70 | Quake Cheats: http://www.catb.org/esr/writings/quake-cheats.html 71 | 72 | Tribes Networking Model:https://www.gamedevs.org/uploads/tribes-networking-model.pdf 73 | 74 | Extrapoliation/Dead Reckoning:https://www.gabrielgambetta.com/entity-interpolation.html 75 | 76 | MMO Networking (I Think): http://ithare.com/contents-of-development-and-deployment-of-massively-multiplayer-games-from-social-games-to-mmofps-with-stock-exchanges-in-between/ 77 | -------------------------------------------------------------------------------- /Scenes/Game.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://Scenes/Gui.tscn" type="PackedScene" id=1] 4 | [ext_resource path="res://Scenes/Prefab/Arena.tscn" type="PackedScene" id=2] 5 | 6 | [node name="Game" type="Node"] 7 | 8 | [node name="Gui" parent="." instance=ExtResource( 1 )] 9 | 10 | [node name="Arena" parent="." instance=ExtResource( 2 )] 11 | -------------------------------------------------------------------------------- /Scenes/Gui.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://Scripts/Gui.gd" type="Script" id=1] 4 | 5 | [node name="Gui" type="CanvasLayer"] 6 | script = ExtResource( 1 ) 7 | 8 | [node name="Status" type="Label" parent="."] 9 | anchor_top = 1.0 10 | anchor_right = 1.0 11 | anchor_bottom = 1.0 12 | margin_top = -45.0 13 | margin_bottom = -15.0 14 | rect_pivot_offset = Vector2( -99, -430 ) 15 | text = "WAITING FOR CONNECTION TO PEER" 16 | align = 1 17 | __meta__ = { 18 | "_edit_use_anchors_": false 19 | } 20 | 21 | [node name="Details" type="Label" parent="."] 22 | anchor_top = 1.0 23 | anchor_right = 1.0 24 | anchor_bottom = 1.0 25 | margin_top = -45.0 26 | margin_bottom = -15.0 27 | rect_pivot_offset = Vector2( -99, -430 ) 28 | align = 1 29 | valign = 2 30 | __meta__ = { 31 | "_edit_use_anchors_": false 32 | } 33 | 34 | [node name="Menu" type="Panel" parent="."] 35 | margin_right = 1024.0 36 | margin_bottom = 600.0 37 | __meta__ = { 38 | "_edit_use_anchors_": false 39 | } 40 | 41 | [node name="Align" type="VBoxContainer" parent="Menu"] 42 | pause_mode = 2 43 | anchor_left = 0.5 44 | anchor_top = 0.5 45 | anchor_right = 0.5 46 | anchor_bottom = 0.5 47 | margin_left = -150.0 48 | margin_top = -53.5 49 | margin_right = 150.0 50 | margin_bottom = 53.5 51 | size_flags_vertical = 9 52 | custom_constants/separation = 7 53 | __meta__ = { 54 | "_edit_use_anchors_": false 55 | } 56 | 57 | [node name="Grid" type="GridContainer" parent="Menu/Align"] 58 | margin_right = 300.0 59 | margin_bottom = 192.0 60 | custom_constants/vseparation = 7 61 | columns = 2 62 | 63 | [node name="lblAddress" type="Label" parent="Menu/Align/Grid"] 64 | margin_right = 148.0 65 | margin_bottom = 14.0 66 | size_flags_horizontal = 3 67 | text = "Address:" 68 | __meta__ = { 69 | "_edit_use_anchors_": false 70 | } 71 | 72 | [node name="txtAddress" type="TextEdit" parent="Menu/Align/Grid"] 73 | margin_left = 152.0 74 | margin_right = 300.0 75 | margin_bottom = 14.0 76 | size_flags_horizontal = 3 77 | text = "127.0.0.1" 78 | __meta__ = { 79 | "_edit_use_anchors_": false 80 | } 81 | 82 | [node name="lblFrameDelay" type="Label" parent="Menu/Align/Grid"] 83 | margin_top = 21.0 84 | margin_right = 148.0 85 | margin_bottom = 35.0 86 | text = "Frame Delay:" 87 | __meta__ = { 88 | "_edit_use_anchors_": false 89 | } 90 | 91 | [node name="txtFrameDelay" type="TextEdit" parent="Menu/Align/Grid"] 92 | margin_left = 152.0 93 | margin_top = 21.0 94 | margin_right = 300.0 95 | margin_bottom = 35.0 96 | text = "1" 97 | __meta__ = { 98 | "_edit_use_anchors_": false 99 | } 100 | 101 | [node name="lblRollback" type="Label" parent="Menu/Align/Grid"] 102 | margin_top = 42.0 103 | margin_right = 148.0 104 | margin_bottom = 56.0 105 | text = "Rollback Frames:" 106 | __meta__ = { 107 | "_edit_use_anchors_": false 108 | } 109 | 110 | [node name="txtRollback" type="TextEdit" parent="Menu/Align/Grid"] 111 | margin_left = 152.0 112 | margin_top = 42.0 113 | margin_right = 300.0 114 | margin_bottom = 56.0 115 | text = "20" 116 | __meta__ = { 117 | "_edit_use_anchors_": false 118 | } 119 | 120 | [node name="lblDupRange" type="Label" parent="Menu/Align/Grid"] 121 | margin_top = 63.0 122 | margin_right = 148.0 123 | margin_bottom = 77.0 124 | text = "Dup Send Range:" 125 | __meta__ = { 126 | "_edit_use_anchors_": false 127 | } 128 | 129 | [node name="txtDupRange" type="TextEdit" parent="Menu/Align/Grid"] 130 | margin_left = 152.0 131 | margin_top = 63.0 132 | margin_right = 300.0 133 | margin_bottom = 77.0 134 | text = "16" 135 | __meta__ = { 136 | "_edit_use_anchors_": false 137 | } 138 | 139 | [node name="lblPort" type="Label" parent="Menu/Align/Grid"] 140 | margin_top = 84.0 141 | margin_right = 148.0 142 | margin_bottom = 98.0 143 | text = "Port:" 144 | 145 | [node name="txtPort" type="TextEdit" parent="Menu/Align/Grid"] 146 | margin_left = 152.0 147 | margin_top = 84.0 148 | margin_right = 300.0 149 | margin_bottom = 98.0 150 | text = "5678" 151 | 152 | [node name="lblIsLocal" type="Label" parent="Menu/Align/Grid"] 153 | margin_top = 118.0 154 | margin_right = 148.0 155 | margin_bottom = 132.0 156 | text = "Is Local:" 157 | 158 | [node name="checkIsLocal" type="CheckButton" parent="Menu/Align/Grid"] 159 | margin_left = 152.0 160 | margin_top = 105.0 161 | margin_right = 300.0 162 | margin_bottom = 145.0 163 | 164 | [node name="lblPlayerNum" type="Label" parent="Menu/Align/Grid"] 165 | margin_top = 156.0 166 | margin_right = 148.0 167 | margin_bottom = 187.0 168 | text = "Is Player One: 169 | (Local Only)" 170 | __meta__ = { 171 | "_edit_use_anchors_": false 172 | } 173 | 174 | [node name="checkIsPlayerOne" type="CheckButton" parent="Menu/Align/Grid"] 175 | margin_left = 152.0 176 | margin_top = 152.0 177 | margin_right = 300.0 178 | margin_bottom = 192.0 179 | __meta__ = { 180 | "_edit_use_anchors_": false 181 | } 182 | 183 | [node name="btnPlay" type="Button" parent="Menu/Align"] 184 | margin_top = 199.0 185 | margin_right = 300.0 186 | margin_bottom = 219.0 187 | text = "Play" 188 | __meta__ = { 189 | "_edit_use_anchors_": false 190 | } 191 | 192 | [connection signal="pressed" from="Menu/Align/btnPlay" to="." method="_on_play_pressed"] 193 | -------------------------------------------------------------------------------- /Scenes/Prefab/Arena.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://Scenes/Prefab/Player.tscn" type="PackedScene" id=1] 4 | [ext_resource path="res://Scenes/Prefab/Wall.tscn" type="PackedScene" id=2] 5 | 6 | [node name="Arena" type="Node2D"] 7 | 8 | [node name="WallN" parent="." instance=ExtResource( 2 )] 9 | position = Vector2( 513, 30 ) 10 | scale = Vector2( 50, 1 ) 11 | 12 | [node name="WallS" parent="." instance=ExtResource( 2 )] 13 | position = Vector2( 512, 570 ) 14 | scale = Vector2( 50, 1 ) 15 | 16 | [node name="WallE" parent="." instance=ExtResource( 2 )] 17 | position = Vector2( 984, 300 ) 18 | scale = Vector2( 4, 10 ) 19 | 20 | [node name="WallW" parent="." instance=ExtResource( 2 )] 21 | position = Vector2( 40, 300 ) 22 | scale = Vector2( 4, 10 ) 23 | 24 | [node name="Objects" type="Node" parent="."] 25 | 26 | [node name="Player" parent="Objects" instance=ExtResource( 1 )] 27 | position = Vector2( 512, 490 ) 28 | 29 | [node name="Enemy" parent="Objects" instance=ExtResource( 1 )] 30 | position = Vector2( 512, 110 ) 31 | enemy = true 32 | -------------------------------------------------------------------------------- /Scenes/Prefab/Player.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://Scripts/Player.gd" type="Script" id=1] 4 | 5 | [sub_resource type="RectangleShape2D" id=1] 6 | extents = Vector2( 40, 40 ) 7 | 8 | [node name="Player" type="KinematicBody2D"] 9 | collision_mask = 2 10 | script = ExtResource( 1 ) 11 | 12 | [node name="Skin" type="Polygon2D" parent="."] 13 | scale = Vector2( 2, 2 ) 14 | polygon = PoolVector2Array( 20, 0, 20, -20, -20, -20, -20, 20, 20, 20 ) 15 | 16 | [node name="Collider" type="CollisionShape2D" parent="."] 17 | shape = SubResource( 1 ) 18 | 19 | [node name="Count" type="Label" parent="."] 20 | margin_right = 40.0 21 | margin_bottom = 36.0 22 | custom_colors/font_color = Color( 0, 0, 0, 1 ) 23 | __meta__ = { 24 | "_edit_use_anchors_": false 25 | } 26 | -------------------------------------------------------------------------------- /Scenes/Prefab/Wall.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [sub_resource type="RectangleShape2D" id=1] 4 | extents = Vector2( 10, 30 ) 5 | 6 | [node name="Wall" type="StaticBody2D"] 7 | collision_layer = 2 8 | collision_mask = 0 9 | 10 | [node name="Skin" type="Polygon2D" parent="."] 11 | color = Color( 0.0823529, 0.0823529, 0.0823529, 1 ) 12 | polygon = PoolVector2Array( -10, -30, 10, -30, 10, 30, -10, 30 ) 13 | 14 | [node name="Collider" type="CollisionShape2D" parent="."] 15 | shape = SubResource( 1 ) 16 | -------------------------------------------------------------------------------- /Scripts/Gui.gd: -------------------------------------------------------------------------------- 1 | class_name Gui 2 | extends Node 3 | 4 | onready var lblStatus: Label = $Status 5 | 6 | # Track changes, otherwise do nothing (Saves performance on modifying values) 7 | onready var curStatus: int = Session.StatusType.MENU 8 | 9 | func _physics_process(delta: float) -> void: 10 | # Check if anything has changed 11 | if curStatus == Session.Status: return 12 | 13 | # Set status message once 14 | curStatus = Session.Status 15 | match curStatus: 16 | Session.StatusType.WAITING: lblStatus.text = "WAITING FOR CONNECTION TO PEER" 17 | Session.StatusType.PLAYING: lblStatus.text = "CONNECTED TO PEER" 18 | Session.StatusType.END: lblStatus.text = "THE GAME HAS ENDED. DISCONNECTED FROM PEER" 19 | Session.StatusType.MENU: pass 20 | 21 | func _on_play_pressed() -> void: 22 | var address: String = $Menu/Align/Grid/txtAddress.text 23 | var frameDelay: int = int($Menu/Align/Grid/txtFrameDelay.text) 24 | var rollbackFrames: int = int($Menu/Align/Grid/txtRollback.text) 25 | var duplicateRange: int = int($Menu/Align/Grid/txtDupRange.text) 26 | var port: int = int($Menu/Align/Grid/txtPort.text) 27 | 28 | var isLocal: bool = $Menu/Align/Grid/checkIsLocal.pressed 29 | var isPlayerOne: bool = $Menu/Align/Grid/checkIsPlayerOne.pressed 30 | 31 | Session.Address = address 32 | Session.InputDelay = frameDelay 33 | Session.Rollback = rollbackFrames 34 | Session.DupSendRange = duplicateRange 35 | Session.Port = port 36 | 37 | Session.IsLocal = isLocal 38 | Session.IsPlayerOne = isPlayerOne 39 | 40 | Session.ready() 41 | 42 | # Set state to a valid game state 43 | Session.Status = Session.StatusType.WAITING 44 | 45 | $Menu.visible = false 46 | -------------------------------------------------------------------------------- /Scripts/Player.gd: -------------------------------------------------------------------------------- 1 | class_name Player 2 | extends KinematicBody2D 3 | 4 | onready var colExtents = $Collider.shape.get_extents() # Assuming rect shaped 5 | onready var colMask = Rect2(position - colExtents, colExtents * 2) 6 | onready var lblCount = $Count 7 | 8 | export var enemy: bool = false 9 | 10 | const SPEED: int = 7 11 | var count: int = -1 # Testing value 12 | var vCount: int = count 13 | 14 | # Just to set a unique color 15 | func _ready() -> void: 16 | if enemy: $Skin.color = Color.red 17 | else: $Skin.color = Color.blue 18 | 19 | # Reset object state to that in a given game_state, executed once per rollback 20 | func reset_state(game_state : Dictionary) -> void: 21 | # Check if this object exists within the checked game_state 22 | if not game_state.has(name): 23 | # free() # Delete from memory 24 | return 25 | 26 | position.x = game_state[name].x 27 | position.y = game_state[name].y 28 | vCount = game_state[name].count 29 | colMask = game_state[name].colMask 30 | 31 | func frame_start() -> void: 32 | # Set update vars to current values 33 | vCount = count 34 | update_collision_mask() 35 | 36 | func input_update(input: Session.InputDef, game_state : Dictionary) -> void: 37 | # Calculate movement on input 38 | var motion = Vector2(0, 0) 39 | for object in game_state: 40 | if object != name and colMask.intersects(game_state[object].colMask): 41 | vCount += 1 42 | 43 | if not enemy: # Is a Player 44 | if Session.IsDir(input.Player, Session.InputType.U): motion.y -= SPEED 45 | if Session.IsDir(input.Player, Session.InputType.L): motion.x -= SPEED 46 | if Session.IsDir(input.Player, Session.InputType.R): motion.x += SPEED 47 | if Session.IsDir(input.Player, Session.InputType.D): motion.y += SPEED 48 | if Session.IsDir(input.Player, Session.InputType.SEL): vCount /= 2 49 | else: # Reverse enemy input (simulate mirrored screen) 50 | if Session.IsDir(input.Enemy, Session.InputType.U): motion.y += SPEED 51 | if Session.IsDir(input.Enemy, Session.InputType.L): motion.x += SPEED 52 | if Session.IsDir(input.Enemy, Session.InputType.R): motion.x -= SPEED 53 | if Session.IsDir(input.Enemy, Session.InputType.D): motion.y -= SPEED 54 | if Session.IsDir(input.Enemy, Session.InputType.SEL): vCount /= 2 55 | 56 | # move_and_collide for "solid" stationary objects 57 | var collision = move_and_collide(motion) 58 | if collision: 59 | motion = motion.slide(collision.normal) 60 | move_and_collide(motion) 61 | 62 | update_collision_mask() 63 | 64 | func execute() -> void: 65 | # Execute calculated state of object for current frame 66 | count = vCount 67 | lblCount.text = str(count) 68 | 69 | func get_state() -> Dictionary: 70 | # Return dict of state variables to be stored in Frame_States 71 | return {'x': position.x, 'y': position.y, 'count': vCount, 'colMask': colMask} 72 | 73 | func update_collision_mask() -> void: 74 | colMask = Rect2(position - colExtents, colExtents * 2) 75 | -------------------------------------------------------------------------------- /Scripts/Session.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | ###################################### 4 | ###################################### 5 | ### 6 | ### Session Handlers 7 | ### 8 | ############################## 9 | ############################## 10 | 11 | # Debug elements/variables 12 | var IsLocal : bool = false 13 | var IsPlayerOne : bool = false 14 | 15 | # Subscene where players are stored 16 | onready var lblDetails: Label = get_tree().root.get_node("Game/Gui/Details") 17 | onready var objects = get_tree().root.get_node("Game/Arena/Objects") 18 | 19 | 20 | # Modifiable settings 21 | var InputDelay : int = 1 # Amount of input delay in frames 22 | var Rollback : int = 20 # Frame history (Max-Rollback length) 23 | var DupSendRange :int = 16 # Input history to resend (in frames) from past frames 24 | 25 | # Tracks current game status 26 | enum StatusType { WAITING, PLAYING, END, MENU } 27 | var Status = StatusType.MENU 28 | 29 | # Input Trackers 30 | var bufHistory = [] # 256 boolean array, tracks the input history 31 | var StateQueue = [] # Queue for Frame_States of past frames (for Rollback) 32 | var InputReceived = [] # 256 boolean array, tracks if networked inputs for a given frame have arrived 33 | var InputCanRequest = [] # 256 boolean array, tracks if local inputs for a given frame are viable to be sent by request 34 | var InputsHandled = [] # Bool array to compare input arrivals between current frame and previous frame 35 | 36 | var CurFrame : int = 0 # Ranges between 0-255 per circular input array cycle (cycle is every 256 frames) 37 | 38 | ###################################### 39 | ### Input Definitions 40 | ############################## 41 | 42 | enum InputType { U, D, L, R, SEL } 43 | 44 | func SetDir(value: int, dir: int) -> int: 45 | return value | (1 << dir) 46 | 47 | func IsDir(value: int, dir: int) -> bool: 48 | return value & (1 << dir) != 0 49 | 50 | class InputDef: # Single frame of input from both sides 51 | var Player: int 52 | var Enemy: int 53 | 54 | class FrameStateDef: 55 | var State: Dictionary # Dictionary holds the values need for tracking a game's state at a given frame. Keys are child names. 56 | var Frame: int # Frame number according to 256 frame cycle number 57 | var Player: int = 0 58 | var Enemy: int = 0 59 | var Predicted: bool = true # If networked input has not been received yet 60 | 61 | # State keys are child names, values are their individual state dictionaries 62 | # states: Keys are state vars of the children (e.g. x, y), values are the var values 63 | func _init(state : Dictionary, frame : int, player : int, enemy : int, predicted : bool): 64 | self.State = state # Dictionary of dictionaries 65 | self.Frame = frame 66 | self.Player = player 67 | self.Enemy = enemy 68 | self.Predicted = predicted 69 | 70 | ###################################### 71 | ### Core Functions 72 | ############################## 73 | 74 | func _notification(what): 75 | if (what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST): 76 | Send_GameEnd() 77 | get_tree().quit() 78 | 79 | func ready(): 80 | # Disable auto-close from game window 81 | get_tree().set_auto_accept_quit(false) 82 | 83 | # Initialize arrays 84 | for _i in range (0, 256): 85 | bufHistory.append(InputDef.new()) 86 | InputReceived.append(false) 87 | InputCanRequest.append(false) 88 | 89 | # Initialize state queue 90 | for _i in range (0, Rollback): 91 | # Empty local input, empty net input, frame 0, inital game state, treat initial empty inputs as true, actual inputs 92 | StateQueue.append(FrameStateDef.new(get_game_state(), 0, 0, 0, true)) 93 | 94 | for i in range (1, Rollback + 100): 95 | InputsHandled.append(true) 96 | InputReceived[-i] = true # For initialization, pretend all "previous" inputs arrived 97 | 98 | for i in range (0, InputDelay): 99 | InputReceived[i] = true # Assume empty inputs at game start InputDelay window 100 | InputCanRequest[i] = true 101 | 102 | NetReceived = false # Network thread will set to true when a networked player is found 103 | 104 | ########################### 105 | ## Network Initialization 106 | ############ 107 | 108 | peer.set_broadcast_enabled(true) 109 | upnp.discover() 110 | upnp.add_port_mapping(Port) 111 | 112 | # Connecting to end-user 113 | if IsLocal: 114 | # When testing locally, we have to make the clients listen on different ports 115 | if IsPlayerOne: 116 | peer.listen(5000, "*") 117 | peer.set_dest_address(Address, 5001) 118 | else: 119 | peer.listen(5001, "*") 120 | peer.set_dest_address(Address, 5000) 121 | else: 122 | peer.listen(Port, "*") 123 | peer.set_dest_address(Address, Port) 124 | 125 | # Link function pointers to packet headers 126 | BindPacketList() 127 | 128 | # Fire receiving thread 129 | NetThread = Thread.new() 130 | NetThread.start(self, "_receive_data", null, 2) 131 | 132 | func _physics_process(_delta): 133 | if Status == StatusType.MENU: return 134 | 135 | NetMutex.lock() 136 | if (NetReceived): 137 | # If the oldest FrameState in the queue is guessed, 138 | # but the input_queue Input does not yet contain an actual 139 | # input for the oldest FrameState's Frame, then DELAY 140 | if StateQueue[0].Predicted and not InputReceived[StateQueue[0].Frame]: 141 | NetReceived = false # Wait until actual net input is received for guessed oldest FrameState 142 | NetMutex.unlock() 143 | Send_InputRequest(CurFrame) # Send request for needed input 144 | lblDetails.text = "DELAY: Waiting for net input. CurFrame: " + str(CurFrame) 145 | else: 146 | NetMutex.unlock() 147 | lblDetails.text = "" 148 | handle_input() 149 | else: 150 | NetMutex.unlock() 151 | if (Status == StatusType.WAITING): # Search for networked player 152 | Send_GameStart(StatusType.WAITING) # Send ready handshake to opponent 153 | else: # Send request for needed inputs for past frames 154 | Send_InputRequest((CurFrame + 1) % 256) # Send request for needed input 155 | 156 | func handle_input(): # Get input, Rollback if necessary, implement inputs 157 | var pre_game_state = get_game_state() 158 | var predicted: bool = false 159 | var start_rollback: bool = false 160 | 161 | var current_input = null 162 | var current_frame_arrival_array = [] 163 | 164 | var myInput: int = 0 165 | 166 | frame_start_all() # For all children, set their update vars to their current/actual values 167 | 168 | # Record local inputs 169 | if Input.is_action_pressed("ui_up"): myInput = SetDir(myInput, InputType.U) 170 | if Input.is_action_pressed("ui_down"): myInput = SetDir(myInput, InputType.D) 171 | if Input.is_action_pressed("ui_left"): myInput = SetDir(myInput, InputType.L) 172 | if Input.is_action_pressed("ui_right"): myInput = SetDir(myInput, InputType.R) 173 | if Input.is_action_just_pressed("ui_select"): myInput = SetDir(myInput, InputType.SEL) 174 | if Input.is_action_just_pressed("ui_end"): 175 | Send_GameEnd() 176 | Status = StatusType.END 177 | return 178 | 179 | InputMutex.lock() 180 | 181 | bufHistory[(CurFrame + InputDelay) % 256].Player = myInput 182 | 183 | # Send inputs over network 184 | for i in Rollback: 185 | var offset = (CurFrame + InputDelay - i) % 256 186 | Send_ReceiveInput(offset, bufHistory[offset].Player) 187 | 188 | # Get current input arrival boolean values for current frame & old frames eligible for Rollback 189 | for i in range(0, Rollback + 1): 190 | current_frame_arrival_array.push_front(InputReceived[CurFrame - i]) # Oldest frame in front 191 | 192 | InputMutex.unlock() 193 | 194 | # The input from the current frame can now be sent by request 195 | RequestMutex.lock() 196 | InputCanRequest[(CurFrame + InputDelay) % 256] = true 197 | RequestMutex.unlock() 198 | 199 | # Remove current frame's arrival boolean for Rollback condition hash comparison 200 | var current_frame_arrival = current_frame_arrival_array.pop_back() 201 | 202 | # If an input for a past frame has arrived (to fulfill a guess), 203 | if current_frame_arrival_array.hash() != InputsHandled.hash(): 204 | # Iterate through all saved states until the state with the guessed input 205 | # to be replaced by an arrived actual input is found (Rollback will begin with that state) 206 | # then, continue iterating and operating through remaining saved 207 | # states to continue the resimulation process 208 | var state_index = 0 # For tracking iterated element's index in StateQueue 209 | for i in StateQueue: # Index 0 is oldest state 210 | # If an arrived input is for a past frame, 211 | if (InputsHandled[state_index] == false && current_frame_arrival_array[state_index] == true): 212 | 213 | # Set net input in the FrameState from guess to actual input 214 | InputMutex.lock() 215 | i.Enemy = bufHistory[i.Frame].Enemy 216 | InputMutex.unlock() 217 | i.Predicted = false 218 | 219 | # If first Rollback iteration, reset update variables for all children to match Rollback start state 220 | if start_rollback == false: 221 | reset_state_all(i.State) 222 | start_rollback = true 223 | 224 | pre_game_state = get_game_state() 225 | input_update_all(bufHistory[i.Frame], pre_game_state) # Simulate using new true input 226 | 227 | # Continue simulating using currently stored inputs 228 | else: 229 | if start_rollback == true: 230 | pre_game_state = get_game_state() # Save pre-update state value for FrameState 231 | input_update_all(bufHistory[i.Frame], pre_game_state) # Update state using previous input during Rollback resimulation 232 | 233 | if start_rollback == true: 234 | i.State = pre_game_state # Update FrameState with updated state value. 235 | 236 | state_index += 1 237 | 238 | # Reinsert current frame's arrival boolean (for next frame's InputsHandled) 239 | current_frame_arrival_array.push_back(current_frame_arrival) 240 | # Remove oldest frame's arrival boolean (unwanted for next frame's InputsHandled) 241 | current_frame_arrival_array.pop_front() 242 | 243 | current_input = InputDef.new() 244 | InputMutex.lock() 245 | 246 | # If the input for the current frame has not been received 247 | if InputReceived[CurFrame] == false: 248 | # Implement guess of last input used 249 | current_input.Player = bufHistory[CurFrame].Player 250 | current_input.Enemy = bufHistory[CurFrame - 1].Enemy # Guessing with previous frame's input 251 | bufHistory[CurFrame].Enemy = bufHistory[CurFrame - 1].Enemy 252 | 253 | predicted = true 254 | else: # Proceed with actual net input 255 | current_input.Player = bufHistory[CurFrame].Player 256 | current_input.Enemy = bufHistory[CurFrame].Enemy 257 | 258 | InputReceived[CurFrame - (Rollback + 120)] = false # Reset input arrival boolean for old frame 259 | InputMutex.unlock() 260 | 261 | RequestMutex.lock() 262 | InputCanRequest[CurFrame - (Rollback + 120)] = false # Reset viable local input boolean 263 | RequestMutex.unlock() 264 | 265 | if start_rollback == true: 266 | pre_game_state = get_game_state() 267 | 268 | input_update_all(current_input, pre_game_state) # Update with current input 269 | execute_all() # Implement all applied updates/inputs to all child objects 270 | 271 | # Store current frame state into queue 272 | StateQueue.append(FrameStateDef.new(pre_game_state, CurFrame, current_input.Player, current_input.Enemy, predicted)) 273 | 274 | # Remove oldest state 275 | StateQueue.pop_front() 276 | 277 | InputsHandled = current_frame_arrival_array # Store current input arrival array for comaparisons in next frame 278 | CurFrame = (CurFrame + 1)%256 # Increment CurFrame 279 | 280 | func frame_start_all(): 281 | for child in objects.get_children(): 282 | child.frame_start() 283 | 284 | func reset_state_all(state : Dictionary): 285 | for child in objects.get_children(): 286 | child.reset_state(state) 287 | 288 | func input_update_all(input : InputDef, state : Dictionary): 289 | for child in objects.get_children(): 290 | child.input_update(input, state) 291 | 292 | func execute_all(): 293 | for child in objects.get_children(): 294 | child.execute() 295 | 296 | func get_game_state(): 297 | var state = {} 298 | for child in objects.get_children(): 299 | state[child.name] = child.get_state() 300 | return state.duplicate(true) 301 | 302 | 303 | ###################################### 304 | ###################################### 305 | ### 306 | ### Network Handlers 307 | ### 308 | ############################## 309 | ############################## 310 | 311 | enum PacketType { 312 | RECEIVE_INPUT, 313 | INPUT_REQUESTED, 314 | GAME_START, 315 | GAME_END 316 | } 317 | 318 | # Thread Handling 319 | var InputMutex = Mutex.new() 320 | var RequestMutex = Mutex.new() 321 | var NetMutex = Mutex.new() 322 | var NetThread = null # Thread to receive inputs over the network 323 | var NetReceived: bool # Semephore communicate between threads if new inputs have been received 324 | 325 | var Address: String = "127.0.0.1" 326 | var Port: int = 5678 327 | 328 | ###################################### 329 | ### Function Pointers 330 | ############################## 331 | 332 | var Packets = {} 333 | class PacketDef: 334 | signal function(data) 335 | 336 | func _emit(data) -> void: 337 | emit_signal("function", data) 338 | 339 | ###################################### 340 | ### Network Loop 341 | ############################## 342 | 343 | var upnp = UPNP.new() 344 | var peer = PacketPeerUDP.new() 345 | func _receive_data(_userdata): 346 | var data = null 347 | 348 | while(true): 349 | data = peer.get_packet() # Receive a single packet 350 | if data: Packets[data[0]]._emit(data) # Handle Packet 351 | else: peer.wait() # Wait for data to come in 352 | 353 | ###################################### 354 | ### Function Pointer Binder 355 | ############################## 356 | 357 | func BindPacketList(): 358 | # Receive Input 359 | var tmp = PacketDef.new() 360 | tmp.connect("function", self, "HandlePacket_ReceiveInput") 361 | Packets[PacketType.RECEIVE_INPUT] = tmp 362 | 363 | # Input Requested 364 | tmp = PacketDef.new() 365 | tmp.connect("function", self, "HandlePacket_InputRequested") 366 | Packets[PacketType.INPUT_REQUESTED] = tmp 367 | 368 | # Game Starting 369 | tmp = PacketDef.new() 370 | tmp.connect("function", self, "HandlePacket_GameStart") 371 | Packets[PacketType.GAME_START] = tmp 372 | 373 | # Game Ending 374 | tmp = PacketDef.new() 375 | tmp.connect("function", self, "HandlePacket_GameEnd") 376 | Packets[PacketType.GAME_END] = tmp 377 | 378 | ###################################### 379 | ### Function Pointed Handlers 380 | ############################## 381 | 382 | func HandlePacket_ReceiveInput(data) -> void: 383 | # Confirm packet-size integrity 384 | if data.size() != 3: return 385 | 386 | InputMutex.lock() 387 | if InputReceived[data[1]] == false: # If a non-duplicate input arrives for a frame 388 | bufHistory[data[1]].Enemy = data[2] 389 | InputReceived[data[1]] = true 390 | NetMutex.lock() 391 | NetReceived = true 392 | if Status == StatusType.WAITING: 393 | Status = StatusType.PLAYING 394 | NetMutex.unlock() 395 | InputMutex.unlock() 396 | 397 | func HandlePacket_InputRequested(data) -> void: 398 | # Confirm packet-size integrity 399 | if data.size() != 3: return 400 | 401 | var frame = data[1] 402 | RequestMutex.lock() 403 | while (frame != data[2]): # Send inputs for requested frame and newer past frames 404 | if InputCanRequest[frame] == false: 405 | break # Do not send invalid inputs from future frames 406 | Send_ReceiveInput(frame, bufHistory[frame].Player) 407 | frame = (frame + 1)%256 408 | RequestMutex.unlock() 409 | 410 | func HandlePacket_GameStart(data) -> void: 411 | # Confirm packet-size integrity 412 | if data.size() != 2: return 413 | 414 | if Status == StatusType.WAITING: 415 | NetMutex.lock() 416 | Status = StatusType.PLAYING 417 | NetReceived = true 418 | NetMutex.unlock() 419 | 420 | elif (data[1] == StatusType.WAITING): 421 | Send_GameStart(StatusType.PLAYING) 422 | 423 | func HandlePacket_GameEnd(data) -> void: 424 | # Close the port 425 | upnp.delete_port_mapping(Port) 426 | 427 | if Status != StatusType.PLAYING: return 428 | 429 | NetMutex.lock() 430 | Status = StatusType.END 431 | NetMutex.unlock() 432 | 433 | ###################################### 434 | ### Send Packets 435 | ############################## 436 | 437 | func Send_ReceiveInput(frame: int, input: int) -> void: 438 | peer.put_packet( 439 | PoolByteArray([ 440 | PacketType.RECEIVE_INPUT, 441 | frame, 442 | input 443 | ]) 444 | ) 445 | 446 | func Send_InputRequest(frame: int) -> void: 447 | peer.put_packet( 448 | PoolByteArray([ 449 | PacketType.INPUT_REQUESTED, 450 | StateQueue[0].Frame, 451 | frame 452 | ]) 453 | ) 454 | 455 | func Send_GameStart(status: int) -> void: 456 | peer.put_packet( 457 | PoolByteArray([ 458 | PacketType.GAME_START, 459 | status 460 | ]) 461 | ) 462 | 463 | func Send_GameEnd() -> void: 464 | peer.put_packet( 465 | PoolByteArray([ 466 | PacketType.GAME_END 467 | ]) 468 | ) 469 | -------------------------------------------------------------------------------- /export_presets.cfg: -------------------------------------------------------------------------------- 1 | [preset.0] 2 | 3 | name="Windows Desktop" 4 | platform="Windows Desktop" 5 | runnable=true 6 | custom_features="" 7 | export_filter="all_resources" 8 | include_filter="" 9 | exclude_filter="" 10 | export_path="./Godot_Rollback.exe" 11 | script_export_mode=1 12 | script_encryption_key="" 13 | 14 | [preset.0.options] 15 | 16 | custom_template/debug="" 17 | custom_template/release="" 18 | binary_format/64_bits=true 19 | binary_format/embed_pck=true 20 | texture_format/bptc=false 21 | texture_format/s3tc=true 22 | texture_format/etc=false 23 | texture_format/etc2=false 24 | texture_format/no_bptc_fallbacks=true 25 | codesign/enable=false 26 | codesign/identity_type=0 27 | codesign/identity="" 28 | codesign/password="" 29 | codesign/timestamp=true 30 | codesign/timestamp_server_url="" 31 | codesign/digest_algorithm=1 32 | codesign/description="" 33 | codesign/custom_options=PoolStringArray( ) 34 | application/icon="" 35 | application/file_version="" 36 | application/product_version="" 37 | application/company_name="" 38 | application/product_name="" 39 | application/file_description="" 40 | application/copyright="" 41 | application/trademarks="" 42 | 43 | [preset.1] 44 | 45 | name="Mac OSX" 46 | platform="Mac OSX" 47 | runnable=true 48 | custom_features="" 49 | export_filter="all_resources" 50 | include_filter="" 51 | exclude_filter="" 52 | export_path="Builds/Godot_Rollback.zip" 53 | script_export_mode=1 54 | script_encryption_key="" 55 | 56 | [preset.1.options] 57 | 58 | custom_template/debug="" 59 | custom_template/release="" 60 | application/name="" 61 | application/info="Made with Godot Engine" 62 | application/icon="" 63 | application/identifier="" 64 | application/signature="" 65 | application/short_version="1.0" 66 | application/version="1.0" 67 | application/copyright="" 68 | display/high_res=false 69 | privacy/camera_usage_description="" 70 | privacy/microphone_usage_description="" 71 | texture_format/s3tc=true 72 | texture_format/etc=false 73 | texture_format/etc2=false 74 | -------------------------------------------------------------------------------- /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=4 10 | 11 | _global_script_classes=[ { 12 | "base": "Node", 13 | "class": "Gui", 14 | "language": "GDScript", 15 | "path": "res://Scripts/Gui.gd" 16 | }, { 17 | "base": "KinematicBody2D", 18 | "class": "Player", 19 | "language": "GDScript", 20 | "path": "res://Scripts/Player.gd" 21 | } ] 22 | _global_script_class_icons={ 23 | "Gui": "", 24 | "Player": "" 25 | } 26 | 27 | [application] 28 | 29 | config/name="Godot Rollback" 30 | run/main_scene="res://Scenes/Game.tscn" 31 | 32 | [autoload] 33 | 34 | Session="*res://Scripts/Session.gd" 35 | 36 | [debug] 37 | 38 | gdscript/warnings/unsafe_cast=true 39 | 40 | [input] 41 | 42 | ui_accept={ 43 | "deadzone": 0.5, 44 | "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":16777221,"unicode":0,"echo":false,"script":null) 45 | , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777222,"unicode":0,"echo":false,"script":null) 46 | , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null) 47 | ] 48 | } 49 | ui_cancel={ 50 | "deadzone": 0.5, 51 | "events": [ ] 52 | } 53 | ui_focus_next={ 54 | "deadzone": 0.5, 55 | "events": [ ] 56 | } 57 | ui_focus_prev={ 58 | "deadzone": 0.5, 59 | "events": [ ] 60 | } 61 | ui_left={ 62 | "deadzone": 0.5, 63 | "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) 64 | , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null) 65 | , 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) 66 | ] 67 | } 68 | ui_right={ 69 | "deadzone": 0.5, 70 | "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) 71 | , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":15,"pressure":0.0,"pressed":false,"script":null) 72 | , 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) 73 | ] 74 | } 75 | ui_up={ 76 | "deadzone": 0.5, 77 | "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) 78 | , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null) 79 | , 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) 80 | ] 81 | } 82 | ui_down={ 83 | "deadzone": 0.5, 84 | "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":16777234,"unicode":0,"echo":false,"script":null) 85 | , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null) 86 | , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":83,"unicode":0,"echo":false,"script":null) 87 | ] 88 | } 89 | ui_page_up={ 90 | "deadzone": 0.5, 91 | "events": [ ] 92 | } 93 | ui_page_down={ 94 | "deadzone": 0.5, 95 | "events": [ ] 96 | } 97 | ui_home={ 98 | "deadzone": 0.5, 99 | "events": [ ] 100 | } 101 | ui_end={ 102 | "deadzone": 0.5, 103 | "events": [ ] 104 | } 105 | 106 | [layer_names] 107 | 108 | 2d_physics/layer_1="Player" 109 | 2d_physics/layer_2="Wall" 110 | --------------------------------------------------------------------------------