Ray-tracing collision, collision point detection and line draw issues in C#
Asked Answered
M

4

0

I am writing some ray-tracing code in C# for a game. The trace is supposed to beam from the camera view down towards wherever the mouse is pointing. While the ray-trace itself does happen correctly (I can tell from the output log), I am having a few issues beyond that and I have been looking for solutions for hours but could not find any. I have encountered a few plugin libraries in my search but I would like to avoid those as much as possible, and oddly enough looking through the code of one of them didn't provide me with a solution to these issues.

1) Finding the right functionality for detecting collision between the ray-trace and a physics object (static or otherwise) proves to be janky. For whatever reason the trace detects collision with 6 Variant colliders (key value paired) which I'm not sure how it does and does not log collision detection with what it's supposed to. I made sure of the layers, and they correspond, apparently the masks have no effect, but the layers do, and this janky collision detection only happens when layers correspond between the one I set in code and the one set on the collider of a given node object. When they don't correspond, no collision detection happens. The area it detects collision at all appears to be the point where the trace leaves the collider instead of the point where it's entering it.

2) Getting the point coordinates at which the collision happens when it does isn't something that I have been able to do.

3) Drawing a line to display where the trace happens also doesn't work. This is something I wanted to do just to make sure visually that the ray-trace is done correctly.

For reference, this is what the code looks like so far for the ray trace itself (works as intended).

float RayLength = 100f;

var camera3DNodes = GetTree().Root.GetNode<Node3D>("Node3D").FindChildren("Camera3D");
ForEachNode(camera3DNodes);

var camera3D = cameraNodes[0] as Camera3D;
GD.Print(camera3D.Name);

var from = camera3D.ProjectRayOrigin(eventMouseButton.Position);
GD.Print("Vector Start Position: " + from.X + ", " + from.Y + ", " + from.Z);

var to = from + camera3D.ProjectLocalRayNormal(eventMouseButton.Position) * RayLength;
GD.Print("Vector End Position: " + to.X + ", " + to.Y + ", " + to.Z);

Then this is the section for the former 2 issues. The second one I couldn't really get to until the first is working as intended.

Godot.Collections.Array<Rid> intersectionArray = new Godot.Collections.Array<Rid>();
var query = PhysicsRayQueryParameters3D.Create(from, to, 1, intersectionArray);
var collision = GetViewport().World3D.DirectSpaceState.IntersectRay(query);

And this for the last one. I will be honest, I could not figure out what the purpose of SetUV actually is or what it's meant to represent, so that might be contributing to the issue. For context, the "to" and "from" variables are set in code as shown previously.

var surfacetool = new SurfaceTool();
surfacetool.Begin(Mesh.PrimitiveType.Lines);
surfacetool.SetColor(new Color(255, 0, 0));
surfacetool.SetUV(new Vector2(0, 0));
surfacetool.AddVertex(from);
surfacetool.AddVertex(to);

Also excuse the bad architecturing of this, I know how to structure some of this better, but I've done this for the moment while I'm getting used to syntax as I'm new to Godot.

Murmuration answered 16/10, 2023 at 13:1 Comment(0)
S
0

Godot.Collections.Array<Rid> intersectionArray = new Godot.Collections.Array<Rid>();
Why passing an empty array here? This is the array of specific objects you want to exclude. I think perhaps the problem is in your setup of the PhysicsRayQueryParameters3D. Make sure you look over the documentation page: https://docs.godotengine.org/en/stable/classes/class_physicsrayqueryparameters3d.html#class-physicsrayqueryparameters3d-property-exclude

For collision_mask, if you want to use the same mask you can set in the inspector, use the collision_mask member variable as described here: https://docs.godotengine.org/en/stable/tutorials/physics/ray-casting.html#collision-mask. Make sure you're targeting only the layer of the thing you want to hit.

Some other properties of PhysicsRayQueryParameters3D may be important to you as well. Is the thing you're trying to hit derived from Area3D or from PhysicsBody3D? You can set collide_with_areas and collide_with_bodies accordingly.

If you're still not having any luck, and you suspect it's C#'s fault, then try it in GDScript. GDScripts can run alongside C# just fine. If the GDScript works and the C# doesn't, then you've encountered a bug in Godot.

Stargell answered 17/10, 2023 at 21:21 Comment(0)
M
0

I've traced what the output of 6 key value pairs is and turns out they are details relating to the collision of the colliding object. I was expecting just an ID or the name but instead it's 6 pieces of information relating to one and the same object being collided with. I was able to retrieve what I needed, like for example the point of collision by specifying key via collision["position"] though I'm not sure how you would be able to do this if you have access to the key specifically in this instance and wanted to get its information as by default it is a Variant type and so can't be retrieved by casting or any means besides just logging it as I did with GD.Print which seems strange. This has been quite the hassle so far for simply trying to retrieve the collided object or any sort of information of it from a ray-trace but at least it is now functioning.

Still no clue on what SetUV is for and how to go about simply drawing a line.

Murmuration answered 26/10, 2023 at 14:20 Comment(0)
S
0

If you're just drawing the line for debug purposes, the most straightforward way is to use CanvasItem.draw_multiline. I made a simple script here showing how it can be used.

extends Node2D

@export var tracking : Array[Node3D]
var points : PackedVector2Array

func _ready() -> void:
	points = PackedVector2Array()
	points.resize(tracking.size())

func _process(delta: float) -> void:
	queue_redraw()

func _draw() -> void:
	for i:int in range(0, tracking.size(), 2): #every even point starts a line, odd point ends a line
		var from = tracking[i]
		var to = tracking[i + 1]

		var camera = get_viewport().get_camera_3d()
		points[i] = camera.unproject_position(from.global_position)
		points[i + 1] = camera.unproject_position(to.global_position)

	draw_multiline(points, Color.CHARTREUSE)

In your case, you would be using your ray-trace beginning and end global positions instead of the Node3Ds I'm using here.

Stargell answered 27/10, 2023 at 0:39 Comment(0)
M
0

With C# at least I am getting a requirement of having to define a CanvasItem game object to actually use DrawLine() or DrawMultiline() methods. Attempting to create a CanvasItem object brings out a constructor error and searching through the file I can't identify a clear constructor there at all, so I'm not sure how I could even access any of this.

If I'm understanding this correctly, it also appears this is a 2D vector, and I understand projecting that on a plane space, I think, however I'm not entirely sure that would be good enough for debugging every time if I can't see where the line is projected on a 3D space. It does however also appear that Godot doesn't have an option to see the actively visible and configurable game objects during runtime in the editing viewport (at least not yet), only the Remote and Local scene object hierarchies, so maybe we would need to wait for something like that to become part of the engine's implementation first.

Murmuration answered 27/10, 2023 at 16:35 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.