When we think of animation we usually think of a model moving it's arms or swinging it's weapon. This is accomplished in Irrlicht through the SetFrameLoop or AnimateJoints functions of an animated/rigged model. However, animators in Irrlicht serve the more general purpose of modifying a scene node in some way, be it a camera, light, model or a custom scene node. In this tutorial we will be modifying the Skybox/Skydome tutorial. The following topics are covered in this tutorial:
The tutorial's code files and media files are installed along with Ginjo-Builder and can be accessed from the 'Project' window that is shown when the editor starts. This tutorial is called '09_Movement' in the 'Tutorials' tab.
First we create a project and compiler options XML file. (See Render loop for a more detailed description of creating a project.)
Open "Animators.xml" and just like in Render loop add the <exe> and <startup> elements. We will also import the GB namespace so we don't have to type it out every time. (For the full list of XML tags see Compiler options.)
<?xml version="1.0" encoding="utf-8"?> <root> <exe path=".\game.exe" /> <startup name="testAnimators.main" /> <ImportSymbols> <namespace name="GB" /> </ImportSymbols> </root>
In our XML file we specified the startup function as "testAnimators.main". Double-click on "testAnimators.gbc" in the project tree to open it, and copy over the "main" function from the Skybox/Skydome tutorial. Delete the FPS camera scene node code and instead add in the drone model from the Cameras tutorial. We will also add in a camera that has the drone as the parent node. Thus the camera will maintain its position relative to the drone (at Vector3Df(400,300,-300)), even when the drone moves. The code for the drone and camera should look as follows:
//load model from relative path ..\media\GlobalHawk.obj var:scene.Mesh drone = smgr.GetMesh('..\media\GlobalHawk.obj') //add model to scene var:Scene.MeshSceneNode droneNode = smgr.AddMeshSceneNode(drone) if(!isNull(droneNode)){ droneNode.Position=new gb.core.Vector3Df(2700 * 2, 255 * 5, 300) droneNode.Scale=new gb.core.Vector3Df(0.2, 0.2, 0.2) droneNode.SetMaterialFlag(gb.video.MaterialFlag.Lighting, false) } var:scene.CameraSceneNode camera= smgr.AddCameraSceneNode(droneNode, new core.Vector3Df(400,300,-300), droneNode.Position) camera.FarValue = 42000
Note that the droneNode is passed in as the camera's parent. Thus the position vector Vector3Df(400,300,-300) is relative to the drone. When you run the program using the skydome you should see a stationary drone:
Different computers render the scene at different speeds. In order to produce consistent movement we need to measure the elapsed time since the previous iteration of the render loop, and update our drone models position accordingly. We called our IrrlichtDevice variable engine, so we can use either engine.Timer.RealTime to retrieve the systems current time in milliseconds, or we can use engine.Timer.Time to retrieve Irrlicht engine's virtual time. The benefit of the virtual timer is that it can be stopped and started, as well as its speed modified. Modify the render loop as shown below to include the virtual timer and change the drones position based on the elapsed time:
//The render loop runs continuously and redraws the scene. var:gb.video.Color background=new gb.video.Color(160, 160, 160) var:uint32 oldTime var:uint32 newTime var:single timeDiff var:core.Vector3Df dronePosition while(engine.Run()){ //Manual movement: dronePosition=droneNode.Position newTime=engine.Timer.Time timeDiff=convert.ToSingle(newTime-oldTime)/1000 //time difference in seconds dronePosition.Z+=timeDiff*400 //movement speed droneNode.Position=dronePosition oldTime=newTime camera.Target=droneNode.Position //Redrawing the screen requires a call to the drivers BeginScene function. driver.BeginScene(gb.video.ClearBufferFlag.All, background) //The SceneManager will handle rendering the scene graph for us. smgr.DrawAll() //When we are done, we need to call EndScene so that the driver can update the screen. driver.EndScene() }
Note that the camera is attached to the drone, so it will move with it. However, the cameras target needs to be updated to keep looking at the drone. When you run the program you should see the drone moving smoothly.
In a game with a large number of objects (including projectiles) it would be cumbersome and error prone to manually keep track of changes to the scene and the movement of all objects. Animators simplify the task by automatically making changes to a scene node based on the elapsed time. Animators can be attached to any scene node (including cameras and lights) and can change any property of the scene node, not just its position (for example animate the textures of an object). First we are going to keep the camera stationary, and add the "fly straight" animator. This animator moves a scene node from a one position to another. Comment out the code for the manual movement of the camera in the render loop and add the animator code for the drone as shown below:
var:Scene.MeshSceneNode droneNode = smgr.AddMeshSceneNode(drone) var:scene.SceneNodeAnimator Animator if(!isNull(droneNode)){ droneNode.Position=new core.Vector3Df(2700 * 2, 255 * 5, 300); droneNode.Scale=new core.Vector3Df(0.2, 0.2, 0.2) droneNode.SetMaterialFlag(video.MaterialFlag.Lighting, false) //**Animator movement:** var:core.Vector3Df EndPoint=droneNode.Position endpoint.Z+=2500 Animator=smgr.CreateFlyStraightAnimator(droneNode.Position,EndPoint,50) droneNode.AddAnimator(Animator) } var:scene.CameraSceneNode camera camera= smgr.AddCameraSceneNode(null, droneNode.Position+new core.Vector3Df(50,25,-25),droneNode.Position) camera.FarValue = 42000
Note that the camera no longer has the drone as the parent node, and its position is drone's position plus Vector3Df(50,25,-25). When you run the code you should see the drone smoothly fly away. Now put back the line for updating the camera's target in the render loop:
//The render loop runs continuously and redraws the scene. var:gb.video.Color background=new gb.video.Color(160, 160, 160) while(engine.Run()){ camera.Target=droneNode.Position //Redrawing the screen requires a call to the drivers BeginScene function. driver.BeginScene(gb.video.ClearBufferFlag.All, background) //The SceneManager will handle rendering the scene graph for us. smgr.DrawAll() //When we are done, we need to call EndScene so that the driver can update the screen. driver.EndScene() }
When you run the code you should see the drone smoothly fly away, with the camera adjusting to keep track of it.
This tutorial does not include a camera that follows a model with an animator attached. Animators make changes to the scene node based on time and if we changed the camera's target manually, you may find that the camera's movement is jittery/jagged (depending on the speed of your computer). To achieve that effect you would have to extend the model and update the camera's target on the model scene nodes OnAnimate event.
The following animators are included in Irrlicht: