Default Camera Movement not Updating camera.position

Hello,
I am trying to figure out how to move the universal camera with the default key commands, but nothing I do seems to work. The documentation says that the arrow keys will move the camera after camera.attachControl(canvas, true) has been called.

This is not happening in my scene though. The arrow keys don’t seem to be changing any values as I press them.
I also do not see any “move” method on the universal camera API. Is there a method that will do physics and collision detection from point A to point B?
Here is my code:

(Note that I’m passing the HTML canvas element into the exported function:

import { Engine } from "@babylonjs/core/Engines/engine";
import { Scene } from "@babylonjs/core/scene";
import { Vector3 } from "@babylonjs/core/Maths/math";
import { UniversalCamera } from "@babylonjs/core/Cameras/universalCamera";
import { Mesh } from "@babylonjs/core/Meshes/mesh";

// Required side effects to populate the Create methods on the mesh class. Without this, the bundle would be smaller but the createXXX methods from mesh would not be accessible.
import "@babylonjs/core/Meshes/meshBuilder";
import "@babylonjs/core/Materials"

export default function app(canvas){
const engine = new Engine(canvas)

var scene = new Scene(engine);

scene.gravity = new Vector3(0, -9.81, 0)
scene.collisionsEnabled = true

// This creates and positions a free camera (non-mesh)
var camera = new UniversalCamera("camera1", new Vector3(1, 1, 1), scene);

camera.checkCollisions = true
camera.applyGravity = true

camera.ellipsoid = new Vector3(1, 1, 1)

// This targets the camera to scene origin
camera.setTarget(Vector3.Zero());

// This attaches the camera to the canvas
camera.attachControl(canvas, true);

// Our built-in 'sphere' shape. Params: name, subdivs, size, scene
var sphere = Mesh.CreateSphere("sphere1", 16, 2, scene);

sphere.checkCollisions   = true

// Move the sphere upward 1/2 its height
sphere.position.y = 2;

//when I press f, the camera position is printed to the console (this works, so I know canvas is getting events):
canvas.addEventListener("keydown", e=>{
	if(e.key === "f") console.log(camera.position)
})

// Render every frame
engine.runRenderLoop(() => {
    scene.render();
})
}

This is working but you have no ground so your camera is maybe falling forever in the void :slight_smile:

No, there is no movement when I do console.log(camera.position).
I’ll try adding a ground. There was no bug logged in the console.
When you say “moving the camera”, I presume that movement is reflected in the camera.position. Is this correct?

It is correct. Can you repro the issue in the Playground? It will be easier to debug with you

OK, Here is my playground:

https://www.babylonjs-playground.com/#ALGF5R

How do I view the console.log?

I downloaded the file as a zip file, and added in a button component as a child of the canvas element, and gave the canvas a role of application, so keyboard only users could focus the canvas (There are other elements on the page in my application).

Enter Focus Mode

For some reason the handler I attach to the canvas element works 100% of the time, but the camera never works. If I remove the child and application role from the canvas in the downloaded playground example, and that example is the only thing on the page, then I am able to move the camera.

I have no idea why the button would be keeping the camera from getting the events, especially if my handler works perfectly.

What is the camera doing to attach to the canvas?

Well I see no code on your playground :slight_smile: You need to udpate it to highlight your issue so I can help fixing it :slight_smile:

Sorry, I am not understanding how the playground works, save apparently is not working. Try this:
https://www.babylonjs-playground.com/#ALGF5R#2

But the problem is not with my code, it’s with the canvas.

Well just hit save :smiley: Do you get any error when you hit save? It will generate a new URL for you and that’s it

I get a dialogue at the bottom of the page that says type in a title, description and tags, then I hit save. There is no URL other than what is in the address bar.

Here is the code if it helps:

// You have to create a function called createScene. This function must return a BABYLON.Scene object

// You can reference the following variables: scene, canvas

// You must at least define a camera

var createScene = function() {

var scene = new BABYLON.Scene(engine);

var camera = new BABYLON.UniversalCamera(“camera1”, new BABYLON.Vector3(1, 1, 1), scene);

camera.checkCollisions = true

camera.applyGravity = true

camera.ellipsoid = new BABYLON.Vector3(1, 1, 1)

// This attaches the camera to the canvas

camera.attachControl(canvas, true);

// Our built-in ‘sphere’ shape. Params: name, subdivs, size, scene

var sphere = BABYLON.Mesh.CreateSphere(“sphere1”, 16, 2, scene);

sphere.checkCollisions = true

sphere.position.x = 10;

sphere.position.y = 1;

sphere.position.z = 1

canvas.addEventListener(“keydown”, e=>{

if(e.key === “f”) console.log(camera.position)

})

return scene;

};

ok so it worked :slight_smile:

But I see no ground for your camera to land in your PG

OK, I added the ground although it doesn’t seem to help.
How do I view the console.log in the playground?

Well on F12 console, like any other webpage

Where do I open an issue for the playground UI? I’m not able to focus the canvas element with the keyboard without using the mouse to click on the canvas.
If we can fix that problem, then I think this problem will be fixed as well.
None of the users I am targeting use the mouse to navigate the web, so having the canvas within the tabindex is critical.

We are in the process of improving the PG so please do your PR here:

OK, I am doing some research on the best way to do the tabindex. I actually see the tabindex is 1, which means that it is there, it’s just at the top of the screen.

It’s working in this playground example, but not in my react ES6 version. I can tell the element is getting focus, my f callback is working, and when I manually change the camera.position.x, it works. Do you know if there is a way to check if the camera handlers are on the canvas element? Do you have any other ideas why this would not be working?

This function runs in the componentDidMount function and is passed the ref of the canvas element as “canvas”. I was unable to find Color3 in the ES6 modules, so it’s commented out:

import { Engine } from "@babylonjs/core/Engines/engine";
import { Scene } from "@babylonjs/core/scene";
import { Vector3 } from "@babylonjs/core/Maths/math";
import { UniversalCamera } from "@babylonjs/core/Cameras/universalCamera";
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import { StandardMaterial } from '@babylonjs/core/Materials'


// Required side effects to populate the Create methods on the mesh class. Without this, the bundle would be smaller but the createXXX methods from mesh would not be accessible.
import "@babylonjs/core/Meshes/meshBuilder";
import "@babylonjs/core/Materials"

export default function app(canvas){
const engine = new Engine(canvas)

var scene = new Scene(engine);

scene.gravity = new Vector3(0, -9.81, 0)
scene.collisionsEnabled = true

// This creates and positions a free camera (non-mesh)
var camera = new UniversalCamera("camera1", new Vector3(1, 1, 1), scene);
camera.checkCollisions = true
camera.applyGravity = true
camera.ellipsoid = new Vector3(1, 1, 1)
camera.setTarget(Vector3.Zero());
camera.attachControl(canvas, true);


//ground
const ground = Mesh.CreatePlane("ground", 20.0, scene);
ground.material = new StandardMaterial("groundMat", scene);
//ground.material.diffuseColor = new BABYLON.Color3(1, 1, 1); // can't find where to import Color3 from
ground.material.backFaceCulling = false;
ground.position = new Vector3(5, -10, -15);
ground.rotation = new Vector3(Math.PI / 2, 0, 0);
ground.checkCollisions = true

// Our built-in 'sphere' shape. Params: name, subdivs, size, scene
const sphere = Mesh.CreateSphere("sphere1", 16, 2, scene);
sphere.material = new StandardMaterial("Mat", scene);
sphere.checkCollisions   = true

sphere.position.x = 10;
sphere.position.y = 1;
sphere.position.z = 1

canvas.addEventListener("keydown", e=>{
	if(e.key === "f") console.log(camera.position)
})

// Render every frame
engine.runRenderLoop(() => {
scene.render();
})
}

This is where everything is attached. Make sure the canvas is not null maybe?

No canvas is not null, because the line:

canvas.addEventListener("keydown", e=>{
	if(e.key === "f") console.log(camera.position)
})

works perfectly.
I’ll create a create react app example soon and see if I can reduce everything down and pinpoint what’s going on. But that handler works, so I’m really lost on why the:

camera.attachControl(canvas, true);

Does not work. What is going on in camera.attachControl(canvas, true)? What functions is it using? Maybe the ref returned by React is missing some kind of functionality.

This is just using the default addEventListener of the canvas so this should be ok in react as well .

Does this PG work for you?

I centered the ground at the origin (0,0,0), placed the camera at 1.8 units above the ground, and placed the sphere at 10 units out on the Z axis. Ohh, and I added a light and changed the materials a bit.

And a little tip with any playground for monitoring the camera:

  1. Open the “Inspector” - click the button at the top.
    2.Chose “Nodes” then “Camera”. In the panel at bottom right of the screen, look at the “Transform” values - will give you the camera position. You don’t need the event code stuff.

cheers, gryff :slight_smile:

Hello,
I was unable to get the position from the recommended example for react and Babylon, added the event listener:

canvas.addEventListener("keydown", e=>{
	if(e.key === "f") console.log(camera.position)
})

to src/Viewer/index.js, line 26.
Then I tried navigating in the browser with the arrow keys and pressing f to get the current pos. Nothing happened.
I then added a button element within the canvas to grab focus from tab in src/BabylonScene/index.js line 86:

		<canvas
			{...opts}
			ref={this.onCanvasLoaded}
			role="image"
		>
			<button>Focus button</button>
		</canvas>

With the button there, I am able to get f to print the current position, but I am unable to change the position.
I am able to get the movement to work if I am not in a react app.