Godot Version
4.5.1
Question
So I’ve set up this boid in a box made of a static body and an area 3d. At the moment the boid is a rigid body 3d. Right now I’m manually adjusting the flight path but I’d like to design it for later use when I’d like it to hit something and act on its own physics rather than my trajectory. Right now I’m having a problem orienting the ray casts I’m using to detect area 3d nodes that enter its visibility cylinder.
Originally I planned on making my ray cast function like the diagram below. Whenever an object entered the area 3d node it would be detected and a ray cast search process would begin. (this has been achieved). Essentially a hemisphere would be drawn around the origin of the boid and rotated to so that the ray casts would be at radius distance from the boid, with the central ray of the hemisphere being oriented along the boids forward direction/movement. Using its liner velocity to get its forward direction.
After smacking my head against a wall for a week, I’ve realised no matter what I do trying to find a to offset the the angles using the linear velocity even just along the 6 cardinal cases is proving to be outside my current capabilities. I imagine this problem will probably require some matrix maths but I’m not sure how to proceed with that path or even start towards it.
IDK I feel like there is an obvious solution that I am missing rn. But I can’t place my finger on it.
This is the code I’m using on the boid:
func ray_sphere( space_state, origin : Vector3, current_transform : Transform3D, radius : float):
#arrays for storing new and viable
var new_direction : Vector3
var valid_directions = []
# vel convert to direction vector
direction = linear_velocity.normalized()
#ray_cast var
var ray_cast_x = 0.0
var ray_cast_y = 0.0
var ray_cast_z = 0.0
var ray_cast_target = Vector3(ray_cast_x, ray_cast_y, ray_cast_z)
var diff = ray_cast_target - origin
#ray cast var
var query = []
var result_ray = []
## use 3 loops to look through, start from 0deg and work outwards
# create array to store previous checks no repeats
var deg_exceptions : Array = []
# dont test if false, only check once
var testable = false
### offset theta and phi by direction value
#var theta_offset = rad_to_deg((acos(direction.x)))
#var phi_offset = rad_to_deg(acos(direction.y))
for angle_observation in range(0.0, 100.0, 10.0):
for theta in range(-angle_observation, angle_observation+10, 10.0):
for phi in range(90-angle_observation, 90+angle_observation+10, 10.0):
# convert to float
var f_theta = float (theta)
var f_phi = float (phi)
# check if in expception space, if they are dont test
for i in len(deg_exceptions)+1:
if len(deg_exceptions) == 0:
testable = true
# add to exception to ensure no rechecks
deg_exceptions.append(Vector2(theta, phi))
#print('passed')
break
if Vector2(theta, phi) == deg_exceptions[i]:
testable = false
#print(' cant test :', theta,' ', phi)
else:
testable = true
# add to exception to ensure no rechecks
deg_exceptions.append(Vector2(theta, phi))
#print('passed')
break
# if they passed the check test them
if testable == true:
var temp_x:float
var temp_y:float
var temp_z:float
# do the raycast calc
temp_x = cos(deg_to_rad(f_theta))
temp_y = cos(deg_to_rad(f_phi))
temp_z = sin(deg_to_rad(f_theta))
var temp_vect = Vector3(temp_x, temp_y, temp_z)
var temp_vect_norm = temp_vect.normalized()
# use the ray casts direction * the origin to get the cast location
ray_cast_target = origin + radius * temp_vect_norm
diff = ray_cast_target - origin
# cast the ray and check if hit anything
query = PhysicsRayQueryParameters3D.create(origin, ray_cast_target)
query.collide_with_areas = true
query.collide_with_bodies = true
result_ray = space_state.intersect_ray(query)
# when ray is empty pass direction back
if result_ray == {}:
# make sure he solution hasnt been returned at vector.zero
if diff.normalized().length() > .9:
valid_directions.append(diff.normalized())
pass
pass
pass
pass
## check if valid direction has been found, if so break loop
if len(valid_directions) > 0:
break
pass
## checks are over
# after all the checks pick the first one
if len(valid_directions) > 0:
var temp = valid_directions[0].normalized()
if temp.length() > 0.9:
new_direction = temp
## send back direction and use to update velocity
return new_direction
This function does report the right direction back. I’ve spent days making sure to check every value so I know it’s not bugged elsewhere.
This is the Physics process used to call the function:
func _physics_process(delta: float) -> void:
# fetch the space server to do physics stuff
space_server = get_world_3d().direct_space_state
# get resource id
var rid := get_rid()
# physics state
var phys_state := PhysicsServer3D.body_get_direct_state(rid)
## if we have seen a coll update path to avoid
if seen_cols:
# create ray and cast from this boids position, check for collisions on rays and randomly choose the first one isnt hitting anything. Spiral out form theta = 0 phi = 90.
var temp_vel = ray_sphere(space_server, global_position, global_transform, ray_length)
get_tree().set_pause(true)
# make sure were not loading zeros
if temp_vel.length() > .9:
vel = temp_vel
vel *= speed
pass
I’m also using this rotation function to rotate my object to face the direction of motion. The forward direction in the local space would be (1, 0, 0) I think. If I set a random motion it does rotate to match.
func rotation_calc(state : PhysicsDirectBodyState3D, current_transform : Transform3D, target_direction : Vector3 ):
var forward_local_axis : Vector3 = Vector3(1.0, 0.0, 0.0)
var forward_direction : Vector3 = ( current_transform.basis * forward_local_axis).normalized()
var local_speed : float = clampf( rot_vel, 0, PI)
if forward_direction.dot(target_direction) >= 1e-4 or forward_direction.dot(target_direction) <= -1e-4:
state.angular_velocity = (local_speed * forward_direction.cross(target_direction)) / state.step
# its parallel, nudge it out a bit so the other condition can update correctly
if forward_direction.dot(target_direction) < -.99 or (forward_direction.dot(target_direction) < 1e-4 and forward_direction.dot(target_direction) > -1e-4):
state.angular_velocity = (local_speed * Vector3(0.0, 0.0, .01)) / state.step
pass
pass
func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
#update linear velocity
set_linear_velocity(vel)
#update roatation
rotation_calc(state, global_transform, linear_velocity.normalized())
pass
I appreciate you having a look. If the solution is really simple for a beginner and I’ve just over looked something obvious, please just hint at it. Its been almost 6 years since I’ve done anything with trig or matrices. I really need to work on it more.


