"As3.0 advanced animation programming": Chapter 3 equal-angle projection (I)

Source: Internet
Author: User
Tags addchild

What is isometric )?

I was confused when I first came into contact with this concept. Baidu + Google found some articles after N days:

[Reprint] Game and mathematics with an equal angle (45 degrees oblique)

[Reprint] Using illustrator and orthogonal projection principles and basic three-view plotting

And this PPT: http://files.cnblogs.com/yjmyzz/Isometric.rar

We recommend that you read these three articles with patience before reading them:

In the previous 3D basics, 3D lines and fills, background elimination, and 3D lighting, the 3D coordinate system we used basically belongs to 3D perspective projection coordinates. In layman's terms: the farther the object is from the observer, the smaller it looks, and the point that disappears in the distance. It can also be called "3D projection with disappearing points ". Although this projection method is accurate, it is strictly used in animation programming. It has a large overhead and a large amount of computing. Because the distance between the Z axis and the observation point is different, the object size needs to be adjusted (if the light source and other factors are taken into account, the processing capacity will be larger ).

 

While there is no disappearing point in the equisigned projection, the observer's eyes are always parallel, and the angle of the Projection Direction and coordinate axis is a fixed value, although it looks slightly distorted, but in general, the stereoscopy is quite obvious. The important thing is that no matter where you place the stereoscopic image formed by the equisigned projection on the screen, it looks the same.

 

The author of the original book also gave a demonstration to help you understand: Online demonstration

Obviously: the top surface of a cube (square) is deformed on the screen after an equal-angle projection and becomes a diamond. (Click the true isometric button in the online demonstration just now to observe the top of the cube in the front view)

Is the diamond produced by the square after the standard equisigned projection. The angle on the left and right side of the square is 60 degrees. By calculation, the length-width ratio is 1.73, but this ratio is usually calculated, the number of decimal places will be displayed, and the number of decimal places will be annoying (because the length or width must be set to the decimal place when drawing with PS and other software)

 

Therefore, in actual situations, it is more often used to replace the "second-class corner" (click the dimetric button in the online demonstration just now to observe the top of the cube in the front view)

It can be seen that the diamond formed by "second-class angle projection" is more flat than "equal-angle projection", but the width/high ratio of this image is exactly 2, which is easy to process and memory.

 

With the above foundations, we can do some real-time work and think about a problem: the graphics in the conventional 3D space are projected by the second-class angle (for convenience, what is the calculation (or conversion) after second-class angle projection is called an equal-angle projection?

 

Given that any geometric image is always composed of several points, we first define a conventional point3d class:

package {public class Point3D {public var x:Number;public var y:Number;public var z:Number;public function Point3D(x:Number=0,y:Number=0,z:Number=0) {this.x=x;this.y=y;this.z=z;}}}

So the above problem can also be simplified to: how to convert 3D coordinate points into 2D coordinate points on the computer screen in an equal-angle space? (Or vice versa ?)

Conversion formula:
X1 = x-z
Y1 = y * 1.2247 + (x + Z) * 0.5
Z2 = (x + Z) * 0.866-y * 0.707 -- used for layer-deep sorting.

The above formula can convert the coordinate points in the same angular space into coordinate points in the screen space. (I suggest you remember this as a theorem formula. After all, we are not studying mathematics)

This formula can be encapsulated into isoutil. As for future reuse convenience.

Package {import flash. geom. point; public class isoutils {// public static const y_correct: Number = math. cos (-math. PI/6) * Math. sqrt2; public static const y_correct: Number = 1.2247448713915892; // converts a 3D coordinate point in an equisigned space into a 2D coordinate point on the screen. Public static function isotoscreen (POS: point3d ): point {var screenx: Number = POS. x-pos.z; var screeny: Number = POS. y * y_correct + (POS. X + POS. z) * 0.5; return new point (screenx, screeny);} // converts the 2D coordinate points on the screen to a 3D coordinate point in the same corner space. Public static function screentoiso (point: point): point3d {var xpos: Number = point. Y + point. x *. 5; var ypos: Number = 0; var zpos: Number = point. y-point.x *. 5; return New point3d (xpos, ypos, zpos );}}}

Use code to draw an equal-Angle Image and test whether the above Code is correct.

Package {import flash. display. sprite; import flash. display. stagealign; import flash. display. stagescalemode; import flash. geom. point; [SWF (backgroundcolor = 0 xefefef, Height = "200", width = "300")] public class isotransformtest extends sprite {public function isotransformtest () {stage. align = stagealign. top_left; stage. scalemode = stagescalemode. no_scale; var P0: point3d = new point3d (, 0); var P1: point3d = new point3d (, 0, 0); var P2: point3d = new point3d (, 0, 100); var P3: point3d = new point3d (100,); var sp0: Point = isoutils. isotoscreen (P0); var SP1: Point = isoutils. isotoscreen (P1); var SP2: Point = isoutils. isotoscreen (P2); var SP3: Point = isoutils. isotoscreen (P3); var tile: SPRITE = new sprite (); tile. X = 150; tile. y = 50; addchild (tile); tile. graphics. linestyle (0); tile. graphics. moveTo (sp0.x, sp0.y); tile. graphics. lineto (sp1.x, sp1.y); tile. graphics. lineto (sp2.x, sp2.y); tile. graphics. lineto (sp3.x, sp3.y); tile. graphics. lineto (sp0.x, sp0.y); trace (math. cos (-math. PI/6) * Math. sqrt2); // 1.2247448713915892 trace (tile. width, tile. height); // 200 100 conforms to the above mentioned 2: 1 }}}

 

Just as in the OO world, many languages are used to making an object base class an ancestor. In the angular world, we can also create an isoobject base class, encapsulate the set of coordinate transformation in it to facilitate reuse.

Package {import flash. display. sprite; import flash. geom. point; import flash. geom. rectangle; public class isoobject extends sprite {protected VaR _ Position: point3d; protected VaR _ SIZE: Number; protected VaR _ retriable: Boolean = false; // public static const y_correct: number = math. cos (-math. PI/6) * Math. sqrt2; public static const y_correct: Number = 1.2247448713915892; Public Function isoobject (size: Number) {_ size = size; _ Position = new point3d (); updatescreenposition ();} // update the screen coordinate position protected function updatescreenposition (): void {var screenpos: Point = isoutils. isotoscreen (_ Position); super. X = screenpos. x; super. y = screenpos. y;} override public function tostring (): String {return "[isoobject (X:" + _ position. X + ", Y:" + _ position. Y + ", Z:" + _ position. Z + ")]";} // set the X, Y, and Z values of the coordinate space 3D coordinate point override public function set X (value: Number): void {_ position. X = value; updatescreenposition ();} override public function get x (): number {return _ position. x;} override public function set Y (value: Number): void {_ position. y = value; updatescreenposition ();} override public function get y (): number {return _ position. y;} override public function set Z (value: Number): void {_ position. z = value; updatescreenposition ();} override public function get Z (): number {return _ position. z;} // the attribute of _ Position encapsulates public function set position (value: point3d): void {_ Position = value; updatescreenposition ();} public function get position (): point3d {return _ position;} // used in deep sorting. You do not need to handle this public function get depth (): number {return (_ position. X + _ position. z )*. 866-_ position. y *. 707;} // public function set retriable (value: Boolean): void {_ retriable = value;} public function get retriable (): boolean {return _ retriable;} public function get size (): number {return _ size;} public function get rect (): rectangle {return New rectangle (X-size/2, z-size/2, size, size );}}}

People who have been familiar with 3D rendering or animation may know that people are often used to making a basic triangle (or other small shapes) as a basic patch, using these small patches to form a complex 3D model, we can also make a basic isotile patch class.

Package {public class drawnisotile extends isoobject {protected VaR _ height: Number; protected VaR _ color: uint; Public Function drawnisotile (size: Number, color: uint, height: Number = 0) {super (size); _ color = color; _ Height = height; Draw () ;}// draw a rectangle "SMD" protected function draw (): void {graphics. clear (); graphics. beginfill (_ color); graphics. linestyle (0, 0 ,. 5); graphics. moveTo (-size, 0); graphics. lineto (0,-size *. 5); graphics. lineto (size, 0); graphics. lineto (0, size *. 5); graphics. lineto (-size, 0);} // The height attribute is not required for the moment (it is not used in draw) override public function set height (value: number ): void {_ Height = value; Draw () ;}override public function get height (): number {return _ height ;}// set the color public function set color (value: uint): void {_ color = value; Draw ();} public function get color (): uint {return _ color ;}}}

Test isotile:

You can use this as a blank map in the game. OK, continue. It may be meaningless to draw a plane with light. consider more complex objects, such as cubes) box (in fact, the basic idea is not complex. You can increase the position of the patch, and then extend the line of the same length to connect it ).

In the angular world, from the perspective of the observer, a cube can only see three sides (top, left, right). If a light source is added, it should also reflect the differences in the brightness of the color (for example, if a light source is taken from the top right of the box, the top should be the brightest, the right side, and the left side the darker)

Package {public class drawnisobox extends drawnisotile {public function drawnisobox (size: Number, color: uint, height: Number) {super (size, color, height );} override protected function draw (): void {graphics. clear (); // extract the three-color var RED: Int = _ color> 16; var Green: Int = _ color> 8 & 0xff; vaR Blue: Int = _ color & 0xff; // if the light source is in the upper-right corner (so the light source is in the dark area on the left, the brightest area on the top, and the right area on the right) var leftshadow: uint = (red *. 5) <16 | (green *. 5) <8 | (blue *. 5); var rightshadow: uint = (red *. 75) <16 | (green *. 75) <8 | (blue *. 75); var H: Number = _ height * y_correct; // top graphics. beginfill (_ color); graphics. linestyle (0, 0 ,. 5); graphics. moveTo (-_ size,-h); graphics. lineto (0,-_ SIZE *. 5-h); graphics. lineto (_ size,-h); graphics. lineto (0, _ SIZE *. 5-h); graphics. lineto (-_ size,-h); graphics. endfill (); // graphics on the left. beginfill (leftshadow); graphics. linestyle (0, 0 ,. 5); graphics. moveTo (-_ size,-h); graphics. lineto (0, _ SIZE *. 5-h); graphics. lineto (0, _ SIZE *. 5); graphics. lineto (-_ size, 0); graphics. lineto (-_ size,-h); graphics. endfill (); // graphics on the right. beginfill (rightshadow); graphics. linestyle (0, 0 ,. 5); graphics. moveTo (_ size,-h); graphics. lineto (0, _ SIZE *. 5-h); graphics. lineto (0, _ SIZE *. 5); graphics. lineto (_ size, 0); graphics. lineto (_ size,-h); graphics. endfill ();}}}

Test isobox

package {import flash.display.Sprite;import flash.display.StageAlign;import flash.display.StageScaleMode;import flash.events.*;import flash.events.MouseEvent;import flash.geom.Point;[SWF(backgroundColor=0xffffff,height=380,width=600)]public class BoxTest extends Sprite{private var world:Sprite;public function BoxTest(){stage.align = StageAlign.TOP_LEFT;stage.scaleMode = StageScaleMode.NO_SCALE;world = new Sprite();world.x = stage.stageWidth / 2;world.y = 50;addChild(world);for(var i:int = 0; i < 15; i++){for(var j:int = 0; j < 15; j++){var tile:DrawnIsoTile = new DrawnIsoTile(20, 0xcccccc);tile.position = new Point3D(i * 20, 0, j * 20);world.addChild(tile);}}world.addEventListener(MouseEvent.CLICK, onWorldClick);stage.addEventListener(Event.RESIZE,resizeHandler);}private function onWorldClick(event:MouseEvent):void{var box:DrawnIsoBox = new DrawnIsoBox(20, Math.random() * 0xffffff, 20);var pos:Point3D = IsoUtils.screenToIso(new Point(world.mouseX, world.mouseY));pos.x = Math.round(pos.x / 20) * 20;pos.y = Math.round(pos.y / 20) * 20;pos.z = Math.round(pos.z / 20) * 20;box.position = pos;world.addChild(box);}private function resizeHandler(e:Event):void{world.x = stage.stageWidth / 2;}}}

Online Demo

In a bit of explanation, the above Code first draws a blank map, and then registers the mouse click event on the map. Each click will generate an isobox instance on the map.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.