[Do your own questions]: How to Implement cube effects on Canvas, canvas cube

Source: Internet
Author: User

[Do your own questions]: How to Implement cube effects on Canvas, canvas cube

Final demo-> 3d cube

Experience:

  • Move the mouse to find a proper position and Press space to pause
  • Select the 3*3 module to flip, find the adjacent two cubes, click the first cube, and keep the mouse pressed until it is moved to the second cube. For example:

(Click "1" and move until "2" is released. The 3*3 module in the middle line rotates around the graph)

  • Press the Space key to resume the rotation of the cube and continue searching for the next target to be flipped

As follows (Use chrome whenever possible ):

  

 

Review of square scale Rendering

The experience of adding a Square area to the Canvas shows how to use canvas to draw cubes with 3D effects on the Canvas. In the end, a multi-cube demo is provided-> multi-cube.

The specific process can be summarized in the previous article.

The Code defines four objects: garden (scenario), cube (cube), face (surface), and ball (point). The subordination is as follows:

In the cube demo, there are 27 cubes in one scenario. Each cube has 6 faces and 8 points, and each has 4 points; the rendering of each frame is first sorted by the cube's body (this is not the best solution as described earlier), and then the meeting of each cube is drawn based on the sorted results. In the final analysis, the rendering of each frame is the rendering of 8 points in each cube!

With this experience, you can draw a demo of a cube without interaction.-> no interaction cube

The biggest difference between the interactive cube demo and the front multi-cube demo is the color of the face. In fact, it is very simple. You can input a color array during initialization, for example:

// Red, orange, blue, green, yellow, white, // 0 1 2 3 4 5window. colors = ['# ff000000',' # ff6600 ',' # 0000ff', '# 00ff00',' # ffff00', '# ffff']; var color = [// The first row [0, 5, 5, 3, 5, 5], [0, 5, 5, 5, 5], [0, 2, 5, 5, 5], [0, 5, 5, 3, 5, 5], [0, 5, 5, 5, 5], [0, 2, 5, 5, 5], [0, 5, 5, 3, 5, 4], [0, 5, 5, 5, 5, 5, 4], [0, 2, 5, 5, 5, 4], // second row [5, 5, 5, 3, 5], [5, 5, 5, 5, 5, 5, 5], [5, 2, 5, 5, 5], [5, 5, 5, 3, 5], [5, 5, 5, 5, 5], [5, 2, 5, 5, 5], [5, 5, 5, 5, 3, 5, 4], [5, 5, 5, 5, 5, 4], [5, 2, 5, 5, 4], // the third row [5, 5, 1, 3, 5, 5], [5, 5, 1, 5, 5], [5, 2, 1, 5, 5], [5, 5, 5, 1, 3, 5, 5], [5, 5, 1, 5, 5], [5, 2, 1, 5, 5], [5, 5, 1, 3, 5, 4], [5, 5, 1, 5, 5, 4], [5, 2, 1, 5, 5, 4],];

When initializing each cube, an additional parameter is input to implement the color you need.

 

Key Issues

How to interact and how to achieve the rotation of the three modules required by players is the key to the problem.

I finally thought of choosing two adjacent cubes like demo, one listening for the mousedown event and the other listening for the mouseup event. It looks like, two ordered cubes seem to be able to determine the 3*3 module to be rotated (otherwise ). Before looking for the 3*3 module, we must first solve how to determine the two cubes.

 

  • Determination of two cubes

Because the patterns we show on the canvas are actually painted by the h5 native api, and we can not write an event listener like dom. I think the only way to get these two cubes is to judge them.

Traverse 27 cubes on 6*27 sides of a two-dimensional space to determine whether the mouse clicks inside the plane. Here we can reverse the cubes in the scenario, because the cubes in each frame should be re-ordered based on the body heart, the more the next draw, in most cases, the cubes clicked by the mouse should be close to the viewpoint, so we can traverse from the back to the front to speed up searching. When traversing a cube with 6 faces, you don't have to judge if you can't meet each other. The final part of this problem is the judgment of a point in a convex quadrilateral on a two-dimensional system. For details, refer to-> determine whether a point is within the specified convex quadrilateral.

I used the first method of blog.

Due to the lack of mathematical ability, I used the cross product as the dot product at the beginning, and debugging took a long time to discover.

Mouse monitoring:

Document. addEventListener ('mousedown ', function (event) {window. rotateArray = []; var obj = canvas. getBoundingClientRect (); // The (x, y) Coordinate var x = event on the canvas where the mouse clicks. clientX-obj. left; var y = event. clientY-obj. top; var v = new Vector2 (x, y) var ans = getCubeIndex (v); if (ans) window. rotateArray. push (ans );});

The getCubeIndex function traverses 27 cubes and a two-layer loop of 6 faces in each cube.

Points are determined by the convex quadrilateral:

// Determine whether m is in the clockwise direction of a, B, c, d four points in the convex quadrilateral function isPointIn (a, B, c, d, m) {var f = B. minus (). dot (m. minus (a); if (f <= 0) return false; var f = c. minus (B ). dot (m. minus (B); if (f <= 0) return false; var f = d. minus (c ). dot (m. minus (c); if (f <= 0) return false; var f =. minus (d ). dot (m. minus (d); if (f <= 0) return false; return true ;}

So far, the positions of the two clicked cubes in the 27 cubes have been found.

 

  • 3*3 module confirmation

Then we need to find the 3*3 module determined by the two cubes.

We know that each rotation of a cube is a 3*3 module, and such a module has 3*3 = 9 in a cube. Can two adjacent cubes determine the unique 3*3 module? The answer is no, for example:

The cubes 1 and 2 determine the two 3*3 modules in the figure. In fact, if the two cubes are located on the Cube's edge, you can determine the two cubes. For the time being, let's find one or both of them.

How to find it? At first, I thought of maintaining a 3D array and initializing an index value for each cube. The value corresponds to the value of the 3D array. Each cube rotation changes the value of the 3D array at the same time, in this way, the 3*3 module traverses the three dimensions of the 3D array and finds the 3*3 = 9 cubes in any dimension. If there are two cubes that contain the clicked cubes, is a group of solutions. I gave up later, and it was too troublesome to maintain the 3D array.

Finally, I used a deep search to find a closed loop with a length of 8. The first two values have been determined, because this closed loop does not pass through the cube at the center of the cube, so the value of the next vertex of each vertex can be at most four cases, the maximum complexity is O (4 ^ 6), which is completely within the controllable range. In addition, if the searched point is marked out, you do not need to continue the search. The answer is displayed in almost seconds.

Deep Search is as follows:

Function dfs (index) {var cubes = garden. cubes; if (index = 8) {var dis = cubes [window. rotateArray [0]. pos3.getDistance (cubes [window. rotateArray [7]. pos3); if (Math. abs (dis-60)> 10) return; // judge the eight points in a plane var cubes = garden. cubes; var a = cubes [window. rotateArray [1]. pos3.minus (cubes [window. rotateArray [0]. pos3); var B = cubes [window. rotateArray [7]. pos3.minus (cubes [window. rotateArray [6]. pos3 ); // Find a method vector var v = undefined; for (var I = 0; I <27; I ++) {var c = cubes [I]. pos3; if (. isPerpTo (c) & B. isPerpTo (c) {v = c; break;} if (I ===26 & v === undefined) return ;} // determine whether any adjacent vector is a vertical normal vector for (var I = 0; I <7; I ++) {var a = cubes [window. rotateArray [I]. pos3.minus (cubes [window. rotateArray [I + 1]. pos3); if (! A. isPerpTo (v) return ;} //////////////////////////////////////// //// // if it is the front surface, return var zz = 0; for (var I = 0; I <8; I ++) zz + = cubes [window. rotateArray [I]. pos3.z; zz/= 8; if (zz <-40) return; // The type var vv = new Vector3 (); for (var I = 0; I <8; I + = 2) {vv. x + = cubes [window. rotateArray [I]. pos3.x; vv. y + = cubes [window. rotateArray [I]. pos3.y; vv. z + = cubes [window. rotateArray [I]. Pos3.z;} vv. x/= 4; vv. y/= 4; vv. z/= 4; var flag = false; for (var I = 0; I <27; I ++) {var vvv = cubes [I]. pos3 if (vv. getDistance (vvv)> 5) continue; flag = true; break;} if (! Flag) return; for (var I = 0; I <8; I ++) {window. isFindRoute = true; window. rotateFinalArray [I] = window. rotateArray [I];} return;} if (window. isFindRoute) return; for (var I = 0; I <27; I ++) {if (window. hash [I]) continue; // the midpoint of the cube is not found. the midpoint of the cube should be determined later. if (cubes [I] should not be assigned directly. pos3.isEqual (new Vector3 () continue; var front = window. rotateArray [index-1]; var dis = cubes [front]. pos3.getDistance (cubes [I]. pos3); if (Math. abs (dis-60)> 10) continue; window. rotateArray [index] = I; window. hash [I] = true; dfs (index + 1); window. hash [I] = false ;}}

I first look for a closed loop with a length of 8 and find it before making a judgment: (in fact, it will be more efficient to find and judge while searching)

1. Determine whether the eight points are on the same surface. You can select two non-parallel vectors as the normal vectors perpendicular to the two vectors. If these eight points are faceless, the normal vector is perpendicular to any vector composed of two points in the plane.

2. If it is the front plane, return. This judgment is a bit difficult. Let's take a look at it first:

If you operate two cubes, 1 and 2, two loops are obtained. What we want is the above 3*3 module operation, excluding the previous one. Here I will judge based on the average z value. If z is too small (too close to the viewpoint, consider it as the first one), then remove it. In fact, this is not accurate, so the demo may sometimes go wrong, and this is also an issue that cannot be solved by the operator cube. If it is to be solved, the complexity of the program may increase to a level, accurate to the opposite. Fuzzy Judgment is used here. This is also the first way to choose two circuits.

3. the closed loop of the same plane is found, but it does not meet the requirements as follows:

Because the center of the 3*3 module composed of closed loops must be the body of a cube on the cube, which is determined based on this approximation.

So far, we have 3x3 = 9 cubes to be flipped.

 

  • Axis of rotation

Get the cube to be flipped, and finally you just need to get the flip axis.

We have obtained the coordinate changes after rotating around the X and Y axes. Is there a formula for the coordinate changes around any axis? Luckily, the answer is yes-> the coordinates of the new point after a point rotates around the vector in 3D space

  

In this way, we can obtain the vector of the face to be flipped, and then become a unit. In fact, this normal vector must be traversed by a specific body in 27 cubes. But there are two normal vectors in one plane. Fortunately, the closed loop we obtain has a direction, because the angle of the flip must be 90 degrees, we can know the actual position of a cube in the 3*3 module after turning 90 degrees, which is actually the position of the cube in the two forward sides of the closed loop; any of the normal vectors we obtain, place the value into the function for calculation, and select a cube. If the cube rotates 90 degrees around the normal vector, the obtained value is the correct position, that is, the normal solution of this normal vector. (In fact, the other one needs to rotate 270 degrees)

So we will write a rotateP function:

RotateP: function () {if (this. cube. isRotate) {this. cube. index ++; // when a point reaches 60, does it change the isRotate value? It should be reached at all eight points. if (this. cube. index = 480) {this. cube. isRotate = false; this. cube. index = 0;} var c = Math. cos (this. cube. garden. angleP); var s = Math. sin (this. cube. garden. angleP); // (x, y, z) is the unit vector through the origin var x = this. cube. rotateVector. x; var y = this. cube. rotateVector. y; var z = this. cube. rotateVector. z; var new_x = (x * (1-c) + c) * this. pos3.x + (x * y * (1-c)-z * s) * this. pos3.y + (x * z * (1-c) + y * s) * this. pos3.z; var new_y = (y * x * (1-c) + z * s) * this. pos3.x + (y * (1-c) + c) * this. pos3.y + (y * z * (1-c)-x * s) * this. pos3.z; var new_z = (x * z * (1-c)-y * s) * this. pos3.x + (y * z * (1-c) + x * s) * this. pos3.y + (z * (1-c) + c) * this. pos3.z; this. pos3.reset (new_x, new_y, new_z );}

In this way, in the rendering of each frame, the Coordinate Position of the point of the cube to be rotated will also change with the rotateP function, so the rotation effect will appear.

 

Summary

In fact, this is a very boring experience. canvas is not suitable for this 3d effect. But the important thing is the thinking process, not the result.

This is just a demo. If you want to create a real cube game, you need:

  • Cube color Initialization

Now I can set the color of the cube at will. If it is a playable game, first initialize the restored color of the cube and then randomly disrupt the game loading process.

  • Precise judgment of 3*3 modules

As I mentioned earlier, the 3*3 module is inaccurate. For more extreme examples, see:

At this time, I move the mouse over Area 1 and Area 2. I want to rotate a module enclosed by a black arrow, but in reality the program rotates 3*3 surrounded by a yellow arrow, this is because of my fuzzy judgment. I cannot determine which one is, because I have always determined based on the body heart. To get the correct result, I have to go up to the Cube's judgment, I don't know how many times the amount of code will increase. (So try to operate the surface close to the viewpoint during demo operations)

  • Game result judgment

If you use the method of maintaining a 3D array in step 3*3, the judgment here is relatively simple. If not, you have to go back to the preceding judgment. The above is very complicated.

  • Others

Add loading, timing, and so on.

 

If you have better methods or suggestions, please feel free to contact me ~

  

Related Article

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.