diff --git a/src/game/game.ts b/src/game/game.ts index aa7b21d..91b41e6 100644 --- a/src/game/game.ts +++ b/src/game/game.ts @@ -84,7 +84,25 @@ class Game implements ThreeScene { init = (lev: LevelInitializer) => { this.scene = new THREE.Scene(); - this.scene.background = new THREE.Color(0x0a0a12); // Dark blue-gray instead of pure black + + // Gradient background: orange → pink → purple → dark + const canvas = document.createElement('canvas'); + canvas.width = 2; + canvas.height = 512; + const ctx = canvas.getContext('2d')!; + const gradient = ctx.createLinearGradient(0, 0, 0, 512); + gradient.addColorStop(0, '#0a0515'); // dark at top + gradient.addColorStop(0.4, '#2a1040'); // purple + gradient.addColorStop(0.7, '#ff4080'); // pink + gradient.addColorStop(1, '#ff6b35'); // orange at bottom + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, 2, 512); + const bgTexture = new THREE.CanvasTexture(canvas); + bgTexture.magFilter = THREE.LinearFilter; + this.scene.background = bgTexture; + + // Fog to blend distant terrain into sky + this.scene.fog = new THREE.FogExp2(0x2a1040, 0.008); // Groundplane initGround(this.scene); @@ -97,22 +115,22 @@ class Game implements ThreeScene { this.player = new Player(this.scene, { ...level, voxels: mutableVoxels }) initBoxes(this.scene, mutableVoxels, this.player); - // Lights + // Lights - Synthwave sunset style // Subtle ambient light for base visibility - this.scene.add(new THREE.AmbientLight(0x2a2a3a, 2.4)); + this.scene.add(new THREE.AmbientLight(0x3a1a4a, 2.4)); - // Improved hemisphere light for mood - this.scene.add(new THREE.HemisphereLight(0x4a4a5a, 0x1a1a2a, 10)); + // Hemisphere light: orange sky, purple ground + this.scene.add(new THREE.HemisphereLight(0xff6b35, 0x2a1040, 2)); - // Dramatic point lights with better positioning - let pl = new THREE.PointLight(0x8090ff, 10, 0,0.3); + // Dramatic point lights with warm pink tones + let pl = new THREE.PointLight(0xffffff, 10, 0,0.3); pl.position.set(40, -40, 25); pl.castShadow = true; pl.shadow.mapSize.width = 2048; pl.shadow.mapSize.height = 2048; pl.shadow.bias = -0.001; this.scene.add(pl); - let pl1 = new THREE.PointLight(0xff9080, 10, 0,0.3); + let pl1 = new THREE.PointLight(0xffffff, 10, 0,0.3); pl1.position.set(-40, 40, 25); pl1.castShadow = true; pl1.shadow.mapSize.width = 2048; @@ -214,6 +232,10 @@ function initBoxes(scene: THREE.Scene, voxels: Array, player: Player) { mesh.castShadow = true; mesh.receiveShadow = true; mesh.position.set(x, y, z); + + const edges = new THREE.EdgesGeometry(geometry); + const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0x000000, linewidth: 2 })); + mesh.add(line); console.log(player.voxelStandingOn) if (idx != player.level.playerPos.voxelIdx) { mesh.position.setZ(10) @@ -249,69 +271,84 @@ function initGround(scene: THREE.Scene) { var data = generateHeight(128, 128); let vertices = planeGeometry.attributes.position.array; - - console.log(vertices.length) for (var i = 0, j = 0, l = vertices.length; i < l; i++ , j += 3) { - // console.log(vertices[j + 1]) (vertices[j + 1] as number) = data[i]; } + planeGeometry.computeVertexNormals(); var planeWire = new THREE.Mesh(planeGeometry, - new THREE.MeshPhongMaterial({ - color: 0x320032, + color: 0xff00ff, specular: 0x0, - // shininess: 0xffffff, - wireframeLinewidth: 3, + wireframeLinewidth: 2, wireframe: true - - }) - ); - var planeSolid = new THREE.Mesh(planeGeometry, - - new THREE.MeshPhongMaterial({ - color: 0x030211, - specular: 0x0, - shininess: 0x0, - }) ); planeWire.position.y = 0; planeWire.rotateX(Math.PI * 0.5) + + var planeSolid = new THREE.Mesh(planeGeometry, + new THREE.MeshPhongMaterial({ + color: 0x0a0510, + specular: 0x0, + shininess: 0x0, + }) + ); planeSolid.position.y = 0; planeSolid.rotateX(Math.PI * 0.5) planeSolid.receiveShadow = true; - - var grid = new THREE.GridHelper(400, 30, 0x99bbff, 0x99bbff); + var grid = new THREE.GridHelper(200, 20, 0xff00ff, 0xff00ff); grid.rotateX(Math.PI * 0.5); - // grid.position.setZ(0.5); - // console.log(grid.material) + grid.position.z = 0.05; (grid.material as THREE.LineBasicMaterial).opacity = 0.2; (grid.material as THREE.LineBasicMaterial).transparent = true; - // scene.add(planeWire); - // scene.add(planeSolid); - scene.add(grid); + var gridFar = new THREE.GridHelper(200, 40, 0xff00ff, 0xff00ff); + gridFar.rotateX(Math.PI * 0.5); + gridFar.position.z = 0.02; + (gridFar.material as THREE.LineBasicMaterial).opacity = 0.1; + (gridFar.material as THREE.LineBasicMaterial).transparent = true; + + scene.add(planeWire); + scene.add(planeSolid); + //scene.add(gridFar); + //scene.add(grid); } -// used to generate terrain function generateHeight(width, height) { var size = width * height, data = new Uint8Array(size), - perlin = new ImprovedNoise(), quality = 1, z = Math.random() * 200; + perlin = new ImprovedNoise(), quality = 1, z = Math.random() * 20; + for (var j = 0; j < 4; j++) { for (var i = 0; i < size; i++) { - var x = i % width, y = ~ ~(i / width); - data[i] += Math.abs(perlin.noise(x / quality, y / quality, z) * quality * 1.75); - + var ix = i % width, iy = ~ ~(i / width); + data[i] += Math.abs(perlin.noise(ix / quality, iy / quality, z) * quality * 1.75); } quality *= 5; } - for (var j = 0; j < size; j++) { - var x = ((j % width) - 64) - var y = ((~ ~(j / width)) - 64) - data[j] *= Math.sqrt(x * x + y * y) * 0.005 + + for (var i = 0; i < size; i++) { + var x = ((i % width) - 64); + var y = ((~ ~(i / width)) - 64); + var distFromCenter = Math.sqrt(x * x + y * y); + + var flatRadius = 4; + var mountainStart = 50; + + if (distFromCenter < flatRadius) { + data[i] = 0; + } else if (distFromCenter < mountainStart) { + var t = (distFromCenter - flatRadius) / (mountainStart - flatRadius); + var rise = Math.pow(t, 1.5); + data[i] = data[i] * rise * 1.5; + } else { + var valleyWidth = 25; + var valleyDepth = Math.max(0, 1 - Math.abs(x) / valleyWidth); + valleyDepth = Math.pow(valleyDepth, 2); + data[i] = data[i] * (2.5 - valleyDepth * 1.2); + } } return data; }