This is an archived tutorial from the kirupa.com legacy collection. It covers software that may no longer be available, but it is kept online because the ideas still hold up.
This example has two fully solid pyramids. One uses the visibility function for backface culling and the other uses separate movieclips for faces and uses swapDepths for each of those clips to properly arrange them. Those depths are based on an average z of the 3 points making up each face.
[ two drawn solid pyramids ]
this.createEmptyMovieClip("scene1", 1);
scene1._x = 80;
scene1._y = 150;
this.createEmptyMovieClip("scene2", 2);
scene2._x = 220;
scene2._y = 150;
focalLength = 300;
isVisibleBetween = function (a, b, c) {
if (((b._y-a._y)/(b._x-a._x)-(c._y-a._y)/(c._x-a._x)<0) ^ (a._x<=b._x == a._x>c._x)) {
return true;
} else {
return false;
}
};};
make3DPoint = function(x,y,z){
var point = new Object();
point.x = x;
point.y = y;
point.z = z;
return point;
};
Transform3DPointsTo2DPoints = function(points, axisRotations){
var TransformedPointsArray = [];
var sx = Math.sin(axisRotations.x);
var cx = Math.cos(axisRotations.x);
var sy = Math.sin(axisRotations.y);
var cy = Math.cos(axisRotations.y);
var sz = Math.sin(axisRotations.z);
var cz = Math.cos(axisRotations.z);
var x,y,z, xy,xz, yx,yz, zx,zy, scaleFactor;
var i = points.length;
while (i--){
x = points[i].x;
y = points[i].y;
z = points[i].z;
// rotation around x
xy = cx*y - sx*z;
xz = sx*y + cx*z;
// rotation around y
yz = cy*xz - sy*x;
yx = sy*xz + cy*x;
// rotation around z
zx = cz*yx - sz*xy;
zy = sz*yx + cz*xy;
scaleFactor = focalLength/(focalLength + yz);
x = zx*scaleFactor;
y = zy*scaleFactor;
z = -yz;
TransformedPointsArray[i] = make3DPoint(x, y, z);
}
return TransformedPointsArray;
};
pointsArray = [
make3DPoint(-50,29,29),
make3DPoint(0,29,-58),
make3DPoint(50,29,29),
make3DPoint(0,-58,0)
];
scene1.pyramidAxisRotations = make3DPoint(0,0,0);
rotatePyramid1 = function(){
this.pyramidAxisRotations.y -= this._xmouse/2000;
this.pyramidAxisRotations.x += this._ymouse/2000;
var pts2D = Transform3DPointsTo2DPoints(pointsArray, this.pyramidAxisRotations);
var mouseIsDown = Key.isDown(1);
this.clear();
this.lineStyle(2,0x000000,100);
if (isVisibleBetween(pts2D[0], pts2D[1], pts2D[2])){
if (!mouseIsDown) this.beginFill(0x800000, 100);
this.moveTo(pts2D[0].x, pts2D[0].y);
this.lineTo(pts2D[1].x, pts2D[1].y);
this.lineTo(pts2D[2].x, pts2D[2].y);
this.lineTo(pts2D[0].x, pts2D[0].y);
this.endFill();
}
if (isVisibleBetween(pts2D[3], pts2D[1], pts2D[0])){
if (!mouseIsDown) this.beginFill(0x008000, 100);
this.moveTo(pts2D[3].x, pts2D[3].y);
this.lineTo(pts2D[1].x, pts2D[1].y);
this.lineTo(pts2D[0].x, pts2D[0].y);
this.lineTo(pts2D[3].x, pts2D[3].y);
this.endFill();
}
if (isVisibleBetween(pts2D[3], pts2D[2], pts2D[1])){
if (!mouseIsDown) this.beginFill(0x000080, 100);
this.moveTo(pts2D[3].x, pts2D[3].y);
this.lineTo(pts2D[2].x, pts2D[2].y);
this.lineTo(pts2D[1].x, pts2D[1].y);
this.lineTo(pts2D[3].x, pts2D[3].y);
this.endFill();
}
if (isVisibleBetween(pts2D[3], pts2D[0], pts2D[2])){
if (!mouseIsDown) this.beginFill(0x808000, 100);
this.moveTo(pts2D[3].x, pts2D[3].y);
this.lineTo(pts2D[0].x, pts2D[0].y);
this.lineTo(pts2D[2].x, pts2D[2].y);
this.lineTo(pts2D[3].x, pts2D[3].y);
this.endFill();
}
};
scene1.onEnterFrame = rotatePyramid1;
scene2.pyramidAxisRotations = make3DPoint(0,0,0);
scene2.createEmptyMovieClip("face1",1);
scene2.createEmptyMovieClip("face2",2);
scene2.createEmptyMovieClip("face3",3);
scene2.createEmptyMovieClip("face4",4);
rotatePyramid2 = function(){
this.pyramidAxisRotations.y -= this._xmouse/2000;
this.pyramidAxisRotations.x += this._ymouse/2000;
var pts2D = Transform3DPointsTo2DPoints(pointsArray, this.pyramidAxisRotations);
var mouseIsDown = Key.isDown(1);
with(this.face1){
clear();
lineStyle(2,0x000000,100);
if (!mouseIsDown) beginFill(0x800000, 100);
moveTo(pts2D[0].x, pts2D[0].y);
lineTo(pts2D[1].x, pts2D[1].y);
lineTo(pts2D[2].x, pts2D[2].y);
lineTo(pts2D[0].x, pts2D[0].y);
endFill();
swapDepths((pts2D[0].z+pts2D[1].z+pts2D[2].z)/3);
}
with(this.face2){
clear();
lineStyle(2,0x000000,100);
if (!mouseIsDown) beginFill(0x008000, 100);
moveTo(pts2D[0].x, pts2D[0].y);
lineTo(pts2D[1].x, pts2D[1].y);
lineTo(pts2D[3].x, pts2D[3].y);
lineTo(pts2D[0].x, pts2D[0].y);
endFill();
swapDepths((pts2D[0].z+pts2D[1].z+pts2D[3].z)/3);
}
with(this.face3){
clear();
lineStyle(2,0x000000,100);
if (!mouseIsDown) beginFill(0x000080, 100);
moveTo(pts2D[1].x, pts2D[1].y);
lineTo(pts2D[2].x, pts2D[2].y);
lineTo(pts2D[3].x, pts2D[3].y);
lineTo(pts2D[1].x, pts2D[1].y);
endFill();
swapDepths((pts2D[1].z+pts2D[2].z+pts2D[3].z)/3);
}
with(this.face4){
clear();
lineStyle(2,0x000000,100);
if (!mouseIsDown) beginFill(0x808000, 100);
moveTo(pts2D[2].x, pts2D[2].y);
lineTo(pts2D[0].x, pts2D[0].y);
lineTo(pts2D[3].x, pts2D[3].y);
lineTo(pts2D[2].x, pts2D[2].y);
endFill();
swapDepths((pts2D[2].z+pts2D[0].z+pts2D[3].z)/3);
}
};
scene2.onEnterFrame = rotatePyramid2;
|
|
SUGGESTION: Tips for Speed Optimization |
| Flash
isn't fast. Remember that. Flash has
a lot to put up with in terms of showing
things on the screen as well as calculating
everything it needs to at the request
of your Actionscript. Because the Flash
player is meant to be a light and lean
plugin, there can only be so much done
to enhance its speed. With that in mind,
you will need to keep your actionscript
as proficient and efficient as you can
when dealing with a lot of operations
that need to be calculated every frame
- as is the case with 3D in Flash. Here
are some tips you can apply to your
code to help speed things up. Remember,
every little bit counts:
|
|
Using the separate movieclips for each face opens up some possibilities in terms of interaction with those faces. As movieclips, each face is completely separate from the other faces in the shape. Being that they are all their own individual clips, you can assign clip specific actions to certain and single faces. For example, a single face can act as a button. A 3D object with interactive button-faces? Exciting, isn't it?
That wraps up this tutorial. A huge thank you to all of you who buy kirupa's books, became a paid subscriber, watch the videos, and/or interact on the forums. Your support is what keeps writing like this online! 😇
This tutorial was written by senocular, also known as Trevor McCauley. He has been one of this community's most generous teachers since the early Flash days, and he is still around: find him on senocular.com and on the forums.
:: Copyright KIRUPA 2026 //--