trouble with linked objects

Hi,

I have some trouble with linked objects over different blend files.

When I add an object (group_instance) from an other blend from an inactive layer in game the mesh of the object didn’t get update to the position of the instance empty.

test script not finished!

in that case for example grass:


from bge import logic, render
from mathutils import Vector
import random


grass_emitter = logic.getCurrentController().owner
scene = grass_emitter.scene
ObjList = scene.objects
ObjInactList = scene.objectsInactive
rad_range = grass_emitter['Range']
emit_number = grass_emitter['Number']
ground = grass_emitter['Underground']
#random.seed("Whatever")

class Grass():
    def __init__(self):
        self.spawn_obj_list = [o for o in ObjInactList if 'spawn_group' in o if o['spawn_group'] == ground]

    def random_spawn_obj(self):
        choice = random.choice(self.spawn_obj_list)
        return choice

    def range_player(self, obj):
        rand_rot = obj.worldOrientation.to_euler()
        rand_rot.z+=random.uniform(-rad_range*.5, rad_range*.5)

        X = obj.worldPosition.x + random.uniform(-rad_range*.5, rad_range*.5)
        Y = obj.worldPosition.y + random.uniform(-rad_range*.5, rad_range*.5)
        Z = obj.worldPosition.z -5
        max_range = Vector([X,Y,Z])
        return (rand_rot, max_range)

    def spawn_position(self):
        rot, end = self.range_player(grass_emitter)

        start = end.copy()
        start.z+=10
        end = rot.to_matrix()*end
        start = rot.to_matrix()*start
        ray = grass_emitter.rayCast(end, start, 0, ground, 0, 1, 0)
        render.drawLine(start, end, [0, 0, 255])
        if ray[0]:
            spawn_obj = self.random_spawn_obj()
            self.applie_obj(spawn_obj, ray[1])
        else:
            pass

    def applie_obj(self, obj, pos):
        added_obj = scene.addObject(str(obj), None, 0)
        added_obj.worldPosition = pos

    #def delete_obj(self, obj):

    def main(self):
        for i in range(emit_number):
            self.spawn_position()

grass = Grass()

def main():
    grass.main()




Be aware that when you copy instances with addObject you copy all objects. The returned object will be the instantiating object (the empty). Without further action it has no parent/child relationship to the instance members.

[Btw. it does not matter if the instance is linked from a different file.]

In other words:

addObject creates:

  • the emtpy
  • the grass mesh objects

it returns the empty.

As the grass meshes are not parented to the empty they will not follow the explicit position change of it.

Solution A):
Parent the group members to the empty before moving the empty.

Solution B)
Move the group members.

With both options you can access the group members via KX_GameObject.groupMembers


added_instantiator = scene.addObject(str(obj), None, 0)

for instanceMember in added_instantiator.groupMembers:
    ...

Sitenotes:

Your code contains some misleading names:

  • Grass = it tells this is a Grass class which behaves like grass, but does it really behave like grass? [Isn’t it a grass builder/factory?]
  • random_spawn_obj = in difference to the name it does not spawn obj but randomly selects an object out of existing objects
  • range_player = I’m not sure what it does. It looks like it does some random calculation. But I do not see a player
  • spawn_position = sounds like it spawns a position - which is either strange grammar or means something else … it returns a position. A verb (like get, calculate, find, determine) would make it more visible to me.
  • applie_obj = shouldn’t this be an “y”? The first question that comes to my mind: Apply to what? I suggest to call it spawn_object(), or spawn_grass(), add_grass(), create_grass() or simply spawn().
  • main = that means what?

Just some thoughts.

I think I would use the group members “thing”

as always it seams that I have to work out some name/spelling issues

Grass -> should change that to spawn_assets
random_spawn_obj -> random_choice
range_player -> random_range
spawn_position -> get_position()

applie_obj
I was thinking about: applies_objects maybe I should rename it to spawn()

I would use an kd_tree for that but it slows massively down: (without physics they are only for visualize the problem)


from bge import logic, render
from mathutils import Vector, kdtree
import random


grass_emitter = logic.getCurrentController().owner
scene = grass_emitter.scene
ObjList = scene.objects
ObjInactList = scene.objectsInactive
rad_range = grass_emitter['Range']
emit_number = grass_emitter['Number']
ground = grass_emitter['Underground']
ground_obj = ObjList["Terrain"]
vertex_group_color = [0.000000, 1.000000, 0.000000, 1.000000]
#random.seed("Whatever")


class Spawn_Assets():
    def __init__(self):
        self.spawn_obj_list = [o for o in ObjInactList if 'spawn_group' in o if o['spawn_group'] == ground]
        self.kd_tree = {}
        self.get_kd_tree(ground_obj)

    def get_kd_tree(self, obj):
        size = obj.meshes[0].getVertexArrayLength(0)
        mesh = obj.meshes[0]
        kd = kdtree.KDTree(size)

        for d, pn in enumerate(range(mesh.numPolygons)):
            poly = mesh.getPolygon(pn)
            mn = poly.getMaterialIndex()
            verts = []
            for pvn in range(poly.getNumVertex()):
                vn = poly.getVertexIndex(pvn)
                vertex = mesh.getVertex(mn, vn)
                if vertex.color == Vector(vertex_group_color):
                    verts.append(vertex)
            if verts:
                point = Vector([sum(values) for values in zip(*[obj.worldTransform*v.XYZ for v in verts])])/len(verts)
                kd.insert(point, d)

        kd.balance()
        self.kd_tree = kd

    def random_choice(self, r_list):
        choice = random.choice(r_list)
        return choice

    def random_range(self, pos):
        X = pos.x + random.uniform(-rad_range*.15, rad_range*.15)
        Y = pos.y + random.uniform(-rad_range*.15, rad_range*.15)
        Z = pos.z

        max_range = Vector([X,Y,Z])

        return max_range

    def get_positions(self):
        close_list = []
        end = grass_emitter.worldPosition
        for (co, index, dist) in self.kd_tree.find_range(end, rad_range):
            close_list.append(co)

        rand_pos = self.random_choice(close_list)
        afg = self.random_range(rand_pos)
        spawn_obj = self.random_choice(self.spawn_obj_list)
        self.spawn(spawn_obj, afg)

    def spawn(self, obj, pos):
        added_obj = scene.addObject(str(obj), None, 0)
        added_obj.worldPosition = pos


    #def delete_obj(self, obj):

    def main(self):
        for i in range(emit_number):
            self.get_positions()

spawn_assets = Spawn_Assets()
def main():
    spawn_assets.main()