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

如何在ActionScript 3中实现Pac-Man的平滑移动?

ActionScript 3 吃豆人克隆游戏:整数像素限制下的平滑移动实现方案

针对你在整数像素移动限制下的多档位速度卡顿问题,以下是几种可落地的解决方案:

方案1:累积小数位移,整数帧应用(首选)

核心逻辑是维护位移累积器,每帧计算浮点位移并累积,当累积值达到整数阈值时,将整数部分应用到坐标,剩余小数继续累积。这种方式避免了整帧0移动的情况,让移动间隔更均匀,视觉上完全消除卡顿。

修改后的核心代码示例

import flash.display.MovieClip;
import flash.events.Event;
import Configure.Config;

public final class Pacman extends MovieClip {

    private static var _instance:Pacman;
    private var gridx:Number;
    private var gridy:Number;
    private var config:Config;
    private var decx:Number;
    private var decy:Number;
    private var offsetX:Number = 0;
    private var offsetY:Number = 24;
    // 位移累积器,存储未达到整数的浮点位移
    private var xAccumulator:Number = 0;
    private var yAccumulator:Number = 0;
    private const BASE_SPEED:Number = 75.75; // 基础速度(像素/秒)
    private const FPS:Number = 38; // 项目固定帧率
    private var currentSpeedPercent:Number = 100; // 当前速度百分比

    public function Pacman(_x:Number=112, _y:Number=212) {
        if( _instance ){
            throw new Error("Singleton... use getInstance()");
        } 
        _instance = this;
        this.addEventListener( Event.ENTER_FRAME, enterFrameHandler );
        x = _x; 
        y = _y;
        config = Config.getInstance();
    }

    public static function getInstance():Pacman{
        if( !_instance ){
            new Pacman();
        } 
        return _instance;
    }
    
    // 动态设置速度百分比(支持40%-105%,步长5%)
    public function setSpeedPercent(percent:Number):void {
        currentSpeedPercent = Math.max(40, Math.min(105, percent));
    }
    
    private function enterFrameHandler(e:Event):void {
        var _parent = MovieClip(parent);
        var moved:Boolean = false;
        var dx:Number = 0;
        var dy:Number = 0;
        // 计算当前速度对应的每帧浮点位移
        var pixelsPerFrame:Number = BASE_SPEED * (currentSpeedPercent / 100) / FPS;

        updateGridPosition();

        // 方向判断与位移计算
        if(config.isKeyDown(37) && canMoveLeft()) {
            dx = -pixelsPerFrame;
            rotation = 0;
            moved = true;
        }
        if(config.isKeyDown(38) && canMoveUp()) {
            dy = -pixelsPerFrame;
            rotation = 90;
            moved = true;
        }
        if(config.isKeyDown(39) && canMoveRight()) {
            dx = pixelsPerFrame;
            rotation = 180;
            moved = true;
        }
        if(config.isKeyDown(40) && canMoveDown()) {
            dy = pixelsPerFrame;
            rotation = 270;
            moved = true;
        }

        // 累积位移并应用整数移动
        xAccumulator += dx;
        yAccumulator += dy;

        var intX:Number = Math.round(xAccumulator);
        var intY:Number = Math.round(yAccumulator);

        if(intX !== 0) {
            x += intX;
            xAccumulator -= intX;
        }
        if(intY !== 0) {
            y += intY;
            yAccumulator -= intY;
        }

        updateGridPosition();
        
        // 调试文本更新
        _parent.changeTxtPacX(String(x));
        _parent.changeTxtPacY(String(y));
        _parent.changeTxtPacGrid(String(gridx + ":" + gridy));
        _parent.changeTxtPacDec(String(decx + ":" + decy));
    }
    
    private function updateGridPosition():void {
        gridx = (x - offsetX) / 8;
        gridy = (y - offsetY) / 8;
        decx = gridx - Math.floor(gridx);
        decy = gridy - Math.floor(gridy);
    }
    
    private function canMoveLeft():Boolean {
        var leftcontent = MovieClip(parent).getGridContent(Math.floor((gridx + .375) - 1), Math.floor(gridy));
        return leftcontent !== "#";
    }
    
    private function canMoveUp():Boolean {
        var upcontent = MovieClip(parent).getGridContent(Math.floor(gridx), Math.floor((gridy + .375) - 1));
        return upcontent !== "#";
    }
    
    private function canMoveRight():Boolean {
        var rightcontent = MovieClip(parent).getGridContent(Math.floor((gridx - .5) + 1), Math.floor(gridy));
        return rightcontent !== "#";
    }
    
    private function canMoveDown():Boolean {
        var downcontent = MovieClip(parent).getGridContent(Math.floor(gridx), Math.floor((gridy - .5) + 1));
        return downcontent !== "#";
    }

}

关键优势

  • 完全消除0步长导致的卡顿,移动间隔均匀
  • 支持任意速度档位(40%-105%,步长5%),只需调用setSpeedPercent()即可切换
  • 保留原有网格碰撞逻辑,坐标始终为整数,不影响网格计算

方案2:优化步长表,避免0步长

如果坚持使用步长表方案,不要用0步长,而是用1、2的组合凑出平均速度,确保每帧都有移动。例如:

  • 80%速度:目标每帧平均位移≈1.594像素,可使用"22211"循环(5帧总位移8,平均1.6)
  • 40%速度:目标每帧平均位移≈0.797像素,可使用"1110"循环(4帧总位移3,平均0.75),但低速度下仍会有轻微卡顿,不如方案1平滑

方案3:高帧率+子像素渲染(可选)

如果允许开启Flash的子像素渲染(设置movieClip.smoothing = true或cacheAsBitmapMatrix),可以直接使用浮点坐标,视觉上完全平滑。但如果必须限制整数像素,此方案不适用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 06:12:32