canvas塔防小游戏代码
代码语言:html
所属分类:游戏
代码描述:canvas塔防小游戏代码,可随意点击设置攻击位置,还可以选择攻击参数,不能让敌人过右边的红线。
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <style> html, body, main { width: 100%; height: 100%; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; align-items: center; justify-content: center; } canvas { outline: solid 20px black; background: black; border-radius: 10px; } </style> </head> <body > <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/p5.1.6.0.js"></script> <script > "use strict"; console.clear(); function setup() { canvas = createCanvas(800, 600); colorMode(HSL); restart(); } let canvas; let scene; let mouseVector; function restart() { mouseVector = null; scene = new Scene({ health: 100, gameOver: false, canAddTurretAtMouse: false, canvas }); scene.add(new Turret(createVector(2 * width / 3, height / 2))); loop(); } function spawnEnemy() { scene.add(new Enemy()); } function tryAddTurretAtMouse() { if (!scene.data.gameOver && scene.data.canAddTurretAtMouse) { scene.add(new Turret(mouseVector)); } } function checkForGameOver() { if (scene.data.health <= 0) { scene.data.gameOver = true; } return scene.data.gameOver; } /** * ============== * P5 Lifecycle * ============== */ function keyPressed() { if (key === "r") { restart(); } } function mousePressed() { tryAddTurretAtMouse(); scene.executeListeners("mousepressed"); } function draw() { background(11); mouseVector = createVector(mouseX, mouseY); if (checkForGameOver()) { drawGameOver(); return; } scene.update(); if (frameCount % 30 === 0) { spawnEnemy(); } // Draw hover turret scene.data.canAddTurretAtMouse = false; let hoverTurret; const turretNearMouse = scene.closest(mouseVector, "turret", 100); if (inBounds(mouseVector) && !turretNearMouse) { hoverTurret = new Turret(mouseVector); scene.data.canAddTurretAtMouse = true; } /** * Draw start */ if (hoverTurret) hoverTurret.predraw(); scene.draw(); if (hoverTurret) hoverTurret.draw(); // Draw health bar push(); const healthHeight = map(scene.data.health, 0, 100, 0, height); rectMode(CENTER); noStroke(); fill("red"); rect(width - 4, height / 2, 8, healthHeight); pop(); } function drawGameOver() { scene.draw(); // Draw GAME OVER text push(); const NUM_LABELS = 5; const MAX_OFFSET = 4; const LABEL_OFFSET_X = map(mouseX, 0, width, MAX_OFFSET * 2, MAX_OFFSET); const LABEL_OFFSET_Y = map(mouseY, 0, height, MAX_OFFSET * 2, MAX_OFFSET); translate(-LABEL_OFFSET_X * NUM_LABELS, -LABEL_OFFSET_Y * NUM_LABELS); for (let i = 0; i < NUM_LABELS; i++) { translate(LABEL_OFFSET_X, LABEL_OFFSET_Y); const textColor = map(i, -1, NUM_LABELS - 1, 11, 100, true); noStroke(); fill(textColor); textAlign(CENTER, CENTER); textSize(120); text("GAME OVER", width / 2, height / 2); } pop(); } function drawDebugValue(value, title) { const valueStr = title ? `[${title}: ${value}]` : `[${value}]`; noStroke(); fill("lime"); textSize(24); textAlign(LEFT, TOP); text(valueStr, 0, 0); const margin = textWidth("WWW"); translate(ceil(textWidth(valueStr) / margin) * margin + 10, 0); } /** * ========= * Helpers * ========= */ function inBounds(pos) { const oob = pos.x < 0 || pos.x > width || pos.y < 0 || pos.y > height; return !oob; } // Required for getTurnTowardsDiff() function getAngleBetween(p1, p2) { return atan2(p2.y - p1.y, p2.x - p1.x); } // Required for getTurnTowardsDiff() function normalizeAngle(a) { const positiveAngle = a < 0 ? TAU + a % TAU : a % TAU; return positiveAngle > PI ? positiveAngle - TAU : positiveAngle; } function getDistSq(p1, p2) { return sq(p2.x - p1.x) + sq(p2.y - p1.y); } /** * ========= * Classes * ========= */ class Scene { constructor(data) { this.debugShowFrameRate = false; this.debugShowSceneObjsCount = false; this.objsData = {}; this.data = Object.assign({}, data); } get objDataTypeIds() { return Object.keys(this.objsData); } get allObjs() { const allObjs = this.objDataTypeIds.map(id => this.getObjsByType(id)).flat(); allObjs.sort((a, b) => a.zIndex - b.zIndex); return allObjs; } getObjsDataByType(typeId) { this._validateObjDataType(typeId); return this.objsData[typeId]; } getObjsByType(typeId) { return this.getObjsDataByType(typeId).objs; } setObjsByType(typeId, objs) { const objsDataByType = this.getObjsDataByType(typeId); objsDataByType.objs = objs; } add(obj) { if (obj.scene === this) { throw new Error("This object was already added to the scene."); } obj.scene = this; this.getObjsByType(obj.typeId).push(obj); obj._dependencies.filter(dep => dep.scene !== this).forEach(dep => { this.add(dep); }); return obj; } remove(typeId, id) { let objs = this.getObjsByType(typeId); const removeIndex = objs.findIndex(obj => obj.id === id); if (removeIndex === -1) return; const nextObjs = [...objs]; const [objToRemove] = nextObjs.splice(removeIndex, 1); objToRemove.removeDependencies(); objToRemove.scene = null; this.setObjsByType(typeId, nextObjs); } draw() { this.allObjs.forEach((obj) => obj._predraw()); this.allObjs.forEach((obj) => obj._draw()); // Debug statements push(); translate(10, 10); if (this.debugShowFrameRate) { this.debugShowFrameRate__Buffer = this.debugShowFrameRate__Buffer || []; this.debugShowFrameRate__Buffer.push(frameRate()); this.debugShowFrameRate__Buffer = this.debugShowFrameRate__Buffer.slice(-60); const fr = this.debugShowFrameRate__Buffer.reduce((acc, fr) => acc + fr, 0) / this.debugShowFrameRate__Buffer.length; drawDebugValue(fr.toFixed(0), "FPS"); } if (this.debugShowSceneObjsCount) { drawDebugValue(this.allObjs.length, "Objs"); } pop(); } update() { this.allObjs.forEach((obj) => obj._update()); } closestInfo(pos, typeId, range = Infinity, excludeObjs = []) { const objs = this.getObjsByType(typeId); const notFound = { closest: null, closestDist: Infinity }; if (!objs) return notFound; let closest = null; let closestDistSq = Infinity; objs.forEach(obj => { if (excludeObjs.includes(obj)) return; const distSq = getDistSq(pos, obj.pos); if (distSq < closestDistSq) { closestDistSq = distSq; closest = obj; } }); const closestDist = sqrt(closestDistSq); if (closestDist > range) { return notFound; } return { closest, closestDist }; } closest(pos, typeId, range, excludeObjs) { const { closest } = this.closestInfo(pos, typeId, range, excludeObjs); return closest; } _validateObjDataType(typeId) { // If objs data doesn't already exist for that type, add it. if (!this.objsData[typeId]) { this.objsData[typeId] = { typeId, objs: [] }; } } executeListeners(event) { this.allObjs.forEach(obj => { const listeners = obj._listeners[event]; if (listeners && listeners.length > 0) { listeners.forEach(listener => { listener(); }); } }); } } class SceneObj { constructor(pos, angle = 0, radius = 10) { this.typeId = "generic"; this._scene = null; this._dependencies = []; this._listeners = {}; this.zIndex = 0; this.id = `ID-${Math.random()}`; this.pos = pos.copy(); this.angle = angle; this.radius = radius; this.createdFrame = frameCount; } get diameter() { return this.radius * 2; } get scene() { return this._scene; } set scene(scene) { if (scene && this._scene) { throw new Error("Tried overriding existing `scene` property with a new one. Object cannot be a part of two scenes"); } this._scene = scene; } _predraw() { this.predraw(); } predraw() { } _draw() { this.draw(); } draw() { } _update() { this.update(); } update() { } on(event, callback) { const regionalizedCallback = () => { if (this.mouseOver()) { callback(); } }; switch (event) { case "mousedown": case "mousepressed": case "mouseover": this._listeners[event] = this._listeners[event] || []; this._listeners[event].push(regionalizedCallback); break; default: throw new Error("Unknown event type " + event); } } off(event) { switch (event) { case "mousedown": case "mousepressed": case "mouseover": this._listeners[event] = []; break; default: throw new Error("Unknown event type " + event); } } addDependency(dep) { this._dependencies.push(dep); if (this.scene) { this.scene.add(dep); } } detachDependency(dep) { this._dependencies = this._dependencies.filter((dependency) => dep !== dependency); } removeDependencies() { this.........完整代码请登录后点击上方下载按钮下载查看
网友评论0