Numex yayın: public/app.js
This commit is contained in:
parent
80ff56f509
commit
84ab0bd227
183
public/app.js
Normal file
183
public/app.js
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
/* =====================================================================
|
||||||
|
* app.js — OYUN DÖNGÜSÜ • KONTROLLER • FİZİK • HUD • MİNİMAP
|
||||||
|
* Saf 2D canvas arayüz katmanı (engine.js'i kullanır).
|
||||||
|
* ===================================================================== */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/* ---------- Canvas bağlantıları ---------- */
|
||||||
|
const screenCanvas = document.getElementById('game'); // 3D görünüm
|
||||||
|
const mapCanvas = document.getElementById('minimap'); // radar
|
||||||
|
|
||||||
|
const ctx = screenCanvas.getContext('2d');
|
||||||
|
const mctx = mapCanvas.getContext('2d');
|
||||||
|
|
||||||
|
/* ---------- HUD elemanları ---------- */
|
||||||
|
const hudFps = document.getElementById('hud-fps');
|
||||||
|
const hudX = document.getElementById('hud-x');
|
||||||
|
const hudY = document.getElementById('hud-y');
|
||||||
|
const hudAng = document.getElementById('hud-ang');
|
||||||
|
|
||||||
|
/* ---------- Çözünürlük (retro ~640x360) ---------- */
|
||||||
|
function resize() {
|
||||||
|
// CSS ile dolu kare; iç canvas piksel sayısını retro tutalım
|
||||||
|
if (screenCanvas.width !== 640) screenCanvas.width = 640;
|
||||||
|
if (screenCanvas.height !== 360) screenCanvas.height = 360;
|
||||||
|
// CSS ölçeği: genişlikte taşmasın
|
||||||
|
const cssW = Math.min(window.innerWidth - 24, 900);
|
||||||
|
const cssH = cssW * (360 / 640);
|
||||||
|
screenCanvas.style.width = cssW + 'px';
|
||||||
|
screenCanvas.style.height = cssH + 'px';
|
||||||
|
}
|
||||||
|
resize();
|
||||||
|
window.addEventListener('resize', resize);
|
||||||
|
|
||||||
|
/* ---------- Yer kaplama (collider) ---------- */
|
||||||
|
const WALL = {
|
||||||
|
isWall(x, y) {
|
||||||
|
const ix = Math.floor(x), iy = Math.floor(y);
|
||||||
|
if (ix < 0 || iy < 0 || ix >= GRID || iy >= GRID) return true; // kapalı alan
|
||||||
|
return MAP[iy][ix] !== 0;
|
||||||
|
},
|
||||||
|
/* Yarıçap radius'lı (oyuncu gövdesi) çarpışma testi */
|
||||||
|
canMove(nx, ny, radius) {
|
||||||
|
// Köşeleri test et
|
||||||
|
return !(this.isWall(nx - radius, ny - radius) ||
|
||||||
|
this.isWall(nx + radius, ny - radius) ||
|
||||||
|
this.isWall(nx - radius, ny + radius) ||
|
||||||
|
this.isWall(nx + radius, ny + radius));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const PLAYER_R = 0.22; // hücre birimi cinsinden oyuncu gövde yarıçapı
|
||||||
|
|
||||||
|
/* ---------- Tuş durumu ---------- */
|
||||||
|
const keys = {};
|
||||||
|
const MOVE_SPEED = 3.2; // saniyede hücre
|
||||||
|
const TURN_SPEED = 2.6; // radyan/saniye
|
||||||
|
|
||||||
|
document.addEventListener('keydown', (e) => { keys[e.code] = true; });
|
||||||
|
document.addEventListener('keyup', (e) => { keys[e.code] = false; });
|
||||||
|
|
||||||
|
/* ---------- Fizik + giriş (her kare) ---------- */
|
||||||
|
function update(player_, dt) {
|
||||||
|
// Dönüş: sola/sağa (A / D veya ok tuşları)
|
||||||
|
let turn = 0;
|
||||||
|
if (keys['KeyA'] || keys['ArrowLeft']) turn -= 1;
|
||||||
|
if (keys['KeyD'] || keys['ArrowRight']) turn += 1;
|
||||||
|
player_.angle += turn * TURN_SPEED * dt;
|
||||||
|
|
||||||
|
// İleri / geri
|
||||||
|
let move = 0;
|
||||||
|
if (keys['KeyW'] || keys['ArrowUp']) move += 1;
|
||||||
|
if (keys['KeyS'] || keys['ArrowDown']) move -= 1;
|
||||||
|
|
||||||
|
if (move !== 0) {
|
||||||
|
const step = move * MOVE_SPEED * dt;
|
||||||
|
const nx = player_.x + dirX(player_.angle) * step;
|
||||||
|
const ny = player_.y + dirY(player_.angle) * step;
|
||||||
|
|
||||||
|
// Eksen bağımsız çarpışma (duvarlara sürtünerek kayma)
|
||||||
|
if (WALL.canMove(nx, player_.y, PLAYER_R)) player_.x = nx;
|
||||||
|
if (WALL.canMove(player_.x, ny, PLAYER_R)) player_.y = ny;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Açı periyodik tut (0..2π)
|
||||||
|
player_.angle = ((player_.angle % (Math.PI * 2)) + Math.PI * 2) % (Math.PI * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Minimap / radar (sağ üst) ---------- */
|
||||||
|
const CELL_PX = 10; // harita hücre başına px
|
||||||
|
function renderMinimap() {
|
||||||
|
const mw = GRID * CELL_PX;
|
||||||
|
const mh = GRID * CELL_PX;
|
||||||
|
mapCanvas.width = mw;
|
||||||
|
mapCanvas.height = mh;
|
||||||
|
mctx.clearRect(0, 0, mw, mh);
|
||||||
|
|
||||||
|
// Duvarları çiz
|
||||||
|
for (let yy = 0; yy < GRID; yy++) {
|
||||||
|
for (let xx = 0; xx < GRID; xx++) {
|
||||||
|
const tile = MAP[yy][xx];
|
||||||
|
if (tile === 0) continue;
|
||||||
|
const pal = WALL_PALETTE[tile] || WALL_PALETTE[1];
|
||||||
|
mctx.fillStyle = pal.base;
|
||||||
|
mctx.fillRect(xx * CELL_PX + 1, yy * CELL_PX + 1, CELL_PX - 2, CELL_PX - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprite dekor objeleri (minimap: küçük beyaz nokta)
|
||||||
|
if (typeof SPRITES !== 'undefined') {
|
||||||
|
mctx.fillStyle = '#ffffff';
|
||||||
|
for (const sp of SPRITES) {
|
||||||
|
mctx.fillRect(sp.x * CELL_PX - 1, sp.y * CELL_PX - 1, 2, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Işın konisi (oyuncu görüşü): FOV kadar saçılan ışınlar
|
||||||
|
mctx.strokeStyle = 'rgba(255,220,80,0.75)';
|
||||||
|
mctx.lineWidth = 1;
|
||||||
|
const coneSteps = 24;
|
||||||
|
for (let i = 0; i <= coneSteps; i++) {
|
||||||
|
const a = player.angle - FOV / 2 + (i / coneSteps) * FOV;
|
||||||
|
// duvara kadar çek (basit + ince DDA render'ı tekrar dene, görsel için yeterli)
|
||||||
|
const hit = castRay(player.x, player.y, a, 14);
|
||||||
|
const ex = player.x + dirX(a) * hit.dist;
|
||||||
|
const ey = player.y + dirY(a) * hit.dist;
|
||||||
|
mctx.beginPath();
|
||||||
|
mctx.moveTo(player.x * CELL_PX, player.y * CELL_PX);
|
||||||
|
mctx.lineTo(ex * CELL_PX, ey * CELL_PX);
|
||||||
|
mctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Oyuncu noktası
|
||||||
|
mctx.fillStyle = '#ffe14d';
|
||||||
|
mctx.beginPath();
|
||||||
|
mctx.arc(player.x * CELL_PX, player.y * CELL_PX, 3, 0, Math.PI * 2);
|
||||||
|
mctx.fill();
|
||||||
|
// Bakış yönü kısa çizgi
|
||||||
|
mctx.strokeStyle = '#ffffff';
|
||||||
|
mctx.lineWidth = 1.5;
|
||||||
|
mctx.beginPath();
|
||||||
|
mctx.moveTo(player.x * CELL_PX, player.y * CELL_PX);
|
||||||
|
mctx.lineTo((player.x + dirX(player.angle) * 0.45) * CELL_PX,
|
||||||
|
(player.y + dirY(player.angle) * 0.45) * CELL_PX);
|
||||||
|
mctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- HUD ---------- */
|
||||||
|
function updateHUD(fps) {
|
||||||
|
hudFps.textContent = String(Math.round(fps));
|
||||||
|
hudX.textContent = player.x.toFixed(2);
|
||||||
|
hudY.textContent = player.y.toFixed(2);
|
||||||
|
hudAng.textContent = ((player.angle * 180) / Math.PI).toFixed(0) + '°';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Oyun döngüsü ---------- */
|
||||||
|
let lastT = performance.now();
|
||||||
|
let frameCount = 0;
|
||||||
|
let fpsTimer = lastT;
|
||||||
|
let fpsValue = 60;
|
||||||
|
|
||||||
|
function loop(now) {
|
||||||
|
let dt = (now - lastT) / 1000;
|
||||||
|
lastT = now;
|
||||||
|
if (dt > 0.05) dt = 0.05; // sekme arka plana geçtiyse sınırla
|
||||||
|
|
||||||
|
// FPS sayacı
|
||||||
|
frameCount++;
|
||||||
|
if (now - fpsTimer >= 500) {
|
||||||
|
fpsValue = (frameCount * 1000) / (now - fpsTimer);
|
||||||
|
frameCount = 0;
|
||||||
|
fpsTimer = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(player, dt);
|
||||||
|
renderScene(ctx, screenCanvas.width, screenCanvas.height);
|
||||||
|
renderMinimap();
|
||||||
|
updateHUD(fpsValue);
|
||||||
|
|
||||||
|
requestAnimationFrame(loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Başlat ---------- */
|
||||||
|
requestAnimationFrame(loop);
|
||||||
Loading…
Reference in New Issue
Block a user