Home > ActionScript 3.0, Flash Player 10, Mathematics > Z-sorting 3D DisplayObjects for Flash Player 10

Z-sorting 3D DisplayObjects for Flash Player 10

October 13th, 2008

The next release of the Flash Player has support for 2.5D- which boils down to 3D properties, appropriate perspective, but no depth sorting. This method takes a DisplayObjectContainer, transforms its children into world space and sorts them along that z-axis. It’s sloppy because it’s DisplayObject-based and so there will be noticeable “pop” when a DisplayObject is sorted onto the top of the display list in close proximity to another. That said it should be perfect for carousels and other views that aren’t super tight.

Here’s a demo

And here’s the algorithm:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public static function simpleZSort3DChildren( doc:DisplayObjectContainer, recurse:Boolean = true ) : void
{
 
    //transforms from local to world oordinate frame
    var transform:Matrix3D = doc.transform.getRelativeMatrix3D( doc.stage );
 
    var numChildren:int = doc.numChildren;
 
    //v = ( n * 3 )- (x,y,z) set for each child
    var vLength:int = numChildren * 3;
 
    var vLocal:Vector.<Number> = new Vector.<Number>( vLength, true );
    var vWorld:Vector.<Number> = new Vector.<Number>( vLength, true );
 
    //insertion point for child’s coordinates into state vector
    var vIndex:int = 0;
 
    for( var i:int = 0; i <numChildren; i++ )
    {
 
        var child:DisplayObject = doc.getChildAt( i );
        if( recurse && child is DisplayObjectContainer ) simpleZSort3DChildren( DisplayObjectContainer( child ), true );
 
        vLocal[ vIndex ] = child.x;
        vLocal[ vIndex + 1 ] = child.y;
        vLocal[ vIndex + 2 ] = child.z;
 
        vIndex += 3;
 
    }
 
    //populates vWorld with children coordinates in world space
    transform.transformVectors( vLocal, vWorld );
 
 
 
    //bubble sorts children along world z-axis
    for( i = numChildren - 1; i > 0; i-- )
    {
 
        var hasSwapped:Boolean = false;
 
        vIndex = 2;
 
        for( var j:int = 0; j < i; j++ )
        {
 
            //z value at that index for each child
            var z1:Number = vWorld[ vIndex ];
 
            vIndex += 3;
 
            var z2:Number = vWorld[ vIndex ];
 
            if( z2> z1 )
            {
 
                //swap
                doc.swapChildrenAt( j, j + 1 );
 
                //swap z values (don’t need to change x and y because they’re not used anymore)
                vWorld[ vIndex - 3 ] = z2;
                vWorld[ vIndex ] = z1;
 
                //mark as swapped
                hasSwapped = true;
 
            }
 
        }
 
        //if there was no swap, we don’t need to iterate again
        if( !hasSwapped ) return;
 
    }
 
 
}

drew ActionScript 3.0, Flash Player 10, Mathematics

  1. October 13th, 2008 at 19:01 | #1

    Cool, I love random colors!
    notice how they can get really ugly with lots of green an violet :)

  2. November 13th, 2008 at 04:17 | #2

    Nice example. Thanks for sharing.

  3. November 13th, 2008 at 05:12 | #3

    I’m not able to view the example :( I have to manually type the name to the SWF file to view it. Perhaps the embedding script is the culprit.

  4. December 19th, 2008 at 12:55 | #4

    yeah, z-Sorting with Flash 10 is very dissatisfying.
    anyhow, nice example!

    I´ve setup my own little “3d debris pattern” with Flash10, and zorting all my cubes along the z-Axis was very sadly, ´cause of the fact you can see the clippings.

    http://www.dasprinzip.com/prinzipiell/2008/12/04/debris-pattern/

    cheers
    -frank

  5. December 22nd, 2008 at 13:40 | #5

    nice! i’m also using this for thing i’m working on.
    i had to add a couple of minor checks. the first one is to make sure that there’s already Matrix3D on the container (because, there are cases in which it hasn’t been initialized yet)… the second one is to make sure the component has been added to the display list before trying to sort anything.

    the tests perform really well. thanks, man.

    http://rojored.googlecode.com/svn/trunk/docs/example/carousel/c03/bin/Main.swf

    I packed the method on a util class on a swc… are planning to release this stuff any other way?

  6. drew
    December 22nd, 2008 at 17:09 | #7

    Awesome, that looks really good, Gabo!

    As far as doing checks for no Matrix3D and existence on display list, I think it makes a lot of sense to leave that up to the user. This is pretty hammered down and efficient. Maybe I’ll make a static class that allows the user to register for sorting so that I can avoid some of the complex type creation at important-time and handle those sorts of issues…

  7. April 16th, 2009 at 04:33 | #8

    hi,

    thanks for that great code! i’ve found your post on the flash blog where another z sorting class is discussed. i’ve putted your method into a static class for quick access. but one question popped up:

    - why do you use getRelativeMatrix3D( container.stage ); wheres as getRelativeMatrix3D( container ); should work the same way?

    thanks for your answer!

  8. drew
    April 16th, 2009 at 09:26 | #9

    the idea is that it sorts in global coordinates. if the sort only accounted for the container’s transformations, any transformations above it in the display list would be neglected.

  9. JK
    May 9th, 2009 at 12:05 | #10

    this’s great! thank you drew

    there’s a change that child is null
    i’ve try put a condition before doing recurse check

    if(!(child==null && i==0)){ line 22 to 28 }

    and everything seems to be fine
    i wonder if there’s other way to solve this, any suggestion?

  10. June 3rd, 2009 at 22:57 | #11

    This is great code, but after I installed Flash 10.02 it stopped working on this line:

    transform.transformVectors( vLocal, vWorld );

    with this error message:

    TypeError: Error #1009: Cannot access a property or method of a null object reference.

    Help!

  11. drew
    June 4th, 2009 at 08:27 | #12

    Hey Philip,

    It looks like the transformation matrix didn’t get defined which probably means your DisplayObjectContainer isn’t on the display list yet (makes use of its stage reference). Let me know if that’s the case.

  12. drew
    June 4th, 2009 at 08:30 | #13

    JK :

    this’s great! thank you drew

    there’s a change that child is null
    i’ve try put a condition before doing recurse check

    if(!(child==null && i==0)){ line 22 to 28 }

    and everything seems to be fine
    i wonder if there’s other way to solve this, any suggestion?

    i don’t think child should be able to be null… what are the circumstances??

  13. June 4th, 2009 at 10:57 | #14

    @drew

    Hi Drew,

    It is a stage object – a movieClip (called “test”) with movieClips inside. The code is on the timeline (which worked fine before 10.02). I put a delay in to see if that would help, but no luck. I even tried using addChild for the object with no luck.

    this.addEventListener(Event.ENTER_FRAME, sortZ);
    var count = 0;
    function sortZ(e) {
    count++;
    if (count > 100) {
    simpleZSort3DChildren(test, false);
    }
    }

    function simpleZSort3DChildren( doc:DisplayObjectContainer, recurse:Boolean = true ) : void
    …rest of code copied exactly from yours…

  14. June 5th, 2009 at 04:45 | #15

    This works really well for my 3d car experiments, thanks for putting up the code.

  15. June 5th, 2009 at 12:37 | #16

    @drew

    some more info on the error I’m seeing with Flash 10.02. This statement:

    var transform:Matrix3D = doc.transform.getRelativeMatrix3D( doc.stage );

    results in a null value for the var “transform”. But I’ve confirmed “doc” and “doc.stage” are valid objects. The same error occurs if I create a MovieClip in code, add it to the display list, and pass it to simpleZSort3DChildren. Anyone else seeing this problem?

  16. June 5th, 2009 at 15:38 | #17

    @Philip van Allen

    Okay, here’s a workaround. It seems the DisplayObjectContainer must have its “z” property set explicitly. Drew’s code works in Flash 10.02 if you insert the following line

    doc.z = doc.z;

    Before this line

    transform = doc.transform.getRelativeMatrix3D( doc.stage );

  17. July 13th, 2009 at 02:10 | #18

    Thanks. I am using CS3 to publish as Fp10 3D . Your Sorting code shows error in it. Should i get CS4 or is there any trick?

  18. drew
    July 13th, 2009 at 07:22 | #19

    what is the error you’re getting? compile-time or run-time? a couple assumptions are made that could result in a run-time error. 1) the DisplayObjectContainer is on the display list. 2) as in Philip’s comments above, that a 3D property has been set on the DisplayObjectContainer.

  19. mikeccuk
    November 3rd, 2009 at 18:08 | #20

    Hi im new to AS3
    I saved the code above as engine.as file, import engine; on the first line
    I added a 3d cube made in a symbol
    made a frame 2 -> 1 loop
    //
    addEventListener(Event.ENTER_FRAME,newlooper);

    function newlooper(event:Event){
    this.obj1.rotationX=mouseX
    this.obj1.rotationY=mouseY

    }
    ///

    I dont see the sorting work at all. The box still see inside out.
    Any ideas?

  20. Alexis
    January 27th, 2010 at 15:11 | #21

    Can you give the source of a working example? I’m also getting a null value for transform = doc.transform.getRelativeMatrix3D( doc.stage ); even though 3D properties have indeed been set on my doc

  21. drew
    January 27th, 2010 at 16:55 | #22

    I don’t think I have the demo source still, but it would be easy enough to recreate it if necessary. Is your object on the display list? Otherwise stage will be null.

  22. April 4th, 2010 at 16:37 | #23

    Thank you!It helped me!

  1. No trackbacks yet.