First commit
This commit is contained in:
commit
4eef953ed6
26 changed files with 771 additions and 0 deletions
4
.editorconfig
Normal file
4
.editorconfig
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Normalize EOL for all files that Git considers text files.
|
||||||
|
* text=auto eol=lf
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Godot 4+ specific ignores
|
||||||
|
.godot/
|
||||||
|
/android/
|
21
addons/SimpleFormatOnSave/LICENSE.txt
Normal file
21
addons/SimpleFormatOnSave/LICENSE.txt
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025-present VitSoonYoung - <vitsoonyoung+simpleformatonsave@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
10
addons/SimpleFormatOnSave/formatter.gd
Normal file
10
addons/SimpleFormatOnSave/formatter.gd
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
class_name Formatter
|
||||||
|
|
||||||
|
const RuleSpacing = preload("./rules/spacing.gd")
|
||||||
|
const RuleBlankLines = preload("./rules/blank_lines.gd")
|
||||||
|
|
||||||
|
|
||||||
|
func format_code(code: String) -> String:
|
||||||
|
code = RuleSpacing.apply(code)
|
||||||
|
code = RuleBlankLines.apply(code)
|
||||||
|
return code
|
1
addons/SimpleFormatOnSave/formatter.gd.uid
Normal file
1
addons/SimpleFormatOnSave/formatter.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://clqnxlippxlm4
|
7
addons/SimpleFormatOnSave/plugin.cfg
Normal file
7
addons/SimpleFormatOnSave/plugin.cfg
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[plugin]
|
||||||
|
|
||||||
|
name="Simple Format On Save"
|
||||||
|
description="Combination of simple gdscript formatter & Format On Save with extra rules for beatiful formatting...maybe"
|
||||||
|
author="VitSoonYoung"
|
||||||
|
version="0.1"
|
||||||
|
script="simple_format_on_save.gd"
|
65
addons/SimpleFormatOnSave/rules/blank_lines.gd
Normal file
65
addons/SimpleFormatOnSave/rules/blank_lines.gd
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
class_name RuleBlankLines
|
||||||
|
|
||||||
|
var FORMAT_ACTION := "simple_format_on_save/format_code"
|
||||||
|
var format_key: InputEventKey
|
||||||
|
|
||||||
|
|
||||||
|
static func apply(code: String) -> String:
|
||||||
|
var trim1_regex = RegEx.create_from_string("\n{2,}")
|
||||||
|
code = trim1_regex.sub(code, "\n\n", true)
|
||||||
|
code = _blank_for_func_class(code)
|
||||||
|
var trim2_regex = RegEx.create_from_string("\n{3,}")
|
||||||
|
code = trim2_regex.sub(code, "\n\n\n", true)
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
static func _blank_for_func_class(code: String) -> String:
|
||||||
|
var assignment_regex = RegEx.create_from_string(r".*=.*")
|
||||||
|
var statement_regex = RegEx.create_from_string(r"\s+(if|for|while|match)[\s|\(].*")
|
||||||
|
var misc_statement_regex = RegEx.create_from_string(r"\s+(else|elif|\}|\]).*")
|
||||||
|
var func_class_regex = RegEx.create_from_string(r".*(func|class) .*")
|
||||||
|
var comment_line_regex = RegEx.create_from_string(r"^\s*#")
|
||||||
|
var empty_line_regex = RegEx.create_from_string(r"^\s+$")
|
||||||
|
var lines := code.split('\n')
|
||||||
|
var modified_lines: Array[String] = []
|
||||||
|
|
||||||
|
for line: String in lines:
|
||||||
|
# Spaces between functions & classes
|
||||||
|
if func_class_regex.search(line):
|
||||||
|
if modified_lines.size() > 0:
|
||||||
|
var i := modified_lines.size() - 1
|
||||||
|
|
||||||
|
while i > 0 and comment_line_regex.search(modified_lines[i]):
|
||||||
|
i -= 1
|
||||||
|
|
||||||
|
if i == 0:
|
||||||
|
modified_lines.append(line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
modified_lines.insert(i + 1, "")
|
||||||
|
modified_lines.insert(i + 1, "")
|
||||||
|
|
||||||
|
# 1 space between assignment & statement
|
||||||
|
if statement_regex.search(line):
|
||||||
|
if modified_lines.size() > 0:
|
||||||
|
var i := modified_lines.size() - 1
|
||||||
|
|
||||||
|
if assignment_regex.search(modified_lines[i]) and not statement_regex.search(modified_lines[i]):
|
||||||
|
modified_lines.insert(i + 1, "")
|
||||||
|
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Space after a code block (Doesn't work with spaces for now)
|
||||||
|
var indent_count := line.count("\t")
|
||||||
|
|
||||||
|
if indent_count and not misc_statement_regex.search(line):
|
||||||
|
if modified_lines.size() > 0:
|
||||||
|
var i := modified_lines.size() - 1
|
||||||
|
|
||||||
|
if modified_lines[i].count("\t") > indent_count:
|
||||||
|
modified_lines.insert(i + 1, "")
|
||||||
|
|
||||||
|
modified_lines.append(line)
|
||||||
|
|
||||||
|
return "\n".join(modified_lines)
|
1
addons/SimpleFormatOnSave/rules/blank_lines.gd.uid
Normal file
1
addons/SimpleFormatOnSave/rules/blank_lines.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://dr266abkfhg1o
|
155
addons/SimpleFormatOnSave/rules/spacing.gd
Normal file
155
addons/SimpleFormatOnSave/rules/spacing.gd
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
class_name RuleSpacing
|
||||||
|
|
||||||
|
const SYMBOLS = [
|
||||||
|
r"\*\*=",
|
||||||
|
r"\*\*",
|
||||||
|
"<<=",
|
||||||
|
">>=",
|
||||||
|
"<<",
|
||||||
|
">>",
|
||||||
|
"==",
|
||||||
|
"!=",
|
||||||
|
">=",
|
||||||
|
"<=",
|
||||||
|
"&&",
|
||||||
|
r"\|\|",
|
||||||
|
r"\+=",
|
||||||
|
"-=",
|
||||||
|
r"\*=",
|
||||||
|
"/=",
|
||||||
|
"%=",
|
||||||
|
"&=",
|
||||||
|
r"\^=",
|
||||||
|
r"\|=",
|
||||||
|
"~=",
|
||||||
|
":=",
|
||||||
|
"->",
|
||||||
|
r"&",
|
||||||
|
r"\|",
|
||||||
|
r"\^",
|
||||||
|
"-",
|
||||||
|
r"\+",
|
||||||
|
"/",
|
||||||
|
r"\*",
|
||||||
|
">",
|
||||||
|
"<",
|
||||||
|
"-",
|
||||||
|
"%",
|
||||||
|
"=",
|
||||||
|
":",
|
||||||
|
",",
|
||||||
|
];
|
||||||
|
const KEYWORDS = [
|
||||||
|
"and",
|
||||||
|
"is",
|
||||||
|
"or",
|
||||||
|
"not",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
static func apply(code: String) -> String:
|
||||||
|
var string_regex = RegEx.new()
|
||||||
|
string_regex.compile(r'(?<!#)("""|\'\'\'|"|\')((?:.|\n)*?)\1')
|
||||||
|
|
||||||
|
var string_matches = string_regex.search_all(code)
|
||||||
|
var string_map = {}
|
||||||
|
|
||||||
|
for i in range(string_matches.size()):
|
||||||
|
var match = string_matches[i]
|
||||||
|
var original = match.get_string()
|
||||||
|
var placeholder = "__STRING__%d__" % i
|
||||||
|
string_map[placeholder] = original
|
||||||
|
code = _replace(code, original, placeholder)
|
||||||
|
|
||||||
|
var comment_regex = RegEx.new()
|
||||||
|
comment_regex.compile("#.*")
|
||||||
|
var comment_matches = comment_regex.search_all(code)
|
||||||
|
var comment_map = {}
|
||||||
|
|
||||||
|
for i in range(comment_matches.size()):
|
||||||
|
var match = comment_matches[i]
|
||||||
|
var original = match.get_string()
|
||||||
|
var placeholder = "__COMMENT__%d__" % i
|
||||||
|
comment_map[placeholder] = original
|
||||||
|
code = _replace(code, original, placeholder)
|
||||||
|
|
||||||
|
var ref_regex = RegEx.new()
|
||||||
|
ref_regex.compile(r"\$[^.]*")
|
||||||
|
var ref_matches = ref_regex.search_all(code)
|
||||||
|
var ref_map = {}
|
||||||
|
|
||||||
|
for i in range(ref_matches.size()):
|
||||||
|
var match = ref_matches[i]
|
||||||
|
var original = match.get_string()
|
||||||
|
var placeholder = "__REF__%d__" % i
|
||||||
|
ref_map[placeholder] = original
|
||||||
|
code = _replace(code, original, placeholder)
|
||||||
|
|
||||||
|
code = _format_operators_and_commas(code)
|
||||||
|
|
||||||
|
for placeholder in ref_map:
|
||||||
|
code = code.replace(placeholder, ref_map[placeholder])
|
||||||
|
|
||||||
|
for placeholder in comment_map:
|
||||||
|
code = code.replace(placeholder, comment_map[placeholder])
|
||||||
|
|
||||||
|
for placeholder in string_map:
|
||||||
|
code = code.replace(placeholder, string_map[placeholder])
|
||||||
|
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
static func _format_operators_and_commas(code: String) -> String:
|
||||||
|
var indent_regex = RegEx.create_from_string(r"^\s{4}")
|
||||||
|
var new_code = indent_regex.sub(code, "\t", true)
|
||||||
|
|
||||||
|
while (code != new_code):
|
||||||
|
code = new_code
|
||||||
|
new_code = indent_regex.sub(code, "\t", true)
|
||||||
|
|
||||||
|
var symbols_regex = "(" + ") | (".join(SYMBOLS) + ")"
|
||||||
|
symbols_regex = " * ?(" + symbols_regex + ") * "
|
||||||
|
var symbols_operator_regex = RegEx.create_from_string(symbols_regex)
|
||||||
|
code = symbols_operator_regex.sub(code, " $1 ", true)
|
||||||
|
|
||||||
|
# ": =" => ":="
|
||||||
|
code = RegEx.create_from_string(r": *=").sub(code, ":=", true)
|
||||||
|
|
||||||
|
# "a(" => "a ("
|
||||||
|
code = RegEx.create_from_string(r"(?<=[\w\)\]]) *([\(:,])(?!=)").sub(code, "$1", true)
|
||||||
|
|
||||||
|
# "( a" => "(a"
|
||||||
|
code = RegEx.create_from_string(r"([\(\{}]) *").sub(code, "$1", true)
|
||||||
|
|
||||||
|
# "a )" => "a)"
|
||||||
|
code = RegEx.create_from_string(r" *([\)\}])").sub(code, "$1", true)
|
||||||
|
|
||||||
|
# "if(" => "if ("
|
||||||
|
code = RegEx.create_from_string(r"\b(if|for|while|switch|match)\(").sub(code, "$1 (", true)
|
||||||
|
|
||||||
|
var keywoisrd_regex = r"|".join(KEYWORDS)
|
||||||
|
var keyword_operator_regex = RegEx.create_from_string(r"(?<=[ \)\]])(" + keywoisrd_regex + r")(?=[ \(\[])")
|
||||||
|
code = keyword_operator_regex.sub(code, " $1 ", true)
|
||||||
|
|
||||||
|
# tab "a\t=" => "a ="
|
||||||
|
code = RegEx.create_from_string(r"(\t*. * ?)\t * ").sub(code, "$1", true)
|
||||||
|
|
||||||
|
#trim
|
||||||
|
code = RegEx.create_from_string("[ \t]*\n").sub(code, "\n", true)
|
||||||
|
|
||||||
|
# " " => " "
|
||||||
|
code = RegEx.create_from_string(" +").sub(code, " ", true)
|
||||||
|
|
||||||
|
# "= -a" => "= -a"
|
||||||
|
code = RegEx.create_from_string(r"([=,(] ?)- ").sub(code, "$1-", true)
|
||||||
|
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
static func _replace(text: String, what: String, forwhat: String) -> String:
|
||||||
|
var index := text.find(what)
|
||||||
|
|
||||||
|
if index != -1:
|
||||||
|
text = text.substr(0, index) + forwhat + text.substr(index + what.length())
|
||||||
|
|
||||||
|
return text
|
1
addons/SimpleFormatOnSave/rules/spacing.gd.uid
Normal file
1
addons/SimpleFormatOnSave/rules/spacing.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://wfodrj6vpfb6
|
82
addons/SimpleFormatOnSave/simple_format_on_save.gd
Normal file
82
addons/SimpleFormatOnSave/simple_format_on_save.gd
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
@tool
|
||||||
|
extends EditorPlugin
|
||||||
|
|
||||||
|
var FORMAT_ACTION := "simple_format_on_save/format_code"
|
||||||
|
var format_key: InputEventKey
|
||||||
|
var formatter: Formatter
|
||||||
|
|
||||||
|
|
||||||
|
func _enter_tree():
|
||||||
|
add_tool_menu_item("Format (Ctrl+Alt+L)", _on_format_code)
|
||||||
|
|
||||||
|
if InputMap.has_action(FORMAT_ACTION):
|
||||||
|
InputMap.erase_action(FORMAT_ACTION)
|
||||||
|
|
||||||
|
InputMap.add_action(FORMAT_ACTION)
|
||||||
|
format_key = InputEventKey.new()
|
||||||
|
format_key.keycode = KEY_L
|
||||||
|
format_key.ctrl_pressed = true
|
||||||
|
format_key.alt_pressed = true
|
||||||
|
InputMap.action_add_event(FORMAT_ACTION, format_key)
|
||||||
|
resource_saved.connect(on_resource_saved)
|
||||||
|
|
||||||
|
|
||||||
|
func _exit_tree():
|
||||||
|
remove_tool_menu_item("Format (Ctrl+Alt+L)")
|
||||||
|
InputMap.erase_action(FORMAT_ACTION)
|
||||||
|
resource_saved.disconnect(on_resource_saved)
|
||||||
|
|
||||||
|
|
||||||
|
# Return true if formatted code != original code
|
||||||
|
func _on_format_code() -> bool:
|
||||||
|
var current_editor := EditorInterface.get_script_editor().get_current_editor()
|
||||||
|
|
||||||
|
if not (current_editor and current_editor.is_class("ScriptTextEditor")):
|
||||||
|
return false
|
||||||
|
|
||||||
|
var text_edit = current_editor.get_base_editor()
|
||||||
|
var code = text_edit.text
|
||||||
|
|
||||||
|
if not formatter:
|
||||||
|
formatter = Formatter.new()
|
||||||
|
|
||||||
|
var formatted_code = formatter.format_code(code)
|
||||||
|
|
||||||
|
if not formatted_code:
|
||||||
|
return false
|
||||||
|
|
||||||
|
if code.length() == formatted_code.length() and code == formatted_code:
|
||||||
|
return false
|
||||||
|
|
||||||
|
var scroll_horizontal = text_edit.scroll_horizontal
|
||||||
|
var scroll_vertical = text_edit.scroll_vertical
|
||||||
|
var caret_column = text_edit.get_caret_column(0)
|
||||||
|
var caret_line = text_edit.get_caret_line(0)
|
||||||
|
|
||||||
|
text_edit.text = formatted_code
|
||||||
|
text_edit.set_caret_line(caret_line)
|
||||||
|
text_edit.set_caret_column(caret_column)
|
||||||
|
text_edit.scroll_horizontal = scroll_horizontal
|
||||||
|
text_edit.scroll_vertical = scroll_vertical
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func _shortcut_input(event: InputEvent) -> void:
|
||||||
|
if event is InputEventKey and event.is_pressed():
|
||||||
|
if Input.is_action_pressed(FORMAT_ACTION):
|
||||||
|
_on_format_code()
|
||||||
|
|
||||||
|
|
||||||
|
# CALLED WHEN A SCRIPT IS SAVED
|
||||||
|
func on_resource_saved(resource: Resource):
|
||||||
|
if resource is Script:
|
||||||
|
var script: Script = resource
|
||||||
|
var current_script = get_editor_interface().get_script_editor().get_current_script()
|
||||||
|
|
||||||
|
# Prevents other unsaved scripts from overwriting the active one
|
||||||
|
if current_script == script:
|
||||||
|
var is_modified: bool = _on_format_code()
|
||||||
|
|
||||||
|
#if is_modified:
|
||||||
|
#print_rich("[color=#636363]Auto formatted code[/color]")
|
1
addons/SimpleFormatOnSave/simple_format_on_save.gd.uid
Normal file
1
addons/SimpleFormatOnSave/simple_format_on_save.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://b6d7va37k5131
|
1
icon.svg
Normal file
1
icon.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>
|
After Width: | Height: | Size: 995 B |
43
icon.svg.import
Normal file
43
icon.svg.import
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cpufmd5631dpp"
|
||||||
|
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://icon.svg"
|
||||||
|
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
28
player/player.gd
Normal file
28
player/player.gd
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
extends CharacterBody3D
|
||||||
|
|
||||||
|
|
||||||
|
const SPEED = 5.0
|
||||||
|
const JUMP_VELOCITY = 4.5
|
||||||
|
|
||||||
|
|
||||||
|
func _physics_process(delta: float) -> void:
|
||||||
|
# Add the gravity.
|
||||||
|
if not is_on_floor():
|
||||||
|
velocity += get_gravity() * delta
|
||||||
|
|
||||||
|
# Handle jump.
|
||||||
|
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
|
||||||
|
velocity.y = JUMP_VELOCITY
|
||||||
|
|
||||||
|
# Get the input direction and handle the movement/deceleration.
|
||||||
|
# As good practice, you should replace UI actions with custom gameplay actions.
|
||||||
|
var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
|
||||||
|
var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
|
||||||
|
if direction:
|
||||||
|
velocity.x = direction.x * SPEED
|
||||||
|
velocity.z = direction.z * SPEED
|
||||||
|
else:
|
||||||
|
velocity.x = move_toward(velocity.x, 0, SPEED)
|
||||||
|
velocity.z = move_toward(velocity.z, 0, SPEED)
|
||||||
|
|
||||||
|
move_and_slide()
|
1
player/player.gd.uid
Normal file
1
player/player.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://b5io1eg4blrp5
|
24
project.godot
Normal file
24
project.godot
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
; Engine configuration file.
|
||||||
|
; It's best edited using the editor UI and not directly,
|
||||||
|
; since the parameters that go here are not all obvious.
|
||||||
|
;
|
||||||
|
; Format:
|
||||||
|
; [section] ; section goes between []
|
||||||
|
; param=value ; assign values to parameters
|
||||||
|
|
||||||
|
config_version=5
|
||||||
|
|
||||||
|
[application]
|
||||||
|
|
||||||
|
config/name="BoneForge"
|
||||||
|
run/main_scene="uid://bgofilfnbeisl"
|
||||||
|
config/features=PackedStringArray("4.5", "Forward Plus")
|
||||||
|
config/icon="res://icon.svg"
|
||||||
|
|
||||||
|
[editor_plugins]
|
||||||
|
|
||||||
|
enabled=PackedStringArray("res://addons/SimpleFormatOnSave/plugin.cfg")
|
||||||
|
|
||||||
|
[network]
|
||||||
|
|
||||||
|
limits/debugger/max_chars_per_second=99999999
|
24
scenes/chunk_generator.gd
Normal file
24
scenes/chunk_generator.gd
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
@export var chunks = 10
|
||||||
|
const value = 300
|
||||||
|
|
||||||
|
var TerrainHtread: Thread
|
||||||
|
# Called when the node enters the scene tree for the first time.
|
||||||
|
func _ready() -> void:
|
||||||
|
TerrainHtread = Thread.new()
|
||||||
|
TerrainHtread.start(generate_chuks.bind(chunks,value,self))
|
||||||
|
|
||||||
|
func generate_chuks(_chunks,_value,parent):
|
||||||
|
for chunk in _chunks:
|
||||||
|
var checukn :Node3D= Node3D.new()
|
||||||
|
checukn.position = Vector3(randf_range(-_value,_value),0,randf_range(-_value,_value))
|
||||||
|
checukn.set_script(load("res://world_generation/grok_chnk.gd"))
|
||||||
|
parent.call_deferred("add_child",checukn)
|
||||||
|
|
||||||
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _exit_tree() -> void:
|
||||||
|
TerrainHtread.wait_to_finish()
|
1
scenes/chunk_generator.gd.uid
Normal file
1
scenes/chunk_generator.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://e63lq34qepsg
|
29
scenes/world.tscn
Normal file
29
scenes/world.tscn
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
[gd_scene load_steps=5 format=3 uid="uid://bgofilfnbeisl"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://e63lq34qepsg" path="res://scenes/chunk_generator.gd" id="2_rwgxs"]
|
||||||
|
|
||||||
|
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_nnsk1"]
|
||||||
|
sky_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1)
|
||||||
|
ground_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1)
|
||||||
|
|
||||||
|
[sub_resource type="Sky" id="Sky_rwgxs"]
|
||||||
|
sky_material = SubResource("ProceduralSkyMaterial_nnsk1")
|
||||||
|
|
||||||
|
[sub_resource type="Environment" id="Environment_4wyf3"]
|
||||||
|
background_mode = 2
|
||||||
|
sky = SubResource("Sky_rwgxs")
|
||||||
|
tonemap_mode = 2
|
||||||
|
glow_enabled = true
|
||||||
|
|
||||||
|
[node name="world" type="Node3D"]
|
||||||
|
|
||||||
|
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||||
|
environment = SubResource("Environment_4wyf3")
|
||||||
|
|
||||||
|
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
||||||
|
transform = Transform3D(-0.8660254, -0.43301278, 0.25, 0, 0.49999997, 0.86602545, -0.50000006, 0.75, -0.43301266, 0, 0, 0)
|
||||||
|
shadow_enabled = true
|
||||||
|
|
||||||
|
[node name="chunkGenerator" type="Node" parent="."]
|
||||||
|
script = ExtResource("2_rwgxs")
|
||||||
|
chunks = 2000
|
56
world_gen/main.tscn
Normal file
56
world_gen/main.tscn
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
[gd_scene load_steps=9 format=3 uid="uid://cioop0k7xmww8"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://b4q4faaqbat0x" path="res://world_gen/world_generator.gd" id="1_y7qka"]
|
||||||
|
[ext_resource type="Script" uid="uid://b5io1eg4blrp5" path="res://player/player.gd" id="2_nwtrj"]
|
||||||
|
|
||||||
|
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_y7qka"]
|
||||||
|
sky_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1)
|
||||||
|
ground_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1)
|
||||||
|
|
||||||
|
[sub_resource type="Sky" id="Sky_nwtrj"]
|
||||||
|
sky_material = SubResource("ProceduralSkyMaterial_y7qka")
|
||||||
|
|
||||||
|
[sub_resource type="Environment" id="Environment_vrc1b"]
|
||||||
|
background_mode = 2
|
||||||
|
sky = SubResource("Sky_nwtrj")
|
||||||
|
tonemap_mode = 2
|
||||||
|
volumetric_fog_enabled = true
|
||||||
|
volumetric_fog_density = 0.01
|
||||||
|
|
||||||
|
[sub_resource type="FastNoiseLite" id="FastNoiseLite_nwtrj"]
|
||||||
|
noise_type = 3
|
||||||
|
seed = 280
|
||||||
|
frequency = 0.0035
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_y7qka"]
|
||||||
|
resource_name = "PlayerCollision"
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleMesh" id="CapsuleMesh_nwtrj"]
|
||||||
|
resource_name = "PlayerMesh"
|
||||||
|
|
||||||
|
[node name="main" type="Node"]
|
||||||
|
|
||||||
|
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||||
|
environment = SubResource("Environment_vrc1b")
|
||||||
|
|
||||||
|
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
||||||
|
transform = Transform3D(-0.8660254, -0.43301278, 0.25, 0, 0.49999997, 0.86602545, -0.50000006, 0.75, -0.43301266, 0, 0, 0)
|
||||||
|
shadow_enabled = true
|
||||||
|
|
||||||
|
[node name="WorldGenerator" type="Node" parent="."]
|
||||||
|
script = ExtResource("1_y7qka")
|
||||||
|
noise = SubResource("FastNoiseLite_nwtrj")
|
||||||
|
height = 24.0
|
||||||
|
|
||||||
|
[node name="Player" type="CharacterBody3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 104.43206, 0)
|
||||||
|
script = ExtResource("2_nwtrj")
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="Player"]
|
||||||
|
shape = SubResource("CapsuleShape3D_y7qka")
|
||||||
|
|
||||||
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="Player/CollisionShape3D"]
|
||||||
|
mesh = SubResource("CapsuleMesh_nwtrj")
|
||||||
|
|
||||||
|
[node name="Camera3D" type="Camera3D" parent="Player"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0.9604348, 0.27850503, 0, -0.27850503, 0.9604348, 0, 1.8589027, 2.8465767)
|
54
world_gen/main.tscn2944845596.tmp
Normal file
54
world_gen/main.tscn2944845596.tmp
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
[gd_scene load_steps=9 format=3 uid="uid://cioop0k7xmww8"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://b4q4faaqbat0x" path="res://world_gen/world_generator.gd" id="1_y7qka"]
|
||||||
|
[ext_resource type="Script" uid="uid://b5io1eg4blrp5" path="res://player/player.gd" id="2_nwtrj"]
|
||||||
|
|
||||||
|
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_y7qka"]
|
||||||
|
sky_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1)
|
||||||
|
ground_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1)
|
||||||
|
|
||||||
|
[sub_resource type="Sky" id="Sky_nwtrj"]
|
||||||
|
sky_material = SubResource("ProceduralSkyMaterial_y7qka")
|
||||||
|
|
||||||
|
[sub_resource type="Environment" id="Environment_vrc1b"]
|
||||||
|
background_mode = 2
|
||||||
|
sky = SubResource("Sky_nwtrj")
|
||||||
|
tonemap_mode = 2
|
||||||
|
volumetric_fog_enabled = true
|
||||||
|
volumetric_fog_density = 0.01
|
||||||
|
|
||||||
|
[sub_resource type="FastNoiseLite" id="FastNoiseLite_nwtrj"]
|
||||||
|
noise_type = 3
|
||||||
|
frequency = 0.0291
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_y7qka"]
|
||||||
|
resource_name = "PlayerCollision"
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleMesh" id="CapsuleMesh_nwtrj"]
|
||||||
|
resource_name = "PlayerMesh"
|
||||||
|
|
||||||
|
[node name="main" type="Node"]
|
||||||
|
|
||||||
|
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||||
|
environment = SubResource("Environment_vrc1b")
|
||||||
|
|
||||||
|
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
||||||
|
transform = Transform3D(-0.8660254, -0.43301278, 0.25, 0, 0.49999997, 0.86602545, -0.50000006, 0.75, -0.43301266, 0, 0, 0)
|
||||||
|
shadow_enabled = true
|
||||||
|
|
||||||
|
[node name="WorldGenerator" type="Node" parent="."]
|
||||||
|
script = ExtResource("1_y7qka")
|
||||||
|
noise = SubResource("FastNoiseLite_nwtrj")
|
||||||
|
|
||||||
|
[node name="Player" type="CharacterBody3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 104.43206, 0)
|
||||||
|
script = ExtResource("2_nwtrj")
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="Player"]
|
||||||
|
shape = SubResource("CapsuleShape3D_y7qka")
|
||||||
|
|
||||||
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="Player/CollisionShape3D"]
|
||||||
|
mesh = SubResource("CapsuleMesh_nwtrj")
|
||||||
|
|
||||||
|
[node name="Camera3D" type="Camera3D" parent="Player"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0.9604348, 0.27850503, 0, -0.27850503, 0.9604348, 0, 1.8589027, 2.8465767)
|
7
world_gen/new_plane_mesh.tres
Normal file
7
world_gen/new_plane_mesh.tres
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[gd_resource type="PlaneMesh" format=3 uid="uid://rhv7l5ya1qnf"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
resource_local_to_scene = true
|
||||||
|
size = Vector2(256, 256)
|
||||||
|
subdivide_width = 32
|
||||||
|
subdivide_depth = 32
|
149
world_gen/world_generator.gd
Normal file
149
world_gen/world_generator.gd
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
@tool
|
||||||
|
extends Node
|
||||||
|
var generation_thread: Thread = Thread.new()
|
||||||
|
#### CHUNK GENERATION ####
|
||||||
|
@export_category("CHUNK GENERATION")
|
||||||
|
|
||||||
|
const CHUNK_SIZE := 256
|
||||||
|
const CHUNK_BOUNDS := 8
|
||||||
|
|
||||||
|
## How detailed the chunk should be. This could be made a const when debug phase is finished
|
||||||
|
@export_range(4,256,4) var chunk_resolution :=32:
|
||||||
|
set(new_resolution):
|
||||||
|
chunk_resolution = new_resolution
|
||||||
|
update_all_mesh()
|
||||||
|
|
||||||
|
#### WORLD GENERATION ####
|
||||||
|
@export_category("WORLD GENERATION")
|
||||||
|
@export_tool_button("Generate Chunks") var generate_chunk_callable = generate.bind(true)
|
||||||
|
@export var noise: FastNoiseLite:
|
||||||
|
set(new_noise):
|
||||||
|
noise = new_noise
|
||||||
|
update_all_mesh()
|
||||||
|
if noise:
|
||||||
|
noise.changed.connect(update_all_mesh) #change all thes meshes after eahc changes
|
||||||
|
@export_range(4.0,128.0,4.0) var height := 64.0:
|
||||||
|
set(new_height):
|
||||||
|
height = new_height
|
||||||
|
update_all_mesh()
|
||||||
|
|
||||||
|
var loaded_chunks := {}
|
||||||
|
|
||||||
|
|
||||||
|
# Called when the node enters the scene tree for the first time. ALSO IN EDITOR
|
||||||
|
func _ready() -> void:
|
||||||
|
generate(true)
|
||||||
|
|
||||||
|
|
||||||
|
func generate(_ForceRegen:bool=false):
|
||||||
|
print('Generation starts')
|
||||||
|
generate_chunks_around(Vector3(0,0,0), _ForceRegen)
|
||||||
|
|
||||||
|
|
||||||
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
func _process(_delta: float) -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
func generate_noise(_seed:float):
|
||||||
|
noise = FastNoiseLite.new()
|
||||||
|
|
||||||
|
|
||||||
|
# This function is better called in another thread, doing all the heavy process before adding it to the scene tree
|
||||||
|
func generate_chunk(position: Vector3, _ForceRegen:bool = false):
|
||||||
|
|
||||||
|
WorkerThreadPool.add_task(func():
|
||||||
|
var mesh_instance = MeshInstance3D.new()
|
||||||
|
update_mesh(mesh_instance)
|
||||||
|
mesh_instance.position = position
|
||||||
|
call_deferred("add_child", mesh_instance)
|
||||||
|
var chunk_coord = get_chunk_coord(position)
|
||||||
|
loaded_chunks[chunk_coord] = mesh_instance
|
||||||
|
print("chunk at : ",str(position),", coord ",str(chunk_coord), " loaded")
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func get_height(x:float,y:float) -> float:
|
||||||
|
return noise.get_noise_2d(x,y) * height
|
||||||
|
|
||||||
|
|
||||||
|
func get_normal(x:float,y:float) -> Vector3:
|
||||||
|
var epsilon := CHUNK_SIZE / chunk_resolution
|
||||||
|
var normal := Vector3(
|
||||||
|
(get_height(x + epsilon,y) - get_height(x - epsilon, y))/(2.0 *epsilon),
|
||||||
|
1.0,
|
||||||
|
(get_height(x, y + epsilon) - get_height(x, y - epsilon))/(2.0 *epsilon)
|
||||||
|
|
||||||
|
)
|
||||||
|
return normal.normalized()
|
||||||
|
|
||||||
|
|
||||||
|
func update_all_mesh():
|
||||||
|
for chunk in loaded_chunks:
|
||||||
|
update_mesh(loaded_chunks[chunk])
|
||||||
|
|
||||||
|
|
||||||
|
func update_mesh(chunk_terrain_instance:MeshInstance3D):
|
||||||
|
var plane: PlaneMesh = PlaneMesh.new() #load("res://world_gen/new_plane_mesh.tres").duplicate()
|
||||||
|
plane.subdivide_depth = chunk_resolution
|
||||||
|
plane.subdivide_width = chunk_resolution
|
||||||
|
plane.size = Vector2(CHUNK_SIZE,CHUNK_SIZE)
|
||||||
|
|
||||||
|
var plane_arrays := plane.get_mesh_arrays()
|
||||||
|
var vertex_array: PackedVector3Array = plane_arrays[ArrayMesh.ARRAY_VERTEX]
|
||||||
|
var normal_array: PackedVector3Array = plane_arrays[ArrayMesh.ARRAY_NORMAL]
|
||||||
|
var tangent_array: PackedFloat32Array = plane_arrays[ArrayMesh.ARRAY_TANGENT]
|
||||||
|
|
||||||
|
for i:int in vertex_array.size():
|
||||||
|
var vertex := vertex_array[i]
|
||||||
|
# Convert local vertex coordinates to world coordinates
|
||||||
|
var world_x = chunk_terrain_instance.position.x + vertex.x
|
||||||
|
var world_z = chunk_terrain_instance.position.z + vertex.z
|
||||||
|
var normal := Vector3.UP
|
||||||
|
var tangent := Vector3.RIGHT
|
||||||
|
|
||||||
|
if noise:
|
||||||
|
vertex.y = get_height(world_x, world_z)
|
||||||
|
normal = get_normal(world_x, world_z)
|
||||||
|
tangent = normal.cross(Vector3.UP)
|
||||||
|
|
||||||
|
vertex_array[i] = vertex
|
||||||
|
normal_array[i] = normal
|
||||||
|
tangent_array[4*i] = tangent.x
|
||||||
|
tangent_array[4*i+1] = tangent.y
|
||||||
|
tangent_array[4*i+2] = tangent.z
|
||||||
|
tangent_array[4*i+3] = 1.0
|
||||||
|
|
||||||
|
var array_mesh := ArrayMesh.new()
|
||||||
|
array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES,plane_arrays)
|
||||||
|
chunk_terrain_instance.mesh = array_mesh
|
||||||
|
|
||||||
|
|
||||||
|
## Convert position to chunk coordinates
|
||||||
|
func get_chunk_coord(position:Vector3) -> Vector2i:
|
||||||
|
var coord_chunk_x = int(floor(position.x / CHUNK_SIZE))
|
||||||
|
var coord_chunk_z = int(floor(position.z / CHUNK_SIZE))
|
||||||
|
return Vector2i(coord_chunk_x,coord_chunk_z)
|
||||||
|
|
||||||
|
|
||||||
|
func generate_chunks_around(position:Vector3,ForceRegen:bool = false):
|
||||||
|
|
||||||
|
# Convert position to chunk coordinates
|
||||||
|
var position_to_coord := get_chunk_coord(position)
|
||||||
|
|
||||||
|
print("closest chunk : "+str(Vector2i(position_to_coord.x,position_to_coord.y)))
|
||||||
|
|
||||||
|
for x in range(position_to_coord.x - CHUNK_BOUNDS,position_to_coord.x + CHUNK_BOUNDS+1):
|
||||||
|
for z in range(position_to_coord.y - CHUNK_BOUNDS, position_to_coord.y + CHUNK_BOUNDS+1):
|
||||||
|
var chunk_coord := Vector2i(x,z)
|
||||||
|
var chunck_position := Vector3i(x * CHUNK_SIZE,0,z * CHUNK_SIZE)
|
||||||
|
print("trying to load chunk at coordinate : "+str(chunk_coord))
|
||||||
|
if ForceRegen:
|
||||||
|
loaded_chunks.clear()
|
||||||
|
for child in get_children():
|
||||||
|
child.queue_free()
|
||||||
|
|
||||||
|
if not loaded_chunks.has(chunk_coord):
|
||||||
|
print("generate chunk at : "+str(chunck_position))
|
||||||
|
generate_chunk(chunck_position)
|
1
world_gen/world_generator.gd.uid
Normal file
1
world_gen/world_generator.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://b4q4faaqbat0x
|
Loading…
Reference in a new issue