Clicking on a 3D object

Ah, I think that I see now.

I think that what is happening is this: when a collision event is called, a collision-entry is passed as a parameter to the method being called by the event–in this case, the “print” function. This collision-entry contains various pieces of information about the collision.

As a result, the “print”-function is getting not only the text that you provide, but also the collision-entry, and is thus printing both.

If you want this to not happen, I suggest creating a new function to be called by the collision-event, one that accepts a collision-entry parameter, but that doesn’t print that collision-entry.

Okay, going back to your original code, I see the problem occurring now.

I think that what’s happening is that, because the ray isn’t being moved out of the target-object, it’s registering as continuing to collide the whole time, even between clicks. So when you click the second time, the ray doesn’t collide anew–it’s merely still colliding. That means that, instead of generating “in” events, the system is generating “again” events–and you’re currently only accepting “in” events.

This, I suspect, is another thing that might be fixed by switching to a CollisionHandlerQueue instead of a CollisionHandlerEvent.

If you really want to use a CollisionHandlerEvent, however, then you can perhaps work around the problem by setting the ray’s “from”-bitmask to be zero at the end of the “click” function, and setting it to be one (or some other value as appropriate) at the start of the function. That way the ray will no longer be detected as colliding between clicks, and thus any collisions found as a result of clicks will be registered as “new”, and thus “into” events.

Fair enough–it seems that I misunderstood the original problem, and I wasn’t aware that you had already implemented the use of your own traverser. My mistake!

It’s ok. It’s actually my fault. I didn’t mean to put the whole thing.

So you mean something like this?

def myFunction(collisionEntry):
    print("Smiley Clicked!")

So can I do something like this?:

base.accept('into-smiley', myFunction)
base.accept('again-smiley', myFunction)

I want to stick with the event one because in my real game, I want it to generate an event. You can understand what I mean because I am going to make a new topic which is of the category ‘Panda3D Features in Development’

Exactly, yes! :slight_smile:

You can, but I suspect that it’s going to cause more problems for you. (Unless you use the bitmask approach that I mentioned above.)

Still, it should be possible to make it work using that approach.

I suppose my question is this: what does a collision-event offer that the mouse-click event doesn’t? What advantage do you gain by having a separate event be called after the mouse-event? At the least, if I may ask, what is it that you’re trying to do that calls specifically for a collision-event?

Perhaps your thread will answer my questions, but I’ll leave them here for now.

I tried it, but I am still getting the same issue, even with

base.accept('again-smiley', myFunction)

So what to do?

I don’t know how to use it

Did you add an “again” pattern, as you added the “in” pattern?

Remember that getting a collision-event calls for two things: “accept”-ing a function to be called, and registering an event-pattern.

If you’re not sure of what I mean by “registering an event-pattern”, look at the top of your code, just after you create your CollisionHandlerEvent.

The manual has information on how bitmasks work–see this page, I believe. The topic is also covered in my “Beginner’s Tutorial”, in Lesson 9.

(Neither shows exactly what to do for this specific instance, but both show how bitmasks work in general, with one or more examples, I believe.)

[quote=“panda3dmastercoder, post:24, topic:27083”]

Sorry! Don’t answer this. I forgot to set the out pattern

1 Like

No sorry I didn’t. I just realized

Fair enough! That happens. :slight_smile:

Now it prints infinitely. I suspect I should use:

collHandEvent.addOutPattern('outof-%in')

and then:

base.accept('outof-smiley', myOutOfFunction)

And the code of myOutOfFunction should be:

def myOutOfFunction(collisionEntry):
    pass

right?

No, I don’t think so. After all, unless you click somewhere other than on your smiley, nothing is moving your ray our of the target-object, and thus no “out” pattern will be generated.

This was the problem that I was expecting when using both the “into” and “again” patterns.

The main solution that I see here is to use bitmasks to essentially “turn off” the ray between clicks. (If you’re still unclear on how bitmasks work, remember the links that I gave above.)

If I may ask again, why is it that you’re set on using a collision-event, especially when you’re already driving this via another event (the mouse-event)? From my perspective–which admittedly is external and lacking in context–it really does seem like you’re doing this in a way that makes it rather more difficult than it might be.

I tested it out, and like you said, unless I click anywhere else, it prints infinitely.
I think that is OK for me, though. I will give you a spoiler alert, so read if you want to.
SPOILER ALERT
What my game that I was speaking about earlier is that you are in a castle, and there are many rooms. To get to the next room, you need a key, which is in a hidden chest. So when you find the chest and click it, it opens showing a pop-up. So if I click elsewhere, the chest will close. So I think that will do for me

In that case, do you really need the player to able to click on the object more than once? It seems to me that you could just react to the first click on the chest (as you were doing when you were only using the “in”-pattern).

Personally, I would likely still implement this using a CollisionHandlerQueue than a CollisionHandlerEvent, I think. For ray-casting it’s just far simpler, I find.

Plus, a CollisionHandlerEvent will produce events for everything that the ray intersects with, I believe, so if two of your clickable objects end up one behind the other then a single click might interact with both of them. CollisionHandlerQueue, however, allows you to sort its collision results and then take only the first one, thus allowing you to respond to only the closest collision.

Still, if you’re set on using a CollisionHandlerEvent, then I’ll leave the argument about CollisionHandlerQueue here–I’ve given my arguments, and repeating them ad nauseum might get–or have gotten–tiresome to see.

In a room there will only be one chest, so it’s OK. So what I should do is:

#At the beginning, after import statements and Traverser and CollisionHandlerDefinitions
collHandEvent.addInPattern('in-%in')
collHandEvent.addInPattern('out-%in')
#######################################################################

#In function 'click'
base.accept('in-smiley', myFunction)
base.accept('out-smiley', myOutOfFunction)

right?

My whole code (just in case):

from direct.showbase.ShowBase import ShowBase
from panda3d.core import *

ShowBase()

base.cTrav = CollisionTraverser()

collHandEvent = CollisionHandlerEvent()
collHandEvent.addInPattern('into-%in')
collHandEvent.addOutPattern('outof-%in')

smiley = loader.loadModel('models/smiley')
smiley.reparentTo(render)
smiley.setPos(0, 25, 0)
smiley.setTag('smileyTag', '1')

cNode = CollisionNode('smiley')
cNode.addSolid(CollisionSphere(0, 0, 0, 1))
smileyC = smiley.attachNewNode(cNode)

pickerNode = CollisionNode('mouseRay')
pickerNP = camera.attachNewNode(pickerNode)
pickerRay = CollisionRay()
pickerNode.addSolid(pickerRay)
base.cTrav.addCollider(pickerNP, collHandEvent)

def myFunction(collisionEntry):
    print('Smiley Clicked!')

def myOutOfFunction(collisionEntry):
    pass

def click():
    mpos = base.mouseWatcherNode.getMouse()
    pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
    base.cTrav.traverse(render)
    
    base.accept('into-smiley', myFunction)
    base.accept('outof-smiley', myOutOfFunction)

base.accept('mouse1', click)

base.run()

P.S. Now that you know about my game, can you tell me how you made the Models/Misc/environment.egg file in your game so I can use the same method for the castle. I already use blender (which I think is what you use too, considering the .blend files you have). So can you tell me anything that helps make a castle in blender fatster?

Honestly, I don’t think that the “out”-pattern will be all that useful to you. But otherwise it’s likely enough okay.

Wow, that’s… that’s a topic for an entire tutorial, really. I’d recommend looking up tutorials on how to model things in Blender–you may find better instruction there.

I need out to close the chest, remember?

I already know a thing or two about blender. I can texture, create models etc. So I will try to make a castle on my own. If you were the one who made the enemy models, can you tell me how to animate them? I really need animations for the Actors in my game

Hmm… Okay, that make sense. Fair enough, then!

In that case, one suggestion that I might make is to create elements that you can re-use. For example, let’s say that you castle has four turrets, one at each corner. You can likely model just one turret, and then copy-paste it into place. Similarly, things like pillars or statues may be repeated in this way, too.

[edit]
Oh, one more thing: Make use of layers. They can allow you to separate out various parts so that you can work on some without others getting in the way.

For example, it may be easier to work on the interior if the roof isn’t present. To this end, having the roof be kept in a separate layer can allow you to essentially “switch off” the roof while you’re dealing with the interior, and then “switch it back on” when time comes to export.
[/edit]

I was the one, and essentially the process is this:
(Noting that it’s based on my experience with the version of Blender that I use; things may have changed in subsequent versions.)

  • Create an Armature for the object, approximating how you want it to bend.
  • Apply an Armature Modifier to the object, specifying that it use the Armature made in the previous step.
  • Using the “Weight Paint” mode, set the bone-weighting on the object as appropriate.
    • In my version of Blender, at least, there’s an option in the “Weights” menu of that mode called “Assign Automatic From Bones”. This will attempt to automatically assign weights for the various bones. While it’s not always perfect, it can provide a good start in some cases, I think.
  • Open up the “Dope Sheet”.
  • Here, set the context-dropdown to “Action Editor”
  • Create an “Action” for each animation that you intend.
    • Make a point of pressing the button labelled “F” beside each Action! This should cause it to be saved, even if no object is apparently using it. Without it, your animations may be lost!
  • In each action, adjust your armature and set down keyframes to make your animation.

Now, beyond that we get to exporting. I use an old-ish version of Blender along with YABEE, but I think that this approach is somewhat deprecated now. I’m afraid that I don’t know much about the newer export methods.

If the above list-description is too vague, or you’re not familiar with some of the elements, then again I suggest looking for a tutorial–this sort of thing isn’t really well-suited to sitting down and writing out in a forum thread in great detail, I feel!

I am not sure what an Armature is. But I do know the Action Editor. How do you create an action? I never tried the F key, so I will try it now. I will read the post carefully and see what to do in blender.

By the way, I use Blender 2.9.1, and I save them as blend files and convert them later with blend2bam (which is installed by pip under the command: pip install panda3d-blend2bam).

In that case, I really do recommend looking up a good tutorial on animating in Blender. Especially as I’m still using an older version, and the process may have changed.

To be clear, this is a button on the screen, not a key on the keyboard!

Where on the screen?