<!doctype html>
<html lang="ca">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Mini Joc Multiacció</title>
<style>
:root{--bg:#0f172a;--ground:#0ea5a4;--panel:#0b1220}
body{margin:0;font-family:Inter,Arial;background:var(--bg);color:#e6eef8;display:flex;flex-direction:column;align-items:center;gap:12px;padding:12px}
canvas{background:linear-gradient(#071029,#0b1220);border-radius:8px;box-shadow:0 8px 30px rgba(2,6,23,0.6)}
.hud{width:900px;display:flex;justify-content:space-between;align-items:center;gap:12px}
.panel{background:var(--panel);padding:8px;border-radius:8px;font-size:14px}
.controls{font-size:13px;color:#cbd5e1}
button{background:#06b6d4;border:0;padding:8px 10px;border-radius:6px;color:#042027;cursor:pointer}
@media(max-width:960px){canvas{width:100%;height:auto}}
</style>
</head>
<body>
<div class="hud">
<div class="panel">Vida: <span id="hp">100</span></div>
<div class="panel controls">
Controls: ← → moure | Shift córrer | Space saltar | Z atacar | X esquivar | E recollir
</div>
<div class="panel"><button id="restart">Reinicia</button></div>
</div>
<canvas id="game" width="900" height="420"></canvas>
<script>
// Joc multiacció bàsic en Canvas
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const keys = {};
window.addEventListener('keydown', e => keys[e.key.toLowerCase()] = true);
window.addEventListener('keyup', e => keys[e.key.toLowerCase()] = false);
document.getElementById('restart').onclick = () => init();
// Estat del jugador
const player = {
x: 100, y: 0, w: 36, h: 48,
vx: 0, vy: 0,
speed: 1.6, runMult: 1.8,
onGround: false,
facing: 1, // 1 dreta, -1 esquerra
hp: 100,
invulnerable: 0,
inventory: [],
state: 'idle', // idle, run, jump, attack, dash
dashTimer: 0,
attackTimer: 0
};
// Entorn
const gravity = 0.45;
const groundY = 360;
let lastTime = 0;
let items = [];
let enemies = [];
// Inicialitza nivell, objectes i enemics
function init(){
player.x = 100; player.y = 0; player.vx = 0; player.vy = 0;
player.hp = 100; player.inventory = []; player.invulnerable = 0;
items = [
{x: 420, y: groundY-18, w:18, h:18, type:'gem'},
{x: 700, y: groundY-18, w:18, h:18, type:'potion'}
];
enemies = [
{x: 520, y: groundY-48, w:36, h:48, vx:-0.6, hp:30},
{x: 760, y: groundY-48, w:36, h:48, vx:-0.4, hp:20}
];
}
init();
// Colisió AABB
function collide(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;
}
// Lògica del jugador
function updatePlayer(dt){
// Invulnerabilitat countdown
if(player.invulnerable>0) player.invulnerable = Math.max(0, player.invulnerable - dt);
// Moviment horitzontal
let move = 0;
if(keys['arrowleft'] || keys['a']) move -= 1;
if(keys['arrowright'] || keys['d']) move += 1;
const running = keys['shift'];
const spd = player.speed * (running ? player.runMult : 1);
// Dash (esquivar) amb X
if(keys['x'] && player.dashTimer <= 0 && player.onGround){
player.dashTimer = 0.22; // durada dash en segons
player.vx = 10 * (player.facing || 1);
player.state = 'dash';
}
// Si dash actiu, decrementa i aplica fricció
if(player.dashTimer > 0){
player.dashTimer -= dt;
player.vx *= 0.92;
} else {
// Aplicar moviment normal
player.vx = move * spd;
if(move !== 0) player.facing = move > 0 ? 1 : -1;
}
// Saltar
if((keys[' '] || keys['space']) && player.onGround){
player.vy = -10.5;
player.onGround = false;
player.state = 'jump';
}
// Atac amb Z
if(keys['z'] && player.attackTimer <= 0){
player.attackTimer = 0.28;
player.state = 'attack';
}
// Aplicar gravetat
player.vy += gravity;
player.x += player.vx;
player.y += player.vy;
// Col·lisió amb terra
if(player.y + player.h >= groundY){
player.y = groundY - player.h;
player.vy = 0;
player.onGround = true;
if(player.dashTimer <= 0 && player.attackTimer <= 0){
player.state = Math.abs(player.vx) > 0.1 ? 'run' : 'idle';
}
} else {
player.onGround = false;
}
// Atac: comprovar col·lisions amb enemics
if(player.attackTimer > 0){
player.attackTimer -= dt;
// hitbox frontal
const hit = {
x: player.x + (player.facing>0 ? player.w : -30),
y: player.y + 8,
w: 30, h: player.h - 16
};
enemies.forEach(e=>{
if(e.hp>0 && collide(hit,e)){
e.hp -= 20;
}
});
}
// Recollir amb E
if(keys['e']){
items = items.filter(it=>{
if(collide(player, it)){
player.inventory.push(it.type);
return false;
}
return true;
});
}
// Limitar posicions dins canvas
player.x = Math.max(0, Math.min(canvas.width - player.w, player.x));
}
// Lògica enemics simple
function updateEnemies(dt){
enemies.forEach(e=>{
if(e.hp <= 0) return;
e.x += e.vx;
// girar en límits
if(e.x < 300 || e.x > 860) e.vx *= -1;
// col·lisió amb jugador
if(collide(e, player) && player.invulnerable <= 0){
player.hp -= 10;
player.invulnerable = 0.8;
player.vx = -player.facing * 4;
}
});
// eliminar enemics morts
enemies = enemies.filter(e=>e.hp>0);
}
// Render
function draw(){
// fons
ctx.clearRect(0,0,canvas.width,canvas.height);
// cel
const g = ctx.createLinearGradient(0,0,0,canvas.height);
g.addColorStop(0,'#071029'); g.addColorStop(1,'#071a2a');
ctx.fillStyle = g; ctx.fillRect(0,0,canvas.width,canvas.height);
// terra
ctx.fillStyle = '#0ea5a4'; ctx.fillRect(0, groundY, canvas.width, canvas.height-groundY);
// items
items.forEach(it=>{
ctx.fillStyle = it.type === 'gem' ? '#f472b6' : '#60a5fa';
ctx.fillRect(it.x, it.y, it.w, it.h);
ctx.fillStyle = '#021124'; ctx.font='12px Arial'; ctx.fillText(it.type, it.x-6, it.y-6);
});
// enemics
enemies.forEach(e=>{
ctx.fillStyle = '#ef4444';
ctx.fillRect(e.x, e.y, e.w, e.h);
ctx.fillStyle = '#fff'; ctx.font='12px Arial'; ctx.fillText('HP:'+e.hp, e.x, e.y-6);
});
// jugador (simple sprite rectangle)
ctx.save();
// parpelleig si invulnerable
if(player.invulnerable > 0 && Math.floor(player.invulnerable*10)%2===0) ctx.globalAlpha = 0.4;
ctx.fillStyle = '#fbbf24';
ctx.fillRect(player.x, player.y, player.w, player.h);
// cap per indicar direcció
ctx.fillStyle = '#0b1220';
if(player.facing>0) ctx.fillRect(player.x+player.w-6, player.y+8, 6, 8);
else ctx.fillRect(player.x, player.y+8, 6, 8);
ctx.restore();
// HUD inventari i vida
document.getElementById('hp').textContent = player.hp;
ctx.fillStyle = '#e6eef8'; ctx.font='13px Arial';
ctx.fillText('Inventari: ' + (player.inventory.join(', ') || 'buit'), 12, 18);
// Indicador d'estat
ctx.fillStyle = '#cbd5e1'; ctx.fillText('Estat: ' + player.state, 12, 36);
}
// Bucle principal
function loop(ts){
const dt = Math.min(0.033, (ts - lastTime) / 1000 || 0.016);
lastTime = ts;
updatePlayer(dt);
updateEnemies(dt);
draw();
// condició de final
if(player.hp <= 0){
ctx.fillStyle = 'rgba(0,0,0,0.6)'; ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = '#fff'; ctx.font='36px Arial'; ctx.fillText('Has perdut', canvas.width/2 - 80, canvas.height/2);
return;
}
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
</script>
</body>
</html>6 views