You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
        }
    }
}

故障根因

  1. 逻辑执行顺序错误:当前代码先执行玩家/地图位移,再运行碰撞检测。碰撞逻辑中修改lukeSpeed、回退坐标的操作,无法影响本帧已经完成的位移计算,等于碰撞检测完全没拦住移动。
  2. 循环逻辑覆盖bug:遍历碰撞数组时,只要任意一个碰撞盒未触发碰撞,就会直接把lukeSpeed重置为10。如果数组内有多个碰撞对象,前一个碰撞触发的减速、回退逻辑,会被后一个未触发碰撞的对象直接覆盖失效。
  3. 碰撞对象静默失效:代码没有校验cantGoThrough数组内的对象是否存在。迁移主地图时如果HitboxZaun实例名写错、所在关键帧未加载,存入数组的会是null值,hitTestObject传入null时不会触发碰撞、也不会抛出控制台错误,直接表现为功能完全失效。
  4. 卷轴移动适配缺失:当玩家走到屏幕边缘、触发背景卷轴移动时,碰撞后的回退逻辑只修改玩家坐标,没有同步处理背景位移,此时全局坐标下的碰撞位置计算错误,会出现穿模。

修复方案

  1. 调整帧循环执行顺序:将碰撞检测逻辑放到位移代码之前,先判断碰撞状态再决定是否执行移动,从根源避免先移动后检测的穿模问题。
  2. 重构碰撞循环逻辑:遍历碰撞数组时单独维护isCollided布尔标记,所有碰撞盒遍历完成后,再根据标记统一处理速度、回退逻辑,避免循环内的状态覆盖。
  3. 增加碰撞对象校验:loadArray函数中加入非空判断,确认所有存入数组的碰撞盒实例真实存在,第一时间发现实例名错误、元件未加载的问题。
  4. 补全卷轴场景的碰撞回退逻辑:碰撞触发时,同时判断当前是玩家移动阶段还是背景卷轴阶段,对应回退玩家坐标或背景坐标,保证全局坐标下碰撞位置正确。

内容的提问来源于stack exchange,提问作者Fids

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 04:42:27