I’ve been working on a basic First Person control template object that so far works pretty well (mouse look, movement, skybox support). But when I trigger a scene reset the terminal tells me the objects have been freed. So I implemented a little check that looks to see if the object is invalid, and if so run init() again to recreate the object. Now Blender just crashes, and I’m stumped as to what is causing it. Any help would be much appreciated.
Try changing from line 57 down in SkyboxCam.py to:
skybox_cam = None
def main(cont):
if skybox_cam:
if skybox_cam.hasSkybox:
#next we get the skybox camera ONLY if we haven't already found it
if not skybox_cam.gotSkyboxCam:
skybox_cam.getSkyboxCam()
else:
skybox_cam.update()
else:
skybox_cam = SkyboxCam(cont)
and then change your other modules to something similar
EDIT: I just tested your files, without changing anything, and it doesn’t crash on me. I’m using blender 2.67a.
Interesting. What OS are you on? I’m currently working with the Mac OS X version of 2.67a, and I have other problems with that version from time to time as well (stupid choppy mouselook), so that could be the cause of it. At home I use Linux/Windows, so it’s not quite as much of an issue, I’ll have to see if it works fine there.
Also, trying your solution throws an UboundLocalError, but I do see what the concept is and it should work once I toy with it a little, thanks!
Hmm, my original code still seems to be crashing under Linux. Could it have something to do with how I’m using bge.types.PyObjectPlus.invalid? Here’s how I’m currently checking to see if the object has been freed or not:
if skybox_cam.cont.invalid:
skybox_cam.__init__(bge.logic.getCurrentController())
It could be something in your init function is still referencing the previous object, that was happening to me in a file recently if I just ran the __init__function on a new object, I had to re instance the class.
It would be better to provide one download. You could even distribute an archive.
Anyway. You have some flaws in your design:
Better create a new python object. This is a quick fix.
What is “main” supposed to do? Better give the function a name that describe what it is supposed to do. This way you can skip all this unnecessary comments.
The most important code is placed at the end of the module. It is pretty disturbing. I usually start reading at from top to bottom and expect relevant code first. I might even skip he lower parts when I’m not interested in the little details of the implementation.
You bind a class instance to a very specific controller. And this dependency is your real problem.
This instance runs with this controller ONLY and only with this controller.
I strongly advice to remove this dependency.
The logic should depend on the current controller not a controller that was running the code in the past. This does not need to be the same controller as you already discoverred.
How to solve the dependency?
pretty simple:
-> Remove the reference to the controller from the class. There is no need for it.
mouse_look = MouseLook()
The instance can request the context (bge.logic…) by itself.
# rather than
# self.cont.activate(self.aPlayerMotion) <- depends on a concrete controller and a concrete actuator from the past
# use
from bge.logic import getCurrentController
...
getCurrentController().activate("PlayerMotion")
The same belongs to references to gameObjects:
–> player - it directly belongs to the current controller. There is no need to store a reference in the instance. Finally it is only used in init() so a local variable is the better choice.
–> player cam -> This indeed is an object you can store a reference to. But you need to ensure it is up-to-date before using it. Provide logic to update the camera:
def self.getCamera(self):
if self._camera and not self._camera.invalid:
return self._camera
self._camera = self.retrieveCameraFromScene()
return self._camera
...
Thanks Monster. The reason for binding the class to an instance and having the main() function is I want to do a lot of initialization stuff, and I’m not sure of a better way to do it. I know some people use a simple check to run some init code while avoiding class-based structure, like:
if not 'init' in object:
#do all the initialization stuff
object['init'] = True
Is that equally okay practice, or is there a better way to create the class instance on the object?
def init(controller):
do some heavy initialization
def doSomethingWithoutToCareInitialization(controller):
do your normal processing without caring initialization
This way you trigger the first function just once after game object creation. Just make sure it is placed above any dependent function calls.
This is only worth if you need a lot of (static) initialization.
If you want be more flexibility (e.g. change external properties at runtime) you better get the latest version shortly before using them:
from bge.logic import getCurrentController
def demonstrateReadingProperty():
propertyOwner= getCurrentController().owner
...
propertyName = "property"
propertyValue = propertyOwner.get(propertyName)
print("The property '{}' at object '{}' has a value of '{}".format(
propertyName, propertyOwner.name, propertyValue))
...
Okay, that makes sense then. Now in actual execution, that init function won’t get called automatically, will it? Would you give it it’s own Python controller (in Module mode) that only gets triggered once, before everything else?
That is right. Just use an always sensor without pulses and a python controller.
This is the reason, why it makes most sense if there is a lot of initialization going on e.g. Creating classes, reading files and so on.
The main benefit is, it is much easier to identify initalization code and working code.