How to set up the camera so that the view is from a third person?

Hello, I managed to create a code where the cameras follow the “tank” object. But with the mouse, you can zoom in and out of the camera. Tell me how to set up the camera so that it moves like in GTA behind the car?

My code:
“index.html”

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.min.js"></script>
<script src="https://cdn.babylonjs.com/babylon.js"></script>
<script src="pointerLock.js"></script>
<title> Babylon FPS</title>
</head>
<body>

<canvas id="renderCanvas"></canvas>


</body>
</html>

main.js

window.addEventListener('DOMContentLoaded', function(){
            // get the canvas DOM element
            var canvas = document.getElementById('renderCanvas');

            // load the 3D engine
            var engine = new BABYLON.Engine(canvas, true);
            
            
             var createFollowCamera = function(scene,canvas,target){
              var camera = new BABYLON.FollowCamera("tankFollowCamera",new BABYLON.Vector3(10,0,10), scene);
              camera.heightOffset= 2;
              camera.rotationOffset = 180;
              camera.cameraAcceleration = .1;
              camera.maxCameraSpeed = 1;
              camera.lockedTarget = target;
              camera.attachControl(canvas, true);
              return camera;
            }
            

            // createScene function that creates and return the scene
            var createScene = function () {
                    var scene = new BABYLON.Scene(engine);
                    
                    var sun = new BABYLON.PointLight("Omni0", new BABYLON.Vector3(60, 100, 10), scene);
                    
                    

                  
                    

                    // Ground
                    var ground = BABYLON.Mesh.CreateGroundFromHeightMap("ground", "https://www.babylonjs-playground.com/textures/heightMap.png", 200, 200, 100, 0, 10, scene, false);
                    var groundMaterial = new BABYLON.StandardMaterial("ground", scene);
                    groundMaterial.diffuseTexture = new BABYLON.Texture("https://www.babylonjs-playground.com/textures/grass.png", scene);
                    groundMaterial.diffuseTexture.uScale = 6;
                    groundMaterial.diffuseTexture.vScale = 6;
                    groundMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
                    ground.position.y = -2.05;
                    ground.material = groundMaterial;

                    // Skybox
                        var skybox = BABYLON.Mesh.CreateBox("skyBox", 1000.0, scene);
                        var skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
                        skyboxMaterial.backFaceCulling = false;
                        skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("https://www.babylonjs-playground.com/textures/TropicalSunnyDay", scene);
                        skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
                        skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
                        skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
                        skyboxMaterial.disableLighting = true;
                        skybox.material = skyboxMaterial;        
                 
                
                var tank = new BABYLON.MeshBuilder.CreateBox("tank",{height:.5, depth:1, width:1},scene);
                var tankMaterial = new BABYLON.StandardMaterial("tankMaterial",scene);
                tankMaterial.diffuseColor = new BABYLON.Color3(1,0,5);
                
                tank.material = tankMaterial
                tank.position.y = 5;
                tank.speed = 1;
                tank.frontVector = new BABYLON.Vector3(0,0,1);
                
                
                
                
                //var camera = 
                createFollowCamera(scene,canvas,tank);
               
                console.log(tank.move);
                
                
                //set up input map
                var inputMap = {};
                  scene.actionManager = new BABYLON.ActionManager(scene);
                  scene.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnKeyDownTrigger, function (evt) {
                      inputMap[evt.sourceEvent.key] = evt.sourceEvent.type == "keydown";
                     
                  }));
                  scene.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnKeyUpTrigger, function (evt) {
                      inputMap[evt.sourceEvent.key] = evt.sourceEvent.type == "keydown";
                      
                  }));
                
                scene.onBeforeRenderObservable.add(function(){
                     if(tank){
                      if(inputMap["w"] || inputMap["ArrowUp"]){
                        tank.moveWithCollisions(tank.frontVector.multiplyByFloats(tank.speed,tank.speed,tank.speed));
                      }
                      if(inputMap["a"] || inputMap["ArrowLeft"]){
                         tank.rotation.y -= .1;
                         tank.frontVector = new BABYLON.Vector3(Math.sin(tank.rotation.y),0,Math.cos(tank.rotation.y));
                      }
                      if(inputMap["s"] || inputMap["ArrowDown"]){
                         tank.moveWithCollisions(tank.frontVector.multiplyByFloats(-tank.speed,-tank.speed,-tank.speed)); 
                      }
                      if(inputMap["d"] || inputMap["ArrowRight"]){
                         tank.rotation.y += .1;
                         tank.frontVector = new BABYLON.Vector3(Math.sin(tank.rotation.y),0,Math.cos(tank.rotation.y));
                       } 
                     }
                     
                      
                  })
                return scene;
              };
            
            
           
            // call the createScene function
            var scene = createScene();
            createPointerLock(scene); 
            // run the render loop
            engine.runRenderLoop(function(){
                scene.render();
            });

            // the canvas/window resize event handler
            window.addEventListener('resize', function(){
                engine.resize();
            });
        });

pointerLock.js

  let createPointerLock = function(scene) {
  let canvas = scene.getEngine().getRenderingCanvas();
  canvas.addEventListener("click", event => {
    canvas.requestPointerLock = canvas.requestPointerLock || canvas.msRequestPointerLock || canvas.mozRequestPointerLock || canvas.webkitRequestPointerLock;
    if(canvas.requestPointerLock) {
      canvas.requestPointerLock();
    }
  }, false);
}; 

link to https://codepen.io/Smith37/pen/OJxdmaw

It all depends on the way you construct your scene. You can parent the model you want to the camera and position it accordingly (for example - one unit on the z axis), or use for example the follow camera, that does the job for you:

If you can create a playground with your code we will be able to help a lot more :slight_smile:

And welcome to the forum!

2 Likes

Cooked up a simple example just to give you an idea of what you can do:
Example third person camera | Babylon.js Playground (babylonjs.com)

Here I parented the character model to the camera and positioned it so it’s slighly in front and below the center view of the camera. Another point is that, whenever the camera rotates, I “undo” this rotation on the character, so that it doesn’t rotate on the camera pivot and stays in place on the “ground”.

If you have any more questions, please let us know!

4 Likes

Hey welcome!

You can look around with the mouse and move with WASD.

I’m not the author of the script, I’ve just created a PG with a script I found somewhere earlier and saved it for future use :slight_smile:
Enjoy!

3 Likes

It looks part of the problem is that you’re trying to control the camera in two competing ways simultaneously. The FollowCamera has logic to follow the mesh around but when you call attachControl() on the camera then competing logic is introduced to control the camera’s position and rotation in a completely different way…

Once the attachControl() line is commented out it seems like you’ve got the keyboard controls and follow camera working well already… So you could add mouse movement to control the mesh’s rotation for example, which controls the follow camera indirectly. Or you could for example use the mouse movement to control aspects of the follow behavior such as “rotationOffset” instead.

I don’t recall how the mouse behaves in GTA thou so IDK exactly what you’re going for with the mouse controls? :thinking:

Depends on what you;re trying to achieve. Look at my example here:

Hello, I need the camera to be at an angle of 45 degrees, like in racing, and rotate only when the user presses the arrows. Here I managed to fix something, but the mouse is still actively involved in moving the object.

The fix is at the link in the first codepen post.

Here is the code for the camera itself, which I fixed.

var createFollowCamera = function(scene,canvas,target){
             // //var camera = new BABYLON.FollowCamera("tankFollowCamera",new BABYLON.Vector3(10,0,10), scene);
              //camera.heightOffset= 2;
             // camera.rotationOffset = 180;
             // camera.cameraAcceleration = .1;
             /// camera.maxCameraSpeed = 1;
             // camera.lockedTarget = target;
             // camera.attachControl(canvas, true); 
              var camera = new BABYLON.ArcRotateCamera("camera", -Math.PI/2, Math.PI/3, 10, BABYLON.Vector3.Zero(), scene);
              camera.setTarget(target);
              camera.inputs.remove(camera.inputs.attached.mousewheel);
              camera.attachControl(canvas, true);
              
              return camera;
            }

I also have a code that, when pressed, removes the cursor and allows the scene to be moved with the mouse.

  let createPointerLock = function(scene) {
  let canvas = scene.getEngine().getRenderingCanvas();
  canvas.addEventListener("click", event => {
    canvas.requestPointerLock = canvas.requestPointerLock || canvas.msRequestPointerLock || canvas.mozRequestPointerLock || canvas.webkitRequestPointerLock;
    if(canvas.requestPointerLock) {
      canvas.requestPointerLock();
    }
  }, false);
}; 

If you don’t want the mouse to control the camera, you can detach its controls using

camera.inputs.attached.pointers.detachControl();
camera.inputs.attached.mousewheel.detachControl();

Detach mouse controls from camera | Babylon.js Playground (babylonjs.com)

Hello @Lex34 just checking in, was your question answered?

1 Like