Dragging Issues

Hello! I am trying to drag an object on the P3D screen. But my code is not working :frowning:. So can anyone help with fixing my code:

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

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        self.disableMouse()

        self.drag = False

        self.myTraverser = CollisionTraverser()
        collHandEvent = CollisionHandlerEvent()
        collHandEvent.addInPattern("%fn-into-%in")

        self.smiley = loader.loadModel("models/smiley")
        self.smiley.setPos(0, 25, 0)
        self.smiley.reparentTo(self.render)

        cNode = CollisionNode("smiley")
        cNode.addSolid(CollisionSphere(0, 0, 0, 1.1))
        smileyC = self.smiley.attachNewNode(cNode)

        self.pickerNode = CollisionNode("picker")
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.myTraverser.addCollider(self.pickerNP, collHandEvent)

        self.accept("mouse1", self.click)
        self.accept("mouse1-up", self.setDrag, [False])

    def setDrag(self, val):
        self.drag = val

    def click(self):
        mpos = self.mouseWatcherNode.getMouse()
        self.pickerRay.setFromLens(self.camNode, mpos.getX(), mpos.getY())

        self.accept("picker-into-smiley", self.setDrag, [True])

    def drag(self, task):
        if self.drag:
            if self.mouseWatcherNode.hasMouse():
                self.smiley.setPos(self.mouse.getPos())
        return Task.cont

game = Game()
game.run()


I got the issuse. I forgot to add drag(self, task) to the taskMgr. But now I am getting this error:

Traceback (most recent call last):
File “C:\Users\Rahul\AppData\Local\Programs\Python\Python38-32\drag.py”, line 53, in
game = Game()
File “C:\Users\Rahul\AppData\Local\Programs\Python\Python38-32\drag.py”, line 34, in init
self.taskMgr.add(self.drag)
File “C:\Users\Rahul\AppData\Local\Programs\Python\Python38-32\lib\site-packages\direct\task\Task.py”, line 394, in add
task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
File “C:\Users\Rahul\AppData\Local\Programs\Python\Python38-32\lib\site-packages\direct\task\Task.py”, line 413, in __setupTask
self.notify.error(
File “C:\Users\Rahul\AppData\Local\Programs\Python\Python38-32\lib\site-packages\direct\directnotify\Notifier.py”, line 130, in error
raise exception(errorString)
Exception: add: Tried to add a task that was not a Task or a func

Code:

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

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        self.disableMouse()

        self.drag = False

        self.myTraverser = CollisionTraverser()
        collHandEvent = CollisionHandlerEvent()
        collHandEvent.addInPattern("%fn-into-%in")

        self.smiley = loader.loadModel("models/smiley")
        self.smiley.setPos(0, 25, 0)
        self.smiley.reparentTo(self.render)

        cNode = CollisionNode("smiley")
        cNode.addSolid(CollisionSphere(0, 0, 0, 1.1))
        smileyC = self.smiley.attachNewNode(cNode)

        self.pickerNode = CollisionNode("picker")
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.myTraverser.addCollider(self.pickerNP, collHandEvent)

        self.accept("mouse1", self.click)
        self.accept("mouse1-up", self.setDrag, [False])

        self.taskMgr.add(self.drag)


    def setDrag(self, val):
        self.drag = val

    def click(self):
        mpos = self.mouseWatcherNode.getMouse()
        self.pickerRay.setFromLens(self.camNode, mpos.getX(), mpos.getY())

        self.accept("picker-into-smiley", self.setDrag, [True])
        
    def drag(self, task):
        if self.drag:
            if self.mouseWatcherNode.hasMouse():
                self.smiley.setPos(self.mouse.getPos())
        
        return Task.cont

game = Game()
game.run()

Wait, I figured out drag is also a variable.

Ok. I know I made many goof-ups. But this is a real problem. My smiley is not moving:

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

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        self.disableMouse()

        self.drag = False

        self.myTraverser = CollisionTraverser()
        collHandEvent = CollisionHandlerEvent()
        collHandEvent.addInPattern("%fn-into-%in")

        self.smiley = loader.loadModel("models/smiley")
        self.smiley.setPos(0, 25, 0)
        self.smiley.reparentTo(self.render)

        cNode = CollisionNode("smiley")
        cNode.addSolid(CollisionSphere(0, 0, 0, 1.1))
        smileyC = self.smiley.attachNewNode(cNode)

        self.pickerNode = CollisionNode("picker")
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.myTraverser.addCollider(self.pickerNP, collHandEvent)

        self.accept("mouse1", self.click)
        self.accept("mouse1-up", self.setDrag, [False])

        self.taskMgr.add(self.dragFunc)


    def setDrag(self, val):
        self.drag = val

    def click(self):
        mpos = self.mouseWatcherNode.getMouse()
        self.pickerRay.setFromLens(self.camNode, mpos.getX(), mpos.getY())

        self.accept("picker-into-smiley", self.setDrag, [True])
        
    def dragFunc(self, task):
        if self.drag:
            if self.mouseWatcherNode.hasMouse():
                self.smiley.setPos(self.mouse.getPos())
        
        return Task.cont

game = Game()
game.run()

``'

Simply put, you’re never telling your traverser to traverse the scene-graph.

Although you’re also missing a parameter in your “setDrag” function: remember that a collision-event will include a collision-entry parameter, as well as whatever other parameters you specify (in this case a boolean value).

By the way, you don’t need to “accept” your “picker-into-smiley” event every time that you click–just doing so once should be enough. Unless you intend to remove the event under some condition or another, of course!

(There are a few other things that will call for fixing, it seems, but for now I’ll leave those for you to find and figure out!)

I forgot I am not using base.cTrav

1 Like

I did a few changes, and now the smiley just disappears. Can you take my code and make all necessary editing?

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

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        self.disableMouse()

        self.drag = False

        self.myTraverser = CollisionTraverser()
        collHandEvent = CollisionHandlerEvent()
        collHandEvent.addInPattern("%fn-into-%in")

        self.smiley = loader.loadModel("models/smiley")
        self.smiley.setPos(0, 25, 0)
        self.smiley.reparentTo(self.render)

        cNode = CollisionNode("smiley")
        cNode.addSolid(CollisionSphere(0, 0, 0, 1.1))
        smileyC = self.smiley.attachNewNode(cNode)

        self.pickerNode = CollisionNode("picker")
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.myTraverser.addCollider(self.pickerNP, collHandEvent)

        self.accept("mouse1", self.click)
        self.accept("mouse1-up", self.setDrag, [False, None])

        self.taskMgr.add(self.dragFunc)


    def setDrag(self, val, collEnt):
        self.drag = val

    def click(self):
        mpos = self.mouseWatcherNode.getMouse()
        self.pickerRay.setFromLens(self.camNode, mpos.getX(), mpos.getY())
        self.myTraverser.traverse(render)

        self.accept("picker-into-smiley", self.setDrag, [True])
        
    def dragFunc(self, task):
        if self.drag:
            if self.mouseWatcherNode.hasMouse():
                mpos = self.mouseWatcherNode.getMouse()
                self.smiley.setPos(mpos.getX(), 0, mpos.getY())
        
        return Task.cont

game = Game()
game.run()

Uh, no, I’m not going to do all of your work for you. :stuck_out_tongue:

I will tell you where you’re going wrong, if I’m not much mistaken, however:

Remember that the camera is located at a y-position of 0. And appropriately, you’re initially positioning your object at a y-position of 25.

However, when you drag your object, you’re setting all three of its coordinates–and the value that you’re giving for the y-coordinate is 0.

So you mean I should set it to 25?

Not Fair. You know more than me

Exactly. Or just set the x- and z- coordinates separately.

But (A) you’ll likely learn more if you figure it out for yourself, and (B) it’s not really fair for me to have to do all of your work, and (C) it’s Sunday, a day on which I rest.

I’m happy to help you, and especially to help you to learn–but just doing it all for you is a bit much, I feel.

Also, I just learned that on this forum, if you enter (C) you end up with a copyright symbol! (C) XD

I can read your code and understand

Where I live, Sunday is a working day

Sure, but I think that you’ll likely learn more if I explain and you implement.

But not where I live, nor for me.

And it’s still not fair for me to do all your work. :stuck_out_tongue:

OK, whatever. Now I changed my code to what you told. But it is dragging awfully slowly. And I can’t drag continuously after I stop dragging. I have to first click, like the problem with many of my collision queries. And when I do all of that, and try to drag again, It goes back to (0, 25, 0)

And also, the smiley is behind the mouse even though moving. I want it to be exactly where the mouse is

Have you tried printing out the values given by “mouseWatcherNode.getMouse()”? That should tell you where the problem lies.

Hmm… I’m not quite sure of what problem you’re describing here. Is click-and-drag not what you want? If not, could you explain again please what it is that you want to have happen?

I suspect that this is connected to the first problem that I addressed above–let’s see whether fixing that also fixes this.

I am talking about the problem in this one: Clicking on a 3D object. Read it and you will find when I said that I need to click on an object, then click outside so that I can re-click.

Will try

Ah, I see. Well, we also discussed the reason for that in the same thread, I believe–see this post, specifically. (After the quote “That’s right So how to fix it?”.)

Good good! :slight_smile:

So I should generate an again event like you mentioned in that post?

That’s not quite what I said there, I think, but it could probably be made to work, I imagine. Personally, I favour the bitmask-solution that I suggested, or using a CollisionHandlerQueue.

Eventually you suggest me to use an again collision. I don’t understand what are BitMasks. They are very confusing (to me)