Like a strategy game camera type

Right mouse button - turn camera around target
mouse wheel - adjust distance to target
mouse pointer to the window edge - camera moving

from direct.directbase import DirectStart
from direct.showbase import DirectObject 
from pandac.PandaModules import Vec3
import math

class CameraHandler(DirectObject.DirectObject):
  def __init__(self):
    base.disableMouse()
    base.camera.setPos(0,20,20)
    base.camera.lookAt(0,0,0)
    self.mx,self.my=0,0
    self.dragging=False
    self.target=Vec3()
    self.camDist=40   
    self.setTarget(0,0,0)
    self.turnCameraAroundPoint(0,0,self.target,self.camDist)
    self.accept("mouse3",self.startDrag)
    self.accept("mouse3-up",self.stopDrag)
    self.accept("wheel_up",lambda : self.adjustCamDist(0.9))
    self.accept("wheel_down",lambda : self.adjustCamDist(1.1))
    taskMgr.add(self.dragTask,'dragTask')    
  def turnCameraAroundPoint(self,tx,ty,p,dist):
        newCamHpr=Vec3()         
        camHpr=base.camera.getHpr()
        newCamHpr.setX(camHpr.getX()+tx)
        newCamHpr.setY(camHpr.getY()-ty)
        newCamHpr.setZ(camHpr.getZ())
        base.camera.setHpr(newCamHpr)
        angleradiansX = newCamHpr.getX() * (math.pi / 180.0)
        angleradiansY = newCamHpr.getY() * (math.pi / 180.0)
        base.camera.setPos(  dist*math.sin(angleradiansX)*math.cos(angleradiansY)+p.getX(),
                           -dist*math.cos(angleradiansX)*math.cos(angleradiansY)+p.getY(),
                           -dist*math.sin(angleradiansY)+p.getZ()  )
        base.camera.lookAt(p.getX(),p.getY(),p.getZ() )                
  def setTarget(self,x,y,z):
    self.target.setX(x)
    self.target.setY(y)
    self.target.setZ(z)
  def startDrag(self):
    self.dragging=True
  def stopDrag(self):
    self.dragging=False
  def adjustCamDist(self,aspect):
    self.camDist=self.camDist*aspect
    self.turnCameraAroundPoint(0,0,self.target,self.camDist)
  def dragTask(self,task):
    if base.mouseWatcherNode.hasMouse():
        mpos = base.mouseWatcherNode.getMouse()  
        if self.dragging:
            self.turnCameraAroundPoint((self.mx-mpos.getX())*100,(self.my-mpos.getY())*100,self.target,self.camDist)        
        else:
            moveY=False
            moveX=False
            if self.my>0.8:
                angleradiansX = base.camera.getH() * (math.pi / 180.0)
                aspect=(1-self.my-0.2)*5
                moveY=True
            if self.my<-0.8:
                angleradiansX = base.camera.getH() * (math.pi / 180.0)+math.pi
                aspect=(1+self.my-0.2)*5
                moveY=True
            if self.mx>0.8:
                angleradiansX2 = base.camera.getH() * (math.pi / 180.0)+math.pi*0.5
                aspect2=(1-self.mx-0.2)*5
                moveX=True
            if self.mx<-0.8:
                angleradiansX2 = base.camera.getH() * (math.pi / 180.0)-math.pi*0.5
                aspect2=(1+self.mx-0.2)*5
                moveX=True                                
            if moveY:    
                self.target.setX(self.target.getX()+math.sin(angleradiansX)*aspect)
                self.target.setY(self.target.getY()-math.cos(angleradiansX)*aspect)
                self.turnCameraAroundPoint(0,0,self.target,self.camDist)
            if moveX:   
                self.target.setX( self.target.getX()-math.sin(angleradiansX2)*aspect2 )
                self.target.setY( self.target.getY()+math.cos(angleradiansX2)*aspect2 )
                self.turnCameraAroundPoint( 0,0,self.target,self.camDist )                
        self.mx=mpos.getX()
        self.my=mpos.getY()                               
    return task.cont   

to use - save code in the separate file, then import CameraHandle and call CameraHandle() before run()

Nice work! iI had to change "class CameraHandler(DirectObject.DirectObject): " to "class CameraHandle(DirectObject): " for it to run, but again nice work and very smooth! :smiley:

Thanks. I am just a beginner and therefore, i can do something wrong or not by an optimal way :slight_smile: I think that CameraHandle(DirectObject) - more correctly then my variant.

Very cool.
Good job!
:slight_smile:

Thanks :slight_smile:

Then you must have changed the import line.
It’s either this:

from direct.showbase import DirectObject
class CameraHandler(DirectObject.DirectObject):

or this:

from direct.showbase.DirectObject import DirectObject
class CameraHandler(DirectObject):

awesome, this is what I actually was looking for!

I made my own camera but it was eh and not smooth.

This one I was able to move the code over and it worked perfect for what I was doing.

Just got to fine tweek it for my own speed and adjustments.

Great work!

to try this nice snippet I put together a small but real world example as a quick showup of the CameraHandler class:

# -*- coding: utf-8 -*-
""" the classic panda3D hello world snippet for 1.7 and beyond + RPG camera by ninth
"""
import math
from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import Sequence
from panda3d.core import Point3,Vec3
from direct.showbase import DirectObject

class CameraHandler(DirectObject.DirectObject):
  def __init__(self):
    base.disableMouse()
    base.camera.setPos(0,20,20)
    base.camera.lookAt(0,0,0)
    self.mx,self.my=0,0
    self.dragging=False
    self.target=Vec3()
    self.camDist=40
    self.setTarget(0,0,0)
    self.turnCameraAroundPoint(0,0,self.target,self.camDist)
    self.accept("mouse3",self.startDrag)
    self.accept("mouse3-up",self.stopDrag)
    self.accept("wheel_up",lambda : self.adjustCamDist(0.9))
    self.accept("wheel_down",lambda : self.adjustCamDist(1.1))
    taskMgr.add(self.dragTask,'dragTask')
    
  def turnCameraAroundPoint(self,tx,ty,p,dist):
    newCamHpr=Vec3()
    camHpr=base.camera.getHpr()
    newCamHpr.setX(camHpr.getX()+tx)
    newCamHpr.setY(camHpr.getY()-ty)
    newCamHpr.setZ(camHpr.getZ())
    base.camera.setHpr(newCamHpr)
    angleradiansX = newCamHpr.getX() * (math.pi / 180.0)
    angleradiansY = newCamHpr.getY() * (math.pi / 180.0)
    base.camera.setPos(
      dist*math.sin(angleradiansX)*math.cos(angleradiansY)+p.getX(),
      -dist*math.cos(angleradiansX)*math.cos(angleradiansY)+p.getY(),
      -dist*math.sin(angleradiansY)+p.getZ()
    )
    base.camera.lookAt(p.getX(),p.getY(),p.getZ() )
    
  def setTarget(self,x,y,z):
    self.target.setX(x)
    self.target.setY(y)
    self.target.setZ(z)
    
  def startDrag(self): self.dragging=True
    
  def stopDrag(self): self.dragging=False
  
  def adjustCamDist(self,aspect):
    self.camDist=self.camDist*aspect
    self.turnCameraAroundPoint(0,0,self.target,self.camDist)
    
  def dragTask(self,task):
    if base.mouseWatcherNode.hasMouse():
      mpos = base.mouseWatcherNode.getMouse()
      if self.dragging:
        self.turnCameraAroundPoint((self.mx-mpos.getX())*100,(self.my-mpos.getY())*100,self.target,self.camDist)
      else:
        moveY=False
        moveX=False
        if self.my>0.8:
          angleradiansX = base.camera.getH() * (math.pi / 180.0)
          aspect=(1-self.my-0.2)*5
          moveY=True
        if self.my<-0.8:
          angleradiansX = base.camera.getH() * (math.pi / 180.0)+math.pi
          aspect=(1+self.my-0.2)*5
          moveY=True
        if self.mx>0.8:
          angleradiansX2 = base.camera.getH() * (math.pi / 180.0)+math.pi*0.5
          aspect2=(1-self.mx-0.2)*5
          moveX=True
        if self.mx<-0.8:
          angleradiansX2 = base.camera.getH() * (math.pi / 180.0)-math.pi*0.5
          aspect2=(1+self.mx-0.2)*5
          moveX=True
        if moveY:
          self.target.setX(self.target.getX()+math.sin(angleradiansX)*aspect)
          self.target.setY(self.target.getY()-math.cos(angleradiansX)*aspect)
          self.turnCameraAroundPoint(0,0,self.target,self.camDist)
        if moveX:
          self.target.setX( self.target.getX()-math.sin(angleradiansX2)*aspect2 )
          self.target.setY( self.target.getY()+math.cos(angleradiansX2)*aspect2 )
          self.turnCameraAroundPoint( 0,0,self.target,self.camDist )
      self.mx=mpos.getX()
      self.my=mpos.getY()
    return task.cont

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

    environ = self.loader.loadModel("models/environment")
    environ.reparentTo(render)
    # Apply scale and position transforms on the model.
    environ.setScale(0.25, 0.25, 0.25)
    environ.setPos(-8, 42, 0)

    # Add the camera management
    camH=CameraHandler()

    # Load and transform the panda actor - Note that is mandatory to store the actor object in a non volatile member otherwise you won't see it animate.
    self.pandaActor = Actor(
      "models/panda", {"walk": "models/panda-walk"}
    )
    self.pandaActor.setScale(0.2, 0.2, 0.2)
    self.pandaActor.reparentTo(render)
    # Loop its animation.
    self.pandaActor.loop("walk")

    # Create the four lerp intervals needed for the panda to walk back and forth.
    pandaPosInterval1 = self.pandaActor.posInterval(
      9, Point3(0, -10, 0), startPos=Point3(0, 10, 0)
    )
    pandaPosInterval2 = self.pandaActor.posInterval(
      9, Point3(0, 10, 0), startPos=Point3(0, -10, 0)
    )
    pandaHprInterval1 = self.pandaActor.hprInterval(
      1, Point3(180, 0, 0), startHpr=Point3(0, 0, 0)
    )
    pandaHprInterval2 = self.pandaActor.hprInterval(
      1, Point3(0, 0, 0), startHpr=Point3(180, 0, 0)
    )

    # Create and play the sequence that coordinates the intervals.
    pandaPace = Sequence(
      pandaPosInterval1, pandaHprInterval1, pandaPosInterval2,
      pandaHprInterval2, name="pandaPace"
    )
    pandaPace.loop()

world=myworld()
world.run()

Very nice snippet

Probably worth adding something to the constructor to move the mouse pointer if that’s possible. i.e., center it on screen. When I run the sample it immediately zoomed off to the right because my mouse pointer was over that side of the screen.

Cheers,
Gary

This should center the mouse:

base.win.movePointer(0, base.win.getXSize()/2, base.win.getYSize()/2)
  • Daniel.

Couldn’t have been easier to use - thanks.

“to use - save code in the separate file, then import CameraHandle and call CameraHandle() before run()”

Could not really be much simpler.

Cheers,
Gary

Thx for the mouse centering update. I just had that happen.