2D Sonic平台游戏碰撞检测故障:角色穿透平台
2D Sonic平台游戏碰撞问题:Sonic穿透平台
我正在开发一款2D Sonic平台游戏,目前在实现Sonic实体与平台的碰撞功能时遇到困难——Sonic会直接穿透平台,就像collisionCheck()方法完全未生效一样。我已调试过collisionCheck()方法,且确认Sonic和平台的Bounding Box绘制正确。以下是相关代码:
sonic.js
class Sonic { constructor(game) { this.game = game; this.game.sonic = this; // special entity this.position = { x: 0, y: 0 } this.velocity = { x: 0, // 向右为正 y: 0 // 向下为正 } this.speed = 1300; this.jumpSpeed = 200; this.spinSpeed = 1400; this.spritesheet = ASSET_MANAGER.getAsset("./sprites/realSonicSheet.png"); this.updateBB(); this.animations = []; this.state = 0; this.direction = 0; this.loadAnimations(); this.ground = 550; this.onGround = false; this.BB = null; this.lastBB = null; } loadAnimations() { for (var i = 0; i < 4; i++) { // 四种状态: idle, running, jumping, spinning this.animations.push([]); for (var k = 0; k < 2; k++) { // 两个方向:左右 this.animations[i].push([]); } } // idle 状态(state=0) // 向右(direction=0) this.animations[0][0] = new Animator(this.spritesheet, 5, 723, 45, 45, 1, 0.33, 0, false, true); // 向左(direction=1) this.animations[0][1] = new Animator(this.spritesheet, 741, 38, -41, 47, 1, 0.33, 0, true, true); // 有问题的话改成true true // 跑步动画 // 向右 this.animations[1][0]= new Animator(this.spritesheet, 2, 916, 49, 45, 14, 0.08, 0, false, true); // 向左 this.animations[1][1] = new Animator(this.spritesheet, 745, 230, -49, 47, 14, 0.08, 0, false, true); // 跳跃动画 // 向右 this.animations[2][0] = new Animator(this.spritesheet, 340, 1160, 47, 56, 8, 0.08, 0, false, true); // 向左 this.animations[2][1] = new Animator(this.spritesheet, 410, 480, -47, 56, 8, 0.08, 0, false, true); // 旋转动画 // 向右 this.animations[3][0] = new Animator(this.spritesheet, 1, 1113, 47, 40, 10, 0.08, 0, false, true); // 向左 this.animations[3][1] = new Animator(this.spritesheet, 746, 427, -47, 40, 10, 0.08, 0, false, true); } updateBB() { this.BB = new BoundingBox(this.position.x, this.position.y, 155, 130, "red"); console.log("This is this.BB", this.BB); } update() { const GRAVITY = 0.5; // 先保存上一帧的碰撞盒,用于碰撞方向判断 this.lastBB = new BoundingBox(this.BB.x, this.BB.y, this.BB.width, this.BB.height, this.BB.color); let standingOnPlatform = false; let canvasHeight = 768; // 左移 if (this.game.left) { this.position.x -= this.speed * this.game.clockTick; this.direction = 1; this.state = 1; } // 旋转左移 if (this.game.spin && this.game.left) { this.position.x -= this.spinSpeed * this.game.clockTick; this.state = 3; } // 右移 if (this.game.right) { this.position.x += this.speed * this.game.clockTick; this.direction = 0; this.state = 1; } // 跳跃:仅在地面时生效 if (this.game.jump && this.onGround) { this.velocity.y = -this.jumpSpeed; this.onGround = false; this.state = 2; } // 旋转动作 if (this.game.spin) { this.position.x += this.spinSpeed * this.game.clockTick; this.position.y += 10; this.state = 3; } // 无操作时回到idle状态 if (!this.game.left && !this.game.right && !this.game.jump && !this.game.spin) { this.state = 0; } // 应用重力 this.velocity.y += GRAVITY; this.position.y += this.velocity.y; // // 检测是否掉出屏幕 // if (this.position.y > canvasHeight) { // window.location.reload(); // this.position.y = 300; // } // 更新当前位置的碰撞盒 this.updateBB(); // 执行碰撞检测 this.collisionCheck(); // 更新地面状态 this.onGround = standingOnPlatform; } collisionCheck() { this.game.entities.forEach(entity => { if (this !== entity && entity.BB && this.BB.collide(entity.BB)) { if (entity instanceof Platform) { // 从上方落到平台 if (this.velocity.y > 0 && this.lastBB.bottom <= entity.BB.top) { this.position.y = entity.BB.top - this.BB.height; this.velocity.y = 0; this.onGround = true; return; } // 从下方撞到平台 if (this.velocity.y < 0 && this.lastBB.top >= entity.BB.bottom) { this.position.y = entity.BB.bottom; this.velocity.y = 0; return; } // 从右侧撞到平台 if (this.velocity.x > 0 && this.lastBB.right <= entity.BB.left) { this.position.x = entity.BB.left - this.BB.width; this.velocity.x = 0; return; } // 从左侧撞到平台 if (this.velocity.x < 0 && this.lastBB.left >= entity.BB.right) { this.position.x = entity.BB.right; this.velocity.x = 0; return; } } } }); } draw(ctx) { if(this.state < 0 || this.state > 3) this.state = 0; let done = this.animations[this.state][this.direction].drawFrame(this.game.clockTick, ctx, this.position.x - this.game.camera.x , this.position.y); if (done) { this.animations[this.state][this.direction].elapsedTime = 0; this.state = 0; } if (PARAMS.DEBUG) { this.game.ctx.strokeStyle = "red"; this.game.ctx.strokeRect(this.BB.x - this.game.camera.x, this.BB.y, this.BB.width, this.BB.height); } } }
platform.js
class Platform { constructor(game, x, y, width, height) { this.game = game; this.position = { x: x, y: y }; this.width = width; this.height = height; // 统一碰撞盒属性名为BB,和Sonic保持一致 this.BB = { x: this.position.x, y: this.position.y, width: this.width, height: this.height }; this.spritesheet = ASSET_MANAGER.getAsset("./sprites/floor.png"); } updateBB() { this.BB.x = this.position.x; this.BB.y = this.position.y; } update() { this.updateBB(); } draw(ctx) { ctx.drawImage(this.spritesheet, this.position.x - this.game.camera.x, this.position.y, this.width, this.height); if (PARAMS.DEBUG) { ctx.strokeStyle = "lime"; ctx.strokeRect( this.BB.x - this.game.camera.x, this.BB.y, this.BB.width, this.BB.height ); } } }
核心问题与修复说明
碰撞盒属性名不匹配:原Platform类使用
boundingBox作为碰撞盒属性,但Sonic的碰撞检测代码判断的是entity.BB,导致碰撞检测无法识别平台。已将Platform的碰撞盒属性名改为BB,统一命名规则。lastBB更新时机错误:原代码每次调用
updateBB()都会覆盖lastBB,导致碰撞判断时的“上一帧位置”与当前帧位置一致,无法正确识别碰撞方向。现在调整为在移动操作前保存当前碰撞盒到lastBB。重复应用重力:原代码中两次执行重力叠加和位置更新,导致Sonic下落速度翻倍,即使碰撞生效也可能快速穿透平台。已移除重复的重力代码,仅保留一次。
跳跃逻辑不合理:原代码直接修改
position.y实现跳跃,且未判断是否在地面,导致无限跳。改为通过修改velocity.y实现跳跃,且仅在onGround为true时触发。地面状态未正确同步:原代码中
standingOnPlatform变量始终为false,导致onGround状态无法正确更新。现在在碰撞检测中检测到站在平台时,直接设置onGround为true。
内容的提问来源于stack exchange,提问作者J45
相关产品推荐
相关产品推荐

