using python in the gameEngine

Hi everybody,

I just wanted to share to way I use python in the gameEngine. If you work this way, all communications between gameObjects will be much simpler, and you can script your whole game from one python script.

So, here’s how it works:

first create an empty (or other object), and add an always sensor, but be sure to uncheck the first ’ button, so this is run only once, at the beginning, attach a python script with the following code:


import GameLogic
GameLogic.Objects={}

Ok, so now we have a dictionary ({}) attached to GameLogic.
On the other objects, add a prop called ‘init’ and set it to 1 (int), then add a property sensor, and set it to ‘init equals 1’, attach a python script to this sensor wit the following code:


import GameLogic
try:
    GameLogic.Object #object=initialized
    init=1
except:
    init=0

if init:
    controller = GameLogic.getCurrentController()
    owner = controller.getOwner()
    name = owner.getName()
    if not GameLogic.Object.has_key(name):
        GameLogic.Object[name]=controller
    owner.init=0

This script will ‘register’ the object it is attached to, and put it in the GameLogic.Objects dictionary. If you want to use any actuators or sensors, just attach them to the python script.

Now, the beaty from this is that you can call any object from any place, for example:


import GameLogic

obj = GameLogic.Objects['Plane.234']   #get object Plane.234
act = obj.getActuators()
own = obj.getOwner()

for obj in GameLogic.Objects.values():    #loop through all objects, and trigger ipo animation
    ipoAct = obj.getActuator('ipo')
    GameLogic.addActiveActuator(ipoAct,1)


Anyway I really like working with the gameEngine in this way, because it’s very simple, and I hate drawing lines between objects because it get’s very ugly. The message actuator also has it’s problems, because I can never send more than one message at a time (or I need to use multiple messag actuators).

I’m a bit busy at the moment so I don’t have an example blend file,
But people who use python should probably get the idea :wink:

Blend on,

Jasper

Oops, a mistake

the line in the script:

name = own.getName()

should be:

name = own.getName()[2:]

This will strip the first two characters from the name, because an object name always starts with ‘OB’ followed by the actual name.

Very smart! I use a method similar to this in the level editor, but it’s not quite as unified and I never thought of saving the controller to the dictionary. Geniously simple!

-Very nice!Thank you

Great idea! Thank you! :smiley:

Script looks handy, but I keep getting a “KeyError”, any ideas?

You’ll get a keyerror if the key you enter isn’t in the dictionary. For example, if you try this:

obj = GameLogic.object[“MyObject”]

and there is nothing in GameLogic.object named “MyObject”, then you will get a keyerror.

Check two things: That you are trying to access an object with the right name (it’s case sensetive), and that the object is adding itself to GameLogic.object.

Can anyone explain why the original poster had trouble with the message actuator/sensor? I am having similar problems wrt sending a second message from the same script. Is it a known bug?

This isn’t working for me, but I admit that I was initialiing the Objects dictionary in a different way at first.
What’s to prevent the script that registers objects from running before the script that instanciates the Objects dictionary? Seems a bit flaky to me, but I am new.
OK, I got it working finally, thanks.
The other issue I have now, is getting new objects (addObjectActuator) to register themselves. I can’t seem to get the getLastCreatedObjects thing to work until the next pulse, which is akward.

Ok burper, I realize what your having trouble with now - actuators can only be called ONCE per script run. Because when you go “GameLogic.addActiveActuator(blah,1)” it DOESN’T fire the actuator then. It adds it to the internal list of actuators that are GOING to fire. so if you have this:

actuator.message = “This is message1”
GameLogic.addActiveActuator(actuator,1) #Tells gameblender to fire it
actuator.message = “This is message2”
GameLogic.addActiveActuator(actuator,1) #Tells gameblender to fire

then the script will run, and it will most likely send “This is message2” because that’s the last one it was.

I hope I made sense.

The easiest solution is to use global variables rather than messages

Aha! Many thanks Saluk, that makes perfect sense. Now I understand the comment about using multiple actuators.

Now I’d like to get newly added objects “registered” as per the ideas in this thread. If I use addObject, it does appear to clone the existing data structure for the object (i.e. I can set independant positions), but I need to send a message to its’ pythonController so it can register itself and can’t seem to make that work. Or, is there any way to get to the (cloned) pythonController from an object (mesh) reference?

Thanks again!

if you use addobject, on the next frame that the python script is run you can call an addobjectactuator.getLastAddedObject() to return the last object the actuator added, then set all the properties etc from there. Another way of doing it, is to have all objects automatically add themselves to a global list on GameLogic, and have seperate “data” objects just look for the next available object of it’s type to give it’s data to.

It’s getLastCreatedObject()
There doesn’t seem to be a setProperty() method, but maybe print dir(obj) isn’t telling me everything. I need to get to it’s pythonController, or set it somehow. http://danaburns.home.attbi.com/blend/
Upon further toying, maybe it is working? I still don’t understand why the print ADDME doesn’t work, but after a third frame there does appear to be a pythoncontroller printing out. Hmmm…
I’ve been going with something similar to your second idea, with a pool of recyclable, registerable objects, but it’s a bitch to create 100 of these all with their init properties and pythoncontrollers set. Any way to copy and paste complete objects?
Thanks for the help. I have a lot more to learn about python, so will understand if you ignore me for awhile until I catch up :wink:

I’ve been using a similar method to that for sometime now. I decided to index all the important objects in my game against a seperate gameID property that i gave them, instead of using the objects name. That way you get a higher level of abstraction from the blender interface, because you’re only dealing with the objects in the terms that the user will see them in, instead of having to worry about what actual blender object represents them.

I suppose it wouldn’t make much difference in many cases, but because of the way that my game works, i have to use different blender objects in different parts of the game to stand for the same game object, but at different stages of its evolution. So when it comes to the common scripting that all stages of the game object will share, i only have to write 1 bit of code that retrieves the object from the dictionary according to what its game name is, and I don’t have to worry about detecting what stage of evolution its at and so have to work out what blender object to retrieve.

Anyway i think i’ve bored u 2 death enough so… :slight_smile:

Keith. 8)

Making progress!
I have mesh objects on layer 2 and can create new named objects (on layer 1) with the specified mesh and specified name and have them self-register in the GameLogic.objectsDictionary.
Well, I’m excited :wink:

Next up:
-delete named object (DONE)
-named object mutators (position, replaceMesh, whatever else is interesting) (DONE:replaceMesh and setPosition)
-add to named adderObj (location)
-near/collision sensors and try writing back on the socket (I’m just realizing now that this will be tricky. The cloned mesh object won’t “know” it’s name as kept in the dictionary, so I will need to set a property when it’s created)

Thanks all, esp. Saluk. Curious now Saluk, what problems do you think there are on the client side of blender-python socket stuff?

Question:
OK, so using this technique I have my objects’ controllers all “registered” in GameLogic.objControllers. I do this by having an InitSensor (always with first button off) connected to a PythonController called register.py.
If I now want to handle multiple sensors, e.g. a NearSensor and a CollisionSensor, then I need to connect them to the same PythonController “register.py”, yes? If so, how do I detect which sensor has triggered me? If not, how do I get my objects’ controllers key?

OK, time to give back to the community, at least a tiny bit. For what it’s worth, I’ve created the following webpage that demonstrates the “object dictionary” approach discussed in this thread as well as a couple other concepts: http://danaburns.home.attbi.com/blend/multidemo.html

Text of the page follows:
<h3>What this is</h3>

I have a blender file that connects to a server and processes commands from it to: add, delete and change blender GameObjects. It is very simple, but can serve as a demonstration of the general use of: python in the game engine, using socket communication, and using the “object dictionary” approach discussed in this thread.

It is not a full multi-user demo, as the server only sends “scripted” commands at this point. A richer demo is in the works.

Current working commands from the server include:

<pre>

add [objectName] [meshName] [x] [y] [z]
This adds an object to the scene at the specified coordinates, with the specified mesh.
It adds an entry to the object dictionary using objectName as the key
and that objects’ pythonController as the value.

remove [objectName]
removes the specified object using EndObject

pos [objectName] [x] [y] [z]
sets the position of the specified object

replace [objectName] [meshName]

exit
closes the client socket. no more processing takes place.

</pre>

<h3>Where to get it and how to run it</h3>

The demo blend file, along with it’s python files, and a simple server written in java, are at: My Blend Page in a file named MultiDemo.zip. Sorry for not being all-python, but the server isn’t very important here and I am more comfortable with java than python at this point.

INSTALL and RUN: unzip, unjar, javac, runs in one window. Or, follow along in the discussion below, using copy/paste with code snippets and make your logicbricks look like the pictures included. run blender in another window (to see print output) and use the P key to start. Let run until you see “exit” being processed.

<h3>Details of how this works:</h3>

layer 1 contains the “me” object (MO) and the “ADDER” object. Both are of type Empty: <img src=me.jpg>

MO: has an always-sensor attached so that it is executed in every game frame. Its’ job is to read commands from the socket and process them. More on this later. Since this socket-reading is all that it does, its’ object type is “Empty”. Here is the pythonController (PCTR) called me.py, but read further along before trying to understand it all.

ADDER: is used to add objects using the AddObjectActuator connected to it. The reason a separate object is used is partly for clarity (see “Where am I going with this” below). adder.jpg

ADDER contains the initialization code (reason below), which is done via an “initSensor”, which is an always sensor with the first button clicked off so it only happens once. The GameLogic object is used to hold all global variables that are initialized. Global variables include: the “object dictionary” (OD, see below), lastCommand (see “2-step process” below), the client socket connection and a “done” boolean. <a href=adder.py>Here is the adder.py PCTR</a>

The reason that ADDER contains the init code and not ME, is that ME has an always sensor attached and would need to test something like GameLogic.isInitYet during every game frame (cycle). ADDER only requires an initSensor, and is also on layer 1, so it is a good place for it.

The “object dictionary” (OD) is a python dictionary that holds references to the objects we create, so that we may edit and delete them later from the me.py script. Actually, the values in the dictionary are references to PCTRs attached to said objects, so we can get to the object (controller.getOwner()) AND the controllers’ sensors and actuators. The act of entering a key/value pair in the OD is called “registering”. These controllers register themselves when their registper.pt PCTR is called just after creation time. This is described in more detail in the “2-step” creation process below.

The “BASE” object is an object of type “Empty” that exists on layer 2. It is the object that gets created during the first part of the 2-step add process. All the logic bricks created on this object will be replicated for each object created, as well as everything else associated with this object. Most important are the initSensor which triggers the self-register PCTR “register.py” and the editting actuators attached to the PCTR. At this point, endact and replaceact are included, which do the EndObject and ReplaceMesh respectively. Later, when real multi-user is developed, we’ll most likely need some sensors for these “THEM” objects. base.jpg

Note that the namespace for the new objects is maintained and dictated by the server, because only it knows what clients are connecting and what they are doing. The new name (newName) is stored in the global GameLogic during the first step (see “2-step” below) and retrieved by the self-registration script later.

The meshes that will be used in the demo are placed on layer 3 and given names that are “well known” to the server. If you are implementing anything based on this blender file, you need to add your meshes here, name them well and use those names in your protocol/server.

protocol, 2/3 step add process:

&lt;ul&gt;
	-  Only one commandline from the server is processed during a game frame. This is both so that the graphics will be smoother, and because some commands take multiple "steps" to complete.  By 'step", I mean that control must be returned to the gameEngine in order for it to update it's internal state, before additional work can be done to complete the processing of the command.

	- When an "add" command is read from the socket, during the first step, the position of ADDER is set. If I "activate" the addActuator at this point, the object will appear first before the position is changed. The reasons for this require an analysis of the source code of the game engine, which I won't do here. In this case, since BASE is of type Empty, it doesn't matter, it's the replaceMesh that we have to wait to do.  So instead, we go ahead and call addActiveActuator for BASE, and then the command is placed in a global "lastCommand" for processing during the next invocation of MO.  This is the reason for the processing of lastCommand before socket processing in me.py.

	- During the next cycle, addActiveActuator could be called, except that we do not have a reference to the PCTR for the new BASE yet, if it even exists (I think it does). Again, the reasons have to do with the internal gameEngine logic. Basically, the object and all it's logicbricks are created in one cycle, but the detection  and action of the "initSensor" doesn't happen until the next cycle. So our 2-step process is actually a 3 step, since we skip again.

	- In the third step, we could see from the OD that the new PCTR for the new object has run and registered itself. Now we can look it up from ME, find the replaceMeshActuator attached to it, set the meshName on that actuator, and activate it.
[/list]

<h3>Where am I (we?) going with this, roughly in priority order:</h3>

<ul>

  • I’d like to clean this up a bit and create a real tutorial so I can give something back to the community. :slight_smile: Specifically, the naming of things could be better (e.g. register should be called base), me and ADDER “could” be combined (not sure this is good) etc…

  • fill out the protocol with stuff I know will work easily, e.g. “visible”, “orient”, “force”, etc… and allow for specifically named ADDER’s, which will be cloned from the base ADDER. This allows creating at named locations instead of XYZ’s.

  • exploration of multiple users
    <ul>

    • I need to create a simple “world” in which to play, or borrow a ready-made one.
    • “me” camera, motion controls for moving around, probably just “parent” the camera to ME and apply some simple keyboard sensors and motion actuators.
    • server logic for controlling simplest MU game possible, probably just moving around looking at each other, which means position/motion updates. What frequency?
    • me object will need to update the server with position information, and possibly sensor trigger “events” such as near, collide etc…
    • if using simply setPosition to update THEM’s based on the protocol seems jerky, try updating motion stuff to smooth this out
    • this is where the rubber meets the road and things get tricky! timing is tricky. what happens when clients collide? Because it’s across the network, things happen in different time-space continuums :slight_smile: Did you collide with me, or did I collide with you? Is your motion more important than the motion of the object in my blender-client that represents you? That’s why we keep it REAL simple at first and maybe just have large “cushions” around each other to prevent any sort of weirdness, like client objects being tangled up forever.
    • performance?
      <ul>
      • interleaving multi-step commands?
      • compressed protocol
      • 3-step to 2-step by finding the new controller from scene?
      • LOD/visibility optimizations?
        [/list]
    • I say “exploration” as I have my doubts about how far this can go, but it should be fun! But, I don’t think that limitations here will stop me from my real goal:
      [/list]
  • My intentions for this are for a MU game that does not depend on frequent updates from other clients. The server protocol is actually quite old (10 years) and based on telnet. This blender client will only be a simulation of what is going on on the server, and it doesn’t matter if things appear a bit different to each client, at least as far as the 3d stuff is concerned. The internal state of the server has no 3d info. I plan on creating a java proxy server that converts the existing protocol to my blender protocol.

  • Not sure I’ll ever need this, but realize that I could create multiple base type objects with different logicbricks. What can i change in python wrt logicbricks?

[/list]

Looking pretty good burper, good luck with the protocol.

I’m almost ready with mine, soo soo close:) Just got to work out that stupid bug in my server. I like the little demonstration you have of objects appearing and disapearing:)

Keep working on it.

Thanks Saluk. Questions for you:
You’ve said in the past that there were issues with the blenders’ version of python wrt the socket implementation for the client side of things. Is this true in general, or just with your client code? If general, what sort of issues await me?
Second question: if you were starting over, and looking to use my protocol for your game, what things would you need added? Or is it’s architecture fundamentally flawed? Fire away, I’m brand new to blender and python and am hungry for the feedback.
Crawling up the UV-textures-vertexpainting learning curve, but I suppose my demo can be wireframe for now. Maybe will switch to writing a simple server for forum readers to play with and bump into each other :slight_smile:

The problem I had was my own bugged code, I had threads that I couldn’t figure out how to stop running, and other threads that they are waiting on etc, so I have fixed that problem. Just be aware that with any code that has threads or some form of blocking function, blender won’t close unless all the processes are shut down.
My other main problem that I still have is that the server won’t allow any new connections once someone logs out. If you close blender manually (end task) rather than killing the threads and safely quitting blender, the server keeps on ticking and you can reconnect again as many times as you want. It’s most likely a line or two in my server that’s wrong, so I should complete it over the weekend.

Your protocol looks pretty nifty, it’s a bit more structured than mine currently. I’m not totally clear where your going with it, it looks pretty good for a multiuser design type of environment, where lag isn’t as much of an issue. For people having different characters though, who have to move and aim in realtime, I settled on setting properties for which direction people are moving and aiming, so as to only have to send network traffic when those variables change. Just an idea. I’m pretty new at this as well, so if you come up with any more brilliant ideas, I’d like to hear them:)

Hopefully I can get my demo out soon, I want to change my client side (blender) coding a lot because it’s too complicated to change or add things.