Blender Game engine Hitscan

I’m trying to make a hit scan weapon in my game (i know bge is no longer supported).
I know a little bit of python and understand that i could use castray() but this function seems to have 4 million inputs and the documentation i found wasn’t very clear.
I’m wanting to cast a ray from the camera either constantly or when a button is pressed (i can work with both probably). Once the ray is in contact with an “enemy” its health property would need to be changed.
Is this at all possible?

Thanks in advance!

You’ll have to be mindful of collision groups or look for objects with specific properties.

from bge.logic import getCurrentController
cam = getCurrentController().owner.scene.active_camera
ray_distance = 12 #meters

#vecTo = a point directly in front of the camera up to ray_distance
vecTo = cam.worldPosition.copy()-(cam.worldOrientation.col[2]*ray_distance)

#detect only objects with "actor" property;
#ignores all others, returns first actor hit.
hitObj, hitPos, hitNormal = cam.rayCast(vecTo, prop="actor", xray=True)

if hitObj: #we've hit something
    #run your damage formula thingy here.
    hitObj["health"] -= 1

You can read more here.
https://docs.blender.org/api/2.79/bge.types.KX_GameObject.html?highlight=kx_gameobject%20raycast#bge.types.KX_GameObject.rayCast

upload.blend (601.7 KB) thanks, but I’m still struggling, i have attached my blend file (please excuse anything funky you see).
Sorry about this and thanks for your help.
As shown in the file the camera has the python script activated by the left mouse button and the cube has both a health property and an is set as an actor in the physics tab.
Really sorry if i have missed something stupid.
Thanks

What problem are you having? The cube loses health when I click.

on the cube there is a equal to property that is linked to a simple motion and it isnt working. how can you tell its loosing health?

I marked health as a debug property. You can do so in the properties panel, clicking on the button with the blue circle and an i. Then game->show debug->show debug properties.

And uppon downing the cube’s health, it jumped pretty high on the z – seems to be working as intented. However if health drops below 0 the cube won’t jump, you could use “less than” on the property sensor rather than equal.

really sorry. I’m using the exact same blend file i uploaded. i used the debug properties (thanks for telling me about that). I press the left mouse button when aiming at the box and just nothing happens. I’m very confused. Any idea? The debug doesn’t change for me. did you alter anything?

That’s odd. I only added a crosshair so I could tell where I was aiming.
Is the console spitting out any errors? Maybe my code uses something that’s not present in your build of blender.

Im silly to not check the console. rayCast() takes no keyword arguments

Oh crap. What build are you using?

2.79 why? i have used raycast in other scripts. For instance:

if obj is own.rayCastTo(obj, radar.distance):

@jaimekelly

Try this other method :arrow_down: it’s simpler

Raycast_Damage.blend (143.4 KB)

1 Like

To check the API, minor changes between versions and all that.
Because it’s not taking keyword arguments we’ll have to pass things in order.

cam.rayCast(vecTo, None, 0, "actor", 0, True)
1 Like

OK. Both of you thankyou. both methods worked randomperson’s is quicker but Liebranca’s is more flexible.

For anyone who reads this this is the full script:

from bge.logic import getCurrentController
cam = getCurrentController().owner.scene.active_camera
ray_distance = 12 #meters

#vecTo = a point directly in front of the camera up to ray_distance
vecTo = cam.worldPosition.copy()-(cam.worldOrientation.col[2]*ray_distance)

#detect only objects with "actor" property;
#ignores all others, returns first actor hit.
hitObj, hitPos, hitNormal = cam.rayCast(vecTo, None, 0, "actor", 0, True)

if hitObj: #we've hit something
    #run your damage formula thingy here.
    hitObj["health"] -= 1

put this on your camera and link it to an always sensor
Then just give your target a “health” property.
Final question Liebranca, in that vector is there a property/ material filter? so it only detects objects with a certain property besides health?

Thank you both of you.

1 Like

Passing a property name to raycast makes it so only objects with a property named such are detected. If you want to sort by materials as well, you’ll need to get the hitPoly as well and check it’s material name.

hitObj, hitPos, hitNormal, hitPoly = cam.rayCast(
        vecTo, None, 0, "actor", 0, True, 1)

if hitObj:
    if hitPoly:
        print(hitPoly.material_name)

Or if you’re certain all the objects you’ll be casting rays towards use only one material, you could try

if hitObj:
    print(hitObj.meshes[0].getMaterialName(0))

If I’m not mistaking, when the poly argument for raycast is set to 2 it returns UV coordinates as a fifth value (so hitObj, hitPos, hitNormal, hitPoly, hitUV = raycast(yadda, yadda) ). I haven’t played around much with that so can’t give any advice on it. But the thing is is there and likely can be used for complex funk.

No offense taken. I personally never got .raycast() to work. Will try the script later.

:slight_smile:

why don’t you create a simple function? saves you a lot of coding. also you don’'t have to at the distance to the vector, you can simply use it in the rycast itself.

def camera_ray(cont):
    #shoots a ray in front of camera

    own = cont.owner
    distance = 20
    
    camera      = own.scene.active_camera
    y_direction = camera.worldPosition - camera.worldOrientation.col[2] 
    
    ray         = camera.rayCast(y_direction, camera, distance)

then simply execute any other code you need

    hit_obj = ray[0]

    if hit_obj:
        if 'health' in hit_obj:
            #do anything you like
            hit_obj['health'] -= 1

            if hit_obj['health'] <= 0:
                hit_obj.endObject()

this way you can reuse the ray instead of simply looking for one specific property.

Well, uh, I didn’t give it much thought to be honest; just wrote a basic example, and looking for a property + xray simplifies things a bit, what with objects that should not be detected by the ray getting in the way, that kind of thing.

But now that I think about it… this makes it so my ray ignores walls. Ouch.
Definitively not a great idea in retrospective :B

Sorry im replying late.
how would i make this ray stop at walls?
currently it works through anything, is there even a way to do so?

You’ll have to pass false for xray. The raycast will stop at the nearest hit, but return None if the property is not found.

cam.rayCast(vecTo, None, 0, "actor", 0, False)