Seeing as I’m going to be using the same Collison and whatnot for client players and online players, I put the code for setting that up in a separate file I can import.
The collision is built around a “centerNode” that I can use as a handle. When I just run the code to set up the collision shapes and parent the camera to the centerNode, everything works fine.
The problem arises back in main.py when I try to move the centerNode.
centerNode.setPos(0, 0, 3) gives “NameError: ‘centerNode’ is not defined”.
The core of the problem, I imagine, is that “centreNode” is defined in the separate file, and thus isn’t defined within the scope of the “main.py” file. Thus some means of referencing its location in the separate file is called for.
The exact means by which this might be done likely depends on the context: How are you importing that separate file? And is “centreNode” a member of some class or other?
Maybe you could make this work with python tags.
You would pack everthing you want external access to, in a single class, including the hitbox and then setup a tag for the hitbox.
EDIT:
On second thought, the hitbox probably is not required to be part of such a class. As long as the tag points towards an object of that class, it should give access to that object nonetheless.
That is a good point: when dealing with collisions, Python-tags can be a useful means of accessing related data.
There is a caveat, however: doing so can result in circular references, which can interfere with garbage collection and thus potentially cause memory leaks.
That isn’t to say that it shouldn’t be done–merely that caution and cleanup might be wise!
Of course, if the relationship between collider and “centerNode” is static–if, for example, the “centerNode” is always the parent or the parent-of-the-parent of the collider–then one can simply use that relationship to access it. This can be done via the “getParent” method of the “NodePath” class.
Yes, centerNode is defined in a function in another file, which is imported and called in main.py.
The relationship is in fact static, centerNode only exists to sort the nodes belonging to different players, and to act as a handle to move them all at once.
Right now, I can use the getParent of the camera, as this is a FPS game.
But with online players or npcs, would I need to have some some other way of referencing their centerNodes?
The way the function is set up, whatever gets attached in the position to the camera is a parameter.
I could probably just define the aim collision in the game loop and use that as a reference.
Hum. Perhaps a bit more information would be useful here, then–I was imagining that you were trying to access “centerNode” in a collision-response event, in which case you would have direct access to the collision-node.
So, in what context are you trying to access “centerNode”? What are you trying to do, and under what conditions (including, if applicable, in response to what)?
There’s likely a simple way of doing this, but without more information it’s hard to guess at what might work, I fear!
I am able to move it in the function where it’s defined, so that’s a good start.
However, I can’t move it outside of the function whatsoever, even in the same file.
Because I am going to use the centerNode down the line for movement, this could be a problem.
I might as well share my code here, if it helps.
main.py:
from direct.showbase.ShowBase import ShowBase
from gameCode.characters_and_players.pcSharedFunctions import *
class MyGame(ShowBase):
def __init__(self):
ShowBase.__init__(self)
#this only disables the "model viewer" controls.
base.disableMouse()
#Later, this should lead to a menu.
self.scene = self.loader.loadModel("models/environment")
self.scene.reparentTo(self.render)
self.scene.setPos(0, 42, 0)
pcCollideSetup(base.camera)
#centerNode = camera.getParent()
#centerNode.setpos(0, 0, 4)
#moveToSpawn(0, 0, 3)
teGame = MyGame()
teGame.run()
As you currently have it, “centerNode” is a local variable–while the node itself should persist, the variable that holds it, “centerNode”, will presumably be lost when “pcCollideSetup” is done.
Further, being a local variable, it is indeed only accessible from within “pcCollideSetup”.
Now, there are a few ways that you could address this:
You could give the node a unique name, and then search the scene-graph for it.
This might be a little cumbersome at times
You could create a global variable
This can become messy as more global variables are made
You could create a module-level variable. That should be available to be referenced via its module.
In short, a variable defined outside of the “pcCollideSetup” method.
You could store a reference to the node in something else that persists
Of course, you’d need such a something else first
Let me focus on that last possibility for a moment: If you were to create a class to hold your character-nodes, and any other character-specific data that you might have (e.g. health), then you could create instances of that class. These instances could then be instantiated and stored in the main script, and thus be accessible from it.
(If the design of your game becomes more complicated, you might end up with layers of this: your “Game” class instantiating a “Level” class, which in turn instantiates your “Character” class, for example.)
Sorry for the late reply, I’ve unfortunately been busy. And thank you so much for the help! as you may be able to tell, I’m totally new to this sort of thing.
I’ve rewrote my code to have all nodes defined as a class that I can instance.
However, I get in pcSharedFunctions.py:
centerNode = render.attachNewNode
NameError: name “render” is not defined
I assume this is because render doesn’t exist in that file.
Should I try and import showbase? or could something else be going wrong here?
Edit: I just tried importing showbase, which doesn’t seem to do anything. I’ll look into this tomorrow, as it is getting late.
Hmm… Do I presume correctly that your main “game” class still inherits from ShowBase?
If so, then where does the line above get executed in the overall flow of the program–is it before or after the initialisation of ShowBase in your main “game” class?
That is, consider the following program:
“SomeOtherClass.py”
from panda3d.core import PandaNode
class SomeOtherClass():
def __init__(self):
render.attachNewNode(PandaNode("cat"))
“main.py”
from direct.showbase.ShowBase import ShowBase
from SomeOtherClass import SomeOtherClass
class MyGame(ShowBase):
def __init__(self):
myOtherObj = SomeOtherClass()
ShowBase.__init__(self)
game = MyGame()
game.run()
In the above, follow the order in which things are executed: First our “MyGame” class is instantiated. Then our “SomeOtherClass” class is instantiated. Then the code within “SomeOtherClass”'s “__init__” method is run. Then ShowBase is initialised.
But there’s a problem there: If I’m not much mistaken, ShowBase sets up things like “render” and “loader” when it’s initialised. Thus, since the use of “render” occurs before ShowBase is initialised, it doesn’t yet exist.
However, if we were simply to swap the order of “myOtherObj = SomeOtherClass()” and “ShowBase.__init__(self)”, ShowBase will be initialised before SomeOtherClass, and thus before the use of “render”, and so the code should work!
Now, I am rather guessing at this being the problem in your case–perhaps I’m wrong! If so, perhaps you could post your code–that way we might be able to see where the problem lies and so hopefully help you better.
No, I don’t think that this will help at all, I’m afraid. ^^;
Thank you, looking at your example it became apparent that the problem was that I simply do not understand how to write classes. Now things are working better, except for addSolid(), as used here:
from panda3d.core import CollisionNode
from panda3d.core import CollisionSegment
class pcColliders:
def __init__(self):
centerNode = render.attachNewNode("charaCenter")
charaColl = centerNode.attachNewNode(CollisionNode("charaColHolder"))
charaLine = CollisionSegment(0, 0, 0.25, 0, 0, -0.25)
charaColl.addSolid(charaLine)
When I try and run the program, I get “‘panda3d.core.NodePath’ has no attribute addSolid”.
My advice to you is not to use attachNewNode this method breaks the logic.
from panda3d.core import CollisionNode
from panda3d.core import CollisionSegment
from panda3d.core import NodePath
class pcColliders:
def __init__(self):
centerNode = NodePath("charaCenter")
centerNode.reparentTo(render)
cn = CollisionNode("charaColHolder")
charaLine = CollisionSegment(0, 0, 0.25, 0, 0, -0.25)
cn.addSolid(charaLine)
charaColl = NodePath(cn)
charaColl.reparentTo(centerNode)
By using NodePath, you will maintain a clear understanding of what you are doing. In your case, you are trying to set a method for the CollisionNode to the scene graph object. The way out is to use the node method.
charaColl.node().addSolid(charaLine)
However, this is a strange way. It is better to do it in the correct sequence.
I don’t really agree that the use of “attachNewNode” is an inherent problem, but do agree with serega regarding the core of the problem: “addSolid” is a method of the node, not the NodePath. That is, it’s to be added to the node held within the NodePath, and not to the NodePath.