问题
I'm writing and animation loop using three.js and all the examples (mrdoob, stemkoski) I see online use unprotected globals at the beginning of the script. I tried to enclose these in the init() function and then pass them as arguments through the animation loop. However, the renderer is coming up undefined.
I'm not sure what I'm missing below. My primary question is how to understand best practice for setting up an animation loop with good closure (protecting variables that would otherwise be global). Thanks!
// THE MAIN ANIMATION LOOP:
// UPDATE the scene
function update(keyboard, controls, stats, clock) {
// delta = change in time since last call (in seconds)
var delta = clock.getDelta();
// functionality provided by THREEx.KeyboardState.js
if ( keyboard.pressed("z") )
{
// do something
}
controls.update();
stats.update();
};
// RENDER the scene
function render(renderer, scene, camera) {
renderer.render(scene, camera);
};
// ANIMATE the scene
function animate(scene, camera, renderer, controls, stats, keyboard, clock) {
requestAnimationFrame(animate);
render(renderer, scene, camera);
update(keyboard, controls, stats, clock);
};
// *********************
// INITIALIZES THE SCENE
function init(images) { // `images` is passed by a callback from loadImages
// standard global variables, held privately
var container, scene, camera, renderer, controls, stats;
var keyboard = new THREEx.KeyboardState();
var clock = new THREE.Clock();
///////////
// SCENE //
///////////
scene = new THREE.Scene();
////////////
// CAMERA //
////////////
// set the view size in pixels (custom or according to window size)
var SCREEN_WIDTH = 1920, SCREEN_HEIGHT = 1080;
// var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
// camera attributes
var VIEW_ANGLE = 20, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
// set up camera
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
// add the camera to the scene
scene.add(camera);
// the camera defaults to position (0,0,0)
// so pull it back (z = 400) and up (y = 100) and set the angle towards the scene origin
// (x,y,z)
camera.position.set(0,150,1000);
camera.lookAt(scene.position);
//////////////
// RENDERER //
//////////////
// create and start the renderer; choose antialias setting.
if (Detector.webgl)
renderer = new THREE.WebGLRenderer( {antialias:true} );
else
renderer = new THREE.CanvasRenderer();
renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
// attach div element to variable to contain the renderer
container = document.getElementById( 'ThreeJS' );
// attach renderer to the container div
container.appendChild( renderer.domElement );
///////////
// STATS //
///////////
// displays current and past frames per second attained by scene
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
///////////
// LIGHT //
///////////
// create a light
var light = new THREE.PointLight(0xffffff);
light.position.set(100,250,0);
scene.add(light);
////////////
// IMAGES //
////////////
var element1 = THREE.ImageUtils.loadTexture(images.dresser10);
var element2 = THREE.ImageUtils.loadTexture(images.dresser14);
var element1Material = new THREE.SpriteMaterial( { map: element1, useScreenCoordinates: true, alignment: THREE.SpriteAlignment.topLeft } );
var sprite = new THREE.Sprite(element1Material);
sprite.position.set( 50, 50, 0 );
sprite.scale.set( 64, 64, 1.0 ); // imageWidth, imageHeight
scene.add(sprite);
animate(container, scene, camera, renderer, controls, stats, keyboard, clock);
};
// ********************************************************
// CHECKS TO SEE IF THE WINDOW HAS LOADED BEFORE PROCEEDING
// Once the window is loaded, calls the init function
window.addEventListener ("load", eventWindowLoaded, false);
function eventWindowLoaded() {
loadImages(init); // calls to initialize the scene once the images are loaded
}
回答1:
I followed @Bergi 's advice above and rewrote the animation loop in a Crockford style module that returns an object full of privileged methods that can access the now protected variables. Here it is for anyone looking for a similar pattern:
// ************************
// THE MAIN ANIMATION LOOP:
var animLoop = (function () {
// standard global variables, held privately in this module
var container, scene, camera, renderer, controls, stats;
var keyboard = new THREEx.KeyboardState();
var clock = new THREE.Clock();
// SCENE
scene = new THREE.Scene();
// CAMERA
var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
var VIEW_ANGLE = 20, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
scene.add(camera);
camera.position.set(0,150,1000);
camera.lookAt(scene.position);
// RENDERER
if (Detector.webgl) {
renderer = new THREE.WebGLRenderer( {antialias:true} );
} else {
renderer = new THREE.CanvasRenderer();
}
renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
container = document.getElementById( 'ThreeJS' );
container.appendChild( renderer.domElement );
// LIGHT
var light = new THREE.PointLight(0xffffff);
light.position.set(100,250,0);
scene.add(light);
// IMAGES
var images;
var element1, element2, element1Material, sprite;
// RETURN:
// *** returns an object full of functions with priviledged access to the private variables listed above ***
return {
setImages: function (images_) { // sets the value of the images (array) above
images = images_;
},
createSprites: function () {
var element1 = THREE.ImageUtils.loadTexture(images.dresser10.src);
var element1Material = new THREE.SpriteMaterial( { map: element1, useScreenCoordinates: true, alignment: THREE.SpriteAlignment.topLeft } );
var sprite = new THREE.Sprite(element1Material);
sprite.position.set( 50, 50, 0 );
sprite.scale.set( 64, 64, 1.0 );
scene.add(sprite);
},
update: function () {
var delta = clock.getDelta();
// functionality provided by THREEx.KeyboardState.js
if ( keyboard.pressed("z") )
{
// do something
}
},
render: function () {
renderer.render(scene, camera);
}
};
}());
// ANIMATE the scene
function animate() {
requestAnimationFrame( animate );
animLoop.render();
animLoop.update();
};
// INITIALIZES THE SCENE
function init(images) { // `images` is passed by a callback not included here
animLoop.setImages(images); // places the initial array of images as a private variable in the animLoop object
animLoop.createSprites();
animate();
};
window.addEventListener ("load", eventWindowLoaded, false);
function eventWindowLoaded() {
loadImages(init); // calls to initialize the scene once the images are loaded
};
来源:https://stackoverflow.com/questions/22158744/how-to-create-closure-protect-globals-in-an-animation-loop