feat: sound, generator, etc.
This commit is contained in:
parent
e983346ffc
commit
d6eaa09346
BIN
assets/audio/fire.wav
Normal file
BIN
assets/audio/fire.wav
Normal file
Binary file not shown.
BIN
assets/audio/impact-0.wav
Normal file
BIN
assets/audio/impact-0.wav
Normal file
Binary file not shown.
BIN
assets/audio/impact-1.wav
Normal file
BIN
assets/audio/impact-1.wav
Normal file
Binary file not shown.
BIN
assets/audio/impact-2.wav
Normal file
BIN
assets/audio/impact-2.wav
Normal file
Binary file not shown.
BIN
assets/audio/music.mp3
Normal file
BIN
assets/audio/music.mp3
Normal file
Binary file not shown.
BIN
assets/audio/rumble.wav
Normal file
BIN
assets/audio/rumble.wav
Normal file
Binary file not shown.
@ -31,7 +31,9 @@
|
||||
<script src='lib/physics-module.js'></script>
|
||||
<script src='lib/ammo.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/color-scheme.min.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id='gui'>
|
||||
@ -44,10 +46,14 @@
|
||||
<p>
|
||||
Score: <span id='score'>000</span>
|
||||
</p>
|
||||
<p>
|
||||
Hits: <span id='hits'>00</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<script src='src/utils.js'></script>
|
||||
<script src='src/config.js'></script>
|
||||
<script src='src/audio.js'></script>
|
||||
<script src='src/gui.js'></script>
|
||||
<script src='src/objects.js'></script>
|
||||
<script src='src/world.js'></script>
|
||||
|
1
lib/color-scheme.min.js
vendored
Normal file
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
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
30
src/audio.js
Normal 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();
|
111
src/ball.js
111
src/ball.js
@ -1,10 +1,5 @@
|
||||
const RADIUS = 10;
|
||||
const SEGMENTS = 40;
|
||||
const material = new THREE.MeshLambertMaterial({
|
||||
color: 0xFFFFFF,
|
||||
emissive: 0xFF0000,
|
||||
emissiveIntensity: 0.4,
|
||||
});
|
||||
const ballPhysics = new PHYSICS.SphereModule({
|
||||
mass: 2,
|
||||
friction: 0.5,
|
||||
@ -20,7 +15,11 @@ const ball = new WHS.Sphere({
|
||||
z: -30,
|
||||
y: 10
|
||||
},
|
||||
material,
|
||||
material: new THREE.MeshLambertMaterial({
|
||||
color: 0xFFFFFF,
|
||||
emissive: 0xFF0000,
|
||||
emissiveIntensity: 0.4,
|
||||
}),
|
||||
|
||||
modules: [
|
||||
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.fireLight = fireLight;
|
||||
|
||||
ball.addTo(app);
|
||||
ball.add(fireLight);
|
||||
|
||||
camera.native.target = ball.native;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -79,66 +73,37 @@ ball.checkLife = () => {
|
||||
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 start = ball.position;
|
||||
for (var p = 0; p < particleCount; p++) {
|
||||
|
||||
// create a particle with random
|
||||
// position values, -250 -> 250
|
||||
var pX = start.x + THREE.Math.randFloatSpread(radius),
|
||||
pY = start.y + THREE.Math.randFloatSpread(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;
|
||||
//particle.color.offsetHSL(Math.random(), 0, Math.random());
|
||||
particle.lifetime = Math.random() < 0.5 ? 0 : ball.params.geometry.radius + THREE.Math.randFloat(0.5, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT;
|
||||
|
||||
// add it to the geometry
|
||||
particles.vertices.push(particle);
|
||||
particles.colors.push(new THREE.Color(1, 0, 0));
|
||||
}
|
||||
|
||||
particles.colorsNeedUpdate = true;
|
||||
|
||||
for (var i = 0; i < spriteCount; i++) {
|
||||
var pX = start.x + THREE.Math.randFloatSpread(radius),
|
||||
pY = start.y + THREE.Math.randFloatSpread(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;
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
var points = WHS.MeshComponent.from(new THREE.Points(particles, pMaterial));
|
||||
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 => {
|
||||
fire.addTo(app);
|
||||
})
|
||||
@ -154,31 +119,39 @@ ball.checkLife = () => {
|
||||
);
|
||||
|
||||
fire.geometry.vertices.forEach((v, i) => {
|
||||
v.y += THREE.Math.randFloat(0, SPREAD_SPEED / 4);
|
||||
v.x += THREE.Math.randFloatSpread(SPREAD_SPEED) + velocity.x * -0.02;
|
||||
v.z += THREE.Math.randFloatSpread(SPREAD_SPEED) + velocity.z * -0.01;
|
||||
let x = v.x + THREE.Math.randFloatSpread(SPREAD_SPEED) + velocity.x * -0.02;
|
||||
let y = v.y + THREE.Math.randFloat(0, SPREAD_SPEED / 4);
|
||||
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);
|
||||
}
|
||||
|
||||
if (v.y >= v.lifetime) {
|
||||
if (fire.isSmoke) {
|
||||
v.y = FIRE_HEIGHT/3 + THREE.Math.randFloat(0, FIRE_HEIGHT_RANDOMNESS);
|
||||
if (!v.isSmoke && fire.material.vertexColors === THREE.VertexColors) {
|
||||
v.isSmoke = true;
|
||||
fire.geometry.colors[i].setHex(0x444444)
|
||||
v.lifetime += FIRE_HEIGHT * 0.5;
|
||||
} else {
|
||||
v.y = Math.min(radius, radius * velocity.z / -50);
|
||||
}
|
||||
y = Math.min(radius, radius * velocity.z / -50);
|
||||
|
||||
v.x = 0;
|
||||
v.z = -ball.params.geometry.radius * 3;
|
||||
x = 0;
|
||||
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;
|
||||
|
||||
if (fire.material.vertexColors === THREE.VertexColors) {
|
||||
const r = THREE.Math.randFloat(0.8, 1);
|
||||
const g = THREE.Math.randFloat(0, 0.2);
|
||||
const b = THREE.Math.randFloat(0, 0.2);
|
||||
fire.geometry.colors[i].setRGB(r, g, b);
|
||||
v.set(x, y, z);
|
||||
|
||||
v.isSmoke = false;
|
||||
|
||||
if (fire.material.vertexColors === THREE.VertexColors) {
|
||||
const r = THREE.Math.randFloat(0.8, 1);
|
||||
const g = THREE.Math.randFloat(0, 0.2);
|
||||
const b = THREE.Math.randFloat(0, 0.2);
|
||||
fire.geometry.colors[i].setRGB(r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -189,5 +162,19 @@ ball.checkLife = () => {
|
||||
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
34
src/controls.js
vendored
@ -1,14 +1,18 @@
|
||||
const DFORCE = 3e3;
|
||||
const DFORCE = 6e3;
|
||||
const AFORCE = 500;
|
||||
const VFORCE = 1e4;
|
||||
|
||||
Mousetrap.bind('left', () => {
|
||||
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 });
|
||||
});
|
||||
|
||||
Mousetrap.bind('right', () => {
|
||||
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 });
|
||||
});
|
||||
|
||||
@ -19,20 +23,20 @@ Mousetrap.bind('up', () => {
|
||||
//ball.methods.updateVelocity();
|
||||
});
|
||||
|
||||
Mousetrap.bind('space', () => {
|
||||
if (!ball.started) return;
|
||||
if (ball.physics.data.touches.length) {
|
||||
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.methods.updateVelocity();
|
||||
});
|
||||
//Mousetrap.bind('space', () => {
|
||||
//if (!ball.started) return;
|
||||
//if (ball.physics.data.touches.length) {
|
||||
//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.methods.updateVelocity();
|
||||
//});
|
||||
|
||||
Mousetrap.bind('down', () => {
|
||||
if (!ball.started) return;
|
||||
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.methods.updateVelocity();
|
||||
});
|
||||
//Mousetrap.bind('down', () => {
|
||||
//if (!ball.started) return;
|
||||
//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.methods.updateVelocity();
|
||||
//});
|
||||
|
||||
|
||||
|
@ -3,6 +3,7 @@ const GUI = {
|
||||
score: document.getElementById('score'),
|
||||
speed: document.getElementById('speed'),
|
||||
time: document.getElementById('time'),
|
||||
hits: document.getElementById('hits'),
|
||||
},
|
||||
setScore(s) {
|
||||
GUI.elements.score.textContent = Math.floor(s).toString().padStart(3, '0');
|
||||
@ -13,6 +14,9 @@ const GUI = {
|
||||
setStartingTime() {
|
||||
GUI.startingTime = moment();
|
||||
},
|
||||
setHits(s) {
|
||||
GUI.elements.hits.textContent = Math.floor(s).toString().padStart(2, '0');
|
||||
},
|
||||
tickTime() {
|
||||
const now = moment();
|
||||
const diff = moment.duration(now.diff(GUI.startingTime));
|
||||
|
@ -1,36 +1,127 @@
|
||||
function level0() {
|
||||
function jumpBoard(x, z) {
|
||||
const cylinder = new WHS.Cylinder({
|
||||
const step = rad(10);
|
||||
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: {
|
||||
radiusTop: 50,
|
||||
radiusBottom: 1,
|
||||
radiusSegments: 3,
|
||||
heightSegments: 1,
|
||||
height: 2,
|
||||
width,
|
||||
depth,
|
||||
height,
|
||||
},
|
||||
|
||||
material: new THREE.MeshPhongMaterial({
|
||||
color: 0xffff00,
|
||||
color: 0xffffff,
|
||||
}),
|
||||
|
||||
position: [x, 0, z],
|
||||
rotation: [Math.PI, rad(-35), 0],
|
||||
position: [x, y, z],
|
||||
|
||||
rotation,
|
||||
|
||||
modules: [
|
||||
new PHYSICS.CylinderModule({
|
||||
new PHYSICS.BoxModule({
|
||||
mass: 0,
|
||||
friction: 1,
|
||||
restitution: 0,
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
//cylinder.addTo(app);
|
||||
ground.add(cylinder);
|
||||
box.addTo(app);
|
||||
return box;
|
||||
}
|
||||
|
||||
//jumpBoard(5, -50);
|
||||
//jumpBoard(5, -100);
|
||||
//
|
||||
//ring({ x: 0, y: 10, z: -150 });
|
||||
const SPACE_UNIT = 250;
|
||||
|
||||
const patterns = [{
|
||||
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();
|
||||
|
@ -40,9 +40,13 @@ const entrance = () => {
|
||||
GUI.setStartingTime();
|
||||
const diff = camera.position.y - ball.position.y;
|
||||
ball.started = true;
|
||||
level0();
|
||||
|
||||
audio.rumble.play();
|
||||
|
||||
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);
|
||||
directionalLight.position.set(directionalLight.position.x, directionalLight.position.y, ball.position.z - 300);
|
||||
GUI.setScore(-ball.position.z / 100);
|
||||
|
@ -1,2 +1,4 @@
|
||||
const rad = (deg) => deg * Math.PI / 180;
|
||||
|
||||
const rand = (items) => items[Math.floor(Math.random() * items.length)];
|
||||
|
||||
|
@ -34,7 +34,7 @@ let ground;
|
||||
(function groundinit() {
|
||||
const WIDTH = 500;
|
||||
const SIZE = 1e5;
|
||||
const mesh = new WHS.Plane({
|
||||
ground = new WHS.Plane({
|
||||
geometry: {
|
||||
width: WIDTH,
|
||||
height: SIZE,
|
||||
@ -51,12 +51,14 @@ let ground;
|
||||
}),
|
||||
new PHYSICS.PlaneModule({
|
||||
mass: 0,
|
||||
restitution: 0,
|
||||
}),
|
||||
],
|
||||
material: new THREE.MeshPhongMaterial({ color: 0xffffff })
|
||||
});
|
||||
|
||||
ground = mesh;
|
||||
ground.SIZE = SIZE;
|
||||
|
||||
ground.addTo(app);
|
||||
}());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user