官术网_书友最值得收藏!

Making the camera smoothly follow an object

In this recipe you will learn how to program a simple camera system that follows an object smoothly, without giving the impression of being glued to the back of the target.

Getting ready

See the recipe Setting up the game structure to create the basic application framework for the following steps.

How to do it...

Let's build a third person camera system:

  1. Add this code to Application.py:
    from direct.showbase.ShowBase import ShowBase
    from direct.actor.Actor import Actor
    from panda3d.core import Vec3
    from direct.interval.IntervalGlobal import *
    from FollowCam import FollowCam
    
    class Application(ShowBase):
        def __init__(self):
            ShowBase.__init__(self)
    
            self.world = loader.loadModel("environment")
            self.world.reparentTo(render)
            self.world.setScale(0.5)
            self.world.setPos(-8, 80, 0)
    
            self.panda = Actor("panda", {"walk": "panda-walk"})
            self.panda.reparentTo(render)
            self.panda.setHpr(270, 0, 0)
            self.panda.loop("walk")
    
            self.walkIval1 = self.panda.posInterval(2, Vec3(-8, -8, 0), startPos = Vec3(8, -8, 0))
            self.walkIval2 = self.panda.posInterval(2, Vec3(-8, 8, 0), startPos = Vec3(-8, -8, 0))
            self.walkIval3 = self.panda.posInterval(2, Vec3(8, 8, 0), startPos = Vec3(-8, 8, 0))
            self.walkIval4 = self.panda.posInterval(2, Vec3(8, -8, 0), startPos = Vec3(8, 8, 0))
    
            self.turnIval1 = self.panda.hprInterval(0.5, Vec3(180, 0, 0), startHpr = Vec3(270, 0, 0))
            self.turnIval2 = self.panda.hprInterval(0.5, Vec3(90, 0, 0), startHpr = Vec3(180, 0, 0))
            self.turnIval3 = self.panda.hprInterval(0.5, Vec3(0, 0, 0), startHpr = Vec3(90, 0, 0))
            self.turnIval4 = self.panda.hprInterval(0.5, Vec3(-90, 0, 0), startHpr = Vec3(0, 0, 0))
    
            self.pandaWalk = Sequence(self.walkIval1, self.turnIval1,
                                      self.walkIval2, self.turnIval2,
                                      self.walkIval3, self.turnIval3,
                                      self.walkIval4, self.turnIval4)
            self.pandaWalk.loop()
            self.followCam = FollowCam(self.cam, self.panda)
  2. Add a new file to the project. Call it FollowCam.py.
  3. Copy the following code to the file you just created:
    from direct.showbase.ShowBase import ShowBase
    from panda3d.core import Vec3
    
    class FollowCam():
        def __init__(self, camera, target):
            self.dummy = render.attachNewNode("cam" + target.getName())
            self.turnRate = 2.2
            self.camera = camera
            self.target = target
            taskMgr.add(self.updateCamera, "updateCamera" + target.getName())
    
        def updateCamera(self, task):
            self.dummy.setPos(self.target.getPos())
            heading = self.clampAngle(self.dummy.getH())
    
            turnDiff = self.target.getH() - heading
            turnDiff = self.clampAngle(turnDiff)
    
            dt = globalClock.getDt()
            turn = turnDiff * dt
            self.dummy.setH(heading + turn * self.turnRate)
    
            self.camera.setPos(self.dummy.getPos())
            self.camera.setY(self.dummy, 40)
            self.camera.setZ(self.dummy, 10)
            self.camera.lookAt(self.target.getPos() + Vec3(0, 0, 7))
    
            return task.cont
    
        def clampAngle(self, angle):
            while angle < -180:
                angle = angle + 360
    
            while angle > 180:
                angle = angle - 360
    
            return angle
  4. Press F6 to start the application. You should be able to see a panda walking in circles while the camera follows it:

How it works...

We use the constructor of our Application class to set up the scene containing the walking panda and the background scenery. In the last line we create a new instance of our FollowCam, which contains the camera tracking code that is the core of this recipe.

To make the FollowCam work correctly and to be able to have multiple cameras follow different objects, we have to pass the camera we want to be updated and its target to the constructor, where we set up a few things we need for updating the camera. For example, we add a task that will call the updateCamera() method each frame. Additionally, the target's name is appended to both the dummy object's and the task's names to avoid name clashes in the case where we need to use more than one FollowCam instance. The dummy object is an invisible helper object that will help us to position the camera, as you will see in the following paragraphs.

The updateCamera() method is where all the work is happening: We move the dummy to our target's current position and get its current heading. The heading angle (in degrees) is clamped to the range of values from 180 to 180. We do this to avoid the camera getting stuck or continuing to turn because of the ambiguous nature of angles.

In the next steps, we calculate the difference between the target's heading and that of our dummy object, which is also clamped to avoid the undesired results described in the previous paragraph. In the following lines we can find the explanation for the camera's smooth turning—every frame, the dummy object's heading converges towards the heading of the camera target just a little bit. This is intentional; as it is multiplied by the time it took to complete the last frame. Additionally, we can also influence how fast the camera turns by adjusting turnRate.

In the final steps, the camera is first moved to the position of the dummy and then pushed away again along the dummy's local axis to its final position. After setting the camera's lookAt() target, we are done!

There's more...

In this version, the camera only supports smooth turning for objects that only change their heading. Other rotation axes can be added rather easily, as they work exactly the same as the one presented in this recipe!

主站蜘蛛池模板: 长春市| 嵩明县| 和硕县| 揭西县| 蓝田县| 海淀区| 修武县| 昭平县| 盈江县| 同仁县| 鄱阳县| 商城县| 锡林郭勒盟| 若尔盖县| 九江县| 黔江区| 玛曲县| 宁安市| 高淳县| 金山区| 曲周县| 泾源县| 方正县| 鲜城| 天峻县| 安乡县| 修武县| 彰化县| 盐亭县| 湖州市| 临猗县| 荥经县| 朝阳市| 保山市| 胶州市| 日土县| 永城市| 莱芜市| 沧州市| 黄冈市| 鄂托克前旗|