mirror of
https://gitlab.com/arnekeller/tunnel-racer.git
synced 2024-11-10 01:30:37 +00:00
434 lines
10 KiB
JavaScript
434 lines
10 KiB
JavaScript
const vertexShaderGlow = `
|
|
uniform vec3 viewVector;
|
|
uniform float c;
|
|
uniform float p;
|
|
varying float intensity;
|
|
varying vec3 vColor;
|
|
|
|
vec3 hsv2rgb(vec3 c) {
|
|
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
|
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
|
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
|
}
|
|
|
|
void main()
|
|
{
|
|
vec3 vNormal = normalize( normalMatrix * normal );
|
|
vec3 vNormel = normalize( normalMatrix * viewVector );
|
|
intensity = pow( c - dot(vNormal, vNormel), p );
|
|
|
|
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
|
vColor = hsv2rgb(vec3(fract(gl_Position[2] / 270.0), 0.7, 0.7));
|
|
//vColor = vec3((sin(position[2] / 360.0) + 1.0) * 0.5, (sin(position[2] / 1000.0) + 1.0) * 0.5, (sin(position[2] / 70.0) + 1.0) * 0.5);
|
|
//vColor = vec3(0.5, 0, 0.5);
|
|
}
|
|
`;
|
|
|
|
const fragmentShaderGlow = `
|
|
varying vec3 vColor;
|
|
varying float intensity;
|
|
void main()
|
|
{
|
|
vec3 glow = vColor * intensity;
|
|
gl_FragColor = vec4( glow, 1.0 );
|
|
}
|
|
`;
|
|
|
|
const fragmentShader = `
|
|
#include <common>
|
|
|
|
uniform vec3 iResolution;
|
|
uniform float iTime;
|
|
uniform sampler2D iChannel0;
|
|
|
|
// By Daedelus: https://www.shadertoy.com/user/Daedelus
|
|
// license: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
|
|
#define TIMESCALE 0.25
|
|
#define TILES 8
|
|
#define COLOR 0.7, 1.6, 2.8
|
|
|
|
varying vec2 vUv;
|
|
varying float z;
|
|
|
|
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
|
{
|
|
vec2 uv = fragCoord.xy / iResolution.xy;
|
|
uv.x *= iResolution.x / iResolution.y;
|
|
|
|
vec4 noise = texture2D(iChannel0, floor(uv * float(TILES)) / float(TILES));
|
|
float p = 1.0 - mod(noise.r + noise.g + noise.b + (iTime + z) * float(TIMESCALE), 1.0);
|
|
p = min(max(p * 3.0 - 1.8, 0.1), 2.0);
|
|
|
|
vec2 r = mod(uv * float(TILES), 1.0);
|
|
r = vec2(pow(r.x - 0.5, 2.0), pow(r.y - 0.5, 2.0));
|
|
p *= 1.0 - pow(min(1.0, 12.0 * dot(r, r)), 2.0);
|
|
|
|
fragColor = vec4(COLOR, 1.0) * p;
|
|
}
|
|
|
|
void main() {
|
|
mainImage(gl_FragColor, vUv * iResolution.xy);
|
|
}
|
|
`;
|
|
const vertexShader = `
|
|
varying vec2 vUv;
|
|
varying float z;
|
|
void main() {
|
|
vUv = uv;
|
|
z = -position[1];
|
|
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
|
}
|
|
`;
|
|
|
|
const diameterOfTunnel = 100;
|
|
const spawnInterval = 40;
|
|
let lastSpawned = 0;
|
|
|
|
var gn;
|
|
var headSet = false;
|
|
var betaReference = 0.0;
|
|
var leftRightMove = 0.0;
|
|
var upDownMove = 0.0;
|
|
|
|
var speed = 5.0;
|
|
let score = 0;
|
|
let removed = 0;
|
|
let running = false;
|
|
let frameCount = 0;
|
|
|
|
function logger(text) {
|
|
console.log(text);
|
|
}
|
|
|
|
function init_gn() {
|
|
var args = {
|
|
logger: logger
|
|
};
|
|
|
|
gn = new GyroNorm();
|
|
|
|
gn.init(args).then(function() {
|
|
var isAvailable = gn.isAvailable();
|
|
if (!isAvailable.deviceOrientationAvailable) {
|
|
console.log({
|
|
message: 'Device orientation is not available.'
|
|
});
|
|
}
|
|
|
|
if (!isAvailable.accelerationAvailable) {
|
|
console.log({
|
|
message: 'Device acceleration is not available.'
|
|
});
|
|
}
|
|
|
|
if (!isAvailable.accelerationIncludingGravityAvailable) {
|
|
console.log({
|
|
message: 'Device acceleration incl. gravity is not available.'
|
|
});
|
|
}
|
|
|
|
if (!isAvailable.rotationRateAvailable) {
|
|
console.log({
|
|
message: 'Device rotation rate is not available.'
|
|
});
|
|
}
|
|
|
|
start_gn();
|
|
}).catch(function(e) {
|
|
console.error(e);
|
|
});
|
|
|
|
document.addEventListener('keydown', e => {
|
|
if (e.key === "ArrowLeft") {
|
|
leftRightMove = -10.0;
|
|
} else if (e.key === "ArrowRight") {
|
|
leftRightMove = 10.0;
|
|
} else if (e.key === "ArrowDown") {
|
|
upDownMove = -10.0;
|
|
} else if (e.key === "ArrowUp") {
|
|
upDownMove = 10.0;
|
|
}
|
|
});
|
|
document.addEventListener('keyup', e => {
|
|
if (e.key === "ArrowLeft") {
|
|
leftRightMove = 0;
|
|
} else if (e.key === "ArrowRight") {
|
|
leftRightMove = 0;
|
|
} else if (e.key === "ArrowDown") {
|
|
upDownMove = 0;
|
|
} else if (e.key === "ArrowUp") {
|
|
upDownMove = 0;
|
|
}
|
|
});
|
|
}
|
|
|
|
function stop_gn() {
|
|
gn.stop();
|
|
}
|
|
|
|
function start_gn() {
|
|
gn.start(gnCallBack);
|
|
}
|
|
|
|
function gnCallBack(data) {
|
|
if (!headSet) {
|
|
betaReference = data.do.beta;
|
|
} else {
|
|
let leftRight = data.do.alpha > 180.0 ? "right" : "left";
|
|
leftRightMove = data.do.alpha > 180.0 ? 360.0 - data.do.alpha : -data.do.alpha;
|
|
let upDown = data.do.beta > betaReference ? "up" : "down";
|
|
upDownMove = data.do.beta - betaReference;
|
|
}
|
|
}
|
|
|
|
function set_head_gn() {
|
|
gn.setHeadDirection();
|
|
headSet = true;
|
|
}
|
|
|
|
document.getElementById("set_head").addEventListener("click", () => {
|
|
document.getElementById("log").innerHTML += "clicked button<br>";
|
|
set_head_gn();
|
|
});
|
|
|
|
init_gn();
|
|
|
|
import * as THREE from './three.module.js';
|
|
let scene, renderer;
|
|
const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 10000);
|
|
|
|
const geometrySphere = new THREE.SphereGeometry(11, 32, 32);
|
|
const materialCube = new THREE.MeshBasicMaterial();
|
|
const customMaterial = new THREE.ShaderMaterial({
|
|
uniforms: {
|
|
"c": {
|
|
type: "f",
|
|
value: 0.8
|
|
},
|
|
"p": {
|
|
type: "f",
|
|
value: 2.4
|
|
},
|
|
viewVector: {
|
|
type: "v3",
|
|
value: camera.position
|
|
}
|
|
},
|
|
vertexShader: vertexShaderGlow,
|
|
//vertexColors: true,
|
|
fragmentShader: fragmentShaderGlow,
|
|
side: THREE.FrontSide,
|
|
blending: THREE.AdditiveBlending,
|
|
transparent: true
|
|
});
|
|
|
|
|
|
const loader = new THREE.TextureLoader();
|
|
const texture = loader.load('/bayer.png');
|
|
texture.minFilter = THREE.NearestFilter;
|
|
texture.magFilter = THREE.NearestFilter;
|
|
texture.wrapS = THREE.RepeatWrapping;
|
|
texture.wrapT = THREE.RepeatWrapping;
|
|
const uniforms = {
|
|
iTime: {
|
|
value: 0
|
|
},
|
|
iResolution: {
|
|
value: new THREE.Vector3(1, 1, 1)
|
|
},
|
|
iChannel0: {
|
|
value: texture
|
|
},
|
|
};
|
|
const material = new THREE.ShaderMaterial({
|
|
vertexShader,
|
|
fragmentShader,
|
|
uniforms,
|
|
});
|
|
const borderGeometry = new THREE.PlaneGeometry(1, 1);
|
|
|
|
const cubes = [];
|
|
const borders = [];
|
|
|
|
init();
|
|
animate();
|
|
|
|
document.getElementById("start").onclick = () => {
|
|
document.getElementById("start").style.zIndex = -10;
|
|
document.getElementById("start").style.visibility = "hidden";
|
|
speed = 5.0;
|
|
score = 0;
|
|
for (let i = 0; i < cubes.length; i++) {
|
|
scene.remove(cubes[i]);
|
|
}
|
|
cubes.length = 0;
|
|
removed = 0;
|
|
running = true;
|
|
}
|
|
|
|
function gameOver() {
|
|
running = false;
|
|
document.getElementById("score").innerText = document.getElementById("score").innerText + " - Game Over!";
|
|
document.getElementById("start").style.zIndex = 0;
|
|
document.getElementById("start").style.visibility = "visible";
|
|
document.getElementById("start").innerText = "Restart";
|
|
}
|
|
|
|
function init() {
|
|
|
|
//
|
|
|
|
scene = new THREE.Scene();
|
|
|
|
camera.position.set(0, 10, 25);
|
|
camera.lookAt(scene.position);
|
|
|
|
const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
|
|
scene.add(ambientLight);
|
|
|
|
const pointLight = new THREE.PointLight(0xffffff, 0.8);
|
|
camera.add(pointLight);
|
|
scene.add(camera);
|
|
|
|
|
|
renderer = new THREE.WebGLRenderer();
|
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
document.body.appendChild(renderer.domElement);
|
|
|
|
|
|
window.addEventListener('resize', onWindowResize);
|
|
|
|
}
|
|
|
|
function onWindowResize() {
|
|
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
|
|
}
|
|
|
|
function animate() {
|
|
|
|
requestAnimationFrame(animate);
|
|
|
|
uniforms.iTime.value = frameCount / 60.0;
|
|
frameCount++;
|
|
|
|
if (running) {
|
|
|
|
for (let i = 0; i < cubes.length; i++) {
|
|
if (cubes[i].material !== materialCube) {
|
|
continue;
|
|
}
|
|
let x1 = cubes[i].position.x;
|
|
let y1 = cubes[i].position.y;
|
|
let z1 = cubes[i].position.z;
|
|
let x2 = camera.position.x;
|
|
let y2 = camera.position.y;
|
|
let z2 = camera.position.z;
|
|
if (z2 > z1) {
|
|
// account for very fast speeds
|
|
// (prevents clipping through obstacles)
|
|
z2 = Math.max(z1, z2 - speed);
|
|
}
|
|
let dist_squared = (x1 - x2) ** 2 + (y1 - y2) ** 2 + (z1 - z2) ** 2;
|
|
if (dist_squared <= (cubes[i].geometry.parameters.radius * cubes[i].scale.x) ** 2) {
|
|
gameOver();
|
|
break;
|
|
}
|
|
}
|
|
|
|
camera.position.z -= 0.5 * speed;
|
|
speed += 0.01;
|
|
camera.position.x += 0.1 * leftRightMove;
|
|
camera.position.y += 0.1 * upDownMove;
|
|
|
|
while (camera.position.z < lastSpawned - spawnInterval) {
|
|
lastSpawned -= spawnInterval;
|
|
|
|
// randomly spawn large spheres
|
|
let scale = Math.random() < 0.1 ? 5.0 : 1.0;
|
|
|
|
const meshCube = new THREE.Mesh(geometrySphere, materialCube);
|
|
meshCube.position.z = camera.position.z - speed * 210;
|
|
meshCube.position.x = (2 * Math.random() - 1.0) * diameterOfTunnel;
|
|
meshCube.position.y = (2 * Math.random() - 1.0) * 0.9 * diameterOfTunnel;
|
|
meshCube.scale.multiplyScalar(scale);
|
|
|
|
const outlineMesh = new THREE.Mesh(geometrySphere, customMaterial);
|
|
outlineMesh.scale.multiplyScalar(scale * 1.17);
|
|
outlineMesh.position.z = meshCube.position.z;
|
|
outlineMesh.position.x = meshCube.position.x;
|
|
outlineMesh.position.y = meshCube.position.y;
|
|
meshCube.add(outlineMesh);
|
|
scene.add(outlineMesh);
|
|
cubes.push(outlineMesh);
|
|
|
|
scene.add(meshCube);
|
|
cubes.push(meshCube);
|
|
}
|
|
|
|
}
|
|
renderer.render(scene, camera);
|
|
|
|
|
|
for (let i = 0; i < cubes.length; i++) {
|
|
if (cubes[i].position.z > camera.position.z + cubes[i].geometry.parameters.radius * cubes[i].scale.x) {
|
|
scene.remove(cubes[i]);
|
|
cubes.splice(i, 1);
|
|
i--;
|
|
removed++;
|
|
}
|
|
}
|
|
if (removed >= 2 || score == 0) {
|
|
score += removed / 2;
|
|
removed = removed % 2;
|
|
document.getElementById("score").innerText = score;
|
|
}
|
|
if (borders.length == 0 || borders[borders.length - 1].position.z - camera.position.z > -1200) {
|
|
const newZ = borders.length == 0 ? -200 : borders[borders.length - 1].position.z - diameterOfTunnel * 2;
|
|
|
|
let border = new THREE.Mesh(borderGeometry, material);
|
|
//border.position.x = -70;
|
|
border.position.y = -diameterOfTunnel;
|
|
border.rotation.x = -Math.PI / 2;
|
|
border.position.z = newZ;
|
|
border.scale.multiplyScalar(diameterOfTunnel * 2);
|
|
scene.add(border);
|
|
borders.push(border);
|
|
|
|
border = new THREE.Mesh(borderGeometry, material);
|
|
border.position.y = diameterOfTunnel;
|
|
border.rotation.x = Math.PI / 2;
|
|
border.position.z = newZ;
|
|
border.scale.multiplyScalar(diameterOfTunnel * 2);
|
|
scene.add(border);
|
|
borders.push(border);
|
|
|
|
border = new THREE.Mesh(borderGeometry, material);
|
|
border.position.x = -diameterOfTunnel;
|
|
border.rotation.y = Math.PI / 2;
|
|
border.position.z = newZ;
|
|
border.scale.multiplyScalar(diameterOfTunnel * 2);
|
|
scene.add(border);
|
|
borders.push(border);
|
|
|
|
border = new THREE.Mesh(borderGeometry, material);
|
|
border.position.x = diameterOfTunnel;
|
|
border.rotation.y = -Math.PI / 2;
|
|
border.position.z = newZ;
|
|
border.scale.multiplyScalar(diameterOfTunnel * 2);
|
|
scene.add(border);
|
|
borders.push(border);
|
|
}
|
|
for (let i = 0; i < borders.length; i++) {
|
|
if (borders[i].position.z > camera.position.z + diameterOfTunnel + 50) {
|
|
scene.remove(borders[i]);
|
|
borders.splice(i, 1);
|
|
i--;
|
|
}
|
|
}
|
|
} |