Ask for help in beating the hamster

I want to play a game of groundhog, but I can’t always let the hammer follow the mouse and watch “samples \ chessboard”, but I still don’t know how to operate. Can someone help me?

Hmm… What do you mean when you say that you “can’t always let the hammer follow the mouse”? Are you saying that you want the player to be able to “put down” the hammer? Or that you’re having trouble moving the hammer with the mouse? Or that you want to apply limitations on how the hammer moves? Or something else?

I think a short code will help you.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import CollisionTraverser, CollisionNode, CollisionHandlerQueue, CollisionRay, BitMask32
from direct.task.Task import Task

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

        self.disableMouse()
        camera.setPosHpr(0, -12, 8, 0, -35, 0)

        self.pickerRay = CollisionRay()

        self.squares = loader.loadModel("smiley")
        self.squares.reparentTo(render)

        self.dragging = False

        self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask')
        self.accept("mouse1", self.set_drag_status, [True])
        self.accept("mouse1-up", self.set_drag_status, [False])

    def mouseTask(self, task):
        if self.mouseWatcherNode.hasMouse():
            mpos = self.mouseWatcherNode.getMouse()
            self.pickerRay.setFromLens(self.camNode, mpos.getX(), mpos.getY())
            if self.dragging != False:
                nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin())
                nearVec = render.getRelativeVector(camera, self.pickerRay.getDirection())
                self.squares.setPos(nearPoint+nearVec*((0-nearPoint.getZ())/nearVec.getZ()))
        return Task.cont

    def set_drag_status(self, bool):
        self.dragging = bool

demo = ChessboardDemo()
demo.run()

Simplified to a few lines, now it looks clear. The panda3d examples are complicated by excessive logic. Trying to cram the whole game sometimes hides the essence of the implementation.

The effect I want to achieve is that the hammer follows the movement of the mouse pointer without clicking the mouse. When the mouse is clicked, the hammer will play the animation of smashing the gopher!

It is very difficult, but I did it. :slight_smile:

from direct.showbase.ShowBase import ShowBase
from panda3d.core import CollisionTraverser, CollisionNode, CollisionHandlerQueue, CollisionRay, BitMask32
from direct.task.Task import Task

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

        self.disableMouse()
        camera.setPosHpr(0, -12, 8, 0, -35, 0)

        self.pickerRay = CollisionRay()

        self.squares = loader.loadModel("smiley")
        self.squares.reparentTo(render)

        self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask')

    def mouseTask(self, task):
        if self.mouseWatcherNode.hasMouse():
            mpos = self.mouseWatcherNode.getMouse()
            self.pickerRay.setFromLens(self.camNode, mpos.getX(), mpos.getY())
            nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin())
            nearVec = render.getRelativeVector(camera, self.pickerRay.getDirection())
            self.squares.setPos(nearPoint+nearVec*((0-nearPoint.getZ())/nearVec.getZ()))
        return Task.cont
        
demo = ChessboardDemo()
demo.run()

Thank you very much for your reply, it is clearer, but it still confuses me. Why not use a cam camera directly, but use a virtual camera?

self.pickerRay.setFromLens(self.camNode, mpos.getX(), mpos.getY())
            if self.dragging != False:
                nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin())
                nearVec = render.getRelativeVector(camera, self.pickerRay.getDirection())

You can make it more convenient for you, I just redid the existing code. In the future, you can click on objects, for example, to understand whether your gopher is under the blow of a hammer. Just like in chess.

Is using a virtual camera just for convenience? ?

I meant what you can do as you see fit. The camera was used to communicate with the 3D scene.

I can understand the camera as the communication with the scene. What I do n’t understand is why I use a virtual camera. The position of the ray above uses the camNode of base.cam, that is, the base.cam camera is used, but why When getting the coordinates, how about converting them into the space coordinates of the camera virtual camera? What does this transformation accomplish?

Well, it clearly sets the position in the 3D space of the position from where the beam and direction are emitted.

https://docs.panda3d.org/1.10/python/reference/panda3d.core.CollisionRay

OK, let’s take a closer look at how to use rays! Thank you very much, this has helped me a lot!

I still have to ask for your help. I have n’t understood the formula for setting the point coordinates for a long time, can you help me explain it again?

nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin())
nearVec = render.getRelativeVector(camera, self.pickerRay.getDirection())
self.squares.setPos(nearPoint+nearVec*((0-nearPoint.getZ())/nearVec.getZ()))

To be honest, there is nothing to explain, If divided into stages.

nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin())
nearVec = render.getRelativeVector(camera, self.pickerRay.getDirection())
Dz = nearVec.getZ()
Vz = nearPoint.getZ()
H = 0
relZ = (H-Vz)/Dz
posZ = nearVec*relZ
pos = nearPoint+posZ
self.squares.setPos(pos)

Another approach might be something like this:

self.groundPlane = Plane(Vec3(0, 0, 1), Vec3(0, 0, 0))

def mouseTask(self, task):
        if self.mouseWatcherNode.hasMouse():
            mpos = self.mouseWatcherNode.getMouse()
            nearPoint = Point3()
            farPoint = Point3()
            mousePos3D = Point3()
            base.camLens.extrude(mpos, nearPoint, farPoint)
            renderNear = render.getRelativePoint(base.camera, nearPoint)
            renderFar = render.getRelativePoint(base.camera, farPoint)
            self.groundPlane.intersectsLine(mousePos3D, renderNear, renderFar)

In short:

  • Construct a plane representing the “ground”.
    • This is defined simply by a position and a normal-vector.
  • Get the mouse-position
  • Ask the camera-lens to “extrude” the mouse position into 3D, giving is a near-point and a far-point corresponding to that mouse-position.
  • Get the positions of the near- and far- points relative to “render”.
  • Using those new near- and far- points, ask the plane to give us the intersection of itself with the line that they make.
    • The result is stored in “mousePos3D”, which can then be used to place the hammer, and to determine whether a groundhog has been hit.
1 Like

Thank you very much for your explanation, I have understood. In fact, the first one is the projection calculation in my uncle’s school. I have mastered this formula. I also found a solution to fight the mole. Thank you very much!

1 Like

Thank you very much for you explanation, I have understood. In fact, the first one is the projection calculation in my uncle’s school. I have mastered this formula. I also found a solution to fight the mole. Thank you very much!

1 Like