Skip to content

novia #3

@emiliusmarin-svg

Description

@emiliusmarin-svg

<!doctype html>

<title>Sistema Solar 3D - Juego (Amor)</title> <style> html, body { margin: 0; height: 100%; overflow: hidden; background: #05060a; } #app { width: 100%; height: 100%; position: relative; } canvas { display: block; }
.hud{
  position: fixed; left: 12px; top: 12px;
  padding: 10px 12px;
  background: rgba(0,0,0,0.35);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 12px;
  color: rgba(255,255,255,0.85);
  font: 14px/1.25 system-ui, -apple-system, Segoe UI, Roboto, Arial;
  user-select: none;
  max-width: 360px;
  backdrop-filter: blur(6px);
}
.hud b{ color: rgba(255,255,255,0.95); }
.hud .row{ margin-top: 6px; opacity: 0.95; }
.hint{
  position: fixed; left: 12px; bottom: 12px;
  color: rgba(255,255,255,.72);
  font: 13px/1.25 system-ui, -apple-system, Segoe UI, Roboto, Arial;
  user-select: none;
  background: rgba(0,0,0,0.25);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 12px;
  padding: 8px 10px;
  backdrop-filter: blur(6px);
}
.btn{
  display: inline-block;
  margin-top: 8px;
  padding: 6px 10px;
  border-radius: 10px;
  border: 1px solid rgba(255,255,255,0.18);
  background: rgba(255,70,120,0.12);
  color: rgba(255,255,255,0.92);
  cursor: pointer;
}
.btn:active{ transform: translateY(1px); }

.centerMsg{
  position: fixed; inset: 0;
  display: grid; place-items: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity 220ms ease;
}
.centerMsg.show{ opacity: 1; }
.centerMsg .box{
  pointer-events: none;
  padding: 14px 16px;
  border-radius: 16px;
  background: rgba(0,0,0,0.35);
  border: 1px solid rgba(255,255,255,0.14);
  color: rgba(255,255,255,0.9);
  font: 16px/1.25 system-ui, -apple-system, Segoe UI, Roboto, Arial;
  backdrop-filter: blur(8px);
  text-align: center;
  max-width: 520px;
}
</style>
Controles
🖱️ Arrastra = rotar • Rueda = zoom • Doble click = reset
⌨️ W A S D moverte • Espacio subir • Shift bajar
🖱️ Click = “pulse” del sol ✨
Modo vuelo: ON
Tip: acércate a María ✨ para verla gigante 💖
Siempre contigo 💫
<script type="module"> import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js"; import { OrbitControls } from "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/controls/OrbitControls.js"; // ======= CONFIG ======= const RING_RADIUS = 110; const RING_TILT_X = 0.45; const RING_TILT_Z = 0.15; const TEXT_NEON = "rgba(255,70,120,0.98)"; const TEXT_GLOW = "rgba(255,70,120,0.85)"; // “Gameplay” let flyMode = true; const MOVE_SPEED = 1.5; // velocidad base const MOVE_SPEED_FAST = 3.0; // con Alt const DAMP = 0.88; // ======= Setup ======= const container = document.getElementById("app"); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 3000); const defaultCamPos = new THREE.Vector3(0, 90, 260); const defaultTarget = new THREE.Vector3(0, 25, 0); camera.position.copy(defaultCamPos); const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.setSize(window.innerWidth, window.innerHeight); container.appendChild(renderer.domElement); const controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.06; controls.minDistance = 80; controls.maxDistance = 900; controls.target.copy(defaultTarget); window.addEventListener("dblclick", () => { camera.position.copy(defaultCamPos); controls.target.copy(defaultTarget); controls.update(); pulseSol(1.0); showCenterMsg("Siempre contigo 💫"); }); // ======= HUD ======= const toggleFlyBtn = document.getElementById("toggleFly"); toggleFlyBtn.addEventListener("click", () => { flyMode = !flyMode; toggleFlyBtn.textContent = `Modo vuelo: ${flyMode ? "ON" : "OFF"}`; showCenterMsg(flyMode ? "Modo vuelo ACTIVADO ✈️" : "Modo vuelo DESACTIVADO 💤"); }); const centerMsg = document.getElementById("centerMsg"); const centerMsgBox = document.getElementById("centerMsgBox"); let msgTimer = null; function showCenterMsg(text){ centerMsgBox.textContent = text; centerMsg.classList.add("show"); clearTimeout(msgTimer); msgTimer = setTimeout(() => centerMsg.classList.remove("show"), 900); } showCenterMsg("Siempre contigo 💫"); // ======= Luces ======= scene.add(new THREE.AmbientLight(0xffffff, 0.18)); const sunLight = new THREE.PointLight(0xffffff, 2.7, 1500); sunLight.position.set(0, 0, 0); scene.add(sunLight); const fillLight = new THREE.DirectionalLight(0xffffff, 0.35); fillLight.position.set(120, 140, 120); scene.add(fillLight); // ======= Estrellas ======= function makeStars(count = 1600) { const geom = new THREE.BufferGeometry(); const positions = new Float32Array(count * 3); const spread = 1400; for (let i = 0; i < count; i++) { const i3 = i * 3; positions[i3 + 0] = (Math.random() - 0.5) * spread; positions[i3 + 1] = (Math.random() - 0.5) * spread; positions[i3 + 2] = (Math.random() - 0.5) * spread; } geom.setAttribute("position", new THREE.BufferAttribute(positions, 3)); const mat = new THREE.PointsMaterial({ color: 0xffffff, size: 1.6, sizeAttenuation: true, transparent: true, opacity: 0.65 }); const points = new THREE.Points(geom, mat); scene.add(points); return points; } const stars = makeStars(); // ======= Helpers visuales ======= function glowSprite(size = 210, opacity = 0.9, tint = [255,255,255]) { const c = document.createElement("canvas"); c.width = 256; c.height = 256; const g = c.getContext("2d"); const [r, gg, b] = tint; const grd = g.createRadialGradient(128, 128, 8, 128, 128, 128); grd.addColorStop(0.00, `rgba(${r},${gg},${b},${opacity})`); grd.addColorStop(0.22, `rgba(${r},${gg},${b},${opacity * 0.55})`); grd.addColorStop(1.00, "rgba(255,255,255,0)"); g.fillStyle = grd; g.beginPath(); g.arc(128, 128, 128, 0, Math.PI * 2); g.fill(); const tex = new THREE.CanvasTexture(c); const spr = new THREE.Sprite(new THREE.SpriteMaterial({ map: tex, transparent: true, depthWrite: false })); spr.scale.set(size, size, 1); return spr; } function neonLineMaterial(opacity = 0.25) { return new THREE.LineBasicMaterial({ color: 0xff4a7d, transparent: true, opacity }); } function addRing(radius, tiltX, tiltZ, opacity = 0.22, segments = 220) { const curve = new THREE.EllipseCurve(0, 0, radius * 1.08, radius * 0.86, 0, Math.PI * 2, false, 0); const pts2 = curve.getPoints(segments); const pts3 = pts2.map(p => new THREE.Vector3(p.x, 0, p.y)); const geom = new THREE.BufferGeometry().setFromPoints(pts3); const line = new THREE.LineLoop(geom, neonLineMaterial(opacity)); line.rotation.x = tiltX; line.rotation.z = tiltZ; scene.add(line); return line; } function textSprite(text, fontSize = 54, color = TEXT_NEON, glow = TEXT_GLOW) { const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); const pad = 48; ctx.font = `800 ${fontSize}px system-ui, -apple-system, Segoe UI, Roboto, Arial`; const metrics = ctx.measureText(text); const w = Math.ceil(metrics.width + pad * 2); const h = Math.ceil(fontSize + pad * 2); canvas.width = w; canvas.height = h; ctx.font = `800 ${fontSize}px system-ui, -apple-system, Segoe UI, Roboto, Arial`; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.shadowColor = glow; ctx.shadowBlur = 26; ctx.strokeStyle = "rgba(255,120,170,0.35)"; ctx.lineWidth = 6; ctx.strokeText(text, w / 2, h / 2); ctx.fillStyle = color; ctx.fillText(text, w / 2, h / 2); const tex = new THREE.CanvasTexture(canvas); tex.colorSpace = THREE.SRGBColorSpace; const mat = new THREE.SpriteMaterial({ map: tex, transparent: true, depthWrite: false }); const spr = new THREE.Sprite(mat); const scale = 0.18; spr.scale.set(w * scale, h * scale, 1); return spr; } function makeGlassPlanet(radius = 10, tint = 0xff7aa7) { const geom = new THREE.SphereGeometry(radius, 32, 32); const mat = new THREE.MeshPhysicalMaterial({ color: tint, transparent: true, opacity: 0.12, roughness: 0.05, metalness: 0.0, transmission: 1.0, thickness: 0.7, clearcoat: 1.0, clearcoatRoughness: 0.15, emissive: tint, emissiveIntensity: 0.08 }); return new THREE.Mesh(geom, mat); } // ======= Sol blanco + glow ======= const sunGroup = new THREE.Group(); scene.add(sunGroup); const sun = new THREE.Mesh( new THREE.SphereGeometry(22, 40, 40), new THREE.MeshStandardMaterial({ color: 0xffffff, emissive: 0xffffff, emissiveIntensity: 0.9, roughness: 0.2, metalness: 0.0 }) ); sunGroup.add(sun); const sunGlow = glowSprite(240, 0.95, [255,255,255]); sunGroup.add(sunGlow); const sunNeonGlow = glowSprite(290, 0.45, [255, 70, 120]); sunGroup.add(sunNeonGlow); // “pulse” al click let solPulse = 0; function pulseSol(amount = 1.0){ solPulse = Math.min(1.5, solPulse + amount); } window.addEventListener("click", () => pulseSol(0.55)); // ======= Anillo Saturno ======= const mainRing = addRing(RING_RADIUS, RING_TILT_X, RING_TILT_Z, 0.22); addRing(RING_RADIUS + 10, RING_TILT_X, RING_TILT_Z, 0.12); addRing(RING_RADIUS - 12, RING_TILT_X, RING_TILT_Z, 0.10); // ======= Frases (todas en el mismo anillo) ======= const phrases = [ "te amo", "te quiero", "siempre quiero estar contigo", "mi bebe", "mi chula", "mi amor" ]; const ringGroup = new THREE.Group(); ringGroup.rotation.x = RING_TILT_X; ringGroup.rotation.z = RING_TILT_Z; scene.add(ringGroup); const bodies = phrases.map((txt, i) => { const group = new THREE.Group(); ringGroup.add(group); const sprite = textSprite(txt, 54 - Math.min(i, 3) * 6); group.add(sprite); const planetSize = 9 + Math.min(txt.length, 28) * 0.25; const planet = makeGlassPlanet(planetSize); group.add(planet); const halo = glowSprite(planetSize * 9.5, 0.35, [255, 70, 120]); group.add(halo); return { group, sprite, planet, halo, angle: (i / phrases.length) * Math.PI * 2, speed: 0.0045 + i * 0.0006, bobAmp: 3.5 + i * 0.35, radius: RING_RADIUS, phase: Math.random() * Math.PI * 2 }; }); // ======= MARÍA especial ======= const MARIA_RADIUS = 170; addRing(MARIA_RADIUS, 0.15, -0.20, 0.28); addRing(MARIA_RADIUS + 9, 0.15, -0.20, 0.14); const maria = { group: new THREE.Group(), angle: Math.random() * Math.PI * 2, speed: 0.0032, radius: MARIA_RADIUS, tiltX: 0.15, tiltZ: -0.20, bobAmp: 6.0 }; scene.add(maria.group); const mariaSprite = textSprite("María ✨", 78, "rgba(255,120,180,1)", "rgba(255,120,180,0.95)"); maria.group.add(mariaSprite); const mariaPlanet = makeGlassPlanet(22, 0xff9ac2); maria.group.add(mariaPlanet); const mariaHalo = glowSprite(320, 0.55, [255, 120, 180]); maria.group.add(mariaHalo); // ======= Controles tipo juego (WASD) ======= const keys = new Set(); window.addEventListener("keydown", (e) => { keys.add(e.code); if (e.code === "KeyF") { // toggle rápido flyMode = !flyMode; toggleFlyBtn.textContent = `Modo vuelo: ${flyMode ? "ON" : "OFF"}`; showCenterMsg(flyMode ? "Modo vuelo ACTIVADO ✈️" : "Modo vuelo DESACTIVADO 💤"); } }); window.addEventListener("keyup", (e) => keys.delete(e.code)); let vel = new THREE.Vector3(0,0,0); function updateFly(dt){ if (!flyMode) return; // dirección basada en cámara const forward = new THREE.Vector3(); camera.getWorldDirection(forward); forward.normalize(); const right = new THREE.Vector3().crossVectors(forward, camera.up).normalize(); const up = new THREE.Vector3(0,1,0); let speed = keys.has("AltLeft") || keys.has("AltRight") ? MOVE_SPEED_FAST : MOVE_SPEED; const acc = new THREE.Vector3(0,0,0); if (keys.has("KeyW")) acc.add(forward); if (keys.has("KeyS")) acc.sub(forward); if (keys.has("KeyD")) acc.add(right); if (keys.has("KeyA")) acc.sub(right); if (keys.has("Space")) acc.add(up); if (keys.has("ShiftLeft") || keys.has("ShiftRight")) acc.sub(up); if (acc.lengthSq() > 0) acc.normalize().multiplyScalar(speed); // suavizado vel.multiplyScalar(DAMP); vel.addScaledVector(acc, dt * 60); camera.position.addScaledVector(vel, dt * 60); // mueve el target junto para que orbit controls no “jale” raro controls.target.addScaledVector(vel, dt * 60); } // ======= Resize ======= window.addEventListener("resize", () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); // ======= Animación ======= let t = 0; let last = performance.now(); function animate(now) { requestAnimationFrame(animate); const dt = Math.min(0.05, (now - last) / 1000); last = now; t++; updateFly(dt); stars.rotation.y += 0.0007; // pulse del sol solPulse *= 0.92; const pulse = 1 + Math.sin(t * 0.02) * 0.035 + solPulse * 0.10; sunGlow.scale.set(240 * pulse, 240 * pulse, 1); sunNeonGlow.scale.set(290 * (1 + Math.sin(t * 0.018) * 0.03 + solPulse * 0.08), 290 * (1 + Math.sin(t * 0.018) * 0.03 + solPulse * 0.08), 1); mainRing.material.opacity = 0.20 + Math.sin(t * 0.01) * 0.02; for (const b of bodies) { b.angle += b.speed; const x = Math.cos(b.angle) * b.radius * 1.08; const z = Math.sin(b.angle) * b.radius * 0.86; const y = Math.sin(b.angle * 2.0 + b.phase) * b.bobAmp; b.group.position.set(x, y, z); const hp = 1 + Math.sin(t * 0.03 + b.phase) * 0.05; const base = b.planet.geometry.parameters.radius * 9.5; b.halo.scale.set(base * hp, base * hp, 1); b.sprite.quaternion.copy(camera.quaternion); } maria.angle += maria.speed; const mx = Math.cos(maria.angle) * maria.radius * 1.06; const mz = Math.sin(maria.angle) * maria.radius * 0.84; const my = Math.sin(maria.angle * 1.4) * maria.bobAmp; const mv = new THREE.Vector3(mx, my, mz); mv.applyAxisAngle(new THREE.Vector3(1, 0, 0), maria.tiltX); mv.applyAxisAngle(new THREE.Vector3(0, 0, 1), maria.tiltZ); maria.group.position.copy(mv); const mariaPulse = 1 + Math.sin(t * 0.03) * 0.06 + solPulse * 0.06; mariaHalo.scale.set(320 * mariaPulse, 320 * mariaPulse, 1); mariaSprite.quaternion.copy(camera.quaternion); // si te acercas a María, mensaje romántico const distToMaria = camera.position.distanceTo(maria.group.position); if (distToMaria < 95) { showCenterMsg("Te amo, María 💖"); } controls.update(); renderer.render(scene, camera); } animate(performance.now()); </script>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions