RTS Soldiers' Steering target

I have a soldier, if you click on him, he is set to active unit (click off him he deactivates). He is bought and spawned through a building you can build.

My problem, is that I can’t figure out a good way to make him go towards the place I right-clicked.
I had an empty parented to him, and if he is active and I right-click on the ground, the position of the empty would go there and he would steer to there. But the empty is parented to him. So I had it remove parent when I clicked on the ground. But the way I got that empty was through a child list. So I tried to parent it back to the owner after the steering was done, but then it couldn’t do that because the child link was broken and the reference couldn’t be made.

I can make the soldier attack specific enemies on a right-click. So I am going to have to work that in to the solution.

Thanks

thats weird, im using the same method and it works for me, post a part of the code and we can compare to see what is wrong, here is mine:

for path in own.children:    
    if path.name[-5:] == "_path":
         pathFind.target = path

the target destination object NAME must be like this “wathever_path”

Well I royally messed it up. But using what you did:

The indents got messed up.

    children = owner.children    
       for dest in children:
           if dest.name == 'where':
                steer.target = dest
                dest.removeParent()
            

Works better, but the wrong child being set to dest. :spin:

Now I am changing it to:

children = owner.children    
    for dest in children:
        if 'dest' in dest:
            steer.target = dest
            dest.removeParent()
            

This just screws up the whole parent thing. It gets the wrong child
And the target isn’t being set.

i forgot to say that i made the remove parent by using a logic brick, try that, i also got some problems doing this

children = own.children
for dest in children:

thats why in my code i did it like

for dest in own.children:

its weird i know, but try, it may or may not work

None of the child list methods are working. I had it print the object it got, and it was right. But it didn’t set it as the steering target and if didn’t assign the right object as dest. Assigning the target outside of the if statements is the only way I can get this to even remotely work. But both ways (find property in object or find name in list) don’t removeParent()

Here is the full script:

import bge

def main():
    cont = bge.logic.getCurrentController()
    owner = cont.owner
    scene = bge.logic.getCurrentScene()
    
    steer = owner.actuators['Steering']
    lmb = owner.sensors['Mouse']
    rmb = owner.sensors['Mouse2']
    overany = owner.sensors['Mouse3']
    active = owner['active']   
    time = owner['time']
    
    children = owner.children
    for dest in children:
        if dest.name == 'where':
            print ('works', dest)
            dest.removeParent()

    steer.target = dest
    cont.activate(steer)
    
    if active == False:
        owner.setVisible(False)
    
    #selecting
    if lmb.positive:
        if overany.hitObject == owner:
            owner['active'] = True
            if 'soldier' in overany.hitObject:
                owner['active'] = True
        if not 'soldier' in overany.hitObject:
            owner['active'] = False
    
    #active unit
    if active == True:
        owner.setVisible(True)
        #action
        if rmb.positive:
            if 'ground' in overany.hitObject:
                dest.worldPosition = overany.hitPosition

  



i dont see why it is not working for you, i extracted a part of your code, and did it like this

import bge

cont = bge.logic.getCurrentController()
owner = cont.owner
scene = bge.logic.getCurrentScene()
    
steer = owner.actuators['Steering']




children = owner.children
for dest in children:
    if dest.name == 'where':
        print ('works', dest)
        dest.removeParent()
        steer.target = dest
        cont.activate(steer)



and it worked, i tested it with a script, maybe making it with a module is screwing things up

Huh. I am using 2.65, if that means anything. The module doesn’t matter. Script does the same thing. Ugh.

I am going to download 2.66a and see what happens

Nope, still doesn’t work. I think it has to do with:
dest.position = overany.hitPosition

EDIT!!

I moved all that under the rmb if statement. And it works! but I can only do it once :\

    #active unit    
    if active == True:
        owner.setVisible(True)
        #action
        if rmb.positive:
            if 'ground' in overany.hitObject:
                children = owner.children
                for dest in children:
                    if dest.name == 'where':
                        dest.removeParent()
                        steer.target = dest
                        cont.activate(steer)
                        print ('works, ob being seeked is', dest)
                        dest.position = overany.hitPosition
        

Might have found the problem

children = owner.children
for dest in children:
    if dest.name == 'where':
        print ('works', dest)
        dest.removeParent()

steer.target = dest
cont.activate(steer)

  1. dest is always going to be the last child in the list (regardless of the name) unless you invoke break after you’ve found the object in question.
  2. when calling removeParent(), dest will no longer be a child of owner

You could replace that block of code with this one (untested):


if not 'target' in owner:
    for dest in owner.children:
        if dest.name == 'where':
            print ('works', dest)
            dest.removeParent()
            owner['target'] = dest

if 'target' in owner:
    steer.target = owner['target']
    cont.activate(steer)
else:
    print ("Warning - target not defined")

clever, store the name in a property, i was doing the same, but directly storing it into the steering actuator, but it seems that this doesnt works :confused:

Hmm… you were on the right track.
One could do steer.target = dest in the loop and call it good.
We shouldn’t need to constantly reassign the target object, assuming it doesn’t change when targeting an enemy (which is feasible, as you can re-position and parent the target to anything)

EDIT
Revised code:

if not 'target' in owner:
    for dest in owner.children:
        if dest.name == 'where':
            print ('works', dest)
            dest.removeParent()
            owner['target'] = dest
            steer.target = dest
            cont.activate(steer)

EDIT defining “target = steering.target” fixed it. Thanks guys for the help!

I haven’t fiddled with it yet, but I need the child to be called once. I want to add in a boolean so it goes false when you defined dest.

Bah! Even then I can only get it to work once.

I about ready to rip Python a new one. Even though all the checks are good, it won’t do whhat I think I am telling it to do. It tells me that “target is referenced before assigned”. Even though the boolean reads True.

if rmb.positive:           
     if 'ground' in overany.hitObject:
                
                if owner['get'] == False:
                    for dest in children:
                        if dest.name == 'where':
                            dest.removeParent()
                            steer.target = dest
                            target = steer.target
                            cont.activate(steer)
                            print ('works, obj being seeked is', target)
                            target.worldPosition = overany.hitPosition
                            owner['get'] = True
               
                if owner['get'] == True:
                    target.worldPosition = overany.hitPosition # This target it says is being reffed before assigned >:(
                    print ('works2, obj being seeked is', dest)
                    cont.activate(steer)

Forgive my noobism, and I might be totally wrong. But can’t you get the object from a list of objects in the scene, instead of getting it from a list of children? Maybe that’ll fix it… Don’t quote me on it, though.

I am spawning the soldier in. So using children is the (as far as I know) the best way. The problem with getting it out of the scene objects list, is that then every soldier would need a unique name to look for. Spawning all I have to look for in the children is the one name.

Python isn’t being a whiny ****. It’s genuinely unable to find the variable you’ve referenced:
In the second if statement you reference target. However, you define target in a local scope, meaning that it is only defined when the code block is executed; if owner[‘get’] == False.
Some tips:
Don’t use if owner[‘get’] == False. Use

if not owner['get']:

Don’t then do the reverse condition. Just change if owner[‘get’] == True (which should be if “owner[‘get’]:”) to “else:”

Oh right, you’e adding more. Well, can’t you spawn in a general object where you click, then have all active units pathfind to it?
That would probably require a full change to your current system I realize, but if Blender itself can do it, you can too! xD
(By the way, how do you get an object to snap to a mouse click point? I’m new to post-2.5 Blender, so I’m trying to learn what I can. :P)

Thanks, Agoose.

how do you get an object to snap to a mouse click point

If you are familiar with using Blender python, you would have a mouse over any sensor and in the code you would put:

what you want to name the sensor = owner.sensors[‘what the mouse sensor is named’]

When you have the object that you want to snap to where you clicked (I will just make that the owner of the controller)

owner.worldPosition = what you want to name the sensor.hitPosition

That is without clicking.

Ahh, I just needed to know that .hitPosition variable. Thank you!
Sorry to ask for help in the place where you’re asking for help… I hate to have been rude.