Attempt for a very simple tetris clone in Blender Game engine (to learn using it)

Hello community,

this is my first attempt to use the blender game engine and my first steps in python
(I know how to program, used Java a lot before…)
So I want to create a little tetris clone. (with Blender 2.55 beta)
(Explanation might be a little long, but necessary)

So far i “modelled” the bricks as groups of 4 cubes each.
They should move together, and stay in shape when colliding with the
ground or already existing bricks.
But when there is a horizontal row of cubes the according cubes
should disappear and the “rest” of the brick should drop down, the
remaining cubes should be together again then.
I already tried it with parenting, but i do not remember the exact
settings now, whether the empty was static and the cubes dynamic or
the other way round.
This gave odd results up to now. Maybe i will retry it later.
Well, NOW i am experimenting with groups of cubes.
When i add a group instance object (the brick) the cubes are
automatically added to the game engine as well, they are all dynamic.
They fall together and when hitting the ground the brick “collapses” to its parts
So far so good.
Now i want them to stay in shape, so when hitting the ground i want do:

cube.suspendDynamics()

But the problem is to “find” the correct cube instances belonging to
the currently adde group instance. (they are NOT children)
The KX_GameObject has no “group support” whatsoever, so I try to
subclass it in a separate script (or module?) called “Groups.py”


import bge

class GroupObject(bge.types.KX_GameObject):
   
    def __init__(self, obj):
        if type(obj) == bge.types.KX_GameObject:
            bge.types.KX_GameObject.__init__(obj)
            self.__group = None
            self.__members = []
            
    def __del__(self):
        self.__group = None
        del self.__members[:]
        del self.__members
        #del self.__gameObject
   
    def addMember(self, groupObject):
        if type(groupObject) == GroupObject:
            self.__members.append(groupObject)
            groupObject.__group = self

    def getMembers(self):
        return self.__members
   
    members = property(getMembers)
   
    def getGroup(self):
        return self.__group  
   
    group = property(getGroup)

The purpose of this class is to give access to the group members.
My “main” script (its a mess at this point, however)



import bge
import bpy
import copy
from Groups import GroupObject
from List import mylist

cont = bge.logic.getCurrentController()
scene = bge.logic.getCurrentScene()

own = cont.owner
timer = cont.sensors["Always"]
#keyb = cont.sensors["Near"]

if hasattr(bge.logic, "pX") == 0:
    bge.logic.pX = -4
   
def createStone():

    #store current object!
    currObj = None
    currAct = None
   # bge.logic.id = bge.logic.id + 1
 
  #  print(bge.logic.id)

    #randomize stone type 0Q 1I 2S 3L
    typeRnd = cont.actuators["Type"]
    cont.activate(typeRnd)

    type = own.get("type")
    print(type)
   
    if type == 0:
        currAct = "QStone" #cont.actuators["QStone"]
       
    elif type == 1:
        currAct = "IStone" #cont.actuators["IStone"]
      
    elif type == 2:
        currAct = "SStone" #cont.actuators["SStone"]
       
    elif type == 3:
        currAct = "LStone" #cont.actuators["LStone"]
   
    elif type == 4:
        currAct = "TStone" #cont.actuators["TStone"]
   
   # cont.activate(currAct)
   
#    if currAct.objectLastCreated == None:
#        return None
#    else:
#        print("EQUAL: " + str(currAct.object == 

currAct.objectLastCreated))
   
    currObj = GroupObject(scene.addObject(currAct, currAct, 0))
 
    l = mylist(scene.objects)
    members = bpy.data.groups[currAct].objects
   
    for member in members:
        child = scene.objects[member.name]
        index = l.lastIndex(child)
        child = scene.objects[index]
        currObj.addMember(GroupObject(child))
       
    #randomize posX
 
    posXRnd = cont.actuators["PosX"]
    cont.activate(posXRnd)
   
    posX = own.get("posX")
   
    pos = currObj.worldPosition
   # if type == 1:
   #     pos[0] = posX * 2 - 1
   #
   # else:
   #     pos[0] = posX * 2
   
    pos[0] = bge.logic.pX * 4   
    currObj.worldPosition = pos
    bge.logic.pX += 1
   
    print (currObj.worldPosition) 

createStone()
   
#randomize rotX
#rotXRnd = cont.actuators["RotX"]
#cont.activate(rotXRnd)

#rotX = own.get("rotX")
#angleX = rotX * 180

#orientation = currObj.worldOrientation
#print(orientation)

#orientation[


This script should randomly create, place and rotate the bricks after

creating them
It responds to an “Always” sensor.

Another script (snippet) “Stop.py”:



def stop(obj):
   
   # print(type(obj))
   # obj = GroupObject(obj)
    for member in obj.members:
        print(member)
        member.suspendDynamics()

if near.positive:       
    stop(near.hitObject)


This script should access the group members and stop them from falling when the brick
is near to the ground (indeed, near.hitObject WAS from type GroupObject
but it seemed NOT to belong to the scene any more whatsoever)

Now the problem: In GroupObject, when I want to do print(obj) or use it
it tells me that Blender already freed that object? :spin:

OK, I sat several days at that problem… Hope somebody can help me.

Thanks in advance.

Oh, maybe adding the WHOLE .blend might help…

quadblox3.blend (467 KB)

edit: [forget this] Just from your question I think, you are accessing KX_gameObject that are already ended. [/forget this]

I checked your file. I think that is a problem, because you derive from a KX_GameObject. I can’t really help with that. But I think there is no need to do that.

If you want to build a group, you can simply use a container class which organizes multiple KX_GameObjects (e.g. list, dict). With that you can separate organising logic from GameObjects

Thanks for the reply and viewing my file.

My GroupObject contains refs to other group objects. To its members and the “parent” group.
I thought when subclassing KX_GameObject my GroupObjects are added or could be added as well to the scene.objects list.
The near sensor’s hitObject could be a group object (this really worked) and the group and so the sibling members could be easily found.
But when I wrap the KX_GameObject into a group object another instance is created and the reference doesnt match with the one in the scene list. I then tried to retain the original ref inside the group object with a has-a relation (in addition to the is-a relation) but then the “original” object is suddenly freed ? Why the heck ? Can a KX_GameObject exist outside the scene or not ? It cannot be directly “removed” from the scene without killing it with endObject.

I would look into the source code of KX_GameObject but this is in C/C++ rather than in Python ? I have not figured out yet how THIS can work together. But how does it ?

But how to find a sibling KX_GameObject in relation to the hitObject or how to find the group object to the game object ?
I tried this by storing a static root group in the GroupObject class as starting point to all groups and subgroups (and members, should work recursively) and then traversing thru the group tree to find the matching game object there. Didnt work. Sadly.

How to distinguish between instances of added KX_GameObjects? The name is not unique as seen in the printed out scene.object list. The same name appears multiple times. Seems like i need to access the LAST
object of a name (with mylist.lastIndex, see .blend file) that should be the “current” one if it was appended to the list.
Maybe there is something like a unique ID in the KX_GameObject, which i can use ?
And maybe find the cubes according to their names (the last instances)
But how to find the “parent” of the hitObject ? Find the last index of hitObject and find the last instance of the parent “before” it (using list.count and list.lastIndex somehow?

Questions over questions ?

By the way, dunno if its the right place to tell, but there seems to be a little BUG in the gui of the Vector->Map Material Node in Node Editor. Using rotation, the entered values should be degrees.
But there seems to be a calculation error. To rotate a texture by 45 degrees, for example, one has to enter a value about 2571 or so as degree. Always wondered why entering 45 resulted to 0.0something radians
and those radians are treated as degrees and so there is no visible effect to the rotation
Try this with 2.55 Beta and the “create a checkerboard texture from wood textures” example in the blender wiki. (2.49 version)

So long…
scorpion81

Hello,

creating GroupObjects by deriving from KX_GameObject works now.
Furthermore I have set up a message passing system for:
-Creating Bricks
-Test if they are falling (linearVelocity[2] < 0)
-Stopping all group members if one member stopped falling.
Testing directly for collision works only with the ground because all my cubes are dynamic and Ghost and i cannot turn off ghosting in python dynamically.
When the dynamic cubes are not ghost they instantly fall apart.
But for collision detection between bricks ghosting must be turned off.

Messages are sent like this:
Starting state: Always sensor -> Script controller (to change state)-> create message
Running state:
Create Message -> Script “Create” -> Falling message
Falling message -> Script “Falling” if velocity >= 0 -> “Stop message” else “Falling message”
Stop message -> Script “Stop” -> Create Message
so that a new brick is created when the last one stopped

Problem is: at each step TWO bricks are created, sometimes even three, and velocity detection is laid out for ONE falling brick (and I want only one falling brick)

I can append the new .blend if desired.

scorpion81

Hello and “Bump” again,

creating bricks and stopping works now with message system.
But correct collisions are a nightmare. It can’t be so hard to calculate the collision of cubes or can it ?
With a 60/40 chance the bricks collide, otherwise they fall through to the ground or
are even stuck in the middle of the brick pile.

I am using a group of cubes, each one dynamic,ghost and collision bounds, convex hull, zero margin. Tried already triangle mesh, compound and more margin. No luck either.
Cube size is 2, Physics radius is 0.5
Basically, after hours of fiddling around those settings were the best i have found.

But it COULD be better…

Any suggestions ?
And again, the blend will be appended here if ANYONE is interested…

scorpion81

Oops, it’s blender 2.5 so I can’t really take a look.

Tetris works as a 2d game because it works on a grid.
For each square in the gid it is easy to calculate if it should have a block or not, and then by checking lines and such it is easy to check objectives and then change the position of all the other blocks based on that.

I think it may be best to aproach it as a “blocks as co-ordinates” game rather than as a moving dynamic objects game.

So you would have a grid. For each square int hat grid you would have to make calculations each turn to see whetehr it should be visible, and what colour it should be. You can calculate the dropping groups as a set of numbers based around the “center” block of that group.

How the numbers affect the blocks in the array shoul help you calculate what should be shown each “frame” of the game.

I don’t think Tetris is really all that simple. Unless you know a bit about math (which I don’t really).

Here is an intersting link:

and an interesting, indepth look at his project:
http://digital-diy.com/Swordfish-Projects/tetris-on-a-pic.html

Hello,

I have made all my bricks static now and move them manually (stepwise, like in a grid). So no collision is needed.
What a pity, I thought (mis?)using the physics engine for calculating brick collision makes my life easier.
Now I manually check for each cube whether the position below it is free. If for all cubes of a brick those position is free, the brick is moved down (Moving left/right or even rotating and brick decomposition not considered yet)
If the move was performed, a message is sent to trigger a new move check. If it fails, movement has stopped and a new brick is created (via message)
Special case: If movement directly after brick creation fails (grid is too full), creation of new brick is NOT triggered.

My Problem now is: the bricks fall too fast. How can I slow them down?? Active waiting in a for loop is not so good, the movement is VERY choppy then, and some brick positions seem not to be drawn at all.

Can this been done with changing the message sensor delay, pulse etc. ? Had no luck trying this…

scorpion81

apply a motion or
a IPO animation
or rather than visible objects use empties, slow parent the visible bricks to it.

The model of the logic does not need to match the visible data. E.g. you could perform your tetris logic in an array. Then place the visible blocks according to this model. Similar to the MVC-model. This approach enables you to replace the model, the view and/or the controller later (if you want). (E.g. replacing the array with a dictionary / replacing the visible blocks with spheres.)

Hello,

now i have included Fcurve animations as actuators, for each cube of the brick an animation. Animating the empty representing the group instance didnt have any effect.
The cubes move down 2 units in 10 Frames. This animation is a “loop stop” (What’s the difference to “loop end” ? :eyebrowlift2:) The frame number is passed to a property which triggers the move check. (Have 4 property sensors, one for each cube)
So the fall movement looks better now.
If the move check fails, Animation will be stopped.

But now: the first brick does behave differently from the further bricks added by the edit object actuator. Its Z-Position is set to 10, when doing this to the other bricks, their start positions are 10.123something
Is this related to the animation which might be interrupted when it hasnt reached its end already ?
Means: All bricks behave equally, except the first one. It “hovers” a bit above the ground
And sometimes bricks fall a bit too far into other ones. They should “snap” to the grid.

Next step will be a try to apply the MVC Model by using empties as parents to which movement and calculations will apply.
But, isnt there a problem when I clear the parent ? When I last tried it the transform of the children was reset. Is there some equivalent to the “Clear Parent and Keep Transform” in the game engine ?
My bricks should be decomposable, thats why I messed around with groups and independent members up to now.

scorpion81

Hello,

I dug out my old small game project thread, because I started another attempt to get quadblox working.
This time I (almost) solved the collision problem of the bricks by using dynamic empties and static cubes as children. The cubes have a collision box, compound is checked and zero(!) margin. The parent empties have triangle mesh (might be unnecessary because they are empties ?), compound checked and zero margin also. And the collision sphere radius is 0.1 everywhere so no “unwanted” collison of dynamic empty with static cubes which made the brick fly around. So now the physics engine deals with brick collison, no manual fiddling needed. But…
assigning collision sensors to the brick objects (the empties) makes no sense, because collision is not detected. The empties themselves do not collide but their compound children. Thought the parent could detect collision “indirectly” via its children because they (successfully) build a compound object.
Is there a way to forward the children’s collision to the parent automatically so the parent’s sensor is triggered OR do I have to assign collision/touch sensors to EACH cube by hand (nargh, already 5 brick types with 4 cubes each -> 20 cubes!!)
Can I create sensors on the fly in python to the currently created brick’s children or must it be done via the logic brick gui ? Haven’t found an appropriate function yet.

Random brick creation, movement (left/right) and rotation (left/right, 90 degree steps)
works now, the “hardest” part I think will be detection of full rows and removal of distinct child cubes from bricks (at each collision this check will be performed)
Maybe like so: starting from “collision” row (where the brick stopped) check which brick is at a distinct position (which child cube to be exact) and if at each position of the row there is a cube, remove them from their parents. Dynamics are still active, so the bricks above should come down automatically.

To achieve this i need something like “compound collision detection” or assign sensors to all cubes (sigh -> click click click …)

Any suggestion would help.

scorpion81

Hello,

In the meantime I tried to use sensors attached to the cubes directly but they seem to be simply ignored ?!
Can static objects have sensors ? Does this make sense ?
When adding sensors to the empties (dynamic objects) they appear in the sensor list of the controller BUT … they will never be triggered ? (too small collision radius BUT sufficient compound collision bounds)

So my question again:
How to detect children’s collisions in compound objects ? Or do I have to increase the empty’s collision sphere ?
This might lead to collision with children (I think … because setting the empty in the exact center, between the children, this made my objects fly… well … I try this again…)
I thought the collision boundaries override the collision sphere or radius of an object ?

scorpion81

Hello,

I finally attached the blend with my problem: the Touch (or exchange it with collision)
sensors will simply NOT trigger !! Dont know why…

Use spacebar to create bricks, and arrow keys to move/rotate. The game is still incomplete and buggy.

scorpion81

Attachments

quadbloxNew.blend (545 KB)