Dupliverts in BGE?

terrain

(Thatrius) #1

HI all,

I’ve been looking into making a sandbox game (like Minecraft) in the Game Engine. I’d like to be able to generate a landscape of objects, and - upon clicking one of the objects - it separates from the rest of the landscape and becomes a rigid body.

I’d prefer not to have millions of individual pieces making up the terrain (That might be a bit strenuous on the system); instead I’d like to have the terrain as a single mesh, before a piece is separated from it and the rest restructures.
One way I thought of that might achieve this is to use the remesh modifier on the landscape, and then use dupliverts to distribute the object across the vertices. But unfortunately the dupliverts don’t show up in-game. And even if they did, I’m not sure how I would implement the in-game ‘editability’ that I’m after.

It’s sort of difficult to explain what I’m trying to achieve, which is why I used Minecraft as an example. Is there any way to mimic that sort of editable landscape in BGE?

Thanks!


(mataii) #2

Hi :slight_smile:

Dupliverts, remesh and other modifiers does not work in the game engine.

You migth want to try another method to achieve that. For example I’m using “add object” actuators and deleting objects to perform object change from solid to editable.

I’m making a game with some mechanics like portal and minecraft, and I’m using logic bricks, not because is the best way to do it, but its because the way I can make it, I mean, I find a bit difficult to get into python but instead I feel very confortable working with logic bricks.

Here is a sample gif:


(Lostscience) #3

look at the testvc2

use 2.79 blender to make it work.it won’t work in 2.80 blender.


(jesusmora) #4

ok, first you need to use upbge. upbge has a feature called batching that combines a lot of static objects into a single one for rendering.
then, you need to know python.

a voxel system is almost pure code. you have a matrix that contains the information of all the blocks, in minecraft these are stored in chunks, wich are different matrices, wich are loaded and unloaded as the player aproaches them.
the next part is culling. you determine what meshes should be seen on screen, and only render those cubes.

you need a script that adds and removes cubes using the data from the matrix, and adds/removes/regenerates the batch.

finally, when you select a block, it changes it in the matrix, then updates the batch, and spawns a block.

here is example code from a test game i made:

gamemap = bge.logic.globalDict["gamemap"]
lista = gamemap['matriz']

camera = objList.get('Camera')
textobj = objList.get("minimap_level")
minimap_origin = objList.get("minimap_origin")

selectedleveldictionary = {'0': 'minimap_batch','2': 'green', '10': 'green', '11': 'solarpanelgreen', '12': 'green', '14': 'green', '15': 'green', '16': 'green', '17': 'green', '18': 'green', '19': 'green', '20': 'green', '21': 'green', '22': 'green', '23': 'green', '24': 'green', '25': 'green'}
minimapdictionary = {'0': 'minimap_batch','2': 'cube', '10': 'cube', '11': 'solarpanelblue', '12': 'cube', '14': 'cube', '15': 'cube', '16': 'cube', '17': 'cube', '18': 'cube', '19': 'cube', '20': 'cube', '21': 'cube', '22': 'cube', '23': 'cube', '24': 'cube', '25': 'cube'}
def replaceMinimap(batchobjects, currentlevel, maplist, sizeX, lastlevel):
    newbatch = []
    destructbatch = []
    for abc in batchobjects.objects:
        if abc.name != "minimap_batch":
            destructbatch.append(abc)
    batchobjects.destruct()
    for aerc in destructbatch:
        aerc.endObject()
    for alc in range(sizeX[0]):
        for adc in range(sizeX[1]):
            for aec in range(sizeX[2]):
                if maplist[aec][alc][adc] != 0:
                    if currentlevel == aec:
                        ob1 = scene.addObject(selectedleveldictionary[str(maplist[aec][alc][adc])], minimap_origin, 0)
                        ob1.worldPosition = [((alc-16)*0.1)+520, (adc-16)*0.1, (aec-10)*-0.1]
                        newbatch.append(ob1)
                    else:
                        ob1 = scene.addObject(minimapdictionary[str(maplist[aec][alc][adc])], minimap_origin, 0)
                        ob1.worldPosition = [((alc-16)*0.1)+520, (adc-16)*0.1, (aec-10)*-0.1]
                        newbatch.append(ob1)
    newbatch.append(objList.get("minimap_batch"))
    batch = bge.types.KX_BatchGroup(newbatch)

def levelUpdate(batchtodelete, currentlevel2, map, sizeY, lastlevel2):
    staticbatch = []
    destroybatch = []
    originwall = objList.get("origin_wall")
    wallbatch = [originwall]
    for ghi in batchdelete.objects:
        if ghi.name != "origin_batch":
            destroybatch.append(ghi)
    batchdelete.destruct()
    for jkl in destroybatch:
        jkl.endObject()
    for ijk in range(sizeY[0]):
        for elm in range(sizeY[1]):
            for mno in range(sizeY[2]):
                if map[mno][ijk][elm] != 0:
                    #draw room
                    #draw walls
                    #draw doors
                    if currentlevel2 == mno:
                        #draw room
                        ob3 = scene.addObject(basedictionary[str(map[mno][ijk][elm])], minimap_origin, 0)
                        #-128
                        ob3.worldPosition = [(ijk*8), (elm*8), 0.0]
                        staticbatch.append(ob3)
                        #draw wall
                        ob7 = scene.addObject(walldictionary[str(map[mno][ijk][elm])], minimap_origin, 0)
                        ob7.worldPosition = [(ijk*8), (elm*8), 0.0]
                        wallbatch.append(ob7)
                        #draw door
                        ob8 = scene.addObject(doordictionary[str(map[mno][ijk][elm])], minimap_origin, 0)
                        ob8.worldPosition = [(ijk*8), (elm*8), 0.0]
                        ob8["XPOS"] = ijk
                        ob8["YPOS"] = elm
                        ob8["ZPOS"] = mno
                        ob8["doorobject"] = 1
                elif map[mno][ijk][elm] == 0:
                    if currentlevel2 == mno and currentlevel2 != 25:
                        if map[mno+1][ijk][elm] != 0:
                            ob5 = scene.addObject(floordictionary[str(map[mno+1][ijk][elm])], minimap_origin, 0)
                            ob5.worldPosition = [(ijk*8), (elm*8), 0.0]
                            staticbatch.append(ob5)
                    elif currentlevel2 == 25:
                        ob6 = scene.addObject(floordictionary[str(map[mno][ijk][elm])], minimap_origin, 0)
                        ob6.worldPosition = [(ijk*8), (elm*8), 0.0]
                        staticbatch.append(ob6)
                elif currentlevel2 == mno:
                    ob4 = scene.addobject(basedictionary[str(map[mno][ijk][elm])], minimap_origin, 0)
                    ob4.worldPosition = [(ijk*8), (elm*8), 0.0]
                    staticbatch.append(ob4)
    staticbatch.append("origin_batch")
    batch3 = bge.types.KX_batchGroup(wallbatch)
    batch2 = bge.types.KX_batchGroup(staticbatch)
elif enter == 1 and obj["timer"] > 0.1 and obj["currentlevel"] != obj["lastlevel"]:
    minimap_batch = objList.get("minimap_batch")
    replaceMinimap(minimap_batch.batchGroup, obj["currentlevel"], lista, [32, 32, 25], obj["lastlevel"])
    obj["lastlevel"] = obj["currentlevel"]
    obj["timer"] = 0.0

it is from a voxel minimap, it lacks the chunks and culling.

as you can see, you have to be a programer to do it, just like notch was. it’s not something you can just do as your first game. and that’s just to get a working first version, minecraft was in alpha and beta for a long time until notch added more than just rock and dirt blocks. it’s something very advanced.


(Lostscience) #5

the culling and chunk is in my post.


(Thatrius) #6

Ok, Thanks! I’ve known a bit of Python for quite a while; I think it’s just applying it to Blender that’s the hard part…

After a chunk is updated, does it become a single mesh?


(Thatrius) #7

And please excuse my utter lack of comprehension, but where exactly are the blocks - or chunks - stored? I’ve never had much luck with in-code variables (They don’t exactly ‘save’ beyond one instance of script)


(jesusmora) #8

ok.

no, the hard part is all of the coding for voxels and chuncks. all blender has to do is render it, just like minecraft in java. you could even make a minecraft in c with opengl 1, the problem is performance. there are millions of cubes seen on screen at the same time, and a higher amount of data that has to be processed in the voxels, as well as voxel raycast/lighting calculations.

in upbge, when you take a bunch of objects and put them into a batch, they are sent to the gpu as a single mesh. that’s all that blender can do right now, unless you manage to use some realtime geometry editing.
a batch is rendered much faster than sepparate objects, but it still looks like sepparate meshes, there is no removal of duplicated vertexes. it will look like minecraft and not like subnautica or other more modern voxel games. you can look at the upbge documentation for more information.

a chunk is a single “3d” matrix:

def Create_matrix(matrix_size):
    mapa = [[[0 for x in range(matrix_size)] for lee in range(matrix_size)] for deep in range(25)]
    return mapa

because you want the world to be bigger, you create more chunks and store them in disk as the player aproaches the border of the world.
you can store data in disk in different files, using open. i recommend using pickle to store dictionaries.
you create a dictionary, store it in memory using bge.logic.globalDict["yourDictionary"], and when a chunk is unloaded or the game saved, you save the dictionary to a file, and load a different file into the dictionary. you can’t use saveGlobalDict in this, it has to be different files for the different chunks.

each matrix has values representing ALL of the blocks in it. i used numbers in my example. 0 is an air block, wich mean it should not be rendered, 1 could be dirt, 2 rock, etc.

the player is in a position in this voxel, you take the position of the player, and mathematically cast rays inside the voxel to get the blocks that must be rendered, then the script removes all previous blocks, adds the needed blocks, and puts them into a batch. this process must be repeated every frame.

this is something really advanced and i don’t recommend doing it outside of just testing and learning.