Loading NPC coordinates from a dictionary

Im trying to load objects based on coordinates stored in a dictionary file based on there distances. I had a setup like this but lost my hard drive and cant remember how I set it up. I have a dictionary like this

logic.local_objs = {

“bob”: [10.0, 50.0, 0.0],
“jon”:[30.0, -20.0, 0.0],
"sam:[-20.0, 40.0, 0.0]

}

Simple enough. Im trying to have my project create a list on startup with the values. While the program is running it will get distances to the coordinates and load the npcs as I get close. That part is simple. My problem is im having problems getting it into a list that I can work with. Im sure its not a big dead but I think my list iteration is wrong but I want to end up being able to access the list with something like

for i in logic.local_objs()
own.getDistanceTo(i[[0],[1],[2]) # I know this isnt right

or even trying other methods

for i in logic.local_objs()
print i[0] #to print the 1st coordinate. But im doing something wrong, It just prints the “b” in bob.

giving me the distances from me to the objects then finding out which one is closest. Finding the distance to hard coded coords is no problem.

My problem seems to be my iteration and turning the dictionary data into iterable list I can work with or my iteration itself. I have tried every which way I can think but I just cant get it. Can someone help me figure out the iteration to get the coords out of the dictionry into something I can work with.

Try without pharentesis, for i in logic.local_objs

Or actually

for _, i in logic.local_objs.items():

I didnt actually put the parentheses in my code, dont know why I put that there. I tried what you said and I still can get what im wanting…

Ive tried other things along

I can print logic.local_objs[“sam”] and get the coordinates, or logic.local_objs[“sam”][0] and get the first coordinate. But looping through the list I cant keep track of all the coordinates and there owner. I cant print logic.local_objs[0][0] or I just get “s”. Any times I try to use [] in the iteration, I conly get a letter character.

Im trying to iterate through the list keeping track of the distances and which person is the closest. So I can load sams msh when he is close. Like I said just cant find the right iteration. What im wanting is something like

for i in logic.local_objs
_____distances = getDistanceTo i[0][1][2]
_____if any distance in i < 100
_____load mesh

maybe that makes it clearer. Ive done this before but I cant figure our how to do it. Using the .keys() Will show me the names. But I cant use .keys()[0] for example. Looping through the keys doesnt seem to help. Thinking I have to iterate through the keys then values somehow in the same loop maybe? If anyone can help me out. Will be much appreciated

ok Ive actually had some success. .items() was what i was needing for the iteration.

So I can now do this. So ive got this:

logic.local_objs = {

“bob”: [10.0, 50.0, 0.0],
“jon”:[30.0, -20.0, 0.0],
"sam:[-20.0, 40.0, 0.0]
}

for i in logic.local_objs.items()
____print(i[0]) prints bob jon sam
____print(i[1]) prints [10.0, 50.0, 0.0],[30.0, -20.0, 0.0],[-20.0, 40.0, 0.0]
____print(i[1][0]) prints [10.0],[30.0[-20] or the first coordinate. Which is perfect. Which is bringing up another problem.

So when Ive found out that bob is closest. How do I manipulate bob because once again i[0] prints ALL the names. i[0] is bob, jon, sam. I just want to work with bob now that ive found he is closes. And once again. i[0][0] just prints the “b”. i[0[0]] kicks an errorI must be sooo overthinking this. Basically I just wat to select the first i but i[0] is the 1st sequence and i[0][0] is the first letter.

ok Ive actually had some success. .items() was what i was needing for the iteration.

So I can now do this. So ive got this:

logic.local_objs = {

“bob”: [10.0, 50.0, 0.0],
“jon”:[30.0, -20.0, 0.0],
"sam:[-20.0, 40.0, 0.0]
}

for i in logic.local_objs.items()
____print(i[0]) prints bob jon sam
____print(i[1]) prints [10.0, 50.0, 0.0],[30.0, -20.0, 0.0],[-20.0, 40.0, 0.0]
____print(i[1][0]) prints [10.0],[30.0[-20] or the first coordinate. Which is perfect. Which is bringing up another problem.

So when Ive found out that bob is closest. How do I manipulate bob because once again i[0] prints ALL the names. i[0] is bob, jon, sam. I just want to work with bob now that ive found he is closes. And once again. i[0][0] just prints the “b”. I[0[0]] kicks an errorI must be sooo overthinking this. Basically I just wat to select the first i but i[0] is the 1st sequence.

EDIT: ANOTHER TRY
Another method I can use is

for k,v in logic.local_objs.items():
____print(k,v) will also give me the name followed by coordinates.

but once again k[0]…still gives me “b” and not bob…How do I get bob???

what about dumping out a keylist, then using the keylist to build a vector table using the same index,

then you can use actorList[index] as the key in your dictionary?

(use the list of vectors to get the indicies you need to then get the keys?)

edit: for some reason I thought you had much more data in each dictionary entry

newdict = {1:[X,Y,Z], 2:[X,Y,Z], 3:[X,Y,Z]}
keys = list(newdict.keys())

@superflip: please use [noparse]


[/noparse] tags when posting code.
@BluePrintRandom: I wonder why you do not mention the kdtree. It would fit pretty well in this situation.

@Monster - Only if the NPC don’t move - but I guess ‘saving’ the new position when you get out of range should not use to much resources (re-balancing the tree)

you’re right though - if the NPC don’t move a kdtree would be perfect for doing the distance checks - even if they do move when they are ‘unloaded’ the position could be updated.

I have actually been thinking about using ‘Tiles’ to load and unload,

  1. when a terrain tile is unloading - grab all items that are above the tile
  2. store them as a list and delete them (remove tile)
  3. when you get close again -> load the list over a few frames

While your argument is very valid there are a few things to consider:

  • superflip already wanted to read from a static data structure (dictionary)
  • when the NPC moves you can’t “unload” it as you need to process the motion (not necessarily in the 3D scene, but in the game model as Out Of Sight processing).

Yes, that is what crosses my mind too.

I understand. And yes the items are static…Butt suppose for example I want to make a new list just from the 1st key:value. New list named:

bob = [10.0, 50.0, 0.0]

This would be perfect.

The problem is I cant access the individual 1st key. Returning [0] returns the sequence of keys(bob,jon,sam). But also trying [0][0] does not return bob, only returns the first the 1st character of the seqence “b”. Herein lies my problem.

edit: it sounds like what you need is just - list(dict.keys())

Lets analyze your requirements:

This description is a bit distracting but I guess you want following

We define an entry as an object that contains enough data to create a new game object within the current scene. To do that it should have:

  • coordinates (x,y,z)
  • the name of the game object to get copied from
  • (maybe more)

Your input is

  • a position with coordinates i = (Ix, Iy, Iz)
  • a distance around the coordinates

Your output is a list of entries with coordinates within the distance to the position

Basically what the near sensor does with game objects, but with entries

Your entry is a key/value pair in a dictionary. The key is the name if the game object and the value is the position.

This is no good structure in many ways.
The major problem is that you limit yourself to one possible object. For example you can have just one “bob”. This might be no problem with bob, but how about “stone”, “fence”?
The next problem is that this structure supports access by name. But you need access by position. It is still possible but really inefficient.

I suggest you create a separate “entry” object that keeps all relevant data in one place.

How could it look like?



class Entry:
    def __init__(self, name, position):
        self.name = name
        self.position = position


You can create such objects pretty simple via constructor:


def createSampleEntries():
    entries = [
        Entry("bob", [10.0, 50.0, 0.0]),
        Entry("jon", [30.0, -20.0, 0.0]),
        Entry("sam", [-20.0, 40.0, 0.0])
    ]

    for entry in entries:
        print("name:", entry.name, "position:", entry.position)

    return entries  

Finding near entries. This is no trivial tasks. It you use the option above, you would get a list of entries. A list is not efficient on large number of entries. Imagine you have one million entries. You would need to loop through all of them. This takes way to much time.

So you need a better way to organize the entries (which are no game objects!) that finding them by coordinates or more specific by and era of coordinates. The best is to touch only relevant entries (no entries that are far away).

mathutils.kdtree can help here.
If you have a closer look you will realize this does not care entries. It cares positions and a numeric key (index).

Just now we have no numeric key. But wait! Isn’t a list organized by index? Index is a numeric key. So we can indeed use a list of entries. We feed the kdtree with positions and indices and let it doe it’s magic.


import mathutils

...
def createSearchTree(entries):
    size = len(entries)
    tree = mathutils.kdtree.KDTree(size)
    for index, entry in enumerate(entries):
        tree.insert(entry.position, index)
    tree.balance()
    return tree

with that we can find the keys of near entries. We lookup the entries in the list of entries:


import bge
...
def findNearEntries():
    # prepare the world
    entries = createSampleEntries()
    searchTree = createSearchTree(entries)

    # prepare the input
    measurePosition = (15, 30,0)
    distance = 30

    # perform search
    searchResults = searchTree.find_range(measurePosition, distance)

    # convert to entries
    nearEntries = [entries[searchResult[1]] for searchResult in searchResults]
    
    # show result
    print()
    print("look from", measurePosition, "at a range of", distance)
    for entry in nearEntries:
        print(entry.name, "is near")

    # add game objects
    scene = bge.logic.getCurrentScene()    
    for entry in nearEntries:
        gameObject = scene.addObject(entry.name, entry.name)
        gameObject.worldPosition = entry.position
        print(gameObject.name,"added at", gameObject.worldPosition)

I hope you get the idea.

thank you guys very much for your help. Lots of conversion there. I will try some other methods or poke around in this and that. Sometimes thinks make more sense when you step back for a bit and re-analyze.