commit
c5e9e5aab1
38 changed files with 1849 additions and 0 deletions
@ -0,0 +1,9 @@ |
|||||||
|
root = true |
||||||
|
|
||||||
|
[*] |
||||||
|
charset = utf-8 |
||||||
|
end_of_line = lf |
||||||
|
indent_size = 4 |
||||||
|
indent_style = tab |
||||||
|
trim_trailing_whitespace = true |
||||||
|
insert_final_newline = true |
@ -0,0 +1,2 @@ |
|||||||
|
# Normalize EOL for all files that Git considers text files. |
||||||
|
* text=auto eol=lf |
@ -0,0 +1,6 @@ |
|||||||
|
# Godot 4+ specific ignores |
||||||
|
/.godot/ |
||||||
|
/export_presets.cfg |
||||||
|
|
||||||
|
# Blender specifc ignores |
||||||
|
*.blend? |
@ -0,0 +1,14 @@ |
|||||||
|
public partial class CameraController : Node3D |
||||||
|
{ |
||||||
|
private Vector3 Offset; |
||||||
|
public override void _Ready() |
||||||
|
{ |
||||||
|
Offset = Position; |
||||||
|
} |
||||||
|
|
||||||
|
public override void _Process(double delta) |
||||||
|
{ |
||||||
|
var target = GetParent<Node3D>().GlobalPosition + Offset; |
||||||
|
GlobalPosition = GlobalPosition.Lerp(target, 1 - Pow(0.05f, (float)delta)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
global using System; |
||||||
|
global using System.Collections.Generic; |
||||||
|
global using System.Linq; |
||||||
|
|
||||||
|
global using Godot; |
||||||
|
|
||||||
|
global using static Godot.Mathf; |
@ -0,0 +1,80 @@ |
|||||||
|
public partial class MovementController : Node |
||||||
|
{ |
||||||
|
[ExportGroup("Movement")] |
||||||
|
[Export] public float Acceleration { get; set; } = 2.5f; |
||||||
|
[Export] public float MaxSpeed { get; set; } = 3.0f; |
||||||
|
[Export] public float FrictionFloor { get; set; } = 3.5f; |
||||||
|
[Export] public float FrictionAir { get; set; } = 1.0f; |
||||||
|
[Export] public float SprintMultiplier { get; set; } = 2.0f; |
||||||
|
|
||||||
|
[ExportGroup("Jumping")] |
||||||
|
[Export] public float JumpVelocity { get; set; } = 4.0f; |
||||||
|
/// <summary> Time (in seconds) after pressing the jump button a jump may occur late. </summary> |
||||||
|
[Export] public float JumpEarlyTime { get; set; } = 0.2f; |
||||||
|
/// <summary> Time (in seconds) after leaving a jumpable surface when a jump may still occur. </summary> |
||||||
|
[Export] public float JumpCoyoteTime { get; set; } = 0.2f; |
||||||
|
|
||||||
|
public float TimeSinceJumpPressed { get; private set; } = float.PositiveInfinity; |
||||||
|
public float TimeSinceOnFloor { get; private set; } = float.PositiveInfinity; |
||||||
|
|
||||||
|
private CharacterBody3D Player; |
||||||
|
public override void _Ready() |
||||||
|
{ |
||||||
|
Player = GetParent<CharacterBody3D>(); |
||||||
|
} |
||||||
|
|
||||||
|
public override void _UnhandledInput(InputEvent ev) |
||||||
|
{ |
||||||
|
if (ev.IsActionPressed("move_jump")) { |
||||||
|
TimeSinceJumpPressed = 0.0f; |
||||||
|
GetViewport().SetInputAsHandled(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override void _PhysicsProcess(double delta) |
||||||
|
{ |
||||||
|
var velocity = Player.Velocity; |
||||||
|
var horVelocity = new Vector2(velocity.X, velocity.Z); |
||||||
|
|
||||||
|
var gravity = (float)ProjectSettings.GetSetting("physics/3d/default_gravity"); |
||||||
|
velocity.Y -= gravity * (float)delta; |
||||||
|
|
||||||
|
var input = Input.GetVector("move_left", "move_right", "move_forward", "move_back"); |
||||||
|
var camera = GetViewport().GetCamera3D(); |
||||||
|
var target = input.Rotated(camera.GlobalRotation.Y - Tau / 4) * MaxSpeed; |
||||||
|
|
||||||
|
var isOnFloor = Player.IsOnFloor(); |
||||||
|
var isMoving = target.Dot(horVelocity) > 0.0f; |
||||||
|
var isSprinting = Input.IsActionPressed("move_sprint"); |
||||||
|
|
||||||
|
var accel = isMoving ? Acceleration |
||||||
|
: isOnFloor ? FrictionFloor |
||||||
|
: FrictionAir; |
||||||
|
|
||||||
|
if (isSprinting) { |
||||||
|
target *= SprintMultiplier; |
||||||
|
accel *= SprintMultiplier; |
||||||
|
} |
||||||
|
|
||||||
|
horVelocity = horVelocity.Lerp(target, accel * (float)delta); |
||||||
|
|
||||||
|
velocity.X = horVelocity.X; |
||||||
|
velocity.Z = horVelocity.Y; |
||||||
|
|
||||||
|
if (isOnFloor) TimeSinceOnFloor = 0.0f; |
||||||
|
else TimeSinceOnFloor += (float)delta; |
||||||
|
|
||||||
|
if ((TimeSinceJumpPressed <= JumpEarlyTime) && (TimeSinceOnFloor <= JumpCoyoteTime)) { |
||||||
|
TimeSinceJumpPressed = TimeSinceOnFloor = float.PositiveInfinity; |
||||||
|
velocity.Y = JumpVelocity; |
||||||
|
} else |
||||||
|
TimeSinceJumpPressed += (float)delta; |
||||||
|
|
||||||
|
Player.Velocity = velocity; |
||||||
|
Player.MoveAndSlide(); |
||||||
|
} |
||||||
|
|
||||||
|
public override void _Process(double delta) |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
public enum PhysicsLayer |
||||||
|
{ |
||||||
|
Terrain = 0b0000_0001, |
||||||
|
Objects = 0b0000_0010, |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
<Project Sdk="Godot.NET.Sdk/4.3.0"> |
||||||
|
<PropertyGroup> |
||||||
|
<LangVersion>12</LangVersion> |
||||||
|
<TargetFramework>net6.0</TargetFramework> |
||||||
|
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net7.0</TargetFramework> |
||||||
|
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'ios' ">net8.0</TargetFramework> |
||||||
|
<EnableDynamicLoading>true</EnableDynamicLoading> |
||||||
|
</PropertyGroup> |
||||||
|
</Project> |
@ -0,0 +1,65 @@ |
|||||||
|
#if TOOLS |
||||||
|
|
||||||
|
|
||||||
|
using System.Security.Cryptography.X509Certificates; |
||||||
|
|
||||||
|
[Tool] |
||||||
|
public partial class TerrainPlugin |
||||||
|
: EditorPlugin |
||||||
|
{ |
||||||
|
public override bool _Handles(GodotObject @object) |
||||||
|
=> @object is Terrain; |
||||||
|
|
||||||
|
public override void _EnterTree() |
||||||
|
{ |
||||||
|
// Initialization of the plugin goes here. |
||||||
|
} |
||||||
|
|
||||||
|
public override void _ExitTree() |
||||||
|
{ |
||||||
|
// Clean-up of the plugin goes here. |
||||||
|
} |
||||||
|
|
||||||
|
Terrain _previousTerrain = null; |
||||||
|
public override int _Forward3DGuiInput(Camera3D viewportCamera, InputEvent @event) |
||||||
|
{ |
||||||
|
if (@event is not InputEventMouse) return (int)AfterGuiInput.Pass; |
||||||
|
|
||||||
|
if (!IsInstanceValid(_previousTerrain)) |
||||||
|
_previousTerrain = null; |
||||||
|
|
||||||
|
if (RayCastTerrain(viewportCamera) is var (terrain, pos, normal, shape)) { |
||||||
|
if (_previousTerrain != terrain) { |
||||||
|
_previousTerrain?._MouseExit(); |
||||||
|
terrain._MouseEnter(); |
||||||
|
_previousTerrain = terrain; |
||||||
|
} |
||||||
|
terrain._InputEvent(viewportCamera, @event, pos, normal, shape); |
||||||
|
return viewportCamera.GetViewport().IsInputHandled() |
||||||
|
? (int)AfterGuiInput.Stop : (int)AfterGuiInput.Pass; |
||||||
|
} else { |
||||||
|
_previousTerrain?._MouseExit(); |
||||||
|
_previousTerrain = null; |
||||||
|
return (int)AfterGuiInput.Pass; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
(Terrain Terrain, Vector3 Position, Vector3 Normal, int Shape)? RayCastTerrain(Camera3D camera) |
||||||
|
{ |
||||||
|
var viewport = camera.GetViewport(); |
||||||
|
var mouse = viewport.GetMousePosition(); |
||||||
|
if (!viewport.GetVisibleRect().HasPoint(mouse)) return null; |
||||||
|
|
||||||
|
var from = camera.ProjectRayOrigin(mouse); |
||||||
|
var to = from + camera.ProjectRayNormal(mouse) * camera.Far; |
||||||
|
|
||||||
|
var space = camera.GetWorld3D().DirectSpaceState; |
||||||
|
var query = new PhysicsRayQueryParameters3D { From = from, To = to }; |
||||||
|
var result = space.IntersectRay(query); |
||||||
|
if (result.Count == 0) return null; |
||||||
|
|
||||||
|
if ((GodotObject)result["collider"] is not Terrain terrain) return null; |
||||||
|
return (terrain, (Vector3)result["position"], (Vector3)result["normal"], (int)result["shape"]); |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
@ -0,0 +1,7 @@ |
|||||||
|
[plugin] |
||||||
|
|
||||||
|
name="Terrain Plugin" |
||||||
|
description="" |
||||||
|
author="copygirl" |
||||||
|
version="" |
||||||
|
script="TerrainPlugin.cs" |
After Width: | Height: | Size: 949 B |
@ -0,0 +1,37 @@ |
|||||||
|
[remap] |
||||||
|
|
||||||
|
importer="texture" |
||||||
|
type="CompressedTexture2D" |
||||||
|
uid="uid://dmu6pvke0n7x3" |
||||||
|
path="res://.godot/imported/icon.svg-56083ea2a1f1a4f1e49773bdc6d7826c.ctex" |
||||||
|
metadata={ |
||||||
|
"vram_texture": false |
||||||
|
} |
||||||
|
|
||||||
|
[deps] |
||||||
|
|
||||||
|
source_file="res://assets/icon.svg" |
||||||
|
dest_files=["res://.godot/imported/icon.svg-56083ea2a1f1a4f1e49773bdc6d7826c.ctex"] |
||||||
|
|
||||||
|
[params] |
||||||
|
|
||||||
|
compress/mode=0 |
||||||
|
compress/high_quality=false |
||||||
|
compress/lossy_quality=0.7 |
||||||
|
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/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 |
Binary file not shown.
@ -0,0 +1,51 @@ |
|||||||
|
[remap] |
||||||
|
|
||||||
|
importer="scene" |
||||||
|
importer_version=1 |
||||||
|
type="PackedScene" |
||||||
|
uid="uid://buj1g5fpiuqkd" |
||||||
|
path="res://.godot/imported/terrain.blend-0343d6f0e6459ff0512c30a5803c2760.scn" |
||||||
|
|
||||||
|
[deps] |
||||||
|
|
||||||
|
source_file="res://assets/models/terrain.blend" |
||||||
|
dest_files=["res://.godot/imported/terrain.blend-0343d6f0e6459ff0512c30a5803c2760.scn"] |
||||||
|
|
||||||
|
[params] |
||||||
|
|
||||||
|
nodes/root_type="" |
||||||
|
nodes/root_name="" |
||||||
|
nodes/apply_root_scale=true |
||||||
|
nodes/root_scale=1.0 |
||||||
|
nodes/import_as_skeleton_bones=false |
||||||
|
meshes/ensure_tangents=true |
||||||
|
meshes/generate_lods=true |
||||||
|
meshes/create_shadow_meshes=true |
||||||
|
meshes/light_baking=1 |
||||||
|
meshes/lightmap_texel_size=0.2 |
||||||
|
meshes/force_disable_compression=false |
||||||
|
skins/use_named_skins=true |
||||||
|
animation/import=true |
||||||
|
animation/fps=30 |
||||||
|
animation/trimming=false |
||||||
|
animation/remove_immutable_tracks=true |
||||||
|
animation/import_rest_as_RESET=false |
||||||
|
import_script/path="" |
||||||
|
_subresources={} |
||||||
|
blender/nodes/visible=0 |
||||||
|
blender/nodes/active_collection_only=false |
||||||
|
blender/nodes/punctual_lights=true |
||||||
|
blender/nodes/cameras=true |
||||||
|
blender/nodes/custom_properties=true |
||||||
|
blender/nodes/modifiers=1 |
||||||
|
blender/meshes/colors=false |
||||||
|
blender/meshes/uvs=true |
||||||
|
blender/meshes/normals=true |
||||||
|
blender/meshes/tangents=true |
||||||
|
blender/meshes/skins=2 |
||||||
|
blender/meshes/export_bones_deforming_mesh_only=false |
||||||
|
blender/materials/unpack_enabled=true |
||||||
|
blender/materials/export_materials=1 |
||||||
|
blender/animation/limit_playback=true |
||||||
|
blender/animation/always_sample=true |
||||||
|
blender/animation/group_tracks=true |
Binary file not shown.
@ -0,0 +1,34 @@ |
|||||||
|
[remap] |
||||||
|
|
||||||
|
importer="scene" |
||||||
|
importer_version=1 |
||||||
|
type="PackedScene" |
||||||
|
uid="uid://sul6hrbcaal3" |
||||||
|
path="res://.godot/imported/tree_cone.glb-32c7aa66c7f7007efe8afbb55a64b8be.scn" |
||||||
|
|
||||||
|
[deps] |
||||||
|
|
||||||
|
source_file="res://assets/models/trees/tree_cone.glb" |
||||||
|
dest_files=["res://.godot/imported/tree_cone.glb-32c7aa66c7f7007efe8afbb55a64b8be.scn"] |
||||||
|
|
||||||
|
[params] |
||||||
|
|
||||||
|
nodes/root_type="" |
||||||
|
nodes/root_name="" |
||||||
|
nodes/apply_root_scale=true |
||||||
|
nodes/root_scale=1.0 |
||||||
|
meshes/ensure_tangents=true |
||||||
|
meshes/generate_lods=true |
||||||
|
meshes/create_shadow_meshes=true |
||||||
|
meshes/light_baking=1 |
||||||
|
meshes/lightmap_texel_size=0.2 |
||||||
|
meshes/force_disable_compression=false |
||||||
|
skins/use_named_skins=true |
||||||
|
animation/import=true |
||||||
|
animation/fps=30 |
||||||
|
animation/trimming=false |
||||||
|
animation/remove_immutable_tracks=true |
||||||
|
import_script/path="" |
||||||
|
_subresources={} |
||||||
|
gltf/naming_version=1 |
||||||
|
gltf/embedded_image_handling=1 |
Binary file not shown.
@ -0,0 +1,34 @@ |
|||||||
|
[remap] |
||||||
|
|
||||||
|
importer="scene" |
||||||
|
importer_version=1 |
||||||
|
type="PackedScene" |
||||||
|
uid="uid://c02oimc3fcl4v" |
||||||
|
path="res://.godot/imported/tree_default.glb-e065ad2933f3574ace34d72aa07370d7.scn" |
||||||
|
|
||||||
|
[deps] |
||||||
|
|
||||||
|
source_file="res://assets/models/trees/tree_default.glb" |
||||||
|
dest_files=["res://.godot/imported/tree_default.glb-e065ad2933f3574ace34d72aa07370d7.scn"] |
||||||
|
|
||||||
|
[params] |
||||||
|
|
||||||
|
nodes/root_type="" |
||||||
|
nodes/root_name="" |
||||||
|
nodes/apply_root_scale=true |
||||||
|
nodes/root_scale=1.0 |
||||||
|
meshes/ensure_tangents=true |
||||||
|
meshes/generate_lods=true |
||||||
|
meshes/create_shadow_meshes=true |
||||||
|
meshes/light_baking=1 |
||||||
|
meshes/lightmap_texel_size=0.2 |
||||||
|
meshes/force_disable_compression=false |
||||||
|
skins/use_named_skins=true |
||||||
|
animation/import=true |
||||||
|
animation/fps=30 |
||||||
|
animation/trimming=false |
||||||
|
animation/remove_immutable_tracks=true |
||||||
|
import_script/path="" |
||||||
|
_subresources={} |
||||||
|
gltf/naming_version=1 |
||||||
|
gltf/embedded_image_handling=1 |
Binary file not shown.
@ -0,0 +1,34 @@ |
|||||||
|
[remap] |
||||||
|
|
||||||
|
importer="scene" |
||||||
|
importer_version=1 |
||||||
|
type="PackedScene" |
||||||
|
uid="uid://b0578fhc4t22h" |
||||||
|
path="res://.godot/imported/tree_oak.glb-25d5c85394a71448f9e157e250f5713a.scn" |
||||||
|
|
||||||
|
[deps] |
||||||
|
|
||||||
|
source_file="res://assets/models/trees/tree_oak.glb" |
||||||
|
dest_files=["res://.godot/imported/tree_oak.glb-25d5c85394a71448f9e157e250f5713a.scn"] |
||||||
|
|
||||||
|
[params] |
||||||
|
|
||||||
|
nodes/root_type="" |
||||||
|
nodes/root_name="" |
||||||
|
nodes/apply_root_scale=true |
||||||
|
nodes/root_scale=1.0 |
||||||
|
meshes/ensure_tangents=true |
||||||
|
meshes/generate_lods=true |
||||||
|
meshes/create_shadow_meshes=true |
||||||
|
meshes/light_baking=1 |
||||||
|
meshes/lightmap_texel_size=0.2 |
||||||
|
meshes/force_disable_compression=false |
||||||
|
skins/use_named_skins=true |
||||||
|
animation/import=true |
||||||
|
animation/fps=30 |
||||||
|
animation/trimming=false |
||||||
|
animation/remove_immutable_tracks=true |
||||||
|
import_script/path="" |
||||||
|
_subresources={} |
||||||
|
gltf/naming_version=1 |
||||||
|
gltf/embedded_image_handling=1 |
Binary file not shown.
After Width: | Height: | Size: 967 B |
@ -0,0 +1,35 @@ |
|||||||
|
[remap] |
||||||
|
|
||||||
|
importer="texture" |
||||||
|
type="CompressedTexture2D" |
||||||
|
uid="uid://bpo7mkr6sctqr" |
||||||
|
path.s3tc="res://.godot/imported/dirt.png-67313fa97fa5b9369bec725f203ba6a9.s3tc.ctex" |
||||||
|
metadata={ |
||||||
|
"imported_formats": ["s3tc_bptc"], |
||||||
|
"vram_texture": true |
||||||
|
} |
||||||
|
|
||||||
|
[deps] |
||||||
|
|
||||||
|
source_file="res://assets/textures/terrain/dirt.png" |
||||||
|
dest_files=["res://.godot/imported/dirt.png-67313fa97fa5b9369bec725f203ba6a9.s3tc.ctex"] |
||||||
|
|
||||||
|
[params] |
||||||
|
|
||||||
|
compress/mode=2 |
||||||
|
compress/high_quality=false |
||||||
|
compress/lossy_quality=0.7 |
||||||
|
compress/hdr_compression=1 |
||||||
|
compress/normal_map=0 |
||||||
|
compress/channel_pack=0 |
||||||
|
mipmaps/generate=true |
||||||
|
mipmaps/limit=-1 |
||||||
|
roughness/mode=0 |
||||||
|
roughness/src_normal="" |
||||||
|
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=0 |
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,35 @@ |
|||||||
|
[remap] |
||||||
|
|
||||||
|
importer="texture" |
||||||
|
type="CompressedTexture2D" |
||||||
|
uid="uid://b0jp1dyxugbr7" |
||||||
|
path.s3tc="res://.godot/imported/grass.png-aff98023336e7c812cce14c63596cfa6.s3tc.ctex" |
||||||
|
metadata={ |
||||||
|
"imported_formats": ["s3tc_bptc"], |
||||||
|
"vram_texture": true |
||||||
|
} |
||||||
|
|
||||||
|
[deps] |
||||||
|
|
||||||
|
source_file="res://assets/textures/terrain/grass.png" |
||||||
|
dest_files=["res://.godot/imported/grass.png-aff98023336e7c812cce14c63596cfa6.s3tc.ctex"] |
||||||
|
|
||||||
|
[params] |
||||||
|
|
||||||
|
compress/mode=2 |
||||||
|
compress/high_quality=false |
||||||
|
compress/lossy_quality=0.7 |
||||||
|
compress/hdr_compression=1 |
||||||
|
compress/normal_map=0 |
||||||
|
compress/channel_pack=0 |
||||||
|
mipmaps/generate=true |
||||||
|
mipmaps/limit=-1 |
||||||
|
roughness/mode=0 |
||||||
|
roughness/src_normal="" |
||||||
|
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=0 |
After Width: | Height: | Size: 888 B |
@ -0,0 +1,35 @@ |
|||||||
|
[remap] |
||||||
|
|
||||||
|
importer="texture" |
||||||
|
type="CompressedTexture2D" |
||||||
|
uid="uid://dqyqg6yt7yk3k" |
||||||
|
path.s3tc="res://.godot/imported/rock.png-6fd2e985e4c17866aa31a92f6096a3ad.s3tc.ctex" |
||||||
|
metadata={ |
||||||
|
"imported_formats": ["s3tc_bptc"], |
||||||
|
"vram_texture": true |
||||||
|
} |
||||||
|
|
||||||
|
[deps] |
||||||
|
|
||||||
|
source_file="res://assets/textures/terrain/rock.png" |
||||||
|
dest_files=["res://.godot/imported/rock.png-6fd2e985e4c17866aa31a92f6096a3ad.s3tc.ctex"] |
||||||
|
|
||||||
|
[params] |
||||||
|
|
||||||
|
compress/mode=2 |
||||||
|
compress/high_quality=false |
||||||
|
compress/lossy_quality=0.7 |
||||||
|
compress/hdr_compression=1 |
||||||
|
compress/normal_map=0 |
||||||
|
compress/channel_pack=0 |
||||||
|
mipmaps/generate=true |
||||||
|
mipmaps/limit=-1 |
||||||
|
roughness/mode=0 |
||||||
|
roughness/src_normal="" |
||||||
|
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=0 |
After Width: | Height: | Size: 921 B |
@ -0,0 +1,35 @@ |
|||||||
|
[remap] |
||||||
|
|
||||||
|
importer="texture" |
||||||
|
type="CompressedTexture2D" |
||||||
|
uid="uid://bkwjxg6g2itag" |
||||||
|
path.s3tc="res://.godot/imported/sand.png-451c9e51968306b5331529d9113b3e49.s3tc.ctex" |
||||||
|
metadata={ |
||||||
|
"imported_formats": ["s3tc_bptc"], |
||||||
|
"vram_texture": true |
||||||
|
} |
||||||
|
|
||||||
|
[deps] |
||||||
|
|
||||||
|
source_file="res://assets/textures/terrain/sand.png" |
||||||
|
dest_files=["res://.godot/imported/sand.png-451c9e51968306b5331529d9113b3e49.s3tc.ctex"] |
||||||
|
|
||||||
|
[params] |
||||||
|
|
||||||
|
compress/mode=2 |
||||||
|
compress/high_quality=false |
||||||
|
compress/lossy_quality=0.7 |
||||||
|
compress/hdr_compression=1 |
||||||
|
compress/normal_map=0 |
||||||
|
compress/channel_pack=0 |
||||||
|
mipmaps/generate=true |
||||||
|
mipmaps/limit=-1 |
||||||
|
roughness/mode=0 |
||||||
|
roughness/src_normal="" |
||||||
|
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=0 |
@ -0,0 +1,28 @@ |
|||||||
|
[gd_scene load_steps=5 format=3 uid="uid://daihc7acaxfns"] |
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://MovementController.cs" id="1_akh08"] |
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_urpcu"] |
||||||
|
albedo_color = Color(0.25098, 0.690196, 0.12549, 0.815686) |
||||||
|
|
||||||
|
[sub_resource type="SphereMesh" id="SphereMesh_7ljg8"] |
||||||
|
material = SubResource("StandardMaterial3D_urpcu") |
||||||
|
radius = 0.2 |
||||||
|
height = 0.35 |
||||||
|
|
||||||
|
[sub_resource type="SphereShape3D" id="SphereShape3D_6qbb2"] |
||||||
|
radius = 0.2 |
||||||
|
|
||||||
|
[node name="Character" type="CharacterBody3D"] |
||||||
|
floor_max_angle = 0.698132 |
||||||
|
|
||||||
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="."] |
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.15, 0) |
||||||
|
mesh = SubResource("SphereMesh_7ljg8") |
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."] |
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.2, 0) |
||||||
|
shape = SubResource("SphereShape3D_6qbb2") |
||||||
|
|
||||||
|
[node name="MovementController" type="Node" parent="."] |
||||||
|
script = ExtResource("1_akh08") |
File diff suppressed because one or more lines are too long
@ -0,0 +1,19 @@ |
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://b65o2rhp8qx74"] |
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://sul6hrbcaal3" path="res://assets/models/trees/tree_cone.glb" id="1_8soi2"] |
||||||
|
|
||||||
|
[sub_resource type="CylinderShape3D" id="CylinderShape3D_82bei"] |
||||||
|
radius = 0.25 |
||||||
|
|
||||||
|
[node name="TreeOakCone" type="Node3D"] |
||||||
|
|
||||||
|
[node name="tree_cone2" parent="." instance=ExtResource("1_8soi2")] |
||||||
|
transform = Transform3D(4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0) |
||||||
|
|
||||||
|
[node name="StaticBody3D" type="StaticBody3D" parent="."] |
||||||
|
collision_layer = 2 |
||||||
|
collision_mask = 0 |
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D"] |
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) |
||||||
|
shape = SubResource("CylinderShape3D_82bei") |
@ -0,0 +1,19 @@ |
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://c732i0mrp6klk"] |
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://b0578fhc4t22h" path="res://assets/models/trees/tree_oak.glb" id="1_aojgv"] |
||||||
|
|
||||||
|
[sub_resource type="CylinderShape3D" id="CylinderShape3D_causx"] |
||||||
|
radius = 0.35 |
||||||
|
|
||||||
|
[node name="TreeOakRound" type="Node3D"] |
||||||
|
|
||||||
|
[node name="tree_oak2" parent="." instance=ExtResource("1_aojgv")] |
||||||
|
transform = Transform3D(4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0.171265, 0) |
||||||
|
|
||||||
|
[node name="StaticBody3D" type="StaticBody3D" parent="."] |
||||||
|
collision_layer = 2 |
||||||
|
collision_mask = 0 |
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D"] |
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) |
||||||
|
shape = SubResource("CylinderShape3D_causx") |
@ -0,0 +1,21 @@ |
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://2giwj61d3h66"] |
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://c02oimc3fcl4v" path="res://assets/models/trees/tree_default.glb" id="1_frbcr"] |
||||||
|
|
||||||
|
[sub_resource type="CylinderShape3D" id="CylinderShape3D_0l3l4"] |
||||||
|
height = 4.0 |
||||||
|
radius = 0.3 |
||||||
|
|
||||||
|
[node name="TreeOakTall" type="Node3D"] |
||||||
|
|
||||||
|
[node name="tree_default2" parent="." instance=ExtResource("1_frbcr")] |
||||||
|
transform = Transform3D(4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0) |
||||||
|
|
||||||
|
[node name="StaticBody3D" type="StaticBody3D" parent="."] |
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) |
||||||
|
collision_layer = 2 |
||||||
|
collision_mask = 0 |
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D"] |
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) |
||||||
|
shape = SubResource("CylinderShape3D_0l3l4") |
@ -0,0 +1,66 @@ |
|||||||
|
; 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="SlimeDream" |
||||||
|
run/main_scene="res://level.tscn" |
||||||
|
config/features=PackedStringArray("4.3", "C#", "GL Compatibility") |
||||||
|
config/icon="res://assets/icon.svg" |
||||||
|
|
||||||
|
[dotnet] |
||||||
|
|
||||||
|
project/assembly_name="SlimeDream" |
||||||
|
|
||||||
|
[editor_plugins] |
||||||
|
|
||||||
|
enabled=PackedStringArray("res://addons/terrain/plugin.cfg") |
||||||
|
|
||||||
|
[input] |
||||||
|
|
||||||
|
move_left={ |
||||||
|
"deadzone": 0.5, |
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) |
||||||
|
] |
||||||
|
} |
||||||
|
move_right={ |
||||||
|
"deadzone": 0.5, |
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) |
||||||
|
] |
||||||
|
} |
||||||
|
move_forward={ |
||||||
|
"deadzone": 0.5, |
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) |
||||||
|
] |
||||||
|
} |
||||||
|
move_back={ |
||||||
|
"deadzone": 0.5, |
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) |
||||||
|
] |
||||||
|
} |
||||||
|
move_sprint={ |
||||||
|
"deadzone": 0.5, |
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) |
||||||
|
] |
||||||
|
} |
||||||
|
move_jump={ |
||||||
|
"deadzone": 0.5, |
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) |
||||||
|
] |
||||||
|
} |
||||||
|
|
||||||
|
[layer_names] |
||||||
|
|
||||||
|
3d_physics/layer_1="Terrain" |
||||||
|
3d_physics/layer_2="Objects" |
||||||
|
|
||||||
|
[rendering] |
||||||
|
|
||||||
|
renderer/rendering_method="mobile" |
@ -0,0 +1,226 @@ |
|||||||
|
[Tool] |
||||||
|
public partial class Terrain |
||||||
|
: StaticBody3D |
||||||
|
{ |
||||||
|
[Export] public Vector2I Size { get; set; } = new(64, 64); |
||||||
|
[Export] public float TileSize { get; set; } = 2.0f; |
||||||
|
|
||||||
|
[Export] public Godot.Collections.Array<Texture2D> Textures { get; set; } |
||||||
|
|
||||||
|
public bool IsEditing { get; } = Engine.IsEditorHint(); |
||||||
|
|
||||||
|
Vector2I? _gridHover = null; |
||||||
|
Vector2I? _gridSelectionStart = null; |
||||||
|
Vector2I? _gridSelectionEnd = null; |
||||||
|
bool _isSelecting = false; |
||||||
|
|
||||||
|
Material _editToolMaterial; |
||||||
|
Material _terrainMaterial; |
||||||
|
public override void _Ready() |
||||||
|
{ |
||||||
|
_editToolMaterial = new StandardMaterial3D { |
||||||
|
VertexColorUseAsAlbedo = true, |
||||||
|
ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded, |
||||||
|
DepthDrawMode = BaseMaterial3D.DepthDrawModeEnum.Disabled, |
||||||
|
NoDepthTest = true, |
||||||
|
}; |
||||||
|
|
||||||
|
_terrainMaterial = new StandardMaterial3D { |
||||||
|
AlbedoTexture = Textures?.FirstOrDefault(), |
||||||
|
}; |
||||||
|
|
||||||
|
UpdateMeshAndShape(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Vector2I ToGridPos(Vector3 localPos) |
||||||
|
=> new(RoundToInt(localPos.X / TileSize + Size.X / 2.0f), |
||||||
|
RoundToInt(localPos.Z / TileSize + Size.Y / 2.0f)); |
||||||
|
|
||||||
|
float GetCornerHeight(Vector2I gridPos, Corner corner) |
||||||
|
{ |
||||||
|
return 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
GridCorners GetGridCorners(Vector2I gridPos) |
||||||
|
{ |
||||||
|
var halfSize = TileSize / 2; |
||||||
|
var vx = (gridPos.X - Size.X / 2.0f) * TileSize; |
||||||
|
var vz = (gridPos.Y - Size.Y / 2.0f) * TileSize; |
||||||
|
|
||||||
|
return new() { |
||||||
|
TopLeft = new(vx - halfSize, GetCornerHeight(gridPos, Corner.TopLeft ), vz - halfSize), |
||||||
|
TopRight = new(vx + halfSize, GetCornerHeight(gridPos, Corner.TopRight ), vz - halfSize), |
||||||
|
BottomLeft = new(vx - halfSize, GetCornerHeight(gridPos, Corner.BottomLeft ), vz + halfSize), |
||||||
|
BottomRight = new(vx + halfSize, GetCornerHeight(gridPos, Corner.BottomRight), vz + halfSize), |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void UpdateMeshAndShape() |
||||||
|
{ |
||||||
|
var mesh = GetOrCreateMesh("MeshInstance"); |
||||||
|
var shape = GetOrCreateShape("CollisionShape"); |
||||||
|
|
||||||
|
mesh.ClearSurfaces(); |
||||||
|
mesh.SurfaceBegin(Mesh.PrimitiveType.Triangles); |
||||||
|
var points = new List<Vector3>(); |
||||||
|
|
||||||
|
void AddPoint(Vector3 pos, Vector2 uv) { |
||||||
|
mesh.SurfaceSetUV(uv); |
||||||
|
mesh.SurfaceAddVertex(pos); |
||||||
|
points.Add(pos); |
||||||
|
} |
||||||
|
|
||||||
|
void AddTriangle(Vector3 v1, Vector2 uv1, |
||||||
|
Vector3 v2, Vector2 uv2, |
||||||
|
Vector3 v3, Vector2 uv3) { |
||||||
|
var dir = (v3 - v1).Cross(v2 - v1); |
||||||
|
mesh.SurfaceSetNormal(dir.Normalized()); |
||||||
|
AddPoint(v1, uv1); |
||||||
|
AddPoint(v2, uv2); |
||||||
|
AddPoint(v3, uv3); |
||||||
|
} |
||||||
|
|
||||||
|
for (var x = 0; x < Size.X; x++) { |
||||||
|
for (var z = 0; z < Size.Y; z++) { |
||||||
|
var corners = GetGridCorners(new(x, z)); |
||||||
|
AddTriangle(corners.TopLeft , new(0.0f, 0.0f), |
||||||
|
corners.TopRight , new(1.0f, 0.0f), |
||||||
|
corners.BottomLeft , new(0.0f, 1.0f)); |
||||||
|
AddTriangle(corners.TopRight , new(1.0f, 0.0f), |
||||||
|
corners.BottomRight, new(1.0f, 1.0f), |
||||||
|
corners.BottomLeft , new(0.0f, 1.0f)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
mesh.SurfaceEnd(); |
||||||
|
mesh.SurfaceSetMaterial(0, _terrainMaterial); |
||||||
|
|
||||||
|
shape.Data = [.. points]; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public override void _MouseEnter() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public override void _MouseExit() |
||||||
|
{ |
||||||
|
_gridHover = null; |
||||||
|
} |
||||||
|
|
||||||
|
public override void _InputEvent(Camera3D camera, InputEvent @event, Vector3 position, Vector3 normal, int shapeIdx) |
||||||
|
{ |
||||||
|
if (!IsEditing) return; |
||||||
|
|
||||||
|
var localPos = ToLocal(position); |
||||||
|
var gridPos = ToGridPos(localPos); |
||||||
|
|
||||||
|
if (@event is InputEventMouseButton { ButtonIndex: MouseButton.Left, Pressed: var pressed }) { |
||||||
|
if (pressed) _gridSelectionStart = _gridSelectionEnd = gridPos; |
||||||
|
_isSelecting = pressed; |
||||||
|
camera.GetViewport().SetInputAsHandled(); |
||||||
|
} |
||||||
|
|
||||||
|
if (@event is InputEventMouseMotion) { |
||||||
|
_gridHover = gridPos; |
||||||
|
if (_isSelecting) _gridSelectionEnd = gridPos; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override void _Process(double delta) |
||||||
|
{ |
||||||
|
if (!IsEditing) _gridHover = _gridSelectionStart = _gridSelectionEnd = null; |
||||||
|
|
||||||
|
if ((_gridHover != null) || (_gridSelectionStart != null)) { |
||||||
|
var mesh = GetOrCreateMesh("EditToolMesh"); |
||||||
|
mesh.ClearSurfaces(); |
||||||
|
mesh.SurfaceBegin(Mesh.PrimitiveType.Lines); |
||||||
|
|
||||||
|
|
||||||
|
void AddLine(Vector3 start, Vector3 end) { |
||||||
|
mesh.SurfaceAddVertex(start); |
||||||
|
mesh.SurfaceAddVertex(end); |
||||||
|
} |
||||||
|
|
||||||
|
void AddQuad(Vector3 topLeft, Vector3 topRight, |
||||||
|
Vector3 bottomLeft, Vector3 bottomRight) { |
||||||
|
AddLine(topLeft , topRight ); |
||||||
|
AddLine(topRight , bottomRight); |
||||||
|
AddLine(bottomRight, bottomLeft ); |
||||||
|
AddLine(bottomLeft , topLeft ); |
||||||
|
} |
||||||
|
|
||||||
|
if (_gridHover is Vector2I hover) { |
||||||
|
var corners = GetGridCorners(hover); |
||||||
|
var margin = 0.1f; |
||||||
|
mesh.SurfaceSetColor(Colors.Black); |
||||||
|
AddQuad( |
||||||
|
corners.TopLeft + new Vector3(-margin, 0, -margin), |
||||||
|
corners.TopRight + new Vector3(+margin, 0, -margin), |
||||||
|
corners.BottomLeft + new Vector3(-margin, 0, +margin), |
||||||
|
corners.BottomRight + new Vector3(+margin, 0, +margin) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if (_gridSelectionStart is Vector2I start) { |
||||||
|
var end = _gridSelectionEnd ?? start; |
||||||
|
(start, end) = (new(Min(start.X, end.X), Min(start.Y, end.Y)), |
||||||
|
new(Max(start.X, end.X), Max(start.Y, end.Y))); |
||||||
|
mesh.SurfaceSetColor(Colors.Blue); |
||||||
|
for (var x = start.X; x <= end.X; x++) |
||||||
|
for (var y = start.Y; y <= end.Y; y++) { |
||||||
|
var corners = GetGridCorners(new(x, y)); |
||||||
|
AddQuad(corners.TopLeft, corners.TopRight, corners.BottomLeft, corners.BottomRight); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
mesh.SurfaceEnd(); |
||||||
|
mesh.SurfaceSetMaterial(0, _editToolMaterial); |
||||||
|
} else { |
||||||
|
var meshInstance = (MeshInstance3D)GetNodeOrNull("EditToolMesh"); |
||||||
|
var mesh = (ImmediateMesh)meshInstance?.Mesh; |
||||||
|
mesh?.ClearSurfaces(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
ImmediateMesh GetOrCreateMesh(string name) |
||||||
|
{ |
||||||
|
var meshInstance = (MeshInstance3D)GetNodeOrNull(name); |
||||||
|
if (meshInstance == null) { |
||||||
|
meshInstance = new() { Name = name, Mesh = new ImmediateMesh() }; |
||||||
|
AddChild(meshInstance); |
||||||
|
meshInstance.Owner = this; |
||||||
|
} |
||||||
|
return (ImmediateMesh)meshInstance.Mesh; |
||||||
|
} |
||||||
|
|
||||||
|
ConcavePolygonShape3D GetOrCreateShape(string name) |
||||||
|
{ |
||||||
|
var collisionShape = (CollisionShape3D)GetNodeOrNull(name); |
||||||
|
if (collisionShape == null) { |
||||||
|
collisionShape = new() { Name = name, Shape = new ConcavePolygonShape3D() }; |
||||||
|
AddChild(collisionShape); |
||||||
|
collisionShape.Owner = this; |
||||||
|
} |
||||||
|
return (ConcavePolygonShape3D)collisionShape.Shape; |
||||||
|
} |
||||||
|
|
||||||
|
enum Corner |
||||||
|
{ |
||||||
|
TopLeft, |
||||||
|
TopRight, |
||||||
|
BottomLeft, |
||||||
|
BottomRight, |
||||||
|
} |
||||||
|
|
||||||
|
readonly struct GridCorners |
||||||
|
{ |
||||||
|
public Vector3 TopLeft { get; init; } |
||||||
|
public Vector3 TopRight { get; init; } |
||||||
|
public Vector3 BottomLeft { get; init; } |
||||||
|
public Vector3 BottomRight { get; init; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://b16jht8mfqr21"] |
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://buj1g5fpiuqkd" path="res://assets/models/terrain.blend" id="1_ourjj"] |
||||||
|
|
||||||
|
[node name="TerrainSource" instance=ExtResource("1_ourjj")] |
Loading…
Reference in new issue