ActionScript3.0中hitTestObject无报错但碰撞检测不生效问题
AS3 像素RPG hitTestObject碰撞检测失效问题
问题背景
- 项目为学校课程作业:像素RPG风格ActionScript 3.0小游戏
- 原方案:使用Array存储所有不可穿越场景对象的碰撞盒,配合
hitTestObject方法实现碰撞阻挡 - 故障现象:碰撞逻辑完全不生效,控制台无任何错误、日志输出,无法定位问题
- 复现特征:单独搭建的测试场景中
hitTestObject所有功能运行正常,将代码迁移到主地图、修改数组内存储的碰撞对象后,碰撞功能直接失效
问题相关核心代码
import flash.events.KeyboardEvent; import flash.ui.Keyboard; import flash.display.MovieClip; import flash.events.Event; import flash.display.Stage; this.luke_MC.gotoAndStop("Stand Front Frame"); // 玩家相关变量 var rightPressed: Boolean = new Boolean(false); var leftPressed: Boolean = new Boolean(false); var upPressed: Boolean = new Boolean(false); var downPressed: Boolean = new Boolean(false); var attackButtonPress:Boolean = new Boolean(false); var lukeAlive:Boolean = new Boolean(true); var lukeSpeed: Number = 10; var lukeSwordPower:Number = 10; var lukeDirection:String = "front"; var cantGoThrough: Array = new Array(); // 注册键盘、帧循环监听 stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownListener); stage.addEventListener(KeyboardEvent.KEY_UP, keyUpListener); stage.addEventListener(Event.ENTER_FRAME, GameLoop); loadArray(); function loadArray():void { cantGoThrough[0] = background_MC.HitboxZaun; } // 键盘按下逻辑 function keyDownListener(keyEvent: KeyboardEvent): void { if (keyEvent.keyCode == Keyboard.RIGHT) { rightPressed = true; lukeDirection = "right"; } else if (keyEvent.keyCode == Keyboard.LEFT) { leftPressed = true; lukeDirection = "left"; } else if (keyEvent.keyCode == Keyboard.UP) { upPressed = true; lukeDirection = "back"; } else if (keyEvent.keyCode == Keyboard.DOWN) { downPressed = true; lukeDirection = "front"; } else if (keyEvent.keyCode == Keyboard.SPACE){ attackButtonPress = true; } } // 键盘抬起逻辑 function keyUpListener(keyEvent: KeyboardEvent): void { if (keyEvent.keyCode == Keyboard.RIGHT) { rightPressed = false; } else if (keyEvent.keyCode == Keyboard.LEFT) { leftPressed = false; } else if (keyEvent.keyCode == Keyboard.UP) { upPressed = false; } else if (keyEvent.keyCode == Keyboard.DOWN) { downPressed = false; }else if (keyEvent.keyCode == Keyboard.SPACE){ attackButtonPress = false; } } // 帧循环主逻辑 function GameLoop(loopEvent: Event): void { // 玩家、地图移动逻辑 if (rightPressed) { if (this.luke_MC.x < 800) { this.luke_MC.x += lukeSpeed; } else if (background_MC.x > -9464) { this.background_MC.x -= lukeSpeed; } this.luke_MC.gotoAndStop("Walk Right Frame"); } else if (leftPressed) { if (this.luke_MC.x > 200) { this.luke_MC.x -= lukeSpeed; } else if (background_MC.x < -4850) { this.background_MC.x += lukeSpeed; } this.luke_MC.gotoAndStop("Walk Left Frame"); } else if (upPressed) { if (this.luke_MC.y > 200) { this.luke_MC.y -= lukeSpeed; } else if (background_MC.y < -2469) { this.background_MC.y += lukeSpeed; } this.luke_MC.gotoAndStop("Walk Back Frame"); } else if (downPressed) { if (this.luke_MC.y < 500) { this.luke_MC.y += lukeSpeed; } else if (background_MC.y > -4773) { this.background_MC.y -= lukeSpeed; } this.luke_MC.gotoAndStop("Walk Front Frame"); } else if (attackButtonPress){ // 攻击判定、敌人受击逻辑 switch (lukeDirection){ case "left": this.luke_MC.gotoAndStop("Attack Left Frame"); if(luke_MC.swordLeft_MC.hitTestObject(background_MC.enemy_MC)){ background_MC.enemy_MC.x -= 10; enemyHealth -= lukeSwordPower; } break; case "right": this.luke_MC.gotoAndStop("Attack Right Frame"); if(luke_MC.swordRight_MC.hitTestObject(background_MC.enemy_MC)){ background_MC.enemy_MC.x += 10; enemyHealth -= lukeSwordPower; } break; case "front": this.luke_MC.gotoAndStop("Attack Front Frame"); if(luke_MC.swordFront_MC.hitTestObject(background_MC.enemy_MC)){ background_MC.enemy_MC.y -= 10; enemyHealth -= lukeSwordPower; } break; case "back": this.luke_MC.gotoAndStop("Attack Back Frame"); if(luke_MC.swordBack_MC.hitTestObject(background_MC.enemy_MC)){ background_MC.enemy_MC.y += 10; enemyHealth -= lukeSwordPower; } break; } } else { // 待机帧切换 switch(lukeDirection){ case "left": this.luke_MC.gotoAndStop("Stand Left Frame"); break; case "right": this.luke_MC.gotoAndStop("Stand Right Frame"); break; case "front": this.luke_MC.gotoAndStop("Stand Front Frame"); break; case "back": this.luke_MC.gotoAndStop("Stand Back Frame"); break; } } // 场景碰撞检测 for (var i : int = 0; i< cantGoThrough.length; i++){ if (luke_MC.footBox_MC.hitTestObject(cantGoThrough[i])) { switch (lukeDirection) { case "left": lukeSpeed = 0; luke_MC.x += 10; break; case "right": lukeSpeed = 0; luke_MC.x -= 10; break; case "back": lukeSpeed = 0; luke_MC.y += 10; break; case "front": lukeSpeed = 0; luke_MC.y -= 10; break; } } else { lukeSpeed = 10; } } }
故障根因
- 逻辑执行顺序错误:当前代码先执行玩家/地图位移,再运行碰撞检测。碰撞逻辑中修改
lukeSpeed、回退坐标的操作,无法影响本帧已经完成的位移计算,等于碰撞检测完全没拦住移动。 - 循环逻辑覆盖bug:遍历碰撞数组时,只要任意一个碰撞盒未触发碰撞,就会直接把
lukeSpeed重置为10。如果数组内有多个碰撞对象,前一个碰撞触发的减速、回退逻辑,会被后一个未触发碰撞的对象直接覆盖失效。 - 碰撞对象静默失效:代码没有校验
cantGoThrough数组内的对象是否存在。迁移主地图时如果HitboxZaun实例名写错、所在关键帧未加载,存入数组的会是null值,hitTestObject传入null时不会触发碰撞、也不会抛出控制台错误,直接表现为功能完全失效。 - 卷轴移动适配缺失:当玩家走到屏幕边缘、触发背景卷轴移动时,碰撞后的回退逻辑只修改玩家坐标,没有同步处理背景位移,此时全局坐标下的碰撞位置计算错误,会出现穿模。
修复方案
- 调整帧循环执行顺序:将碰撞检测逻辑放到位移代码之前,先判断碰撞状态再决定是否执行移动,从根源避免先移动后检测的穿模问题。
- 重构碰撞循环逻辑:遍历碰撞数组时单独维护
isCollided布尔标记,所有碰撞盒遍历完成后,再根据标记统一处理速度、回退逻辑,避免循环内的状态覆盖。 - 增加碰撞对象校验:
loadArray函数中加入非空判断,确认所有存入数组的碰撞盒实例真实存在,第一时间发现实例名错误、元件未加载的问题。 - 补全卷轴场景的碰撞回退逻辑:碰撞触发时,同时判断当前是玩家移动阶段还是背景卷轴阶段,对应回退玩家坐标或背景坐标,保证全局坐标下碰撞位置正确。
内容的提问来源于stack exchange,提问作者Fids
相关产品推荐
相关产品推荐

