python code structure (basics)

Hi everybody,

I have been working on some python scripts during the last months. But as I am not really an experienced programmer, I just wrote the things down in a very simple order.

I am working with several moduls and found out, that there are different ways to define several things. Here are some examples:
“init” will be called once at the beginning, “update” every frame

import GameLogic as gl
 
def init():
    scene = gl.getCurrentScene()
    cube = scene.objects["OBCube"]
    cube["offset"] = cube.worldPosition
 
def update():
    scene = gl.getCurrentScene()
    cube = scene.objects["OBCube"]
    ExternalData = something
    cube.worldPosition = ExternalData - cube["offset"]

The same thing would work like this:

import GameLogic as gl
 
scene = gl.getCurrentScene()
cube = scene.objects["OBCube"]
 
def init():
    cube["offset"] = cube.worldPosition
 
def update():
    ExternalData = something
    cube.worldPosition = ExternalData - cube["offset"]

So I never really cared about code structure as the things I wrote did thier job. They still do, but now I would like to know a little more about the basics behind this. Maybe someone got a good link where I can find some explainations about how Blender handles the python moduls?
I would like to know if there is any difference in performance and/or stability between the two ways to use the script. How to do it the “clean” way? :slight_smile:

Thanks in advance,
Quen

Hi Quen,

I like your avatar picture :).

I assume you are using the module mode of the python controller.

If you do so, you can add a parameter to your functions to get the current controller e.g. update(cont). Just as short note.

There are to initializations you should keep in mind.

  1. The module initialization
  2. The object initialization

1. module
(see your second example)
The module initialization is the code at the first level of the module like your import statement.
It will be executed the first and only time you call any function of the module, before this function is executed.

This is nice for import statements, “constants” etc.
You can call other functions as well (e.g. init()).

Be aware it does not matter which object calls the module. So do not use statements like: cont = GameLogic.getCurrentController() unless you are 100% sure the module will never been called by another object (or you change the global variable later).

Modules are objects as well, so you can store data in them e.g. your example with cube =…
In difference to scripts this variable still exist when accessing the module the next time.
I think modules exist as long as the BGE is running. Maybe they are destroyed when loading a new Blendfile/restart the current blend. This could be subject of a test.

2. Object
(see your first example)
This should be done with a separate call like your init(). But you need to make sure it is explicitly called by a python controller (e.g. triggered by an always sensor), before any other logic needs the initilized values. It needs to be done for each object even if they execute the same function.

By the way such call to init() mostikely triggers the module initialization as well.

Remarks:
One thing to the module, that I recognized in Blender:
if you import module A from a module B and
if you call a function from A it seems that there are two instances of the same module.

That is confusing as the modules do not know the variables of eachother. Usually I manage to avoid such constructions.

I hope I haven’t confused you.

Thank you very much for your answer! I really appreciate you supporting this community that much, especially about scripting. :slight_smile:

Nice to hear, it is supposed to reflect my personality. :confused:

Well… :wink:
I have to admit that I had to read it a second time. And I think, I got a little confused about the terminology here. Just to be clear:
modul = the whole script (.py file)
object = a function in this script (e.g. update() )

One thing that is clear now, is that I can define “constants” in the “modul initialization code”, but no “globals”. And that this part will be run only once. I was not aware of that point before!
So basicly it should be better for the performance to create constants at that point (like in my second example), instead of inside an object every time it is called?

I will test around with this a little more. :slight_smile:
Thank you already!

  • correct!

object = KX_GameObjects

which are the owner of a controller/sensor/actuator. Finaly the thing that you can select in the editor and apply logic to.

Python do not really have constants thats why I wrote it as “constants”. You could define any variable and just do not change it later. So it remains constant. I use that to define the property names e.g.

 
prop = "prop"
 
...
obj[prop] = "whatever"
propValue = obj[prop]

That makes it easier to replace the property name in a later stage of the development, as it is defined at one place. But this is my style.

You can define globals at the first level of the script. They are local to the module and global to any function (member) within the module. If you want to change the value of such a global you need to make it available by:


moduleVariable = 100
 
def demo():
  <b>global moduleVariable</b>
  moduleVariable += 1
 
def printDemo():
  print( moduleVariable )

with out “global …” you would declare a new member local variable which is hiding the module variable.
You do not need this if you read the module variable.
It is in a way local, that only this module knows about it.

This depends what you need.
You example 2 is fine as long as you make sure it is first called in the scene you want to reference (Otherwise, the scene and cube would reference something else).

And yes, because it is executed once it would be faster than a test for initialization with each call.
A single call to init() would have the same performance as long as it is called once.

Thank you again :slight_smile:
I think I got it for now. If there will be any new questions comming up in the future, you will hear from me. :wink:

Hi again :wink:

I figured out a strange “lag”-problem when I use sockets in a certain way:
In the beginning of my project, I used a very straight script for revieving data from a socket. It looks like this and is triggered by an always sensor:

import socket
import struct
import GameLogic as gl
import Mathutils as m
 
ip = "192.168.1.10"
port = 1511
multicastAdd = "239.255.42.99"
mreq = struct.pack('4sl', socket.inet_aton(multicastAdd), socket.INADDR_ANY)
sT = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sT.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sT.bind((ip,port))
sT.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
#sT.setblocking(0)
 
try:
 # recieve data
 data, addr = sT.recvfrom(10240)
 ID, bytes, frame, markerSetCount, unknownMarker, rigidBodys = struct.unpack("&lt;HHIIII",data[:20])
 if markerSetCount == 0:
  rbs = []
  pos = []
  quat = []
 
  for n in xrange(rigidBodys):
 
   rbs.append([])
   pos.append([])
   quat.append([])
 
   rb, posX, posY, posZ, quatW, quatX, quatY, quatZ, mCount, errors = struct.unpack("&lt;IfffffffIf",data[((n*40)+20):((n*40)+60)])
   rbs[n].append(rb)
   # putting data in rbs[]
 
   pos[n] = m.Vector([rbs[n][1],rbs[n][2],rbs[n][3]])
   quat[n] = m.Quaternion([-rbs[n][4],rbs[n][6],rbs[n][5],-rbs[n][7]])
 
  scene = gl.getCurrentScene()
 
  camera = scene.objects["OBview"]
 
  camera.worldPosition = pos[1]
  camera.worldOrientation = quat[1]
  # doing more stuff with the data...
except socket.error:
 pass

It worked fine, although I was wondering if it would be that good to establish the socket connection new in every frame! :wink:
So these days I tried around and tried the two following versions.
init() called once in the beginning, recv() with a pulsed always sensor again:

import socket
import struct
import GameLogic as gl
import Mathutils as m
 
def init():
 ip = "192.168.1.10"
 port = 1511
 multicastAdd = "239.255.42.99"
 mreq = struct.pack('4sl', socket.inet_aton(multicastAdd), socket.INADDR_ANY)
 
 gl.sT = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 gl.sT.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 gl.sT.bind((ip,port))
 gl.sT.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
 #gl.sT.setblocking(0)
 
def recv():
 try:
  # recieve data
  data, addr = gl.sT.recvfrom(10240)
  ID, bytes, frame, markerSetCount, unknownMarker, rigidBodys = struct.unpack("&lt;HHIIII",data[:20])
 
  if markerSetCount == 0:
   rbs = []
   pos = []
   quat = []
 
   for n in xrange(rigidBodys):
 
    rbs.append([])
    pos.append([])
    quat.append([])
 
    rb, posX, posY, posZ, quatW, quatX, quatY, quatZ, mCount, errors = struct.unpack("&lt;IfffffffIf",data[((n*40)+20):((n*40)+60)])
    rbs[n].append(rb)
    # putting data in rbs[]
 
    pos[n] = m.Vector([rbs[n][1],rbs[n][2],rbs[n][3]])
    quat[n] = m.Quaternion([-rbs[n][4],rbs[n][6],rbs[n][5],-rbs[n][7]])
 
   scene = gl.getCurrentScene()
 
   camera = scene.objects["OBview"]
 
   camera.worldPosition = pos[1]
   camera.worldOrientation = quat[1]
   # doing more stuff with the data...
 
 except socket.error:
  pass

And finally I tried this one, where only recv() is called by an always sensor:

import socket
import struct
import GameLogic as gl
import Mathutils as m
 
ip = "192.168.1.10"
port = 1511
multicastAdd = "239.255.42.99"
mreq = struct.pack('4sl', socket.inet_aton(multicastAdd), socket.INADDR_ANY)
sT = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sT.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sT.bind((ip,port))
sT.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
#sT.setblocking(0)
 
def recv():
 try:
  # recieve data
  data, addr = sT.recvfrom(10240)
  ID, bytes, frame, markerSetCount, unknownMarker, rigidBodys = struct.unpack("&lt;HHIIII",data[:20])
 
  if markerSetCount == 0:
   rbs = []
   pos = []
   quat = []
 
   for n in xrange(rigidBodys):
 
    rbs.append([])
    pos.append([])
    quat.append([])
 
    rb, posX, posY, posZ, quatW, quatX, quatY, quatZ, mCount, errors = struct.unpack("&lt;IfffffffIf",data[((n*40)+20):((n*40)+60)])
    rbs[n].append(rb)
    # putting data in rbs[]
 
    pos[n] = m.Vector([rbs[n][1],rbs[n][2],rbs[n][3]])
    quat[n] = m.Quaternion([-rbs[n][4],rbs[n][6],rbs[n][5],-rbs[n][7]])
 
   scene = gl.getCurrentScene()
 
   camera = scene.objects["OBview"]
 
   camera.worldPosition = pos[1]
   camera.worldOrientation = quat[1]
   # doing more stuff with the data...
 
 except socket.error:
  pass

I was suprised how much performance I got by using the second or third version, compared to the first. Framerate went from ~45 to almost 80.
BUT: with script no. 2 and 3, I get a latency of around 1 second!
With the first script, the object (“camera” in this sample) reacts in realtime based on the streamed data.
The point is, that I cannot accept that second of latency in my project and that I really would need that preformance boost. So I try to find out, where that latency is comming from and how to remove it. Maybe someone could help me out with this?
I checked the blocking option of the socket, that does not make any difference.
Of course it’s hard to test if you don’t have a stream with data to try around, but maybe someone got a hint for me. :slight_smile:

Greetings,
Quen