多敌人间隔射击异常求助:单敌人正常多敌人仅其一开火
问题根源诊断
嘿,我一眼就看出问题出在哪了——这是典型的闭包作用域陷阱加上方法上下文误用导致的,咱们拆解来看:
setInterval的闭包坑:你在创建敌人时,每个敌人的
interval回调里用了enemy.shooting = true,但这里的enemy是后面循环里声明的外层变量。当循环跑完,所有定时器回调都会共享这个enemy的引用,最终它指向数组的最后一个敌人——所以只有最后一个敌人会被触发射击状态。shoot方法的上下文错误:你的
shoot方法里写了enemy.x + 40,但这里的enemy同样是外层的全局变量,不是当前要开火的那个敌人,子弹位置自然也不对。
针对性修复方案
咱们直接改这两个核心问题,同时加一点内存优化:
- 给每个定时器回调创建独立作用域,捕获当前敌人的专属引用;
- 在
shoot方法里用this指代当前调用方法的敌人; - 别忘了后续敌人被销毁时清理定时器,避免内存泄漏。
修改后的完整代码
function enemies() { if (enemy_soldiers.length == 0) { level += 0.2; // 用let替代var,避免循环变量作用域问题 for (let i = 0; i < (1 + Math.floor(Math.round(level))); i++) { var gx = 1450; var gy = getRandom(430, 630); // 先创建当前敌人对象,存为独立变量 const currentEnemy = { x: gx, y: gy, l: gl, d: getRandom(350, 600), shooting: false, interval: null, shoot: function() { // 用this指代当前开火的敌人,拿到正确的坐标 enemy_bullets.push({ x: this.x + 40, y: this.y + 87, vel: 10, }); } }; // 用立即执行函数(IIFE)创建独立作用域,捕获currentEnemy currentEnemy.interval = setInterval((function(enemyRef) { return function() { enemyRef.shooting = true; }; })(currentEnemy), fire_rate); enemy_soldiers.push(currentEnemy); } } } var enemy; gctx.clearRect(0, 0, 1400, 800); // 推荐用forEach遍历数组,比for...in更安全 enemy_soldiers.forEach(function(enemy) { drawenemy(enemy.x, enemy.y, enemy.l); // AI移动逻辑 const dist = distance(enemy.x, enemy.y, cx, cy); if (dist >= enemy.d) { enemy.x += enemy.x > cx ? -vel : vel; } // 射击逻辑 if (dist <= 600) { if (enemy.shooting) { enemy.shoot(); enemy.shooting = false; } gbctx.clearRect(0, 0, 1400, 800); // 同样用forEach遍历子弹数组 enemy_bullets.forEach(function(enemy_bullet, index) { enemy_bullet.x -= enemy_bullet.vel; if (enemy_bullet.x > 1400 || enemy_bullet.x < -5 || enemy_bullet.y > 800 || enemy_bullet.y < -5) { enemy_bullets.splice(index, 1); } drawEnemyBullet(enemy_bullet.x, enemy_bullet.y); }); } });
额外小贴士
- 当敌人被消灭(比如从
enemy_soldiers数组移除)时,一定要调用clearInterval(enemy.interval),不然定时器会一直跑,浪费内存; - 尽量用
let/const替代var,它们的块级作用域能避免很多莫名其妙的作用域问题; for...in是用来遍历对象属性的,遍历数组最好用forEach或者for...of,不会踩到原型链属性的坑。
内容的提问来源于stack exchange,提问作者PVV2
相关产品推荐
相关产品推荐

