如何在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
相关产品推荐
相关产品推荐

