Need help with building system in 3D

Godot Version 4.0

Cheers, following I want to archieve:

Step 1:

The player selecting an object he wants to place. When he hovering with the mouse over the floor, there is a blueprint of the object gridsnapping at the mouseposition.

What I would do: Fetching the mouse position on the floor, and creating a blueprint object at that position. The grid snapping would I implementing like this (lets ignore y for that example): My grid is 10x10, if the mouse-position is 8.9x2.3, the position of the blueprint object is roundet and placed at 10x0. When the mouse moves, the current blueprint is deleted and a new blueprint is created at the new position.

The problem: How do I get the mouse position on the floor? Fetching the position with GetViewport().GetMousePosition() gives me the position on the screen. Using RayCast will inteference with other objects in the scene, or is there a way to ignore all objects exepts the floor?

Or is there another, better option?

Step 2:

I would like to give the player the possibility to draw a wall with dragging the mouse button.

What I would do: When the player selecting a wall from his inventory a propery “wallPlacement” is set to true. When the player presses the mousebutton I check if wallPlacement = true, if yes and if there is no wall at this grid-snapping-position (step 1), I add a wall. With every movement of the mouse, while holding the mouse button, I check if the mouse moves more vertical or more horizontal and placing the wall accordingly. Or better, creating a rectangle aligned to the grid.

The problem: It seems a bit “overthinked”, maybe there is a simpler solution!?

Thanks in advance!

Step 1:

If your grid is 10x10 px, snapping the mouse position to the grid should work like this:

var grid_step: Vector2 = Vector2(10, 10)
var snapped_mouse_position: Vector2 = get_global_mouse_position().snapped(step)

Edit: Sorry, I realized you are using C# and not GDScript. I don’t use C#, but I’m pretty sure you can easily translate that to C#.

Step 2:

I don’t think there’s anything wrong with your approach but instead of checking the direction of the mouse, you could also check if the surrounding cells have walls and then determine what kind of wall should be placed to connect it to the existing walls around it.

I had a similar issue and after fighting with the design for a couple of hours I decided to just change the design of my 3D mouse input. Here is what I did:

  • Create a basic ShapeCast3D with a sphere.
  • Hide it from the player’s camera & set the collision layer to be different than the player.
  • On PhysicsUpdate for the sphere I update its position according to the position of the mouse, gotten from the viewport that the camera is currently rendering.

And here is the code for getting the mouse position in 3D space:

using Godot;

public static class MouseUtilities
{
    public static Vector3 GetMousePosition3D(Node root)
    {
        var worldSpace = root.GetWindow().World3D.DirectSpaceState;
        var mousePosition = root.GetViewport().GetMousePosition();
        var camera = root.GetViewport().GetCamera3D();
        var start = camera.ProjectRayOrigin(root.GetWindow().Size / 2);
        var end = start + camera.ProjectRayNormal(mousePosition) * 1000;
        var result = worldSpace.IntersectRay(new PhysicsRayQueryParameters3D()
        {
            From = start,
            To = end,
        });

        if (result != null && !result.ContainsKey("position"))
            return Vector3.Zero;

        return result["position"].AsVector3();
    }
}
  • You can then change the shapecast to whatever suits your needs.
  • You can also give it a “blueprint” look by adding a cube mesh with some transparency.
  • You can snap the shape’s position to your desired grid positions via code and control the snapping point as well.