feat: sound, generator, etc.

This commit is contained in:
Mahdi Dibaiee 2018-12-28 23:04:00 +03:30
parent e983346ffc
commit d6eaa09346
17 changed files with 233 additions and 98 deletions

BIN
assets/audio/fire.wav Normal file

Binary file not shown.

BIN
assets/audio/impact-0.wav Normal file

Binary file not shown.

BIN
assets/audio/impact-1.wav Normal file

Binary file not shown.

BIN
assets/audio/impact-2.wav Normal file

Binary file not shown.

BIN
assets/audio/music.mp3 Normal file

Binary file not shown.

BIN
assets/audio/rumble.wav Normal file

Binary file not shown.

View File

@ -31,7 +31,9 @@
<script src='lib/physics-module.js'></script> <script src='lib/physics-module.js'></script>
<script src='lib/ammo.js'></script> <script src='lib/ammo.js'></script>
<script src='lib/mousetrap.js'></script> <script src='lib/mousetrap.js'></script>
<script src='lib/howler.min.js'></script>
<script src='lib/moment.min.js'></script> <script src='lib/moment.min.js'></script>
<script src='lib/color-scheme.min.js'></script>
</head> </head>
<body> <body>
<div id='gui'> <div id='gui'>
@ -44,10 +46,14 @@
<p> <p>
Score: <span id='score'>000</span> Score: <span id='score'>000</span>
</p> </p>
<p>
Hits: <span id='hits'>00</span>
</p>
</div> </div>
<script src='src/utils.js'></script> <script src='src/utils.js'></script>
<script src='src/config.js'></script> <script src='src/config.js'></script>
<script src='src/audio.js'></script>
<script src='src/gui.js'></script> <script src='src/gui.js'></script>
<script src='src/objects.js'></script> <script src='src/objects.js'></script>
<script src='src/world.js'></script> <script src='src/world.js'></script>

1
lib/color-scheme.min.js vendored Normal file

File diff suppressed because one or more lines are too long

4
lib/howler.min.js vendored Normal file

File diff suppressed because one or more lines are too long

30
src/audio.js Normal file
View File

@ -0,0 +1,30 @@
const audio = {
fire: new Howl({
src: ['assets/audio/fire.wav'],
autoplay: true,
loop: true,
volume: 0.3
}),
music: new Howl({
src: ['assets/audio/music.mp3'],
autoplay: true,
loop: true,
volume: 0.1,
}),
rumble: new Howl({
src: ['assets/audio/rumble.wav'],
autoplay: false,
loop: true,
volume: 0.2,
}),
impacts: [0, 1, 2].map(i => new Howl({
src: [`assets/audio/impact-${i}.wav`],
volume: 0.5
})),
}
audio.fire.play();
audio.music.play();

View File

@ -1,10 +1,5 @@
const RADIUS = 10; const RADIUS = 10;
const SEGMENTS = 40; const SEGMENTS = 40;
const material = new THREE.MeshLambertMaterial({
color: 0xFFFFFF,
emissive: 0xFF0000,
emissiveIntensity: 0.4,
});
const ballPhysics = new PHYSICS.SphereModule({ const ballPhysics = new PHYSICS.SphereModule({
mass: 2, mass: 2,
friction: 0.5, friction: 0.5,
@ -20,7 +15,11 @@ const ball = new WHS.Sphere({
z: -30, z: -30,
y: 10 y: 10
}, },
material, material: new THREE.MeshLambertMaterial({
color: 0xFFFFFF,
emissive: 0xFF0000,
emissiveIntensity: 0.4,
}),
modules: [ modules: [
new WHS.TextureModule({ new WHS.TextureModule({
@ -30,21 +29,16 @@ const ball = new WHS.Sphere({
], ],
}); });
const fireLight = new WHS.PointLight({
color: 0xff0000,
intensity: 5,
});
ball.physics = ballPhysics; ball.physics = ballPhysics;
ball.fireLight = fireLight;
ball.addTo(app); ball.addTo(app);
ball.add(fireLight);
camera.native.target = ball.native; camera.native.target = ball.native;
ball.checkLife = () => { ball.checkLife = () => {
if (ball.position.x > ground.geometry.parameters.width / 2 || ball.position.x < -ground.geometry.parameters.width / 2) { if (ball.position.x > ground.geometry.parameters.width / 2
|| ball.position.x < -ground.geometry.parameters.width / 2
|| ball.position.z > -ground.SIZE) {
location.reload(); location.reload();
} }
} }
@ -79,66 +73,37 @@ ball.checkLife = () => {
color: 0xffffff, color: 0xffffff,
}); });
new THREE.Geometry();
var smokeCount = 2000,
smokes = new THREE.Geometry(),
smokeMaterial = new THREE.PointsMaterial({
size: 1.5,
transparent: true,
color: 0x666666,
opacity: 0.5,
});
const radius = ball.params.geometry.radius; const radius = ball.params.geometry.radius;
const start = ball.position; const start = ball.position;
for (var p = 0; p < particleCount; p++) { for (var p = 0; p < particleCount; p++) {
// create a particle with random
// position values, -250 -> 250
var pX = start.x + THREE.Math.randFloatSpread(radius), var pX = start.x + THREE.Math.randFloatSpread(radius),
pY = start.y + THREE.Math.randFloatSpread(radius), pY = start.y + THREE.Math.randFloatSpread(radius),
pZ = start.z + THREE.Math.randFloatSpread(radius), pZ = start.z + THREE.Math.randFloatSpread(radius),
particle = new THREE.Vector3(pX, pY, pZ) particle = new THREE.Vector3(pX, pY, pZ)
particle.lifetime = ball.params.geometry.radius + THREE.Math.randFloat(1, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT; particle.lifetime = Math.random() < 0.5 ? 0 : ball.params.geometry.radius + THREE.Math.randFloat(0.5, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT;
//particle.color.offsetHSL(Math.random(), 0, Math.random());
// add it to the geometry
particles.vertices.push(particle); particles.vertices.push(particle);
particles.colors.push(new THREE.Color(1, 0, 0)); particles.colors.push(new THREE.Color(1, 0, 0));
} }
particles.colorsNeedUpdate = true;
for (var i = 0; i < spriteCount; i++) { for (var i = 0; i < spriteCount; i++) {
var pX = start.x + THREE.Math.randFloatSpread(radius), var pX = start.x + THREE.Math.randFloatSpread(radius),
pY = start.y + THREE.Math.randFloatSpread(radius), pY = start.y + THREE.Math.randFloatSpread(radius),
pZ = start.z + THREE.Math.randFloatSpread(radius), pZ = start.z + THREE.Math.randFloatSpread(radius),
particle = new THREE.Vector3(pX, pY, pZ) particle = new THREE.Vector3(pX, pY, pZ)
particle.lifetime = ball.params.geometry.radius + THREE.Math.randFloat(1, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT; particle.lifetime = Math.random() < 0.5 ? 0 : ball.params.geometry.radius + THREE.Math.randFloat(0.5, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT;
sprites.vertices.push(particle); sprites.vertices.push(particle);
} }
for (var i = 0; i < smokeCount; i++) {
var pX = start.x + THREE.Math.randFloatSpread(radius),
pY = start.y + THREE.Math.randFloat(radius),
pZ = start.z + THREE.Math.randFloatSpread(radius),
particle = new THREE.Vector3(pX, pY, pZ);
particle.lifetime = ball.params.geometry.radius + THREE.Math.randFloat(1, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT * 1.5;
smokes.vertices.push(particle);
}
// create the particle system // create the particle system
var points = WHS.MeshComponent.from(new THREE.Points(particles, pMaterial)); var points = WHS.MeshComponent.from(new THREE.Points(particles, pMaterial));
var spritesPoints = WHS.MeshComponent.from(new THREE.Points(sprites, spriteMaterial)); var spritesPoints = WHS.MeshComponent.from(new THREE.Points(sprites, spriteMaterial));
var smokePoints = WHS.MeshComponent.from(new THREE.Points(smokes, smokeMaterial));
smokePoints.isSmoke = true;
ball.fire = [points, spritesPoints, smokePoints]; ball.fire = [points, spritesPoints];
ball.fire.forEach(fire => { ball.fire.forEach(fire => {
fire.addTo(app); fire.addTo(app);
}) })
@ -154,25 +119,32 @@ ball.checkLife = () => {
); );
fire.geometry.vertices.forEach((v, i) => { fire.geometry.vertices.forEach((v, i) => {
v.y += THREE.Math.randFloat(0, SPREAD_SPEED / 4); let x = v.x + THREE.Math.randFloatSpread(SPREAD_SPEED) + velocity.x * -0.02;
v.x += THREE.Math.randFloatSpread(SPREAD_SPEED) + velocity.x * -0.02; let y = v.y + THREE.Math.randFloat(0, SPREAD_SPEED / 4);
v.z += THREE.Math.randFloatSpread(SPREAD_SPEED) + velocity.z * -0.01; let z = v.z + THREE.Math.randFloatSpread(SPREAD_SPEED) + velocity.z * -0.01;
if (fire.material.vertexColors === THREE.VertexColors) { v.set(x, y, z);
if (!v.isSmoke && fire.material.vertexColors === THREE.VertexColors) {
fire.geometry.colors[i].offsetHSL(7e-4, 0, 0); fire.geometry.colors[i].offsetHSL(7e-4, 0, 0);
} }
if (v.y >= v.lifetime) { if (v.y >= v.lifetime) {
if (fire.isSmoke) { if (!v.isSmoke && fire.material.vertexColors === THREE.VertexColors) {
v.y = FIRE_HEIGHT/3 + THREE.Math.randFloat(0, FIRE_HEIGHT_RANDOMNESS); v.isSmoke = true;
fire.geometry.colors[i].setHex(0x444444)
v.lifetime += FIRE_HEIGHT * 0.5;
} else { } else {
v.y = Math.min(radius, radius * velocity.z / -50); y = Math.min(radius, radius * velocity.z / -50);
}
v.x = 0; x = 0;
v.z = -ball.params.geometry.radius * 3; z = -ball.params.geometry.radius * 3;
v.lifetime = ball.params.geometry.radius + THREE.Math.randFloat(1, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT * (fire.isSmoke ? 1.5 : 1); v.lifetime = ball.params.geometry.radius + THREE.Math.randFloat(1, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT;
v.set(x, y, z);
v.isSmoke = false;
if (fire.material.vertexColors === THREE.VertexColors) { if (fire.material.vertexColors === THREE.VertexColors) {
const r = THREE.Math.randFloat(0.8, 1); const r = THREE.Math.randFloat(0.8, 1);
@ -181,6 +153,7 @@ ball.checkLife = () => {
fire.geometry.colors[i].setRGB(r, g, b); fire.geometry.colors[i].setRGB(r, g, b);
} }
} }
}
}); });
if (fire.material.vertexColors === THREE.VertexColors) { if (fire.material.vertexColors === THREE.VertexColors) {
@ -189,5 +162,19 @@ ball.checkLife = () => {
fire.geometry.verticesNeedUpdate = true; fire.geometry.verticesNeedUpdate = true;
}); });
}); });
const fireLight = new WHS.PointLight({
color: 0xff0000,
intensity: 5,
});
ball.fireLight = fireLight;
ball.add(fireLight);
}()); }());
let hits = 0;
ball.on('collision', (o, v, r, cn) => {
if (o.uuid === ground.native.uuid) return;
rand(audio.impacts).play();
hits++;
GUI.setHits(hits);
});

34
src/controls.js vendored
View File

@ -1,14 +1,18 @@
const DFORCE = 3e3; const DFORCE = 6e3;
const AFORCE = 500; const AFORCE = 500;
const VFORCE = 1e4; const VFORCE = 1e4;
Mousetrap.bind('left', () => { Mousetrap.bind('left', () => {
if (!ball.started) return; if (!ball.started) return;
const v = ball.physics.getLinearVelocity();
ball.physics.setLinearVelocity({ x: Math.min(0, v.x), y: v.y, z: v.z });
ball.physics.applyCentralForce({ x: -DFORCE, y: 0, z: 0 }); ball.physics.applyCentralForce({ x: -DFORCE, y: 0, z: 0 });
}); });
Mousetrap.bind('right', () => { Mousetrap.bind('right', () => {
if (!ball.started) return; if (!ball.started) return;
const v = ball.physics.getLinearVelocity();
ball.physics.setLinearVelocity({ x: Math.max(0, v.x), y: v.y, z: v.z });
ball.physics.applyCentralForce({ x: DFORCE, y: 0, z: 0 }); ball.physics.applyCentralForce({ x: DFORCE, y: 0, z: 0 });
}); });
@ -19,20 +23,20 @@ Mousetrap.bind('up', () => {
//ball.methods.updateVelocity(); //ball.methods.updateVelocity();
}); });
Mousetrap.bind('space', () => { //Mousetrap.bind('space', () => {
if (!ball.started) return; //if (!ball.started) return;
if (ball.physics.data.touches.length) { //if (ball.physics.data.touches.length) {
ball.physics.applyCentralForce({ x: 0, y: VFORCE, z: 0 }); //ball.physics.applyCentralForce({ x: 0, y: VFORCE, z: 0 });
} //}
//ball.speed = Math.min(config.MAX_SPEED, Math.max(config.FINAL_SPEED, ball.speed + config.SPEED_INCREASE)); ////ball.speed = Math.min(config.MAX_SPEED, Math.max(config.FINAL_SPEED, ball.speed + config.SPEED_INCREASE));
//ball.methods.updateVelocity(); ////ball.methods.updateVelocity();
}); //});
Mousetrap.bind('down', () => { //Mousetrap.bind('down', () => {
if (!ball.started) return; //if (!ball.started) return;
ball.physics.applyCentralForce({ x: 0, y: 0, z: AFORCE * 3 }); //ball.physics.applyCentralForce({ x: 0, y: 0, z: AFORCE * 3 });
//ball.speed = Math.min(config.MAX_SPEED, Math.max(config.FINAL_SPEED, ball.speed - config.SPEED_INCREASE)); ////ball.speed = Math.min(config.MAX_SPEED, Math.max(config.FINAL_SPEED, ball.speed - config.SPEED_INCREASE));
//ball.methods.updateVelocity(); ////ball.methods.updateVelocity();
}); //});

View File

@ -3,6 +3,7 @@ const GUI = {
score: document.getElementById('score'), score: document.getElementById('score'),
speed: document.getElementById('speed'), speed: document.getElementById('speed'),
time: document.getElementById('time'), time: document.getElementById('time'),
hits: document.getElementById('hits'),
}, },
setScore(s) { setScore(s) {
GUI.elements.score.textContent = Math.floor(s).toString().padStart(3, '0'); GUI.elements.score.textContent = Math.floor(s).toString().padStart(3, '0');
@ -13,6 +14,9 @@ const GUI = {
setStartingTime() { setStartingTime() {
GUI.startingTime = moment(); GUI.startingTime = moment();
}, },
setHits(s) {
GUI.elements.hits.textContent = Math.floor(s).toString().padStart(2, '0');
},
tickTime() { tickTime() {
const now = moment(); const now = moment();
const diff = moment.duration(now.diff(GUI.startingTime)); const diff = moment.duration(now.diff(GUI.startingTime));

View File

@ -1,36 +1,127 @@
function level0() { function level0() {
function jumpBoard(x, z) { const step = rad(10);
const cylinder = new WHS.Cylinder({ const tan = Math.tan(step);
const BOX_SIZE = 100;
function add({ x, y, z } = {}, { width, depth, height } = {}, rotation = {}) {
y = y || (tan * z) + BOX_SIZE/2;
width = width || BOX_SIZE;
height = height || BOX_SIZE;
depth = depth || BOX_SIZE;
rotation.x = rotation.x || -step;
const box = new WHS.Box({
geometry: { geometry: {
radiusTop: 50, width,
radiusBottom: 1, depth,
radiusSegments: 3, height,
heightSegments: 1,
height: 2,
}, },
material: new THREE.MeshPhongMaterial({ material: new THREE.MeshPhongMaterial({
color: 0xffff00, color: 0xffffff,
}), }),
position: [x, 0, z], position: [x, y, z],
rotation: [Math.PI, rad(-35), 0],
rotation,
modules: [ modules: [
new PHYSICS.CylinderModule({ new PHYSICS.BoxModule({
mass: 0, mass: 0,
friction: 1,
restitution: 0,
}) })
] ]
}); });
//cylinder.addTo(app); box.addTo(app);
ground.add(cylinder); return box;
} }
//jumpBoard(5, -50); const SPACE_UNIT = 250;
//jumpBoard(5, -100);
// const patterns = [{
//ring({ x: 0, y: 10, z: -150 }); items: [3,4,5],
randoms: {
startX() {
return Math.random() > 0.5 ? 200 : -200;
},
},
x(i, props) {
return props.startX + i * 50 * (props.startX === 200 ? -1 : 1);
},
z(i) {
return i * -BOX_SIZE;
},
}, {
items: [3,4,6],
props: {
side: 150,
},
rot: {
y(i, props) {
if (i % 3 == 0) {
return THREE.Math.randFloatSpread(Math.PI);
}
return 0;
}
},
size(i, props) {
return THREE.Math.randInt(BOX_SIZE * 0.8, BOX_SIZE * 1.2);
},
x(i, props) {
if (i % 3 == 0) {
return 0;
} else {
return i % 3 == 1 ? props.side : -props.side;
}
},
z(i) {
if (i % 3 == 0) {
return i * -SPACE_UNIT;
} else {
return i % 3 == 1 ? i * -SPACE_UNIT : (i - 1) * -SPACE_UNIT;
}
}
}];
const applyPattern = (sz, { items, x, z, rot, props, randoms, space, size } = {}) => {
props = props || {};
randoms = randoms || {};
space = space || SPACE_UNIT;
rot = rot || {};
const count = rand(items);
const rs = {};
Object.entries(randoms).forEach(([key, value]) => {
rs[key] = value();
});
Object.assign(props, rs);
let zz;
for (let i = 0; i < count; i++) {
zz = sz + z(i, props);
const rotation = {};
Object.entries(rot).forEach(([key, value]) => {
rotation[key] = value(i, props);
});
let boxSize = BOX_SIZE;
if (size) {
boxSize = size(i, props);
}
add({ x: x(i, props), z: zz }, { width: boxSize, height: boxSize, depth: boxSize }, rotation);
}
return zz - space;
}
let sz = -300;
while (-sz < ground.SIZE) {
const pattern = rand(patterns);
sz = applyPattern(sz, pattern);
}
} }
level0();

View File

@ -40,9 +40,13 @@ const entrance = () => {
GUI.setStartingTime(); GUI.setStartingTime();
const diff = camera.position.y - ball.position.y; const diff = camera.position.y - ball.position.y;
ball.started = true; ball.started = true;
level0();
audio.rumble.play();
app.physics.addEventListener('update', function() { app.physics.addEventListener('update', function() {
camera.position.set(camera.position.x, ball.position.y + diff, ball.position.z + 130); //camera.position.set(camera.position.x, ball.position.y + diff, ball.position.z + 130);
camera.position.set(ball.position.x, ball.position.y + diff, ball.position.z + 130);
camera.native.lookAt(ball.position); camera.native.lookAt(ball.position);
directionalLight.position.set(directionalLight.position.x, directionalLight.position.y, ball.position.z - 300); directionalLight.position.set(directionalLight.position.x, directionalLight.position.y, ball.position.z - 300);
GUI.setScore(-ball.position.z / 100); GUI.setScore(-ball.position.z / 100);

View File

@ -1,2 +1,4 @@
const rad = (deg) => deg * Math.PI / 180; const rad = (deg) => deg * Math.PI / 180;
const rand = (items) => items[Math.floor(Math.random() * items.length)];

View File

@ -34,7 +34,7 @@ let ground;
(function groundinit() { (function groundinit() {
const WIDTH = 500; const WIDTH = 500;
const SIZE = 1e5; const SIZE = 1e5;
const mesh = new WHS.Plane({ ground = new WHS.Plane({
geometry: { geometry: {
width: WIDTH, width: WIDTH,
height: SIZE, height: SIZE,
@ -51,12 +51,14 @@ let ground;
}), }),
new PHYSICS.PlaneModule({ new PHYSICS.PlaneModule({
mass: 0, mass: 0,
restitution: 0,
}), }),
], ],
material: new THREE.MeshPhongMaterial({ color: 0xffffff }) material: new THREE.MeshPhongMaterial({ color: 0xffffff })
}); });
ground = mesh; ground.SIZE = SIZE;
ground.addTo(app); ground.addTo(app);
}()); }());