:::: 菜单 ::::

getBounds不给力啊

刚发现Flash的DisplayObject/getBounds在只有矢量图形的补间中会计算不出正确的大小,总是得到补间所在的前一个关键帧中的形状区域。

证据:getBounds不给力的证据

延伸阅读


  1. Pingback: 纬度网 Unknow Unknow

  2. 我也发现了,还以为是我的问题.有没有发现替代getBounds()的方法.

  3. @kitsudo, 测试中已经得出了“总是得到补间所在的前一个关键帧中的形状区域”,你可以测试,不管拉多长的补间都一样。并不是渲染异步的问题(这里是运算而并非渲染,并不受舞台渲染的影响,代码是同步线性执行的)。

  4. package com.blessjoy.component {
    import com.pblabs.engine.PBE;
    import com.pblabs.engine.core.IAnimatedObject;

    import flash.display.Bitmap;
    import flash.display.MovieClip;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.Point;

    /**
    * 将一个MovieClip转化为一个Bitmap提高效率(通用版本, 对MC本身无任何要求)
    * @author thinkpad
    */
    public class CommonMovieClipAdvRenderer extends Sprite implements IAnimatedObject {
    private var mcInfo : MovieClipInfo;

    public var currentFrame : int = 0;

    private var fps : int;

    private var delta : int;
    private var last : Number;
    private var isPlaying : Boolean;
    private var loop : Boolean;

    private var autoPlay : Boolean;
    private var bitmap : Bitmap;

    public function clone() : CommonMovieClipAdvRenderer {
    var mcr : CommonMovieClipAdvRenderer = new CommonMovieClipAdvRenderer(null, fps, autoPlay, loop);
    mcr.mcInfo = mcInfo;
    return mcr;
    }

    public function CommonMovieClipAdvRenderer(mc : MovieClip,fps : int = 0,autoPlay : Boolean = true,loop : Boolean = true,start : int = 1,end : int = -1) {
    this.autoPlay = autoPlay;
    this.loop = loop;
    this.fps = fps;
    addEventListener(Event.REMOVED_FROM_STAGE, onRemove);
    addEventListener(Event.ADDED_TO_STAGE, onAdd);
    if(fps) {
    this.delta = 1000 / fps;
    }
    this.mcInfo = new MovieClipInfo();
    bitmap = new Bitmap();
    addChild(bitmap);
    if(mc) {
    mc.gotoAndStop(start);
    mcInfo.init(mc, start, end);
    }
    }

    private function onAdd(event : Event) : void {
    if(autoPlay) {
    play();
    }
    PBE.processManager.addAnimatedObject(this);
    }

    private function onRemove(event : Event) : void {
    stop();
    PBE.processManager.removeAnimatedObject(this);
    }

    public function play() : void {
    if(!isPlaying) {
    isPlaying = true;
    }
    }

    public function gotoAndPlay(frame : int) : void {
    gotoAndStop(frame);
    play();
    }

    public function gotoAndStop(frame : int) : void {
    currentFrame = Math.min(mcInfo.totalFrames, frame);
    }

    /**
    * 初始化完毕
    */
    public function get inited() : Boolean {
    return mcInfo.inited;
    }

    public function stop() : void {
    if(isPlaying) {
    isPlaying = false;
    }
    }

    public function onFrame(deltaTime : Number) : void {
    if(!mcInfo.inited) {
    return;
    }
    if(!currentFrame) {
    currentFrame = 1;
    } else {
    if(!isPlaying) {
    return;
    }
    }
    if(delta && PBE.processManager.virtualTime – last mcInfo.totalFrames) {
    stop();
    currentFrame = 1;
    } else {
    bitmap.bitmapData = mcInfo.frames[(currentFrame – 1) % mcInfo.totalFrames];
    var position : Point = mcInfo.positions[(currentFrame – 1) % mcInfo.totalFrames];
    bitmap.x = position.x;
    bitmap.y = position.y;
    currentFrame++;
    }
    }

    override public function set visible(value : Boolean) : void {
    super.visible = value;
    if(!value) {
    stop();
    } else {
    play();
    }
    }
    }
    }

    import com.pblabs.engine.PBE;

    import flash.display.BitmapData;
    import flash.display.MovieClip;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;

    internal class MovieClipInfo {
    public var frames : Array /*BitmapData*/ = [];
    public var positions : Array /*Point*/ = [];

    public var totalFrames : int;

    public var inited : Boolean;

    public function init(mc : MovieClip,start : int = 1,end : int = -1) : void {
    if(start > mc.totalFrames + 1 + end) {
    totalFrames = frames.length;
    inited = true;
    } else {
    PBE.pushStageQuality(“best”);
    var bound : Rectangle = mc.getBounds(mc);
    var width : int = Math.ceil(bound.width);
    var height : int = Math.ceil(bound.height);
    var oldScaleX : Number = mc.scaleX;
    var oldScaleY : Number = mc.scaleY;
    if(oldScaleX != 1 || oldScaleY != 1) {
    width = width * oldScaleX;
    height = height * oldScaleY;
    }
    var bd : BitmapData = new BitmapData(Math.ceil(mc.width) * Math.ceil(oldScaleX), mc.height * oldScaleY, true, 0x0);
    var mtx : Matrix = new Matrix();
    mtx.a = oldScaleX;
    mtx.d = oldScaleY;
    mtx.tx = -Math.floor(bound.x * oldScaleX);
    mtx.ty = -Math.floor(bound.y * oldScaleY);
    bd.draw(mc, mtx);
    PBE.popStageQuality();
    frames.push(bd);
    positions.push(new Point(bound.x, bound.y));
    mc.gotoAndStop(start + 1);
    PBE.processManager.schedule(1000 / PBE.mainStage.frameRate, this, init, mc, start + 1, end);
    }
    }
    }

  5. @鼠标炸弹, 这个是我之前写的一个MC转Bitmap的工具
    最早的时候我是希望可以在构造函数中就全部缓存
    结果出现了全部是第一帧的情况(先gotoAndStop 然后draw的)
    之后摸索了半天就成了现在这个版本了
    测试过形状补间
    通过getBounds获得的大小又那么一点点的误差(大概几个像素应该是取整导致的问题, scale大些比较明显)

    这个代码的灵感来自于一个国内的人写的引擎CopyEngine (GoogleCode上有 这个功能的代码在copyengine.cache.MemoryPoolcache方法 copyengine.cache.CacheMovieClip 是他用到的工具类)
    不过它的这个工具类在处理使用图形做动画的时候会有问题, 对mc有一定要求
    例子有需要的话可以发给你

    (下面的那个代码用到了PBE框架来替代了EnterFrame的部分, 而且有mc第一周期没有显示的问题, 这个可以弥补
    CopyEngine的方案没有这个问题
    )

  6. @鼠标炸弹, 形状补间所使用的MorphShape不能被正确捕捉
    代码的局限性就体现在这里….
    测试的时候没有注意
    用的图形是方变圆, 形状虽然变了
    但是尺寸变化不大……
    汗了….

  7. 呵呵,谢谢kitsudo ,收下了CommonMovieClipAdvRenderer 这几天一直在想这个问题呢,呵呵 ,在这里找到了答案,偶会勤来的..