let player; let gravity = 0.8; let projectiles = []; let specialProjectiles = []; let bombs = []; let groundY = 500; let keys = {}; let powerUps = []; let deathZoneX; let deathZoneY = 600; let controlsP1, controlsP2; let spriteSheets = {}; function preload() { //sprite download spriteSheets.backgrounds= loadImage("assets/Mario-Background.png"); spriteSheets.characters = loadImage("assets/Mario-Character+Item.png"); spriteSheets.specialweapon = loadImage("assets/Mario-Enemy.png"); spriteSheets.tileset = loadImage("assets/Mario-Tileset.png"); } function applyChromaKey(img, keyColor = {r:147, g: 187, b:236}) { img.loadPixels(); for (let i = 0; i < img.pixels.length; i += 4) { const r = img.pixels[i]; const g = img.pixels[i+1]; const b = img.pixels[i+2]; if (r === keyColor.r && g === keyColor.g && b === keyColor.b) { img.pixels[i+3] = 0; } } img.updatePixels(); } class Background { constructor(dayImg, nightImg) { this.bgDay = dayImg; this.bgNight = nightImg; this.mode = 'day'; this.currentImg = this.bgDay; this.tileW = dayImg.width; this.tileH = dayImg.height; } setMode(mode) { if (mode === "day") { this.mode = "day"; this.currentImg = this.bgDay; } else if (mode === "night") { this.mode = "night"; this.currentImg = this.bgNight; } else { console.log("error"); } } modeChange(){ this.setMode(this.mode === "day" ? "night" : "day"); } draw() { for (let y = 0; y < height; y += this.tileH) { for (let x = 0; x < width; x += this.tileW) { image(this.currentImg, x, y); } } } } class Player { constructor(x,y, imgSet, controls){ this.x = x; this.y = y; this.vx = 0; this.vy = 0; this.width = 32; this.height = 32; this.onGround = false; this.jumpCount = 0; this.deathCount = 10; this.imgSet = imgSet; this.controls = controls;//custom keyset this.keys = {};// key pressed 상태 추적적 this.bombHoldStartTime = null; this.skill = { resist: false, fastFire: false, isGiant: false, itemCount: 0 } this.facing = "right"; this.state = "idle"; this.frame = 0; } update() { // moving if (this.keys[this.controls.left]) { this.vx = -5; this.facing = "left"; } else if (this.keys[this.controls.right]) { this.vx = 5; this.facing = "right"; } else { this.vx = 0; } //ground judge this.vy += gravity; this.x += this.vx; this.y += this.vy; if (this.y + this.height >= groundY) { this.y = groundY - this.height; this.vy = 0; this.onGround = true; this.jumpCount = 0; this.state = "idle"; } else { this.onGround = false; this.state = "jump"; } //낙사 if (this.y > deathZoneY) { this.deathCount--; this.state = "dead"; this.respawn(); } } respawn(){ this.x = 100; this.y = 100; this.vx = 0; this.vy = 0; } jump() { if(this.jumpCount < 2) { this.vy = -15; this.jumpCount++; } } //3 attack logic shoot() { console.log("shoot"); const dir = this.facing === 'right' ? 20 : -20; const proj = new Projectile(this.x + this.width/2, this.y + this.height/2, dir); projectiles.push(proj); } dropBomb() { console.log("bomb"); const bombX = this.x + this.width / 2; const bombY = this.y; bombs.push(new Bomb(bombX, bombY)); } fireBigMissile() { console.log("받아라 비장의무기~~"); const dir = this.facing === "right" ? 5 : -5; specialProjectiles.push(new BigMissile(this.x, this.y, dir)); this.skill.itemCount = this.skill.itemCount - 3; } //end.. handleKeyPressed(k) { this.keys[k] = true; if (k === this.controls.jump) this.jump(); if (k === this.controls.attack) this.shoot(); if (k === this.controls.bomb) { if (this.skill.itemCount >= 3 && this.bombHoldStartTime === null) { this.bombHoldStartTime = millis(); } else { this.dropBomb(); } } } handleKeyReleased(k) { this.keys[k] = false; if (k === this.controls.bomb && this.bombHoldStartTime !== null) { const heldDuration = millis() - this.bombHoldStartTime; if (heldDuration >= 2000 && this.skill.itemCount >= 3) { this.fireBigMissile(); // 대형 미사일 발사 this.skill.itemCount = this.skill.itemCount - 3; // 카운트 초기화 } // 초기화 (2초 미만이어도 시간 초기화) this.bombHoldStartTime = null; } } draw() { const img = this.imgSet[this.state][this.frame]; push(); if (this.facing === 'left') { translate(this.x + this.width, this.y); scale(-1,1); image(img, 0, 0, this.width, this.height); } else { image(img, this.x, this.y, this.width, this.height); } pop(); } } class Projectile { constructor(x, y, vx, width = 8, height = 8) { this.x = x; this.y = y; this.vx = vx; this.width = width; this.height = height; this.spawnTime = millis(); this.lifetime = 10000; this.shouldDestroy = false; } update(targets) { this.x += this.vx; for (const t of targets) { if(this.hits(t)) { const knockback = this.vx; t.vx += knockback; this.shouldDestroy = true; } } if (millis() - this.spawnTime > this.lifetime) { this.shouldDestroy = true; } } draw() { fill(255, 100, 0); rect(this.x, this.y, this.width, this.height); } destroy() { return this.shouldDestroy; } // 충돌 체크 메서드 hits(target) { console.log("shoot hit!"); return ( this.x < target.x + target.width && this.x + this.width > target.x && this.y < target.y + target.height && this.y + this.height > target.y ); } } class Bomb { constructor(x,y) { this.x = x; this.y = y; this.width = 16; this.height = 16; this.timer = 100; } update() { this.timer --; if(this.timer <=0){ this.explode(); } } explode() { console.log("exploded"); } draw() { fill(255,0,0); rect(this.x, this.y, this.width, this.height); } } class BigMissile { constructor(x,y,vx) { this.x = x; this.y = y; this.vx = vx; this.width = 64; this.height = 64 this.spawnTime = millis(); this.lifetime = 20000; this.shouldDestroy = false; } update(targets) { this.x += this.vx; for (const t of targets) { if (this.hits(t)) { const knockback = this.vx * 0.1; t.vx += knockback; } } if (millis() - this.spawnTime > this.lifetime) { this.shouldDestroy = true; } } draw() { fill(255, 0, 255); rect(this.x, this.y, this.width, this.height); } destroy() { return this.shouldDestroy; } } class Item { } function handleProjectiles() { for (let i = projectiles.length - 1; i >= 0; i--) { projectiles[i].update([player1, player2]); projectiles[i].draw(); if (projectiles[i].destroy()) { projectiles.splice(i, 1); console.log("shootend"); } } } function handleBombs() { for (let i = bombs.length - 1; i >= 0; i--) { const b = bombs[i]; b.update(); b.draw(); if (b.explode) { bombs.splice(i, 1); // 제거 } } } function handleSpecialProjectiles() { for (let i = specialProjectiles.length - 1; i >= 0; i--) { specialProjectiles[i].update([player1, player2]); specialProjectiles[i].draw(); if (specialProjectiles[i].destroy()) { specialProjectiles.splice(i, 1); } } } let backgroundManager; let P1imgs = []; let P2imgs = []; let itemimgs = []; function setup() { createCanvas(800, 1600); const bgsrc = spriteSheets.backgrounds; const w = 512, h = 512; const bgDay = createImage(w, h), bgNight = createImage(w, h); bgDay.copy(bgsrc, 514, 1565, w, h, 0, 0, w, h); bgNight.copy(bgsrc, 514, 5721, w, h, 0, 0, w, h); backgroundManager = new Background(bgDay, bgNight); controlsP1 = { left:'ArrowLeft', right:'ArrowRight', jump:'ArrowUp', attack:'[', bomb:']' }; controlsP2 = { left:'a', right:'d', jump:'w', attack:'t', bomb:'y' }; const chracterSrc = spriteSheets.characters; const cw = 32, ch = 32; // Mario frames const marioIdle = createImage(cw,ch); marioIdle.copy(chracterSrc, 1, 98, cw,ch, 0,0, cw,ch); const marioWalk1 = createImage(cw,ch); marioWalk1.copy(chracterSrc,75, 98, cw,ch, 0,0, cw,ch); const marioWalk2 = createImage(cw,ch); marioWalk2.copy(chracterSrc,108, 98, cw,ch, 0,0, cw,ch); const marioWalk3 = createImage(cw,ch); marioWalk3.copy(chracterSrc,141, 98, cw,ch, 0,0, cw,ch); const marioJump = createImage(cw,ch); marioJump.copy(chracterSrc,215,98, cw,ch, 0,0, cw,ch); const marioAttack = createImage(cw,ch); marioAttack.copy(chracterSrc,627,98, cw,ch, 0,0, cw,ch); [marioIdle, marioWalk1, marioWalk2, marioWalk3, marioJump, marioAttack] .forEach(img => applyChromaKey(img, {r:147, g:187, b:236})); P1imgs = { idle:[marioIdle], walk:[marioWalk1,marioWalk2,marioWalk3], jump:[marioJump], shoot:[marioAttack] }; // Luigi frames const luigiIdle = createImage(cw,ch); luigiIdle.copy(chracterSrc, 1, 629, cw,ch,0,0, cw,ch); const luigiWalk1 = createImage(cw,ch); luigiWalk1.copy(chracterSrc,75,629, cw,ch,0,0, cw,ch); const luigiWalk2 = createImage(cw,ch); luigiWalk2.copy(chracterSrc,108,629, cw,ch,0,0, cw,ch); const luigiWalk3 = createImage(cw,ch); luigiWalk3.copy(chracterSrc,141,629,cw,ch,0,0, cw,ch); const luigiJump = createImage(cw,ch); luigiJump.copy(chracterSrc,215,629,cw,ch,0,0, cw,ch); const luigiAttack = createImage(cw,ch); luigiAttack.copy(chracterSrc,627,629,cw,ch,0,0, cw,ch); [luigiIdle, luigiWalk1, luigiWalk2, luigiWalk3, luigiJump, luigiAttack] .forEach(img => applyChromaKey(img, {r:147, g:187, b:236})); P2imgs = { idle:[luigiIdle], walk:[luigiWalk1,luigiWalk2,luigiWalk3], jump:[luigiJump], shoot:[luigiAttack] }; const specialSrc = spriteSheets.specialweapon; const ow = 16, oh = 16; // 버섯 const mushImg = createImage(ow, oh); mushImg.copy(chracterSrc, 1, 2126, ow, oh, 0, 0, ow, oh); // 독 const poisonImg = createImage(ow, oh); poisonImg.copy(chracterSrc, 1, 2143, ow, oh, 0, 0, ow, oh); // 거대 버섯 const giantImg = createImage(2*ow, 2*oh); giantImg.copy(chracterSrc, 35, 2143, 2*ow,2*oh, 0, 0, 2*ow, 2*oh); // 파이어 const fireImg = createImage(ow/2, oh/2); fireImg.copy(chracterSrc, 101, 2177, ow/2,oh/2, 0,0, ow/2, oh/2); // 폭탄 const bombImg = createImage(ow, oh); bombImg.copy(chracterSrc, 194, 2143, ow,oh, 0,0, ow, oh); // 강화 파이어 const fireInchImg= createImage(2*ow, oh); fireInchImg.copy(specialSrc, 258, 195, 2*ow, oh/2, 0, 0, 2*ow, oh/2); // 대형 미사일 const missileImg = createImage(4*ow,4*oh); missileImg.copy(specialSrc, 127, 356, 4*ow, 4*oh, 0, 0, 4*ow, 4*oh); [mushImg, poisonImg, giantImg, fireImg, bombImg, fireInchImg, missileImg] .forEach(img => applyChromaKey(img, {r:147, g:187, b:236})); const itemimgs = { mush: [mushImg], poison: [poisonImg], giant: [giantImg], fire: [fireImg], fire_reinforce: [fireInchImg], bomb: [bombImg], bigmissile: [missileImg] }; player1 = new Player(0, 0, P1imgs, controlsP1); player2 = new Player(0, 0, P2imgs, controlsP2); } function draw() { backgroundManager.draw(); rect(0, groundY, width, 100); player1.update(); player1.draw(); player2.update(); player2.draw(); handleProjectiles(); handleBombs(); handleSpecialProjectiles(); } function keyPressed(){ if(key === ' '){//spacebar(임시) backgroundManager.modeChange(); } player1.handleKeyPressed(key); player2.handleKeyPressed(key); } function keyReleased() { player1.handleKeyReleased(key); player2.handleKeyReleased(key); }