Skip to content

Instantly share code, notes, and snippets.

@jsermeno
Created June 11, 2011 09:58
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save jsermeno/1020426 to your computer and use it in GitHub Desktop.
Save jsermeno/1020426 to your computer and use it in GitHub Desktop.
Three.js Game - Animated Sprites + Backbone.js - http://catchvar.com/threejs-game-animated-sprites-backbonejs
// Bind context
_.bindAll( this, "animate", "render", "update" );
// Initialize camera
this.camera = new THREE.Camera( 45, window.innerWidth / window.innerHeight, -2000, 10000 );
this.camera.projectionMatrix = THREE.Matrix4.makeOrtho( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, -2000, 10000 );
this.camera.position.y = 70.711;
this.camera.position.x = 100;
this.camera.position.z = 100;
// Create scene
this.scene = new THREE.Scene();
// Create projector
this.projector = new THREE.Projector();
// Create renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
// Load scene
appView = new Game.Views.App({ el: renderer.domElement });
document.body.appendChild(renderer.domElement);
Game.Controllers.App = (function() {
var
renderer,
appView,
// Game loop
loops = 0,
nextGameTick = (new Date).getTime(),
// Constants
FPS = 60,
MAX_FRAME_SKIP = 10,
SKIP_TICKS = 1000 / FPS;
return {
// App variables
camera: null,
scene: null,
projector: null,
/*
Initialize scene
*/
initialize: function() {
},
/*
function animate
Game loop - requests each new frame
*/
animate: function() {
},
/*
function update
Handles game state updates
*/
update: function() {
},
/*
function render
*/
render: function() {
}
};
})();
/*
function animate
Game loop - requests each new frame
*/
animate: function() {
requestAnimationFrame( this.animate );
this.render();
},
/*
function update
Handles game state updates
*/
update: function() {
appView.update();
},
/*
function render
Keeps updates at around 50 per second while trying to render the scene as fast as possible
*/
render: function() {
loops = 0;
// Attempt to update as many times as possible to get to our nextGameTick 'timeslot'
// However, we only can update up to 10 times per frame
while ( (new Date).getTime() > nextGameTick && loops < MAX_FRAME_SKIP ) {
this.update();
nextGameTick += SKIP_TICKS;
loops++;
}
// Render our scene
renderer.render( this.scene, this.camera );
}
Game.Views.App = Backbone.View.extend({
events: {
"click": "clickWorld"
},
/*
function initialize
*/
initialize: function() {
},
/*
function update
*/
update: function() {
},
/*
function renderLand
Renders the landscape of the world and attaches to scene
*/
initLand: function() {
},
/*
function clickWorld
Handles clicks for the entire world, since we don't really have sub-elements of the canvas
*/
clickWorld: function(e) {
}
});
Game.Views.App = Backbone.View.extend({
events: {
"click": "clickWorld"
},
/*
function initialize
*/
initialize: function() {
_.bindAll( this, "update" );
this.initLand();
this.characterView = new Game.Views.Character();
},
/*
function update
*/
update: function() {
this.characterView.update();
},
/*
function renderLand
Renders the landscape of the world and attaches to scene
*/
initLand: function() {
var
grass,
plane,
uvs, i, j,
mesh;
// Load texture
grass = THREE.ImageUtils.loadTexture( "textures/grass.gif" );
grass.wrapT = grass.wrapS = THREE.RepeatWrapping;
// Create plane
plane = new THREE.Plane(8, 8, 8, 8);
plane.doubleSided = true;
// Create plane texture mapping
for ( i = 0; i < plane.faceVertexUvs[ 0 ].length; i ++ ) {
uvs = plane.faceVertexUvs[ 0 ][ i ];
for ( j = 0; j < uvs.length; j ++ ) {
uvs[ j ].u *= 8;
uvs[ j ].v *= 8;
}
}
// Create mesh for plane
mesh = new THREE.Mesh( plane, new THREE.MeshBasicMaterial( { map: grass, wireframe: false} ));
mesh.rotation.x = -90 * Math.PI / 180;
mesh.scale.x = mesh.scale.y = mesh.scale.z = 100;
// Add object to scene
Game.Controllers.App.scene.addObject( mesh );
},
/*
function clickWorld
Handles clicks for the entire world, since we don't really have sub-elements of the canvas
*/
clickWorld: function(e) {
// Move character
this.characterView.moveCharacter(e);
}
});
window.Game = {
Models: {},
Controllers: {},
Views: {}
};
Game.Views.Character = Backbone.View.extend({
// View variables
startVector: new THREE.Vector3(),
endVector: new THREE.Vector3(),
dirVector: new THREE.Vector3(),
goalVector: new THREE.Vector3(),
isWalking: false,
SPEED: 10,
IMAGE_OFFSET: .04167,
/*
Initialize the character
*/
initialize: function() {
},
/*
Update character
*/
update: function() {
},
/*
Create character sprite, collision detection, and add to scene
*/
initCharacter: function() {
},
/*
function setPosition
utility function to set position of both boundingMesh and sprite at the same time
*/
setPosition: function(x, y, z) {
},
/*
function moveCharacter
translates x, y coordinates to world space and updates character
*/
moveCharacter: function(e) {
}
});
Game.Views.Character = Backbone.View.extend({
// View variables
startVector: new THREE.Vector3(),
endVector: new THREE.Vector3(),
dirVector: new THREE.Vector3(),
goalVector: new THREE.Vector3(),
isWalking: false,
SPEED: 10,
IMAGE_OFFSET: .04167,
/*
Initialize the character
*/
initialize: function() {
_.bindAll( this, "moveCharacter", "update" );
this.initCharacter();
},
/*
Update character
*/
update: function() {
var
dir,
camera = Game.Controllers.App.camera;
if ( this.isWalking ) {
dir = new THREE.Vector3();
dir.sub( this.goalVector, this.sprite.position);
if (Math.abs( dir.x ) < 1 && Math.abs( dir.z ) < 1) {
this.isWalking = false;
}
dir.normalize();
// Position movement
this.sprite.position.x += this.SPEED * dir.x;
this.sprite.position.z += this.SPEED * dir.z;
// Texture animation
this.sprite.uvOffset.x += this.IMAGE_OFFSET;
if (this.sprite.uvOffset.x > 1.0) {
this.sprite.uvOffset.x = 0.0;
}
// Change camera position to match character
camera.position.x += this.SPEED * dir.x;
camera.position.z += this.SPEED * dir.z;
camera.target.position.x += this.SPEED * dir.x;
camera.target.position.z += this.SPEED * dir.z;
}
},
/*
Create character sprite, collision detection, and add to scene
*/
initCharacter: function() {
var
texture;
// Load sprite map
texture = THREE.ImageUtils.loadTexture( "assets/ball_sprite_red_upper_right.png" );
// Create sprite
this.sprite = new THREE.Sprite( { map: texture, useScreenCoordinates: false, affectedByDistance: true} );
// Set scale to 1/24 of image (200px)
this.sprite.scale.y = .041667 / client.innerHeight;
this.sprite.scale.x = -0.041167 / client.innerHeight;
// Set offset to first sprite of 24 images
this.sprite.uvOffset.x = .95834;
this.sprite.uvScale.x = 0.041167;
// Add collision detection
this.sprite.boundingMesh = new THREE.Mesh( new THREE.Cube(60, 60, 60, 1, 1, 1) );
THREE.Collisions.colliders.push( THREE.CollisionUtils.MeshOBB(this.sprite.boundingMesh) );
// Center sprite at 0, 0, 0 of world
this.setPosition(0, 40, 0);
Game.Controllers.App.scene.addObject( this.sprite );
Game.Controllers.App.scene.addObject( this.sprite.boundingMesh );
},
/*
function setPosition
utility function to set position of both boundingMesh and sprite at the same time
*/
setPosition: function(x, y, z) {
this.sprite.position.set(x, y, z);
this.sprite.boundingMesh.position.set(x, y, z);
},
/*
function moveCharacter
translates x, y coordinates to world space and updates character
*/
moveCharacter: function(e) {
var
camera = Game.Controllers.App.camera,
projector = Game.Controllers.App.projector,
x,
y,
t;
// Convert screen coordinates to NDC coordinates -1.0 to 1.0
x = ( e.clientX / window.innerWidth ) * 2 - 1;
y = - ( e.clientY / window.innerHeight ) * 2 + 1;
// Obtain one vector at click position for each side of the cube mapping
this.startVector.set( x, y, -1.0 );
this.endVector.set( x, y, 1.0 );
console.log("x :" + x + ", y: " + y);
// Convert coordinates back to world coordinates
this.startVector = projector.unprojectVector( this.startVector, camera );
this.endVector = projector.unprojectVector( this.endVector, camera );
// Get direction from startVector to endVector
this.dirVector.sub( this.endVector, this.startVector );
this.dirVector.normalize();
// Find intersection where y = 0
t = this.startVector.y / - ( this.dirVector.y );
// Start walking
this.goalVector.set( this.startVector.x + t * this.dirVector.x,
this.startVector.y + t * this.dirVector.y,
this.startVector.z + t * this.dirVector.z );
this.isWalking = true;
}
});
<!DOCTYPE html>
<html>
<head>
<title>Three.js - Game</title>
<meta charset="utf-8">
<style>
body {
/*background-color: #31C23B;*/
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
<!-- libs -->
<script type="text/javascript" src="js/lib/Three.js"></script>
<script type="text/javascript" src="js/lib/RequestAnimationFrame.js"></script>
<script type="text/javascript" src="js/lib/jquery-1.6.1.min.js"></script>
<script type="text/javascript" src="js/lib/underscore-min.js"></script>
<script type="text/javascript" src="js/lib/backbone.js"></script>
<!-- app -->
<script type="text/javascript" src="js/app.js"></script>
<script type="text/javascript" src="js/views/characterView.js"></script>
<script type="text/javascript" src="js/views/appView.js"></script>
<script type="text/javascript" src="js/controllers/appController.js"></script>
</head>
<body>
<script type="text/javascript">
Game.Controllers.App.initialize();
Game.Controllers.App.animate();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment