diafero wrote:[..], but usually if I have weird Python errors, I forgot to change the class name to match the file name.
LOL !!
Diafero, you hit the nail on its head here!
Actually, I really forgot to name the class as the file in this case. Funny thing is, I already knew that it's important, but, well, sometimes I've got "Ein Brett vor dem Kopf".
To bring it to the point: Removing this mistake, suddenly everything worked fine. Thank you so much for mentioning the obvious.
diafero wrote:Glue Python code allows the C++ code to mess with the runtime state of this Python module...
Aha, so this is the Python part of the engine to Python interface; now I understand why it's called glue.
D'Lanor wrote:tachzusamm wrote:By the way: What is the self.id and self.version under __init__ for?
They have no use within the game itself. They exists because the Max plugin needs them.
Good to know; so I can stop thinking about numbers which are not used elsewhere.
D'Lanor wrote:Yes, each module is loaded from the pak file with the latest time stamp, regardless what age it is from. Duplicates should be avoided. That is why it is the custom to add an age prefix to Python file names/classes.
This can get tricky with global files. For example if a writer heavily modifies a global module and distributes it with his own age then any other age using that module will break.
This is really useful information; I did not know that. It explains why I got this strange effects.
As said, it already works now, like a charm, and totally as expected.
Thanks again to both of you, D'Lanor and diafero.
===================================================================
Okay, now let me reveal why I did all this (warping multiple objects around):
I love to have vegetation in an age. Huge vegetation, grass, bushes, flowers and trees all around the places.
But - there's a problem with it: the number of faces of all objects in total will grow, the more you copy plants around, slowing down the engine's speed. Yes, there's the option to make use of VisRegions, which can help a lot, but putting duplicates of plants in various regions of an age increases the pak's file size and loading time.
So I came up with an idea: What if I just warp objects around between regions, depending on the player's location, instead of just hiding/unhiding them? This could keep the file size lower, while keeping the facility to have objects shown to the player everywhere.
Imagine there are 3 regions A, B and C; some plants are normally located at the (link-in) region A. The player now enters a region B, from where region A can't be seen - now we warp the plants to region B, giving the illusion there is again plenty of vegetation. Same with region C, and back to region A.
Actually dozens of regions could "hold" a vast amount of plants.
True, this is "faking", or let me call it doing illusion - but hey, isn't the whole URU game mostly working this way?
To simplify the handling, I implemented making use of "hooks" in the script. This means, you can put empties (as hooks) in the Age, working as placeholders for the destinations for the real objects. This is much easier than having to type co-ordinates into AlcScript as destinations.
(Next step could be to apply rotation as well in addition to location, if this is possible. If you know how, please tell.)
The currently used Python script:
- Code: Select all
from Plasma import *
from PlasmaTypes import *
actEnter = ptAttribActivator(1, 'Enter region activator')
actExit = ptAttribActivator(2, 'Exit region activator')
doNetForce = ptAttribInt(3, 'netForce flag')
warpObjs = ptAttribSceneobjectList(4, 'list of warp objects')
obList = []
# Warp to a destination object
def WarpObjectToDestOb(warpOb, destOb):
pos = warpOb.position()
dest = destOb.position()
matrix = warpOb.getLocalToWorld()
matrix.translate(ptVector3((dest.getX() - pos.getX()), (dest.getY() - pos.getY()), (dest.getZ() - pos.getZ())))
warpOb.netForce(doNetForce.value)
warpOb.physics.warp(matrix)
return pos
# Warp to a known location
def WarpObjectToPos(warpOb, dest):
pos = warpOb.position()
matrix = warpOb.getLocalToWorld()
matrix.translate(ptVector3((dest.getX() - pos.getX()), (dest.getY() - pos.getY()), (dest.getZ() - pos.getZ())))
warpOb.netForce(doNetForce.value)
warpOb.physics.warp(matrix)
# Note: Change all "ptResponder" to "ptModifier" for a Modifier class
class paraWarpObList(ptModifier,):
__module__ = __name__
def __init__(self):
ptModifier.__init__(self)
self.id = 16005 # (the value does not really matter)
self.version = 1
print ('__init__%s v.%s' % (self.__class__.__name__, self.version))
self.oriPosList = None
def OnFirstUpdate(self):
#print ("---OnFirstUpdate---")
tog = False
for obj in warpObjs.value:
if not tog: # its the warp object
warpOb = obj
else: # its the destination hook
obList.append((warpOb, obj))
tog = not tog
print ("numWarps=%d" % len(obList))
def OnServerInitComplete(self):
#print ("---OnServerInitComplete---")
pass
def OnNotify(self, state, id, events):
#print ("---OnNotify---")
if (not PtWasLocallyNotified(self.key)):
return
if ((id == actEnter.id) and state):
self.MyWarpObject_onEnter()
elif ((id == actExit.id) and state):
self.MyWarpObject_onExit()
def OnSDLNotify(self, VARname, SDLname, playerID, tag):
#print ("---OnSDLNotify---")
pass
def MyWarpObject_onEnter(self):
print 'Warp Trigger Region entered'
bStorePos = False
if self.oriPosList is None:
self.oriPosList = []
bStorePos = True
#print ("ListLen:%d" % len(obList))
for obIndex in range(len(obList)):
#print ("obIndex=%d" % obIndex)
warpOb = obList[obIndex][0]
destOb = obList[obIndex][1]
#print warpOb.getName()
oriPos = WarpObjectToDestOb(warpOb, destOb)
if bStorePos:
self.oriPosList.append(oriPos)
def MyWarpObject_onExit(self):
print 'Warp Trigger Region left'
if self.oriPosList is not None:
for obIndex in range(len(obList)):
warpOb = obList[obIndex][0]
oriPos = self.oriPosList[obIndex]
WarpObjectToPos(warpOb, oriPos)
### Python Glue starts here ###
and an example AlcScript:
- Code: Select all
Region_WarpObject_B:
logic:
modifiers:
- name: logicMod_WarpEnter
cursor: up
flags:
- multitrigger
conditions:
- type: volumesensor
satisfied: true
direction: enter
trignum: -1
activators:
- type: objectinvolume
remote: Region_WarpObject_B
actions:
- type: pythonfile
ref: :WarpObjectPythonFileMod
- name: logicMod_WarpExit
cursor: up
flags:
- multitrigger
conditions:
- type: volumesensor
satisfied: true
direction: exit
trignum: -1
activators:
- type: objectinvolume
remote: Region_WarpObject_B
actions:
- type: pythonfile
ref: :WarpObjectPythonFileMod
actions:
- type: pythonfile
name: WarpObjectPythonFileMod
pythonfile:
file: paraWarpObList
parameters:
- type: activator
ref: logicmod:logicMod_WarpEnter
- type: activator
ref: logicmod:logicMod_WarpExit
- type: int
value: 0 # netForce(0)
- type: sceneobjectlist
refs: ['scnobj:Plant_1', 'scnobj:Plant_1_Dest_B',
'scnobj:Plant_2', 'scnobj:Plant_2_Dest_B',
'scnobj:Plant_3', 'scnobj:Plant_3_Dest_B',
'scnobj:Plant_4', 'scnobj:Plant_4_Dest_B']
Sidenote: Currently, the scripts store the object's original location of the objects before warping is applied, and restore the location if the region is left; I made this for testing. For the behaviour described above, the storing part, restoring location parts (in Python code) and leaving region part (in Python and AlcScript) should be omitted.
Oh, and as a final but important note: Both the object(s) and the destination hook(s) have to be set as Actor in Blender to get it working (otherwise the objects don't get a coordinate system applied, to mention the technical internals).
Thanks to Sirius as well for providing code examples.
Original thread for the warping part:
viewtopic.php?f=59&t=5251