🎮ArcadeLab

PK XD: NEONVERSE ULTRA MP

by GoldenRanger23
1052 lines19.7 KB🛠️ Three.js (3D graphics)
▶ Play
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>PK XD: NEONVERSE ULTRA MP</title>

<style>
* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  font-family: Arial, sans-serif;
  color: white;
  background: #79e8ff;
  touch-action: none;
}

#game {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
}

.panel {
  background: rgba(17, 19, 46, .82);
  border: 1px solid #ffffff55;
  border-radius: 15px;
  padding: 10px;
  backdrop-filter: blur(10px);
}

#topbar {
  position: fixed;
  top: 10px;
  left: 10px;
  right: 10px;
  display: flex;
  justify-content: space-between;
  gap: 8px;
  pointer-events: none;
}

#brand {
  font-size: clamp(13px, 3vw, 22px);
  font-weight: bold;
}

#stats {
  text-align: right;
  color: #ffe66d;
  font-weight: bold;
}

#actions {
  position: fixed;
  top: 82px;
  left: 10px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  width: 155px;
}

button {
  font-size: 12px;
  font-weight: bold;
  color: white;
  background: linear-gradient(135deg, #7857ff, #ff58c8);
  border: 1px solid #ffffff77;
  border-radius: 11px;
  padding: 10px;
  cursor: pointer;
}

button:active {
  transform: scale(.96);
}

#start {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: linear-gradient(135deg, #3520a5dd, #05d3e6bb);
}

#card {
  width: 440px;
  max-width: 100%;
  text-align: center;
  padding: 25px;
}

#card h1 {
  font-size: clamp(28px, 8vw, 45px);
  text-shadow: 0 4px #56269d;
  margin: 10px 0;
}

#play {
  width: 100%;
  font-size: 18px;
  background: linear-gradient(90deg, #36eaff, #a45cff, #ff5bbb);
}

#notice {
  position: fixed;
  top: 20%;
  left: 50%;
  transform: translateX(-50%);
  display: none;
  text-align: center;
  max-width: 85%;
}

#notice.show {
  display: block;
}

#hint {
  position: fixed;
  bottom: 10px;
  left: 50%;
  transform: translateX(-50%);
  text-align: center;
  font-size: 11px;
  width: max-content;
  max-width: 90%;
}

#joystick {
  position: fixed;
  bottom: 25px;
  left: 20px;
  width: 110px;
  height: 110px;
  border: 2px solid #ffffff88;
  border-radius: 50%;
  background: #ffffff22;
  display: none;
}

#stick {
  position: absolute;
  width: 45px;
  height: 45px;
  left: 31px;
  top: 31px;
  border-radius: 50%;
  background: linear-gradient(135deg, #51f7ff, #a35dff);
  border: 2px solid white;
}

#jump {
  position: fixed;
  right: 22px;
  bottom: 35px;
  width: 70px;
  height: 70px;
  border-radius: 50%;
  display: none;
  text-align: center;
  font-size: 19px;
}

@media (pointer: coarse) {
  #joystick, #jump {
    display: block;
  }

  #hint {
    bottom: 145px;
  }
}

@media (max-width: 480px) {
  #actions {
    top: 85px;
    width: 135px;
  }

  #actions button {
    padding: 8px 6px;
    font-size: 11px;
  }
}
</style>
</head>

<body>

<canvas id="game"></canvas>

<div id="topbar">
  <div id="brand" class="panel">
    🌈 NEONVERSE ULTRA MP
    <div style="font-size:10px">YENİ DÜNYA • 3D MACERA</div>
  </div>

  <div id="stats" class="panel">
    <div id="coins">🪙 000</div>
    <div style="font-size:10px;color:white">👥 5 bot</div>
  </div>
</div>

<div id="actions">
  <button id="petBtn">🐾 Evcil hayvan: Açık</button>
  <button id="danceBtn">💃 Dans et!</button>
  <button id="portalBtn">🌀 Neon portal</button>
  <button id="dayBtn">🌙 Gece / Gündüz</button>
</div>

<div id="notice" class="panel">Neonverse'e hoş geldin!</div>

<div id="hint" class="panel">
  WASD: hareket • SPACE: zıpla • Ekranı sürükle: kamera
</div>

<div id="joystick">
  <div id="stick"></div>
</div>

<button id="jump">⬆️</button>

<div id="start">
  <div id="card" class="panel">
    <div style="font-size:50px">🌎✨🛸</div>

    <h1>
      NEONVERSE<br>
      <span style="color:#55f6ff">ULTRA MP</span>
    </h1>

    <p>
      Yepyeni bir dünya seni bekliyor!
      Rengârenk mahalleleri keşfet,
      neon şehrini dolaş, altınları topla
      ve evcil hayvanınla maceraya çık!
    </p>

    <button id="play">🚀 DÜNYAYA KATIL</button>

    <p style="font-size:10px">
      Multiplayer demo • Botlar • 3D şehir
    </p>
  </div>
</div>

<script type="importmap">
{
  "imports": {
    "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js"
  }
}
</script>

<script type="module">
import * as THREE from 'three';

// ==============================
// OYUN MOTORU
// ==============================

const canvas = document.querySelector("#game");

const renderer = new THREE.WebGLRenderer({
  canvas,
  antialias: true
});

renderer.setPixelRatio(Math.min(devicePixelRatio, 1.5));
renderer.setSize(innerWidth, innerHeight);
renderer.shadowMap.enabled = true;
renderer.outputColorSpace = THREE.SRGBColorSpace;

const scene = new THREE.Scene();
scene.background = new THREE.Color("#7ee7ff");
scene.fog = new THREE.Fog("#9deaff", 100, 240);

const camera = new THREE.PerspectiveCamera(
  65,
  innerWidth / innerHeight,
  0.1,
  500
);

scene.add(new THREE.HemisphereLight("#ffffff", "#514080", 2));

const sun = new THREE.DirectionalLight("#fff2d2", 3);
sun.position.set(-35, 70, 35);
sun.castShadow = true;
scene.add(sun);

function material(color, emissive = null) {
  return new THREE.MeshStandardMaterial({
    color,
    roughness: 0.65,
    ...(emissive ? {
      emissive,
      emissiveIntensity: 0.6
    } : {})
  });
}

const box = new THREE.BoxGeometry(1, 1, 1);
const sphere = new THREE.SphereGeometry(1, 18, 14);
const cylinder = new THREE.CylinderGeometry(1, 1, 1, 20);
const ringGeometry = new THREE.TorusGeometry(1, 0.11, 10, 36);

function mesh(geometry, mat, x, y, z, sx=1, sy=1, sz=1) {
  const object = new THREE.Mesh(geometry, mat);

  object.position.set(x, y, z);
  object.scale.set(sx, sy, sz);
  object.castShadow = true;
  object.receiveShadow = true;

  scene.add(object);
  return object;
}

function cube(x, y, z, sx, sy, sz, color) {
  return mesh(box, material(color), x, y, z, sx, sy, sz);
}

// ==============================
// YENİ DÜNYA
// ==============================

// Büyük çimenlik alan
cube(0, -1.1, 0, 260, 2, 260, "#56d9a2");

// Şehir yolları
for (let z = -90; z <= 90; z += 30) {
  cube(0, -0.06, z, 210, 0.12, 7, "#343957");
  cube(0, 0.01, z, 210, 0.025, 0.15, "#ffe9a0");
}

for (let x = -90; x <= 90; x += 30) {
  cube(x, -0.06, 0, 7, 0.12, 210, "#343957");
  cube(x, 0.01, 0, 0.15, 0.025, 210, "#ffe9a0");
}

// Merkez meydanı
cube(0, 0.01, 0, 24, 0.15, 24, "#f8c8f1");
cube(0, 0.1, 0, 16, 0.12, 16, "#d8fbff");

// Neon meydan halkaları
for (const r of [4.3, 6.1]) {
  const ring = mesh(
    ringGeometry,
    material(r === 4.3 ? "#ff4dce" : "#18eaff"),
    0, 0.25, 0, r, 0.85, r
  );

  ring.rotation.x = Math.PI / 2;
}

// ==============================
// RENKLİ EVLER
// ==============================

const houseColors = [
  "#ff79c8",
  "#6e9bff",
  "#ffdc69",
  "#62ead0",
  "#ba8cff",
  "#ff9671"
];

function makeHouse(x, z, i) {
  const width = 7 + i % 3;
  const height = 5 + (i % 4) * 1.15;
  const depth = 7 + i % 2;

  cube(
    x,
    height / 2,
    z,
    width,
    height,
    depth,
    houseColors[i % houseColors.length]
  );

  const roof = mesh(
    new THREE.ConeGeometry(width * 0.78, 2.6, 4),
    material(houseColors[(i + 2) % houseColors.length]),
    x,
    height + 1.1,
    z,
    width * 0.9,
    1,
    depth * 0.9
  );

  roof.rotation.y = Math.PI / 4;

  // Kapı
  cube(
    x,
    height * 0.35,
    z + depth / 2 + 0.04,
    1.1,
    2,
    0.1,
    "#634a87"
  );

  // Pencereler
  for (const wx of [-width * 0.27, width * 0.27]) {
    for (const wy of [height * 0.45, height * 0.72]) {
      cube(
        x + wx,
        wy,
        z + depth / 2 + 0.1,
        1,
        1,
        0.12,
        "#b7f8ff"
      );
    }
  }
}

let buildingNumber = 0;

for (const x of [-75, -48, -18]) {
  for (const z of [-76, -48, 48, 76]) {
    makeHouse(x, z, buildingNumber++);
  }
}

for (const x of [-75, -48, -18]) {
  for (const z of [-76, -48, 48, 76]) {
    makeHouse(z, x, buildingNumber++);
  }
}

// ==============================
// GELECEK ŞEHRİ
// ==============================

function makeTower(x, z, height, width, color) {
  cube(x, height / 2, z, width, height, width * 0.85, color);

  cube(
    x,
    height + 0.35,
    z,
    width * 0.9,
    0.7,
    width * 0.76,
    "#34385e"
  );

  for (let y = 2; y < height - 1; y += 2.7) {
    cube(
      x, y, z + width * 0.43,
      width * 0.68, 0.13, 0.08,
      "#5df6ff"
    );

    cube(
      x + width * 0.38, y, z,
      0.08, 0.13, width * 0.6,
      "#ff6edb"
    );
  }

  cube(
    x,
    height + 1.2,
    z,
    width * 0.25,
    2,
    0.25,
    "#62faff"
  );

  const halo = mesh(
    ringGeometry,
    material("#17cfff"),
    x,
    height + 2.1,
    z,
    width * 0.55,
    1,
    width * 0.55
  );

  halo.rotation.x = Math.PI / 2;
}

makeTower(42, -55, 30, 9, "#535ca5");
makeTower(63, -23, 42, 10, "#5e4c9e");
makeTower(47, 25, 25, 8, "#3b6e9e");
makeTower(75, 55, 36, 11, "#6a4d99");
makeTower(27, 66, 22, 7, "#4a86a7");

// ==============================
// AĞAÇLAR VE PARK
// ==============================

function makeTree(x, z) {
  mesh(
    cylinder,
    material("#8f5a59"),
    x, 1.6, z,
    0.55, 3.2, 0.55
  );

  const colors = ["#61f5b4", "#b0ff67", "#ff99dd"];
  const color = colors[Math.floor(Math.random() * colors.length)];

  mesh(
    sphere,
    material(color),
    x, 4, z,
    2.4, 2.2, 2.4
  );
}

[
  [-8,-11],
  [9,-10],
  [-10,10],
  [10,11],
  [-35,4],
  [-42,16],
  [-60,10],
  [4,43],
  [10,51]
].forEach(([x,z]) => makeTree(x,z));

// Banklar
for (const [x,z] of [[-6,-6],[6,-6],[-6,6],[6,6]]) {
  cube(x, 0.7, z, 3, 0.3, 0.8, "#ff73c9");
  cube(x, 1, z + 0.45, 3, 0.9, 0.25, "#7e4ccc");
}

// ==============================
// UZAY ADASI VE PORTAL
// ==============================

const island = mesh(
  new THREE.CylinderGeometry(13, 8, 4, 9),
  material("#5542a1"),
  0, 12, -103
);

const islandTop = mesh(
  new THREE.CylinderGeometry(13, 13, 0.4, 9),
  material("#5af5dd"),
  0, 14, -103
);

const portalRing = mesh(
  ringGeometry,
  material("#10caff"),
  0, 4, -18,
  3.3, 1, 3.3
);

portalRing.rotation.x = Math.PI / 2;

const portalCore = mesh(
  new THREE.CircleGeometry(2.85, 40),
  new THREE.MeshBasicMaterial({
    color: "#8b5dff",
    transparent: true,
    opacity: 0.7,
    side: THREE.DoubleSide
  }),
  0, 4, -18
);

// Kristaller
for (let i = 0; i < 7; i++) {
  const crystal = mesh(
    new THREE.ConeGeometry(0.9, 4, 5),
    material("#8f4cff"),
    -9 + i * 3,
    16,
    -106 + (i % 2) * 4
  );

  crystal.rotation.z = i % 2 ? 0.2 : -0.2;
}

// ==============================
// OYUNCU KARAKTERİ
// ==============================

function makeAvatar(color = "#35d8ff", skin = "#ffc79a") {
  const group = new THREE.Group();
  scene.add(group);

  function part(geometry, mat, x, y, z, sx=1, sy=1, sz=1) {
    const object = new THREE.Mesh(geometry, mat);

    object.position.set(x, y, z);
    object.scale.set(sx, sy, sz);
    object.castShadow = true;

    group.add(object);
    return object;
  }

  part(
    box,
    material(color),
    0, 1.35, 0,
    0.85, 1.05, 0.48
  );

  const head = part(
    sphere,
    material(skin),
    0, 2.25, 0,
    0.55, 0.57, 0.52
  );

  part(
    sphere,
    material("#3b245e"),
    0, 2.57, -0.03,
    0.57, 0.32, 0.55
  );

  const eyes = material("#24213d");

  for (const x of [-0.19, 0.19]) {
    part(
      sphere,
      eyes,
      x, 2.28, 0.47,
      0.055, 0.07, 0.035
    );
  }

  const arms = [];

  for (const x of [-0.55, 0.55]) {
    const arm = part(
      cylinder,
      material(color),
      x, 1.4, 0,
      0.15, 0.7, 0.15
    );

    arm.rotation.z = x < 0 ? -0.18 : 0.18;
    arms.push(arm);
  }

  const legs = [];

  for (const x of [-0.22, 0.22]) {
    const leg = part(
      cylinder,
      material("#f5f6ff"),
      x, 0.57, 0,
      0.18, 0.65, 0.18
    );

    legs.push(leg);

    part(
      box,
      material("#ff65bb"),
      x, 0.23, 0.1,
      0.28, 0.18, 0.42
    );
  }

  group.userData = {head, arms, legs};
  return group;
}

const player = makeAvatar();
player.position.set(0, 0, 8);

// ==============================
// EVCİL HAYVAN
// ==============================

const pet = new THREE.Group();
scene.add(pet);

const petBody = mesh(
  sphere, material("#ffcc56"),
  0, 0.55, 0,
  0.58, 0.48, 0.7
);

const petHead = mesh(
  sphere, material("#ffdc85"),
  0, 0.9, 0.35,
  0.48, 0.46, 0.45
);

const petTail = mesh(
  sphere, material("#ff8bbf"),
  0, 0.64, -0.66,
  0.18, 0.18, 0.38
);

pet.add(petBody, petHead, petTail);
pet.position.set(2, 0, 9);

// ==============================
// BOTLAR
// ==============================

const botColors = [
  "#ff55ad",
  "#ffe066",
  "#66ffa9",
  "#ac84ff",
  "#ff9866"
];

const bots = [];

for (let i = 0; i < 5; i++) {
  const bot = makeAvatar(botColors[i]);

  bot.position.set(
    (Math.random() - 0.5) * 17,
    0,
    (Math.random() - 0.5) * 17
  );

  bot.userData.angle = Math.random() * Math.PI * 2;
  bot.userData.radius = 6 + Math.random() * 8;
  bot.userData.speed = 0.25 + Math.random() * 0.3;
  bot.userData.phase = Math.random() * 7;

  bots.push(bot);
}

// ==============================
// ALTINLAR
// ==============================

const coins = [];
let collected = 0;

for (let i = 0; i < 35; i++) {
  let x = (Math.random() - 0.5) * 150;
  let z = (Math.random() - 0.5) * 150;

  if (Math.abs(x) < 13 && Math.abs(z) < 13) {
    x += 24;
  }

  const coin = mesh(
    new THREE.TorusGeometry(0.48, 0.16, 10, 20),
    material("#ffd64f", "#ffc400"),
    x, 1.45, z,
    0.8, 0.8, 0.8
  );

  coin.userData.phase = Math.random() * 8;
  coin.userData.collected = false;

  coins.push(coin);
}

// ==============================
// OYUN KONTROLLERİ
// ==============================

let started = false;
let petFollowing = true;
let dancing = false;
let night = false;
let jumpVelocity = 0;
let grounded = true;
let worldTime = 0;

let yaw = 0;
let pitch = 0.18;

const keys = {};

const notice = document.querySelector("#notice");

function toast(message) {
  notice.textContent = message;
  notice.classList.add("show");

  clearTimeout(toast.timer);

  notice.timer = setTimeout(() => {
    notice.classList.remove("show");
  }, 2200);
}

function clamp(value, min, max) {
  return Math.max(min, Math.min(max, value));
}

// Oyuna başla
document.querySelector("#play").addEventListener("click", () => {
  started = true;
  document.querySelector("#start").style.display = "none";
  toast("🌈 Neonverse'e hoş geldin!");
});

// Evcil hayvan
document.querySelector("#petBtn").addEventListener("click", () => {
  petFollowing = !petFollowing;

  document.querySelector("#petBtn").textContent =
    petFollowing
      ? "🐾 Evcil hayvan: Açık"
      : "🐾 Evcil hayvan: Bekle";

  toast(
    petFollowing
      ? "Minik dostun seni takip ediyor!"
      : "Minik dostun bekliyor."
  );
});

// Dans
document.querySelector("#danceBtn").addEventListener("click", () => {
  dancing = !dancing;
  toast(dancing ? "💃 Dans zamanı!" : "🕺 Dans bitti!");
});

// Portal
document.querySelector("#portalBtn").addEventListener("click", () => {
  if (Math.abs(player.position.z + 18) < 9) {
    player.position.set(0, 0, -100);
    toast("🛸 Uzay adasına ışınlandın!");
  } else {
    player.position.set(0, 0, -12);
    toast("🌀 Portal meydanına geldin!");
  }
});

// Gece ve gündüz
document.querySelector("#dayBtn").addEventListener("click", () => {
  night = !night;

  scene.background.set(night ? "#111638" : "#7ee7ff");
  scene.fog.color.set(night ? "#24264e" : "#9deaff");

  sun.intensity = night ? 0.6 : 3;
  toast(night ? "🌙 Neon gecesi!" : "🌞 Gündüz modu!");
});

// Klavye
addEventListener("keydown", event => {
  keys[event.code] = true;

  if (event.code === "Space") {
    event.preventDefault();

    if (grounded && started) {
      jumpVelocity = 8;
      grounded = false;
    }
  }
});

addEventListener("keyup", event => {
  keys[event.code] = false;
});

// Kamera hareketi
let dragging = false;
let lastX = 0;
let lastY = 0;

renderer.domElement.addEventListener("pointerdown", event => {
  if (event.pointerType === "mouse" && event.button !== 0) return;

  dragging = true;
  lastX = event.clientX;
  lastY = event.clientY;

  renderer.domElement.setPointerCapture?.(event.pointerId);
});

addEventListener("pointerup", () => {
  dragging = false;
});

addEventListener("pointermove", event => {
  if (!dragging || !started) return;

  const dx = event.clientX - lastX;
  const dy = event.clientY - lastY;

  lastX = event.clientX;
  lastY = event.clientY;

  yaw -= dx * 0.006;
  pitch = clamp(pitch + dy * 0.003, -0.12, 0.85);
});

// Mobil joystick
const joystick = document.querySelector("#joystick");
const stick = document.querySelector("#stick");

let joyX = 0;
let joyY = 0;
let joystickActive = false;
let joystickPointer = null;

function moveJoystick(event) {
  const rect = joystick.getBoundingClientRect();

  const cx = rect.left + rect.width / 2;
  const cy = rect.top + rect.height / 2;

  joyX = clamp((event.clientX - cx) / 37, -1, 1);
  joyY = clamp((event.clientY - cy) / 37, -1, 1);

  stick.style.left = (31 + joyX * 26) + "px";
  stick.style.top = (31 + joyY * 26) + "px";
}

joystick.addEventListener("pointerdown", event => {
  joystickActive = true;
  joystickPointer = event.pointerId;

  joystick.setPointerCapture(event.pointerId);
  moveJoystick(event);
});

joystick.addEventListener("pointermove", event => {
  if (
    joystickActive &&
    event.pointerId === joystickPointer
  ) {
    moveJoystick(event);
  }
});

function resetJoystick() {
  joystickActive = false;
  joyX = 0;
  joyY = 0;

  stick.style.left = "31px";
  stick.style.top = "31px";
}

joystick.addEventListener("pointerup", resetJoystick);
joystick.addEventListener("pointercancel", resetJoystick);

// Mobil zıplama
document.querySelector("#jump").addEventListener("pointerdown", event => {
  event.preventDefault();

  if (grounded && started) {
    jumpVelocity = 8;
    grounded = false;
  }
});

// Ekran boyutu
addEventListener("resize", () => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(innerWidth, innerHeight);
  renderer.setPixelRatio(Math.min(devicePixelRatio, 1.5));
});

// ==============================
// OYUN DÖNGÜSÜ
// ==============================

const clock = new THREE.Clock();

function animate() {
  requestAnimationFrame(animate);

  const dt = Math.min(clock.getDelta(), 0.04);
  worldTime += dt;

  // Portal animasyonu
  portalRing.rotation.z += dt * 0.7;
  portalCore.material.opacity =
    0.45 + Math.sin(worldTime * 3) * 0.18;

  // Altın animasyonu
  for (const coin of coins) {
    if (coin.userData.collected) continue;

    coin.rotation.y += dt * 1.7;

    coin.position.y =
      1.45 +
      Math.sin(worldTime * 2 + coin.userData.phase) * 0.25;

    const dx = coin.position.x - player.position.x;
    const dz = coin.position.z - player.position.z;

    if (Math.hypot(dx, dz) < 1.65) {
      coin.userData.collected = true;
      coin.visible = false;

      collected++;

      document.querySelector("#coins").textContent =
        "🪙 " + String(collected * 10).padStart(3, "0");

      if (collected % 5 === 0) {
        toast("✨ " + collected * 10 + " jeton kazandın!");
      }
    }
  }

  // Kamera yönüne göre hareket
  let forward =
    (keys.KeyW || keys.ArrowUp ? 1 : 0) -
    (keys.KeyS || keys.ArrowDown ? 1 : 0) -
    joyY;

  let sideways =
    (keys.KeyD || keys.ArrowRight ? 1 : 0) -
    (keys.KeyA || keys.ArrowLeft ? 1 : 0) +
    joyX;

  const magnitude = Math.hypot(forward, sideways);

  if (magnitude > 1) {
    forward /= magnitude;
    sideways /= magnitude;
  }

  const speed =
    keys.ShiftLeft || keys.ShiftRight ? 13 : 8;

  const dx

Game Source: PK XD: NEONVERSE ULTRA MP

Creator: GoldenRanger23

Libraries: three

Complexity: complex (1052 lines, 19.7 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: pk-xd-neonverse-ultra-mp-goldenranger23" to link back to the original. Then publish at arcadelab.ai/publish.