My camera code, it works! But something's not quite right!

Hi,

I made some real progress with my camera code yesterday. I’ve managed to get the camera to rotate around the player and zoom in and out by using the arrow keys (big thanks to Martin and Yellow for their help with this :smiley:).

But I’ve run into a slight problem, whenever I zoom and then rotate the camera, it seems that the camera is no longer centered on the model (or the model is no longer at the center axis of the camera. Hope that makes sense).

This is the code:

# CameraTest.py
# Use the Left & Right Arrow keys to rotate the camera.
# Use the Up & Down Arrow keys to zoom the camera In & Out.
import direct.directbase.DirectStart # Start Panda
from pandac.PandaModules import* # Import the Panda Modules
from direct.showbase.DirectObject import DirectObject # To listen for Events
from direct.task import Task # To use Tasks
from direct.actor import Actor # To use animated Actors 
from direct.interval.IntervalGlobal import * # To use Intervals
import math # To use math (angles, degrees..etc)
import sys 

class World(DirectObject):
    #Constructor
    def __init__(self): 
        
       base.disableMouse() # Disable default camera.
       self.speed = .05 # Controls speed of rotation and zoom.
       self.loadModels() 
       # Setup key controls
       self.accept("escape", sys.exit)
       # Call the camera functions
       self.accept("arrow_left", self.cameraTurn,[-1])
       self.accept("arrow_right", self.cameraTurn,[1])
       self.accept("arrow_up", self.cameraZoom,[-1])
       self.accept("arrow_down", self.cameraZoom,[1])
       # end __init__

    def loadModels(self):
        
        # Load a model
        self.player = loader.loadModel("MODELS/ralph")
        self.player.reparentTo(render) # Make it display/render on the screen.
        self.player.setPos(0, 0, 0) # Position it at the center of the world.
        #Create the camera dummy node
        self.camera_dummy_node = render.attachNewNode("camera_dummy_node")
        #Position the camera dummy node
        self.camera_dummy_node.setPos( 0, 0, 0)
        # Attach the camera dummy node to the player.
        self.camera_dummy_node.reparentTo(self.player)
        # Attach the camera to the dummy node.
        camera.reparentTo(self.camera_dummy_node)
        # Position the camera
        camera.setPos(0, -30, 3) # X = left & right, Y = zoom, Z = Up & down.
        camera.setHpr(0, 0, 0) # Heading, pitch, roll.
        # end loadModels
    
    # Define the CameraTurn function.
    def cameraTurn(self,dir):
        self.camTurn = LerpHprInterval(self.camera_dummy_node, self.speed, Point3(self.camera_dummy_node.getH()-(10*dir), 0, 0))
        self.camTurn.start()
        # end cameraTurn
    
    # Define the cameraZoom function.
    def cameraZoom(self,dir):
        self.camZoom = LerpPosInterval(self.camera_dummy_node, self.speed, Point3(0, self.camera_dummy_node.getY()-(2*dir), 0))
        self.camZoom.start()
        # end cameraZoom
         
# end class World

world = World()

run() 

I’d like to be able to zoom in really close and then rotate the camera around the model (so that I can look at him from any angle). I’d also like to be able to zoom up on the model from any angle, at the moment he seems to go off in whatever direction he’s facing.

But I’m not quite sure what I should do to fix this problem. So as always, any advice would be much appreciated.

Cheers

I think you have to camera.lookAt(model)
You can make a task or maybe its able to make a function intervall:

....
def look(model):
  base.camera.lookAt(model)

def cameraMove(self,dir):
  self.camTurn= Sequence(
    LerpHprInterval(self.camera_dummy_node, self.speed,
      Point3(self.camera_dummy_node.getH()-(10*dir), 0, 0)) ,
    LerpPosInterval(self.camera_dummy_node, self.speed, Point3(0, 
      self.camera_dummy_node.getY()-(2*dir), 0)) ,
    Func(myFunction, model),
    name = "Camera Movement") 
  self.camTurn.start()
  # end cameraTurn
....

This code uses Sequences this means the intervalls, means that the Intervalls are played one after the other.
I havn’t tested it and Im no expert using Intervalls i hope its correct. If not tell me and I’ll try to make it working

Martin

Sorry Martin, you’ve lost me. I’m very new to this and I don’t understand the code you’ve written or how I should work it into my own code :blush:.

Any chance you could write a ‘for dummies’ version? Thanks for trying to help.

Cheers

Ok here is the new one

# CameraTest.py
# Use the Left & Right Arrow keys to rotate the camera.
# Use the Up & Down Arrow keys to zoom the camera In & Out.
import direct.directbase.DirectStart # Start Panda
from pandac.PandaModules import* # Import the Panda Modules
from direct.showbase.DirectObject import DirectObject # To listen for Events
from direct.task import Task # To use Tasks
from direct.actor import Actor # To use animated Actors
from direct.interval.IntervalGlobal import * # To use Intervals
import math # To use math (angles, degrees..etc)
import sys

class World(DirectObject):
	#Constructor
	def __init__(self):
		
		base.disableMouse() # Disable default camera.
		self.speed = .05 # Controls speed of rotation and zoom.
		self.loadModels()
		# Setup key controls
		self.accept("escape", sys.exit)
    # Call the camera functions
		self.accept("arrow_left", self.cameraTurn,[-1])
		self.accept("arrow_right", self.cameraTurn,[1])
		self.accept("arrow_up", self.cameraZoom,[-1])
		self.accept("arrow_down", self.cameraZoom,[1])
		
		#self.cameraMove(1)
		# end __init__

	def loadModels(self):
		
		# Load a model
		self.player = loader.loadModel("MODELS/ralph")
		self.player.reparentTo(render) # Make it display/render on the screen.
		self.player.setPos(0, 0, 0) # Position it at the center of the world.
    #Create the camera dummy node
		self.camera_dummy_node= render.attachNewNode("camera_dummy_node")
		#Position the camera dummy node
		self.camera_dummy_node.setPos( 0, 0, 0)
    # Attach the camera dummy node to the player.
		self.camera_dummy_node.reparentTo(self.player)
		# Attach the camera to the dummy node.
		camera.reparentTo(self.camera_dummy_node)
    # Position the camera
		camera.setPos(0, -30, 3) # X = left & right, Y = zoom, Z = Up & down.
		camera.setHpr(0, 0, 0) # Heading, pitch, roll.
    # end loadModels
   
	# Define the CameraTurn function.
	def look(self):
		base.camera.lookAt(self.player)
	
	def cameraTurn(self,dir):
		self.camTurn= Sequence(
    	LerpHprInterval(self.camera_dummy_node, self.speed,
      	Point3(self.camera_dummy_node.getH()-(10*dir), 0, 0)) ,
			LerpPosInterval(self.camera_dummy_node, self.speed, Point3(0,
      	self.camera_dummy_node.getY()-(2*dir), 0)) ,
			Func(self.look), name = "Camera Turn")
		self.camTurn.start()
  # end cameraTurn
	
	def cameraZoom(self,dir):
		self.camZoom= Sequence(
			LerpPosInterval(self.camera_dummy_node, self.speed, Point3(0,
      	self.camera_dummy_node.getY()-(2*dir), 0)) ,
			Func(self.look), name = "Camera Turn")
	#end camaeraZoom
# end class World

world = World()

run()

Hi Martin, I just tested your new code (thanks for doing that for me).

But sadly, it looks like that isn’t the answer either. Your code runs just fine, but lots of strange things are happening. It’s almost as if the zoom and turn functions are playing at the same time, it’s all over the place and very jerky :unamused:.

I really wish I could find some example code for this. I suppose I could just scrap the idea of a zoom function altogether, but I’d rather not do that, because I think a zoom function is very important.

I also have a horrible suspicion that when I start animating and moving my player that I’m going to run into the same problems with the camera. Any idea’s about what I should try next?

Thanks heaps.

You can fix this by moving the camera itself instead of the pivot point in your zoom function. The pivot point (camera_dummy_node) should never move from the center of the model, since the camera uses it to orbit around the model.

def cameraZoom(self,dir):
    self.camZoom = LerpPosInterval(camera, self.speed, Point3(camera.getX(), camera.getY()-(2*dir), camera.getZ()))
    self.camZoom.start()

you could also use the camera.setFov() function to zoom in, that way your camera stays in the same position.

btw, you might want to concider rotating / repositioning your model instead of the camera, that could save you alot of trouble.

I suppose something like this could benefit from using more traditional terminology… What you’re doing here is actually dollying the camera closer to it’s target, not zooming it. camera.setFov would do something more like a true zoom. It would result in a distortion of the perspective, though, which may not be what you want.

You clever, clever chap Nostgard! That fixed it! Thankyou very, very much, it would never have occured to me to try that.

A big thanks to everybody who replied and tried to help me. You see, what I’m trying to do is create a camera system like that used in games such as ‘NeverWinter Nights’ and ‘Dungeon Siege’.

These are third person, mouse controlled games, where the camera is centered on the player at all times. They use ‘edge screen tracking’ to rotate the camera (if you move your mouse pointer to the edges of the screen the camera rotates left or right around the player).

You can freely rotate the camera at any time, even when the player is moving, so you can look at them from any angle. And you can zoom the camera in and out by use of the mouse wheel.

To create something like this is my goal. But I decided to start off with ‘keyboard’ camera controls first, because it’s supposed to be easier than creating ‘mouse’ camera controls.

But now that I’ve got the keyboard camera controls working, I’m ready to start working on mouse camera controls :smiley:. So brace yourselves, I’m bound to drive you all crazy with my endless questions.

Thanks again to everybody.

Thats a good way of programming. From “easy” to “hard”.
A view tips for working with Mouse:
Don’t forget to disable the default camera control:

base.disableMouse()

To get the Position:

if base.mouseWatcherNode.hasMouse():
  x=base.mouseWatcherNode.getMouseX()
  y=base.mouseWatcherNode.getMouseY()

To get the movment since last frame:

#first init x and y
if base.mouseWatcherNode.hasMouse():
  self.x=base.mouseWatcherNode.getMouseX()
  self.y=base.mouseWatcherNode.getMouseY()

#this is the main code
def func(self):
  if base.mouseWatcherNode.hasMouse():
    movex= self.x - base.mouseWatcherNode.getMouseX()
    movey= self.y - base.mouseWatcherNode.getMouseY()
    self.x=base.mouseWatcherNode.getMouseX()
    self.y=base.mouseWatcherNode.getMouseY()

[movex/movey] is the Vector of the movement between two calls of func()
Hope this helps
Martin

It sure does help Martin, thanks very, very much. I can use all the tips and help I can get. I’m off to experiment with it right now :laughing:.

Thanks again for all your help.