Initial commit

main
copygirl 4 months ago
commit c5e9e5aab1
  1. 9
      .editorconfig
  2. 2
      .gitattributes
  3. 6
      .gitignore
  4. 14
      CameraController.cs
  5. 7
      GlobalUsings.cs
  6. 80
      MovementController.cs
  7. 5
      PhysicsLayer.cs
  8. 9
      SlimeDream.csproj
  9. 19
      SlimeDream.sln
  10. 65
      addons/terrain/TerrainPlugin.cs
  11. 7
      addons/terrain/plugin.cfg
  12. 1
      assets/icon.svg
  13. 37
      assets/icon.svg.import
  14. BIN
      assets/models/terrain.blend
  15. 51
      assets/models/terrain.blend.import
  16. BIN
      assets/models/trees/tree_cone.glb
  17. 34
      assets/models/trees/tree_cone.glb.import
  18. BIN
      assets/models/trees/tree_default.glb
  19. 34
      assets/models/trees/tree_default.glb.import
  20. BIN
      assets/models/trees/tree_oak.glb
  21. 34
      assets/models/trees/tree_oak.glb.import
  22. BIN
      assets/terrain_source.bin
  23. BIN
      assets/textures/terrain/dirt.png
  24. 35
      assets/textures/terrain/dirt.png.import
  25. BIN
      assets/textures/terrain/grass.png
  26. 35
      assets/textures/terrain/grass.png.import
  27. BIN
      assets/textures/terrain/rock.png
  28. 35
      assets/textures/terrain/rock.png.import
  29. BIN
      assets/textures/terrain/sand.png
  30. 35
      assets/textures/terrain/sand.png.import
  31. 28
      character.tscn
  32. 911
      level.tscn
  33. 19
      objects/tree_oak_cone.tscn
  34. 19
      objects/tree_oak_round.tscn
  35. 21
      objects/tree_oak_tall.tscn
  36. 66
      project.godot
  37. 226
      terrain/Terrain.cs
  38. 5
      terrain/terrain_source.tscn

@ -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

2
.gitattributes vendored

@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf

6
.gitignore vendored

@ -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,19 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlimeDream", "SlimeDream.csproj", "{846099AA-EFA7-4879-9BB2-0DBF0A826187}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
ExportDebug|Any CPU = ExportDebug|Any CPU
ExportRelease|Any CPU = ExportRelease|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{846099AA-EFA7-4879-9BB2-0DBF0A826187}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{846099AA-EFA7-4879-9BB2-0DBF0A826187}.Debug|Any CPU.Build.0 = Debug|Any CPU
{846099AA-EFA7-4879-9BB2-0DBF0A826187}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU
{846099AA-EFA7-4879-9BB2-0DBF0A826187}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU
{846099AA-EFA7-4879-9BB2-0DBF0A826187}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU
{846099AA-EFA7-4879-9BB2-0DBF0A826187}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU
EndGlobalSection
EndGlobal

@ -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"

@ -0,0 +1 @@
<svg height="128" width="128" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="124" height="124" rx="14" fill="#363d52" stroke="#212532" stroke-width="4"/><g transform="scale(.101) translate(122 122)"><g fill="#fff"><path d="M105 673v33q407 354 814 0v-33z"/><path 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" fill="#478cbf"/><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"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></g></svg>

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

@ -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.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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…
Cancel
Save