topical media & game development
mobile-query-three-plugins-iimminigame-examples-index.htm / htm
<!doctype html><title>PongGL</title>
<script src="../../../build/tquery-bundle.js"></script>
<script src="../../../vendor/threex/THREEx.KeyboardState.js"></script>
<script src="../../keyboard/tquery.keyboard.js"></script>
<script src="../../../vendor/three.js/ShaderExtras.js"></script>
<script src="../../../vendor/three.js/postprocessing/EffectComposer.js"></script>
<script src="../../../vendor/three.js/postprocessing/BloomPass.js"></script>
<script src="../../../vendor/three.js/postprocessing/DotScreenPass.js"></script>
<script src="../../../vendor/three.js/postprocessing/FilmPass.js"></script>
<script src="../../../vendor/three.js/postprocessing/MaskPass.js"></script>
<script src="../../../vendor/three.js/postprocessing/RenderPass.js"></script>
<script src="../../../vendor/three.js/postprocessing/SavePass.js"></script>
<script src="../../../vendor/three.js/postprocessing/ShaderPass.js"></script>
<script src="../../../vendor/three.js/postprocessing/TexturePass.js"></script>
<script src="../../pproc/tquery.effectcomposer.js"></script>
<script src='../../grassground/tquery.grassground.js'></script>
<script src="../../skymap/tquery.cubetexture.js"></script>
<script src="../../skymap/tquery.skymap.js"></script>
<script src='../../shadowmap/tquery.light.shadowmap.js'></script>
<script src="vendor/webaudio.js"></script>
<script src="vendor/webaudio.sound.jsfx.js"></script>
<script src="vendor/jsfx/audio.js"></script>
<script src="vendor/jsfx/jsfx.js"></script>
<script src="vendor/jsfx/jsfxlib.js"></script>
<script src="../../tweenjs/examples/Tween.js"></script>
<style>
.score {
position : absolute;
top : 0px;
z-index : 1;
font-family : arial, verdana, sans-serif;
font-size : 800%;
font-weight : bolder;
color : #dddddd;
text-shadow : 0 0 0.2em #F87, 0 0 0.2em #F87, 0 0 0.2em #F87;
padding-left : 10px;
padding-right : 10px;
};
#scoreLeft {
left : 0;
}
#scoreRight {
right : 0;
}
</style>
<body>
<div id="scoreLeft" class="score">0</div>
<div id="scoreRight" class="score">0</div>
<script>
// init audio layer
var webaudio = new WebAudio();
webaudio.volume(0.5)
// init each sound
var jsfxParam = ["saw",0.0000,0.4000,0.0000,0.1640,0.0000,0.1880,20.0000,594.0000,2400.0000,0.5440,0.0000,0.0000,0.0100,0.0003,0.0000,0.0000,0.0000,0.0000,0.0000,0.5616,0.0000,0.0000,1.0000,0.0000,0.0000,0.0000,0.0000];
var soundRacketWall = webaudio.createSound().generateWithJsfx(jsfxParam);
var jsfxParam = ["square",0.0000,0.4000,0.0000,0.3560,0.0000,0.1900,20.0000,358.0000,2400.0000,0.3000,0.0000,0.0000,0.0100,0.0003,0.0000,0.0000,0.0000,0.1810,0.0000,0.0000,0.0000,0.0000,1.0000,0.0000,0.0000,0.0000,0.0000];
var soundRacketBall = webaudio.createSound().generateWithJsfx(jsfxParam);
soundRacketBall.volume(0.1)
var jsfxParam = ["square",0.0000,0.4000,0.0000,0.1640,0.0000,0.1380,20.0000,231.0000,2400.0000,0.0000,0.0000,0.0000,0.0100,0.0003,0.0000,0.0000,0.0000,0.0625,0.0000,0.0000,0.0000,0.0000,1.0000,0.0000,0.0000,0.1000,0.0000];
var soundWall = webaudio.createSound().generateWithJsfx(jsfxParam);
soundWall.volume(0.1)
var jsfxParam = ["saw",0.0000,0.4000,0.0000,0.2800,0.0000,0.2380,20.0000,837.0000,2400.0000,-0.7300,0.0000,0.0000,0.0100,0.0003,0.0000,0.0000,0.0000,0.3235,0.0100,0.0000,0.0000,0.0000,1.0000,0.0000,0.0000,0.0000,0.0000];
var soundLose = webaudio.createSound().generateWithJsfx(jsfxParam);
// create a tQuery.World
var world = tQuery.createWorld().boilerplate().start();
// no camera controls is needed
world.removeCameraControls();
world.tCamera().position.y = 1.5;
world.tCamera().position.z = 3.2;
world.tCamera().lookAt(world.tScene().position);
// enable shadow in the renderer
world.tRenderer().shadowMapEnabled = true;
world.tRenderer().shadowMapSoft = true;
// add a skybox
tQuery.createSkymap('skybox').addTo(world);
// create the ground
tQuery.createPlane().addTo(world)
.receiveShadow(true)
.scale(3*1.5, 1, 1.8*1.5)
.translateY(-0.2)
.setLambertMaterial().map('../../grassground/images/grasslight-big.jpg').ambient(0x444444).back();
// add postprocessing effects
world.addEffectComposer()
//.motionBlur(0.7)
.vignette()
.finish();
// lights
tQuery.createAmbientLight().addTo(world).color(0x444444)
tQuery.createPointLight().addTo(world).intensity(2).distance(30);
tQuery.createDirectionalLight().addTo(world)
.position(2, 4, -1.5).color(0xffffff)
.castShadow(true)
.shadowCamera(2, -2, 2, -2, 0.01, 200)
.shadowDarkness(0.7).shadowBias(.002)
//.shadowCameraVisible(true);
// some constants for players
var players = { "right" : {}, "left": {} };
var racketW = 0.05;
var racketD = 0.5;
var racketRangeY= 2;
// init each player
Object.keys(players).forEach(function(playerId){
var player = players[playerId];
// create tQuery.Object3D for a racket
var url = "../../physics/examples/images/rocks.jpg";
var playerColor = playerId === 'right' ? 0xffaacc : 0xccaaff;
var object3d = tQuery.createCube().addTo(world)
.scale(racketW, 0.2, racketD)
.setLambertMaterial().color(playerColor).ambient(0xaaaaaa).map(url).back()
.id(playerId)
.castShadow(true)
object3d.translateX(1.3 * (playerId === "right" ? +1 : -1));
player.object3d = object3d;
})
Function called when playerId scored a point
function addScore(playerId){
var selector = {
right : '#scoreRight',
left : '#scoreLeft'
}[playerId];
var element = document.querySelectorAll(selector)[0];
var score = parseInt(element.innerHTML);
score++;
element.innerHTML= score;
soundLose.play();
// reset ball velocity
ballAngle = 0.2 * Math.random()*Math.PI*2;
ballVelX = Math.cos(ballAngle)*0.03;
ballVelZ = Math.sin(ballAngle)*0.03;
// put it back in the center
ball3d.position(0,0,0);
};
// handle player keyboard
world.loop().hook(function(delta){
var keyboard = tQuery.keyboard();
var speedY = 2;
if( keyboard.pressed('up') ) players['right'].object3d.translateZ(-delta*speedY);
if( keyboard.pressed('down') ) players['right'].object3d.translateZ(+delta*speedY);
if( keyboard.pressed('q') ) players['left'] .object3d.translateZ(-delta*speedY);
if( keyboard.pressed('w') ) players['left'] .object3d.translateZ(+delta*speedY);
// handle racket limit
Object.keys(players).forEach(function(playerId){
var tMesh = players[playerId].object3d.get(0);
if( tMesh.position.z < -fieldD/2 + racketD/2 ) soundRacketWall.play();
if( tMesh.position.z > +fieldD/2 - racketD/2 ) soundRacketWall.play();
tMesh.position.z = Math.max(tMesh.position.z, -fieldD/2 + racketD/2);
tMesh.position.z = Math.min(tMesh.position.z, +fieldD/2 - racketD/2);
});
});
Function called when the ball touch the racket
players['right'].onContact = players['left'].onContact = function(player, ball3d){
// play a sound
soundRacketBall.play();
// increase velocity
ballVelX *= 1.1;
ballVelZ *= 1.1;
if( false ){
var deltaZ = player.object3d.get(0).position.z - ball3d.get(0).position.z;
deltaZ /= racketD;
var speed = Math.sqrt(ballVelX*ballVelX+ballVelZ*ballVelZ)
var ballAngle = Math.atan2(ballVelZ,ballVelX);
console.log(Math.atan2(ballVelZ,ballVelX)/Math.PI*180)
var maxDeltaAng = 30 * Math.PI/180;
var deltaAngle = deltaZ * 2 * maxDeltaAng;
ballAngle += deltaAngle * (player.object3d.id() === "right" ? +1 : -1);
ballVelX = Math.cos(ballAngle)*speed;
ballVelZ = Math.sin(ballAngle)*speed;
}
// change racket color randomly
player.object3d.get(0).material.color.setHex(0xFfffff*Math.random());
};
// constant for the field
var fieldW = 3;
var fieldD = 1.8;
// constant for the ball
var ballRadius = 1/5;
var ballAngle = Math.random()*Math.PI*2;
var ballAngle = 0.2 * Math.random()*Math.PI*2;
var ballVelX = Math.cos(ballAngle)*0.03;
var ballVelZ = Math.sin(ballAngle)*0.03;
// create a tQuery.Object3d for the ball
var ball3d = tQuery.createSphere().addTo(world).scaleBy(ballRadius)
.setLambertMaterial().color(0xFFFF00).ambient(0xFFFFFF).map("../../physics/examples/images/rocks.jpg").back()
.castShadow(true)
world.loop().hook(function(){
// get ball position
var position = ball3d.get(0).position;
// update position
position.x += ballVelX;
position.z += ballVelZ;
// handle border contact
if( position.z < -fieldD/2 + ballRadius/2) wallOnContact(walls['north'])
if( position.z > +fieldD/2 - ballRadius/2) wallOnContact(walls['south'])
// handle score
if( position.x < -fieldW/2 + ballRadius/2) addScore('right');
if( position.x > +fieldW/2 - ballRadius/2) addScore('left');
// bounce the ball if it reach the border
if( position.x < -fieldW/2 + ballRadius/2) ballVelX *= -1;
if( position.x > +fieldW/2 - ballRadius/2) ballVelX *= -1;
if( position.z < -fieldD/2 + ballRadius/2) ballVelZ *= -1;
if( position.z > +fieldD/2 - ballRadius/2) ballVelZ *= -1;
// get the boundaries
position.x = Math.max(position.x, -fieldW/2+ballRadius/2);
position.x = Math.min(position.x, +fieldW/2-ballRadius/2);
position.z = Math.max(position.z, -fieldD/2+ballRadius/2);
position.z = Math.min(position.z, +fieldD/2-ballRadius/2);
// check collision with each player racket
["right", "left"].forEach(function(playerId){
// get tQuery.Object3D for this player
var player = players[playerId];
var object3d = player.object3d;
// get the position in X and Y
var racketX = object3d.get(0).position.x;
var racketZ = object3d.get(0).position.z;
// test each ball boundary
var mayHitLeft = (position.x+ballRadius/2) >= (racketX-racketW/2);
var mayHitRight = (position.x-ballRadius/2) <= (racketX+racketW/2);
var mayHitTop = (position.z+ballRadius/2) >= (racketZ-racketD/2);
var mayHitBottom= (position.z-ballRadius/2) <= (racketZ+racketD/2);
// test if there is collision
if( mayHitLeft && mayHitRight && mayHitTop && mayHitBottom ){
// reaction of a collision
ballVelX *= -1;
var deltaX = racketW/2 + ballRadius/2;
position.x = racketX + (playerId === "right"? -deltaX : +deltaX);
player.onContact && player.onContact(player, ball3d);
}
});
});
/////////////////////////////////////////////////////////////////
// Walls //
/////////////////////////////////////////////////////////////////
var walls = {};
var url = "../../physics/examples/images/plywood.jpg";
walls['north'] = tQuery.createCylinder(0.05, 0.05, 3, 16, 4).addTo(world)
.setLambertMaterial().color(0xFFFFFF).ambient(0xFFFFFF).map(url).back()
.geometry().rotateZ(Math.PI/2).back()
.translateY(-0.1)
.translateZ(-1.8/2 - 0.05/2)
.castShadow(true)
walls['south'] = tQuery.createCylinder(0.05, 0.05, 3, 16, 4).addTo(world)
.setLambertMaterial().color(0xFFFFFF).ambient(0xFFFFFF).map(url).back()
.geometry().rotateZ(Math.PI/2).back()
.translateY(-0.1)
.translateZ(+1.8/2 + 0.05/2)
.castShadow(true)
function wallOnContact(object){
soundWall.play();
var tweenvars = {
scale : 1
};
var tweenFwd = new TWEEN.Tween(tweenvars).to({
scale : 2
}, 100).easing(TWEEN.Easing.Elastic.Out).onUpdate(function(){
object.scale(1, this.scale, this.scale);
});
// setup a backward tween
var tweenBack = new TWEEN.Tween(tweenvars).to({
scale : 1
}, 200).easing(TWEEN.Easing.Linear.None)
.onUpdate(function(){
object.scale(1, this.scale, this.scale);
});
tweenFwd.chain(tweenBack);
tweenFwd.start();
}
// update TWEEN with world.loop
world.loop().hook(function(){
TWEEN.update();
});
</script></body>
(C) Æliens
04/09/2009
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.