Hi all,
Sorry to do this to you, but I need your help again . My code for point & click game controls is almost finished, but I have one slight problem remaining; when my player turns, so too is the camera.
At the moment, to enable the camera to rotate around the player, I have a camera_dummy_node parented to the player and the camera itself parented to the camera_dummy_node. Because of this, the camera seems to be inheriting the Hpr of the player, so when the player turns the camera also turns.
What I want, is for the camera to only rotate when I move the mouse pointer to the edges of the screen (or I use the left & right arrow keys). But although I want the camera to rotate independently of the player, I also want it to remain āfixedā on him, to follow him as he moves and to keep him centered in view at all times (which it currently does).
Anyway, IĆ¢ā¬ā¢ve made a piccy to show (hopefully) what I mean:
In the top image, imagine that IĆ¢ā¬ā¢ve rotated the camera to look at the player from the side. I then click a location to the right of the player.
The second image illustrates what IĆ¢ā¬ā¢d like to happen, the player should turn to face the direction of the click (and then move to that position), but the camera shouldnĆ¢ā¬ā¢t turn, it should remain looking in the direction that it was rotated to (which would now be behind the player).
The third image illustrates what is currently happening, as the player changes his heading, so too does the camera (so itĆ¢ā¬ā¢s still looking at him from the side) which is very bad (especially if you suffer from motion sickness ).
This is the code:
# Left click on the ground to move.
# Rotate the camera by moving the mouse pointer to the edges of the screen or
# with the left & right arrow keys.
# Zoom the camera with the mouse wheel or the up & down arrow keys.
import direct.directbase.DirectStart # Start Panda
from pandac.PandaModules import * # Import the Panda Modules
from direct.showbase.DirectObject import DirectObject # To handle 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
from direct.showbase.PythonUtil import closestDestAngle # For player rotation
import sys
class Controls(DirectObject):
def __init__(self):
base.disableMouse()
self.loadModels()
# Declare variables.
self.position = None
self.playerMovement = None
self.movementSpeed = 6.0 # Controls how long it takes the player to
# move to the clicked destination.
self.speed = .10 # Controls the speed of the camera rotation and zoom.
# Setup collision stuff.
self.picker= CollisionTraverser() # Make a traverser
self.queue=CollisionHandlerQueue() # Make a handler
#Make a collision node for our picker ray
self.pickerNode = CollisionNode('mouseRay')
#Attach that node to the camera since the ray will need to be positioned
#relative to it.
self.pickerNP = camera.attachNewNode(self.pickerNode)
self.pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask())
self.pickerRay = CollisionRay() # Make our ray
self.pickerNode.addSolid(self.pickerRay) # Add it to the collision node
#Register the ray as something that can cause collisions
self.picker.addCollider(self.pickerNode, self.queue)
# Setup controls
self.accept("escape", sys.exit)
self.accept("player-stopped", self.stopWalkAnim)
self.accept('mouse1', self.moveToPosition)
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.accept("wheel_up", self.cameraZoom,[-1])
self.accept("wheel_down", self.cameraZoom,[1])
taskMgr.add(self.edgeScreenTracking, "edgeScreenTracking")
# End __init__
def loadModels(self):
# Load an environment
self.environ = loader.loadModel("MODELS/grass")
self.environ.reparentTo(render) # Make it display on the screen.
self.environ.setPos(0, 0, 0)
self.environ.setHpr(0, 0, 0)
# Load the player and its animations
self.player = Actor.Actor("MODELS/ralph",{"walk":"MODELS/ralph-walk"})
self.player.reparentTo(render) # Make it display on the screen.
self.player.setPos(0, 0, 0) # Position it at the center of the world.
self.player.setHpr(0, 0, 0)
# Create a dummy node for the player turn function
self.npLook = render.attachNewNode("npLook")
#Create a 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)
self.camera_dummy_node.setHpr(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, -35, 18) # X = left & right, Y = zoom, Z = Up & down.
camera.setHpr(0, -25, 0) # Heading, pitch, roll.
# End loadModels
# Define a task to monitor the position of the mouse pointer & rotate
# the camera when the mouse pointer moves to the edges of the screen.
def edgeScreenTracking(self,task):
# Check if the mouse is available
if not base.mouseWatcherNode.hasMouse():
return Task.cont
# Get the relative mouse position, its always between 1 and -1
mpos = base.mouseWatcherNode.getMouse()
if mpos.getX() > 0.99:
self.cameraTurn(1)
elif mpos.getX() < -0.99:
self.cameraTurn(-1)
return Task.cont
# End edgeScreenTracking
# 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(camera, self.speed, Point3(camera.getX(), camera.getY()-(2*dir), camera.getZ()))
self.camZoom.start()
# End cameraZoom
# Define a function to get the position of the mouse click.
def getPosition(self, mousepos):
self.pickerRay.setFromLens(base.camNode, mousepos.getX(),mousepos.getY())
self.picker.traverse(render)
if self.queue.getNumEntries() > 0:
self.queue.sortEntries()
# This is the clicked position.
self.position = self.queue.getEntry(0).getSurfacePoint(self.environ)
return None
# End getPosition
# Define a function to make the player turn towards the clicked position
# and then move to that position.
def moveToPosition(self):
# Get the clicked position
self.getPosition(base.mouseWatcherNode.getMouse())
# Calculate the new hpr
self.npLook.setPos(self.player.getPos())
self.npLook.lookAt(self.position) # Look at the clicked position.
reducedH = self.player.getH()%360.0
self.player.setH(reducedH)
currHpr = self.player.getHpr()
newHpr = self.npLook.getHpr()
newH = closestDestAngle(currHpr[0], newHpr[0])
# Create a turn animation from current hpr to the calculated new hpr.
playerTurn = self.player.hprInterval(.2, Point3(newH, newHpr[1], newHpr[2]))
# Calculate the distance between the start and finish positions.
# This is then used to calculate the duration it should take to
# travel to the new coordinates based on self.movementSpeed
travelVec = self.position - self.player.getPos()
distance = travelVec.length()
# Create an animation to make the player move to the clicked position
playerMove = self.player.posInterval((distance / self.movementSpeed), self.position)
# Put both animations into a sequence
self.playerMovement = Sequence(playerTurn, playerMove)
self.playerMovement.setDoneEvent("player-stopped")
self.player.loop("walk")
self.playerMovement.start()
# End moveToPosition
def stopWalkAnim(self):
# This is called when the movement animation has finished.
# We can then stop the walk animation.
self.player.stop("walk")
self.player.pose("walk",17)
self.playerMove = None
c = Controls()
run()
Does anybody know if there is some way to stop the camera from turning with the player?
Thanks heaps