rayCast() - doesn't detect target at close distances

EDIT: I’m very sorry, I mixed up rayCast() with rayCastTo(), I can’t change the title.

Hi,

I’ve set up a little test scene for point-and-click-adventure style pathfinding, and to find out if I even have to run my pathfinding procedure, I check if the player can see the target (a cube that is placed at the mouse click position). What I don’t understand, is why the rayCastTo()-method I’m using returns None, when player and target are close to each other. Here’s the .blend
Just place the target-Cube near Suzanne and watch it print ‘None’ instead of ‘target’.
What am I doing wrong here?

Thanks in advance,
Jay-D

Changing this hitPos[-1] = 0.1 made it work.

Hm, not really. It works in some cases, in other cases it now prints ‘Room’, so the ray somehow gets stopped by the floor. This is not the behavior I would expect and I think it should work regardless of what the target’s z-position is (as long as it’s inside the room). I’m puzzled…

Ensure that you’re calling it correctly - with the source and target in the correct order. Also I’m not sure if it includes the owner object of the ray if it collided with the mesh so try it with an empty outside of the mesh.

Sent from my Nexus 7 using Tapatalk HD

Sorry, I used rayCast() before but switched to player.rayCastTo(target), so the order should be correct.

Also I’m not sure if it includes the owner object of the ray if it collided with the mesh so try it with an empty outside of the mesh.

I’m not sure if I understand what you mean, but I tried to replace the player, the target and both at the same time with empties, and still rayCastTo() returns None at close distances.

to me seem that return None when ther player and target not there nothing.

read so in this way :
obj.rayCastTo(target) = there some obstacle between obj and target?

if not there anyone return None, otherwise return the obj hit.


def changeTarget(c):
    mOver = c.sensors['mOver']
    lmb = c.sensors['LMB']
    
    if mOver.positive:
        if lmb.positive:
            target.worldPosition = mOver.hitPosition  + mOver.hitNormal * 0.1
            # check player's line of sight to target
            player = c.owner
            if player.rayCastTo(target) == None:
                 print("i see the target")

i’m correct?

EDIT:little correction moving the position along the normal, not sure if is usefull

I think I now understand what you meant, agoose77, the same as MarcoIT wrote, that the target itsself is ignored by the ray. However, this doesn’t seem to be the case, as I can get ‘target’ printed, if I place it to the far right of the player. So I guess I could check for target or None and then assume, that the line of sight is not interrupted, but I still find that behavior too be illogical and maybe even unpredictable. How hard can it be to just check if a line between two objects hits anything else?

The demo file is not really good in presenting your problem.

A) The important objects are not selected nor marked
B) there is no instruction what to do - But you posted then here so it is fine - I think I figured that out

If I you understand you correct - your problem is, you click on Suzanne (system mouse cursor is disabled!) and the cube “target” is placed at the hitPosition. In this situation you do not get an hitpoint?

A) If you cover Suzanne’s origin with the target’s mesh (Suzanne’s origin is inside the mesh) the ray can’t detect and faces as there are none. Remember the ray goes from origin to origin!

B) But you are right there is another issue that prevents the ray to detect the mesh. The first time you move target you will not measure the hitPosition. Just the second raycast does this. You can test this by clicking the upper left corner of the large cube. You will see the first click it does not detects anything. But the second click.

I guess this is because the physical object did not move while your python controller is running. In other words the faces to be detected are not there at the moment you call rayCastTo( ). The distance does not matter. This is just a guess, I do not know if that is the real reason.

To fix that use rayCast( ) rather than rayCastTo( ).

Some remarks (not related to the above issues):

  • no need to enable True Pulse on mOver, LMB does not need Tap.
    [LIST]

    • If your game is complete you will always be over an object. But you do not need to run the python controller all the time.
    • Your Python controller only reacts on Mouse click. So run it on mouse click only. If you really want to drag the target, you can enable True Pulse on LMB (rather than mOver).
  • in code please write meaningful names lmb, a, b, c, hitPos etc. are unnecessary and non-standard abbreviation. You have a computer you can always copy&paste.

  • with function names you do much better, but there is still room for improvement

    • changeTarget implies this code changes the target, but it repositions the target -> misleading name. I suggest to call it setTargetToHitPosition
    • aSeesB is better. As alternative I suggest canSeeEachother --> if canSeeEachOther(player, target): …
  • objs - setting this on module level is dangerous.

  • It is called once and does not reflect any update to the scene.

  • It contains the objects of the scene where an object calls this module first (this can be the incorrect one). I hope you did that for testing only.

  • If you need to grab an object by name you can read it from the current scene at the time when you need it. This code does not run that often so it is no big performance issue.

  • render.showMouse(True)

    • A) is undocumented behavior - nothing indicates this is performed = unwanted side-effect
    • B) it is not called at game start but by any call to the module
    • better enable the mouse cursor with the according Render/Display settings or with code that mentions it does exactly this. E.g. an Object “Mouse.EnableCursor” + controller “mouse.enableCursor”
  • # reposition target -> why not place this code in def repositionTarget()?

  • # check player’s line of sight to target -> this is redundant aSeesB describes it already (see suggestion above)

  • I suggest to place the important functions at the top of the file (e.g functions you can call from bge). A reader can read top down rather then jumping from one location to another.

  • If you return when you know the code has nothing to do you get much smaller code blocks and the blocks move more left = less complexity. (Attention: this does not work in all situations)
    [/LIST]


def setTargetOnHitPosition(controller):
    if not areAllPositive(controller.sensors):
        return

    mOver = controller.sensors['mOver']
    player = controller.owner
    target = player.scene.objects["target"]

    repositionTarget(target, mOver.hitPosition)
    
    # the following code has a different purpose, 
    # it is not mentioned in the function name
    # you can change the function name to reflect 
    # the additional purpose e.g. by adding ...And...
    # - currently used for "debugging"
    if canSeeEachother(player, target):
        print("{} can see {}".format(player.name, target.name))
    else:
        print("{} can't see {}".format(player.name, target.name))      
    
def repositionTarget(target, newPosition):
    target.worldPosition.x = newPosition[0]
    target.worldPosition.y = newPosition[1]
    target.worldPosition.z = 0.5 # really necessary? Maybe set the target at the right Z beforehand and skip this code
            
def canSeeEachother(objectToLookFrom, objectToLookTo):
    viewDistance = objectToLookFrom.getDistanceTo(objectToLookTo)
    rayCastResult = objectToLookFrom.rayCast(
                        objectToLookTo, 
                        objectToLookFrom,
                        viewDistance)
    hitObject = getHitObject(rayCastResult)
    return objectToLookTo is hitObject

def getHitObject(rayCastResult):
    return rayCastResult[0]
      
def areAllPositive(sensors):
     for sensor in sensors:
         if not sensor.positive:
             return False
     return True   

This is refactored code with small changes to the logic.
*Please notice “lmb” is gone. There is no need to know the name of this sensor. Now you could even add more or different sensors.
*The mouse cursor will not be set anymore. (see above)
*The target is read from the same scene as the player resides in.
*The print statement is gone, but replaced by two others.
*rayCastTo() is replaced by rayCast().
*Replaced the comments with functions.
*Added new comments with remarks to think about. They can be removed after you decided how to continue.

Regardless of this remarks your code is pretty good. I can see you are trying to write good code.
I hope this helps

Thanks for the extensive answer, Monster. I’ll do my best to return the favor.

First of all excuse my apparently lousy example-.blend, my initial post was one of those frustrated-latenight-posts, that should better be written after a few much needed hours of restorative sleep. My apologies.
It was never my intention to seed confusion, I was talking about the scenario, when the two objects are close to each other but not intersecting or touching. My understanding is that “player.rayCastTo(target)” casts a ray from KX_GameObject player’s center (represented by Suzanne) to the KX_GameObject target (small cube), ignoring only the mesh of player and returning the first object it hits on its way, which can be the target, meaning there is no other object in between. Looking at it this way, I feel it should never return None.

Your guess, that the rayCastTo() is somehow called before the target’s change of position seemed to be true at first, but if I first place the target next to the player, and then behind one of the obstacles, it should print “None” first and “target” second, as it would alsways be one step behind, but it prints “None”, “Obstacle”, so it does seem to physically change the position before the ray is casted. This is what keeps me awake at night and forces me to write badly thought-out threads.

Using rayCast() instead of rayCastTo() doesn’t help.

About the remarks:
When I start a new project, I usually make a concept file, where I try to get the seemingly most difficult part to work first. As a result I switch stuff around a lot and try many things and don’t really spend much time on meaningful yet concise naming, that normally happens, when I’m more or less convinced, that I can accomplish everything and start a new file to rebuild just what I need in a cleaner way.

  • About True Pulse and Tap: That’s definitely right. I should have switched them back off. Thanks.
  • objs: I know about the dangers of setting objs once, as you assumed correctly, it’s just for testing.
  • render.showMouse(True) - I don’t really see the problem here.

[INDENT=2]A) it sure is documented.
B) the mouse cursor showing up seems to be a pretty good indicator, that it is indeed performed.
C) I think it is only called once, not at the game start, but for testing, I felt it would be sufficient, if the cursor shows up as soon as the mouse moves. The script is run in module mode, so:

The main level code (indentation 0) gets executed the first time the module is accessed.
Source[/INDENT]

  • about the two comments: Sorry, just my hopeless tries to help strangers understand the code.
  • more, smaller functions: In general I would say amen, brother, but is it really necessary to write a new function to get the first element of a tuple? I think a comment would be enough here.

*Added new comments with remarks to think about. They can be removed after you decided how to continue.

  • I’ll think about them, thanks for the permission to delete them.

Wow, that actually was fun. I hope you don’t take this the wrong way, I do appreciate your effort towards clean and understandable code and didn’t mean to be monstrous:p. I promise improvement. Thanks for taking the time to look into this and write such a long answer.

To sum it all up, I still can’t understand, why I don’t get the expected behavior and rayCastTo() returns None sometimes. I hope somebody can shed some light on this.

One Love,
Jay-D

Your demo is not so bad. The same as you feel frustrated with your problem is if I look at a file expecting to see a problem and I have to dig through the file to find it ;).

Btw. I expect the same things from rayCastTo(). Unfortunatelly it does not fulfill our expectations :ba:. For me the rayCast() works much better than rayCastTo() just keep in mind that it is: fromObject.rayCastTo(toObject, fromObject)
I had it the other way around which made it act different to my expectations.

Regarding render.showMouse - with undocumented I mean I have to read the code to see this statement is there, but I did not expect it here. So it is a “hidden feature”. If you use it as test code than it is fine. Unfortunately such things have the awful characteristics to remain in code making you crazy to find out why you can’t disable system mouse cursor when your game development goes on :evilgrin:.

Regarding comments: writing a function is usually not more work than writing a comment. A comment can quickly become a plain lie, while a function name will be questioned if it does not match the expected behavior. Be aware: comments are usually not as good maintained as code. If you really need long comments you should think about the design of your code. Code should not need explanations (except there is technical background knowledge necessary).

Example:
getHitObject(rayCastResult) vs. rayCastResult[0]
Which one is easier to understand (without deep knowledge of rayCast)?
How much cost you a comment?
How much costs you the function?
How much will it cost when you leave without information?

Btw. I do not want to say my suggestions are the best way at all. I just want to show there are ways to make the code cleaner (especially if you have working code already). Working code is better than non-working code. Clean code is much better ;).

Attachments

rayCast_revised2.blend (439 KB)

and that is clean? Monster?? O_O’

:smiley: , i think you joks :wink:

EDIT : a new function add complexity , is not “free” , the user need to know how it work , what argument require, and what return .
if a function increase complexity is useless
“meanly” should cut code , not add code

PS: this is pretty “clean” for me :slight_smile:
(always true is not necessary in this case, but can become usefull after, and not worth make “optimization” on these kind of obj … 0.02 ms more anyway…optimization only at the end… if need)

Attachments

rayCast3.blend (99.7 KB)

Thanks for the answers you two.
I didn’t mean to start a fight about functions vs. comments, I guess it’s also a bit a matter of taste (and laziness :)).

Regarding the problem: I think for my specific case, MarcoIT’s solution (making the target No Collision and checking if the return value of rayCastTo() is None) is the most convenient. Monster, your solution puzzles me, as the target cube seems to fall down after changing it’s position, eventhough it’s set to Static. There still seem to be (rare) moments, when rayCast() returns None.

Nevertheless, I can now continue with my project, but I still find the return values of both rayCast-functions a bit confusing. I wonder if I should create a bug report about it? I still hope somebody can figure out what’s going on inside the rayCast-methods.

As far as I see it can’t fall. It sets the Z axis to constant 0.5 as in your code. If you found a good solution it is fine.

You nail it. I find it confusing too.

A list is indeed not a good container for different objects. A separate return value class would be much more convenient.


The user can guess what it is doing by reading it’s name. If the user has to look inside the function to “discover” what it does - the name is simply bad. There should be no need to look into a function. The only reason to do so is when you are interested in the details.
As less parameters as better it is. Even parameters need good names. A function should return what the name say - nothing else. If a function does more than the name says it “smells” like bad code.

Shorter code does not necessarily mean cleaner code. Indeed a lot of the functions in my above examples should be moved to another module. Then you get short functions and short modules ;).

Btw. you cleaned the code too, by removing all the code that is not necessary :wink: (except import and showMouse - they is still there). Unfortunately you changed to logic as well:

  • Target is now a child of player.
  • Target is not placed at z-Axis 0.5 as in the original file
  • added an always sensor which is unnecessary eating processing time :confused:. True Pulse on LMB is more efficient

def placeTargetAtHitPositionAndSetPropertyWithSeeing(controller):
    player = controller.owner
    target = player.scene.objects['target']
    
    mOver  = controller.sensors['mOver']
    lmb    = controller.sensors['LMB']
    
    if not lmb.positive or not mOver.positive:
        return
    
    target.worldPosition = mOver.hitPosition + mOver.hitNormal * 0.01
    target.worldPosition.z = 0.5

    if not player.rayCastTo(target):
         player["prop"] = "i SEE the target"
    else:
         player["prop"] = "################"
         

this still can be cleaned up further:


from internal import *

def setTargetAtHitPositionAndSetPropertyWithSeeing():
    if not areAllSensorsPositive():
        return
    
    target = findTarget()
    
    setAtHitPosition( target )
    setPropertyWithSeeing()

compare which one is easier to read :wink:

i understand what you mean

i think the point is the “total complexity” of the task that the script should solve.
this in usually the my approach (maybe is wrong)

if the task is simple i prefer keep all in “one piece” ,
the target in this case is keep the script more short possible (if possible also clean of course but is not so matter)

if the task is hard the code need to another approach, must be subdivided in 2 piece:

  • the “core” code
  • the “helper” code
    in this case the task is not more the length , or better, is valid only for the core code.
    the helper code have to make the “dirty work” , have to be correct and robust, no matter if is long.

but the class(instead of the function) , >seem< more adapt to this second approach… :wink:

  • experiment - :slight_smile:

import bge


class NewObject(bge.types.KX_GameObject):
    def __init__(self, oldGob):
        class EmptyClass :
            def seeTarget(target):
                if self.rayCastTo(target):
                    return False
                return True
            
        self.new = EmptyClass
        
        
def changeTarget(cont):
    player = cont.owner
    
    if not "init" in player:
        player["init"] = 1
        player = NewObject(player)
        
        
    target = player.children['target']
    
    mOver  = cont.sensors['mOver']
    lmb    = cont.sensors['LMB']
    
    if lmb.positive:
        if mOver.positive:
            target.worldPosition = mOver.hitPosition + mOver.hitNormal * 0.01
            
            if player.new.seeTarget(target) : 
                player["prop"] = "i SEE the target"
            else:
                player["prop"] = "################"

length + 14 lines
difficulty + 80%
:rolleyes: , but the last line heres clean