Need help understanding ray_cast

hi guys, thanks in advance for any and all help.

I’m trying to make an enemy wander around randomly, while staying in the play area, not running into walls, not walking off ledges (except in certain instances), and not run into obstacles.
to do this I figure firstly, that I’ll want to use the nav mesh for both navigating to the enemies selected destination, but also checking to make sure that destination is within the play area.

my steps for doing this are to 1: get the enemies coordinates,
2: generate a random number within a preset range to either add or subtract from the enemies x (and another for y) coordinates, and shift the z coordinate up a bit,
3: save the new coordinates to then use as the origin for a ray_cast (this is as far as I’ve gotten),
4: ray_cast down form the origin to the nav mesh, (or a set distance to avoid the ledge issue)
5: if the ray_cast hits the nav mesh, spawn an empty at the hit location, (if not, restart loop at generating a new set of coordinates, this ensures target location is in the play area)
6: ray_cast from enemy location (shifted up slightly to start from enemies eye-level) to the new empty,
7: if the ray_cast from enemy to empty hits nothing between them, set empty as target, (if not, restart)
8: feed target to steering actuator and activate it. (creating a path to fallow from enemy to target that avoids walls, objects, and leaving play area.)

this should be totally doable right? the problem is that I’m not sure how exactly to use the ray_cast function. I’ve been searching around the wiki, here, and google for info on it and have found alot of different info but I’m still not entirely sure how to use it.

for 1: how exactly do I make the ray_cast point down? (0,0,-1) for direction?

2: I’ve read somewhere (but can’t find it again) that there is a parameter for ray_cast that can take the name of a property, and that ray_cast can return weather or not it hit an object with that property. is this true? and which ray_cast function does this? (I ask this because I would have multiple areas each with their own nav meshes, so I can’t just do ‘1st hit == nav mesh name’)

3: just about everything I’ve found about ray_cast says something about local vs global space and I’m not sure how exactly that affects what I’m trying to do. if I’m trying to set the ray_cast origin as an offset of the enemies location, would the coordinates returned for the enemy be global? and if so then the origin would be global as well, but then the coordinates returned for where it hit the nav mesh would be in the nav meshes local space and needs to be converted to global before I can set and empty there right?

if I think of more questions (because I think I had more) I’ll add them in. thanks again for any and all advice and guidance you guys can provide :).

Hey there,

Raycast draws a line between two points; you need to provide it with coordinates.
To take an objects world position and shooting a ray downwards,

from mathutils import Vector

distance = 10
origin = Vector(own.worldPosition)
target = Vector([origin.x, origin.y, origin.z - distance])
hitObj, hitPos, hitNormal = own.rayCast(target)
#we don't need to pass origin because it's unchanged from own world position

if hitObj:
    print( "Raycast hit %s at %s"%(hitObj.name, str(hitPos)) )

Localizing the coordinates is necessary if you need the ray to account for object rotation.
The formula goes like this,

axis = 2 #z. 0 is x, 1 is y
target += own.worldOrientation.col[axis] * distance
#you'd need to add up rotation on each axis you want to account for

We can also have the ray detect only objects with certain properties and/or detect only objects in a given collision group by passing optional arguments. For instance,

own.rayCast(target, prop="wall", mask=own.collisionGroup)

You can read more about it on the API
https://docs.blender.org/api/2.79/bge.types.KX_GameObject.html?highlight=kx_gameobject#bge.types.KX_GameObject.rayCast

EDIT: typos.

1 Like

thank you for the explination, and also for the link. I had been trying to find the info I was looking for on that site but all I’d found were these:

https://docs.blender.org/api/current/bpy.types.Object.html?highlight=ray#bpy.types.Object.ray_cast
https://docs.blender.org/api/current/bpy.types.Scene.html?highlight=ray#bpy.types.Scene.ray_cast
https://docs.blender.org/api/current/mathutils.bvhtree.html?highlight=ray#mathutils.bvhtree.BVHTree.ray_cast

and none of those seemed to make things vary clear for me.
I was using “pos = own.position” but from comparing against your code I guess that puts the vector in local space right? and hitpos will return the world position of the hit object correct?
hopefully I’ll be able to get things working now :slight_smile:

position is deprecated; use worldPosition or localPosition. Same for scale and orientation.

Keep in mind python doesn’t create a new instance of a vector, list, array or dictionary when you assign it a name; pos = own.worldPosition returns a reference, not a copy. This means any changes to pos will affect the object’s world position. pos = Vector(own.worldPosition) does return a copy, and changes to pos won’t be reflected in the original.

hitPosition returns the ray’s point of impact in world coordinates. To get the world position of hit object, use hitObj.worldPosition.

1 Like

thank you :smiley: I’m glad I asked about that. sounds like hitPosition does exactly what I need it too. I think this solves all my ray_cast troubles :slight_smile:

I suggest to look at the 2.79 API Documentation of the BGE:

https://docs.blender.org/api/blender_python_api_2_78c_release/bge.types.KX_GameObject.html?highlight=raycast#bge.types.KX_GameObject.rayCastTo

The first two arguments can be vectors, representing xyz coordinates of a point in scene space (worldPosition). This is nice when you do not want to use the origin of specific objects.

1 Like

worldPosition and worldOrientation with or without .copy() is already a vector, so you don’t need to convert it thus you can leave the math module out of it.

i will also suggest to use

own.rayCast(target, from, distance)

instead of

obj.rayCastTo

rayCastTo on its own can cause some trouble the raycast don’t have.

2 Likes

Would you believe me if I told you I didn’t know copy() was a thing? :B
Yay, learning!

You can also use Python’s Copy module if you want a copy of a data block that way.

nah not many know and or use it.
It’s a neat function, it copies the location from current frame instead of the next frame (that without .copy does)

To be more precise:

worldPosition provides you with a reference to the position of the object. This means when the object moves, the value changes.

With worldPosition.copy() you create a copy of the value at the time of copy. When the object moves afterwards the copy will not move with it as it does not refer to the object’s position.

2 Likes