<!
doctype html>
<html lang="bn">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Simple Platformer — Edit করো তোমার মতো</title>
<style>
html,body{height:100%;margin:0;background:#9ad3ff;font-family:system-ui;}
#gameWrap{width:100%;height:100%;display:flex;align-items:center;justify-
content:center;}
canvas{background: linear-gradient(#9ad3ff,#6fc2ff); border:6px solid #2b6cb0;
box-shadow: 0 6px 20px rgba(0,0,0,0.2);}
.info{position:fixed;left:12px;top:12px;background:rgba(255,255,255,0.9);padding:
8px;border-radius:6px;font-size:13px}
</style>
</head>
<body>
<div id="gameWrap">
<canvas id="c" width="960" height="540"></canvas>
</div>
<div class="info">
Arrow / A D = চলা · Space / W = জাম্প · R = রিস্টার্ট<br>
Coins: <span id="coins">0</span> · Lives: <span id="lives">3</span>
</div>
<script>
// === CONFIG ===
const canvas = [Link]('c');
const ctx = [Link]('2d');
const W = [Link], H = [Link];
// টাইল সাইজ - খেলায় পরিবর্তন করলে ম্যাপও আপডেট করতে হবে
const TILE = 48;
// === ইউজার স্টেট ===
let keys = {};
let coinsEl = [Link]('coins');
let livesEl = [Link]('lives');
// === প্লেয়ার অবজেক্ট ===
const player = {
x: 100, y: 100,
w: 36, h: 44,
vx: 0, vy: 0,
speed: 3.5,
jumpPower: 11,
onGround: false,
dir: 1, // 1 = right, -1 = left
alive: true
};
// সহজ physics
const GRAV = 0.6;
const FRICTION = 0.85;
// লেভেল ম্যাপ (টাইল কোড)
// 0 = 空, 1 = প্ল্যাটফর্ম, 2 = কিউইকস্যান্ড (স্লো), 3 = কয়েন, 9 = এনিমি স্পো্ন
পয়েন্ট, 8 = লেভেল এন্ড
// প্রতিটি কলাম 20 টাইল পর্যন্ত দেখানো হবে — এখানে সোজা অভ্যন্তরীণ ম্যাপ
const level = [
// row wise (পড়াশোনা: প্রতিটি সাব-অ্যারে একটি row)
// সহজলি বড় ম্যাপ বানাতে তুমি আরও row যোগ করো
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,3,0,0,0,0],
[0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,3,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,3,0,0,0,0,0,0,9,0,0,0,0,0,0,3,0,0,0,8],
[1,1,1,0,0,0,1,1,1,1,1,0,0,1,1,1,1,1,1,1],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
// নিচে ground লেয়ার (floor)
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
];
// ভ্যারিয়েবল লেভেল লম্বা করতে চাইলে উপরে আরও array যোগ করো
// টাইলস থেকে বিশ্ব কনভার্ট
function tileAt(r,c){
if(r < 0 || r >= [Link]) return 0;
if(c < 0 || c >= level[0].length) return 0;
return level[r][c];
}
// এনিমি লিস্ট init from level (9 positions)
let enemies = [];
let coins = [];
function initEntities(){
enemies = [];
coins = [];
for(let r=0;r<[Link];r++){
for(let c=0;c<level[r].length;c++){
let t = level[r][c];
if(t === 9){
[Link]({x: c*TILE + 6, y: r*TILE + 6, w:36, h:36, dir: -1,
speed:1.2});
// make tile empty afterwards (so player can stand)
level[r][c] = 0;
} else if(t === 3){
[Link]({x: c*TILE + TILE/2 - 10, y: r*TILE + TILE/2 -10, w:20, h:20,
taken:false});
level[r][c] = 0;
}
}
}
}
initEntities();
let coinsCollected = 0;
let lives = 3;
// collision helper
function rectsOverlap(a,b){
return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
}
// world collision with tiles
function collideWithTiles(px,py,pw,ph){
// return collision info: {onGround, vxCorrection, vyCorrection}
let info = {onGround:false, correctedY:py, correctedX:px};
// check four corners
// iterate over tiles that overlap bbox
let left = [Link](px / TILE);
let right = [Link]((px + pw) / TILE);
let top = [Link](py / TILE);
let bottom = [Link]((py + ph) / TILE);
for(let r = top; r <= bottom; r++){
for(let c = left; c <= right; c++){
let t = tileAt(r,c);
if(t === 1){ // solid
let tx = c*TILE, ty = r*TILE;
// AABB resolve
let overlapX = [Link](px + pw - tx, tx + TILE - px);
let overlapY = [Link](py + ph - ty, ty + TILE - py);
if(overlapX > 0 && overlapY > 0){
// resolve by smallest penetration
if(overlapX < overlapY){
// push horizontally
if(px + pw/2 < tx + TILE/2){
[Link] = px - overlapX;
} else {
[Link] = px + overlapX;
}
} else {
// push vertically
if(py + ph/2 < ty + TILE/2){
[Link] = py - overlapY;
[Link] = true;
} else {
[Link] = py + overlapY;
}
}
}
} else if(t === 2){
// quicksand (we'll simulate by reducing speed and letting player sink
slightly)
// If player intersects, set a flag (handled elsewhere)
}
}
}
return info;
}
// reset level / player
function resetLevel(){
player.x = 100; player.y = 50; [Link] = 0; [Link] = 0; [Link] = true;
coinsCollected = 0;
[Link] = coinsCollected;
lives = 3;
[Link] = lives;
initEntities();
}
// update loop
function update(){
// input
if(keys['ArrowLeft'] || keys['a'] || keys['A']){
[Link] = [Link]([Link] - 0.8, -[Link]*1.3);
[Link] = -1;
} else if(keys['ArrowRight'] || keys['d'] || keys['D']){
[Link] = [Link]([Link] + 0.8, [Link]*1.3);
[Link] = 1;
} else {
[Link] *= FRICTION;
if([Link]([Link]) < 0.2) [Link] = 0;
}
// jump
if((keys[' '] || keys['ArrowUp'] || keys['w'] || keys['W']) && [Link]){
[Link] = -[Link];
[Link] = false;
}
// apply gravity
[Link] += GRAV;
if([Link] > 18) [Link] = 18;
// attempt move X
let nextX = player.x + [Link];
let nextY = player.y + [Link];
// check collisions separately for better stability
let colX = collideWithTiles(nextX, player.y, player.w, player.h);
player.x = [Link];
let colY = collideWithTiles(player.x, nextY, player.w, player.h);
player.y = [Link];
[Link] = [Link];
// collect coins
for(let c of coins){
if(![Link] && rectsOverlap({x:player.x,y:player.y,w:player.w,h:player.h}, c)){
[Link] = true;
coinsCollected++;
[Link] = coinsCollected;
}
}
// enemies movement & collisions
for(let e of enemies){
e.x += [Link] * [Link];
// simple tile bump reverse
let leftTile = tileAt([Link]((e.y+e.h/2)/TILE), [Link]((e.x)/TILE));
let rightTile = tileAt([Link]((e.y+e.h/2)/TILE),
[Link]((e.x+e.w)/TILE));
if(leftTile === 1 || rightTile === 1){
[Link] *= -1;
e.x += [Link] * 2;
}
// collide with player
if(rectsOverlap({x:player.x,y:player.y,w:player.w,h:player.h}, e)){
// if player's falling and hits top of enemy => kill enemy
if([Link] > 2 && (player.y + player.h - e.y) < 20){
// stomp
[Link] = true;
[Link] = -8; // bounce
} else {
// player hit -> lose life & respawn
[Link] = false;
}
}
}
// remove dead enemies
enemies = [Link](ev => ![Link]);
// check level end (tile 8 anywhere)
for(let r=0;r<[Link];r++){
for(let c=0;c<level[r].length;c++){
if(level[r][c] === 8){
let tx = c*TILE, ty = r*TILE;
if(rectsOverlap({x:player.x,y:player.y,w:player.w,h:player.h},
{x:tx,y:ty,w:TILE,h:TILE})){
// level complete
alert('বাহ! লেভেল শেষ।Coins: ' + coinsCollected);
resetLevel();
return;
}
}
}
}
// out of bounds
if(player.y > H + 200 || ![Link]){
lives--;
[Link] = lives;
if(lives <= 0){
alert('Game Over — Restarting level');
resetLevel();
} else {
// respawn
player.x = 100; player.y = 50; [Link] = 0; [Link] = 0; [Link] =
true;
}
}
}
// draw loop
function draw(){
[Link](0,0,W,H);
// background: sun & distant hills
[Link] = '#ffe78f';
[Link]();
[Link](820, 80, 48, 0, [Link]*2);
[Link]();
// simple parallax hills
[Link] = '#67c2a6';
[Link](0, H-80, W, 80);
// draw tiles
for(let r=0;r<[Link];r++){
for(let c=0;c<level[r].length;c++){
let t = level[r][c];
let x = c*TILE, y = r*TILE;
if(t === 1){
// solid block
[Link] = '#7b3f00';
[Link](x, y, TILE, TILE);
// brick lines
[Link] = '#5a2f00';
[Link](x+2, y+2, TILE-4, TILE-4);
} else if(t === 2){
[Link] = '#c8a23a';
[Link](x,y,TILE,TILE);
} else if(t === 8){
// flag / end
[Link] = '#ffffff';
[Link](x+TILE/2-4, y+8, 8, TILE-16);
[Link] = '#ff4757';
[Link]();
[Link](x+TILE/2+4, y+12);
[Link](x+TILE-6, y+22);
[Link](x+TILE/2+4, y+32);
[Link]();
}
}
}
// draw coins
for(let c of coins){
if([Link]) continue;
[Link] = '#ffeb3b';
[Link]();
[Link](c.x + c.w/2, c.y + c.h/2, 8, 10, 0, 0, [Link]*2);
[Link]();
[Link] = '#c9a600';
[Link]();
}
// draw enemies
for(let e of enemies){
[Link] = '#ff6b6b';
[Link](e.x, e.y, e.w, e.h);
// eyes
[Link] = '#fff';
[Link](e.x + 8, e.y + 8, 6, 6);
[Link](e.x + e.w - 14, e.y + 8, 6, 6);
}
// draw player (simple blocky)
[Link]();
[Link](player.x + player.w/2, player.y + player.h/2);
if([Link] === -1) [Link](-1,1);
[Link](-player.w/2, -player.h/2);
// body
[Link] = '#0b5';
[Link](0, 0, player.w, player.h);
// face
[Link] = '#ffe0bd';
[Link](6, 6, 12, 12);
// hat
[Link] = '#d00';
[Link](0,0, player.w, 8);
[Link]();
// HUD (score/coins)
[Link] = 'rgba(0,0,0,0.5)';
[Link](12,12,140,36);
[Link] = '#fff';
[Link] = '16px sans-serif';
[Link]('Coins: ' + coinsCollected, 22, 36);
}
// main loop
function loop(){
update();
draw();
requestAnimationFrame(loop);
}
// input listeners
[Link]('keydown', (e)=>{ keys[[Link]] = true; if([Link] === 'r' ||
[Link] === 'R'){ resetLevel(); }});
[Link]('keyup', (e)=>{ keys[[Link]] = false; });
// start
resetLevel();
loop();
</script>
</body>
</html>