Other/ Flash Rectangle Hittest
I couldn't get Flash to do a hitTest correctly to register the collision of two rectangles. Since the rectangles were rotated Flash kept on using the enclosing rectangle defined by the extreme points instead of the image itself.
I don't know if there is a simple way around this problem but I wrote a nice and simple bit of mathematical code to sort it out...
Explanation and code
Each picture is an instance of an Object called a 'Thing'. The Thing.as class contains code to drag the paintings, to decelerate them so that they slide when you release them etc.
The important function for the hitTest is called wehit(). It is inside the Thing class and sees if one of my corners has penetrated inside another Thing.
The basic idea is to work in different, transformed coordinates.
In the usual x/y coordinates it is difficult to find out if the 'me' rectangle has penetrated the 'target' rectangle.
In the transformed coordinates (labelled X and Y) the target movieclip is unrotated and it's now very easy to see if any vertex of the 'me' rectangle has penetrated the 'target' rectangle using the left/right/top/bottom extremes.
Here is the code:
1. private function wehit(k:Thing):Number { 2. //This checks to see if one of my vertices has penetrated another Thing (k) 3. //Transform coordinates so that the target mc is unrotated. 4. var tar:Array = new Array(3); 5. tar[0] = Mathfn.trans(-k.w/2, k.h/2, k, k); 6. tar[2] = Mathfn.trans(k.w/2, -k.h/2, k, k); 7. var me:Array = new Array(3); 8. me[0] = Mathfn.trans(-this.w/2, this.h/2, this, k); 9. me[1] = Mathfn.trans(this.w/2, this.h/2, this, k); 10. me[2] = Mathfn.trans(this.w/2, -this.h/2, this, k); 11. me[3] = Mathfn.trans(-this.w/2, -this.h/2, this, k); 12. var leftx:Number = tar[0][0]; 13. var rtx:Number = tar[2][0]; 14. var bottmy:Number = tar[0][1]; 15. var topy:Number = tar[2][1]; 16. //in these transformed coordinates it is easy to see if we have hit. 17. if (me[0][0]>leftx && me[0][0]<rtx && me[0][1]>topy && me[0][1]<bottmy){ 18. return 1; 19. } 20. else if (me[1][0]>leftx && me[1][0]<rtx && me[1][1]>topy && me[1][1]<bottmy){ 21. return 2; 22. } 23. else if (me[2][0]>leftx && me[2][0]<rtx && me[2][1]>topy && me[2][1]<bottmy){ 24. return 3; 25. } 26. else if (me[3][0]>leftx && me[3][0]<rtx && me[3][1]>topy && me[3][1]<bottmy){ 27. return 4; 28. } 29. return -1; 30. }
Each rectangle has a width 'w' and a height 'h'. These are used to define the vertices 0, 1, 2, 3 as labelled on the diagrams above.
The Mathfn class contains the mathematical function trans() to transform from x,y to X,Y. You can see in lines 5 and 6 that we only need the 0 and 2 vertices of the target to get the bounds left, right, top and bottom as explained above.
The function returns -1 if there is no intersection and otherwise returns the number of the vertex of 'me' that has gone inside the target.
Here are the important mathematical functions.
31. class Mathfn { 32. public static function trans(a:Number, b:Number, t1:Thing, t2:Thing):Array { 33. var pos:Array = rotate(new Array(a, b), t1.cont._rotation); 34. var r:Array = new Array(t1.cont._x+pos[0], t1.cont._y+pos[1]) 35. return (rotate(r, -t2.cont._rotation)); 36. } 37. public static function rotate(a:Array, theta:Number):Array { 38. //simple rotation 39. var r:Array = new Array(); 40. r[0] = a[0]*Math.cos(theta*Math.PI/180)-a[1]*Math.sin(theta*Math.PI/180); 41. r[1] = a[0]*Math.sin(theta*Math.PI/180)+a[1]*Math.cos(theta*Math.PI/180); 42. return r; 43. }
The rotate() function is a very simple rotation of a vector by angle theta.
The trans() function does the real business.
For the 'target' rectangle we take coordinates of vertices 0 and 2 (inside the movieclip), we rotate them by target._rotation and translate by the x/y position of the target. This gives 'r', the real coordinates of the vertices in flash (ie. the x/y coordinates). This is explained better in the image below:
We then rotate this by -target._rotation, this gives us target vertices unrotated as in the green rectangle above, in the X/Y coordinate system.
Vertices 0 and 2 are enough to get the boundary of the green rectangle.
For the 'me' rectangle we again find the real x/y position of each vertex, and then rotate by -target._rotation to transform into the X/Y coordinates in which the target is unrotated.
We need all four vertices to see if any have penetrated inside the target.