Tease than Liuzhen is a dish ah, every day know to write this kind of boring things, write is not necessarily someone to read. I don't think anyone likes it. I like it, and I don't have any eggs. Let's share a wave.
What did everyone see at first sight? (Tease Liu first eye said is sperm tadpole ah, tease than Liu Good evil ah)
To do such a demo can be said that the basic use of a lot of material, the most important material is that circle, this circle for a programmer should be quite simple to draw it. The day before yesterday we talked about if we draw a 2D ring, today we draw a 3D circle, and if we rotate a 2D circle around an axis for a week, we can make a 3D ring.
So we'll start by building a basic class of pipe to build our rings. The general situation we make is to make a whole ring, and then continue to extend to half or even the arc of the circle. First we start with the ring. I first send a wave to see how the information of our last node is distributed.
As we can see from this graph, the point distribution of the entire ring we can rotate around the z axis by a circle at the top of the YZ plane that makes up our entire circle, rotates 180 degrees is a semicircle, rotates 90 degrees is a one-fourth circle, so that we can control the length of the whole ring by how many degrees of rotation. We need to control the width of the ring and the radius of the ring, and the circle in the YZ plane controls the width of our circle. The distance between the center point of the circle at the YZ plane and the rotation point of the circle is the radius of our circle. Then we can draw the pattern of the dots on the entire 3D ring. The point is calculated by giving the point at the angle of the YZ plane circle and how much of the YZ plane's circle revolves around the center point. The point on the circle on the YZ plane is the highest point in the Y-axis component, and we can see it clearly. Now let's assume that if the highest point in the circle on the YZ plane is A1, then the YZ plane rotates 90 degrees around the z axis when we define it as the Y1Z1 plane, then the A1 point becomes the largest component on the x-axis. So they have a bit of the x-axis and y-axis components we define as X=r*sin (rotation angle), Y=r*cos (rotation angle). Because the points on the YZ plane circle are different in their radius around the z-axis, the radius of the r= ring + the width of the ring *cos (the angle of the point on the circle, the angle at the highest point is 0, the angle of the point is 180 degrees, the ascending order of the angles is counterclockwise), So its z-axis component should be the width of the z= Circle *sin (point at the angle of the circle). If the understanding is a bit difficult, we can get out of the brain.
So after understanding this rule, we need to create a basic class to generate a 3D ring. At the same time the width of its ring and the radius of the ring are exposed to the inspect to facilitate our control. At the same time we should also write about our point of view from the angle of the point on the circle and the circle around the z axis to get our points, and we still define the 3 arrays that draw the grid, as well as the total number of points on our circle, and the angle that we can rotate (we cannot draw these points every once in a while, Our practice is to add a dot on a circle every 20 degrees, or we draw more sparse every 30 degrees to add a circle point.
Using unityengine;using system.collections;using System.collections.generic;public class pipe:monobehaviour{public F Loat width of piperadius;//ring public float curveradius;//ring radius public int pipesegmentcount;//defines the number of vertices on the circle public int CURV esegmentcount;//defines the number of circles private list<vector3> _verticles; Private list<int> _indictints; Private list<vector2> _uvlist; [Serializefield] Private Material _meshmaterial;//defines the material of the Ring private void Awake () {_verticles = new List<vec Tor3> (); _indictints = new list<int> (); _uvlist=new list<vector2> (); } private Vector3 GetVector3 (float pipeangle, float curveangle)//degrees of rotation around z-axis by point and circle on the circle {float R = Curve Radius + Piperadius * Mathf.cos (Pipeangle); float x = R * Mathf.sin (Curveangle); Float y = R * Mathf.cos (Curveangle); float z = Piperadius * Mathf.sin (Pipeangle); return new Vector3 (x, y, z); } public void Ondrawgizmos () {for (int i = 0; i < _verticles. Count; i++) {gizmos.drawsphere (_verticles[i],0.1f); } } }
After we have finished building the basic classes, we begin to add a method for adding points. We are still a round of a round Add. They are then given the same drawing order and the UV information of the vertices.
Private mesh Createmesh (int pipecout, int curvecout, float angle) {mesh Mymesh = new mesh (); float Pipeoffset = Mathf.pi * 2/PIPESEGMENTCOUNT; float Curveoffset = angle * Mathf.deg2rad/(CURVESEGMENTCOUNT-1); Curveangle = angle; for (int i = 0, i < Curvesegmentcount; i++) {for (int j = 0; J < Pipesegmentcount; J + +) {Vector3 temp = GetVector3 (Pipeoffset * j, Curveoffset * i); _verticles. ADD (temp); }} for (int i = 0; i < Curvesegmentcount; i++) {for (int j = 0; J < Pipesegmentcou nt J + +) {_uvlist.add (new Vector2 (i% 2, J% 2)); }} for (int i = 0; i < CurvesegmentCount-1; i++) {for (int j = 0; J < pipesegmen TCount; J + +) {_indictints.add (i * pipecout + j); _indictints.add ((i + 1)% Curvecout * pipecout + j); _indictints.add ((i + 1)% Curvecout * Pipecout + (j + 1)% pipecout); _indictints.add (i * pipecout + j); _indictints.add ((i + 1)% Curvecout * Pipecout + (j + 1)% pipecout); _indictints.add (i * pipecout + (j + 1)% pipecout); }} mymesh.vertices = _verticles. ToArray (); Mymesh.triangles = _indictints.toarray (); MYMESH.UV = _uvlist.toarray (); return Mymesh; }
By doing this we can make a 3D ring at a given angle, and we continue to add a response component to it.
_uvlist=new list<vector2> (); <span style= "Background-color:rgb (255, 153, 0); > var mesh = Createmesh (Pipesegmentcount, Curvesegmentcount, n); This. Getcomponent<meshfilter> (). Mesh = mesh;</span>
It's only the first step to finish. Then how do we make the 2-segment ring Seamless link, if the length of the first ring is 90 degrees (we use the angle to define the length of the ring), then the second circle of the angle of the z-ring so that the angle of the previous ring is a negative value corresponding to the first paragraph of the ring length 90 is-90 degrees. So the next paragraph is a negative value of the sum of the previous 2-paragraph lengths, wouldn't it be a lot of trouble if we did that? When we generate the second segment of the ring, we set the second paragraph of the parent object to the first paragraph, then it is relative to the first paragraph around the z angle is the last part of the ring angle negative, when we create the third ring, we set the ring's parent object to the second ring, Then it is the angle of the second ring that is assigned to the second segment of the ring around the z axis, one analogy.
From the right view we can clearly see the parent-child relationship of the game object. If we just connect a few of these rings, then we'll make a couple of paragraphs and just do a big piece of it. So what we're going to do next is take our second paragraph and let it spin around the first paragraph. Instead of having all the rings on a plane. Take a look at the wave.
To make the second and first paragraphs not on a plane, we need to rotate them. So how do you rotate and move? Analysis and analysis of a truncated wave.
First we need to get the second ring around the ring's local y-axis to move the radius of the ring (not the width of the ring)
The picture is not very good, but the sequence of steps from left to right, from the top to the bottom, will see how it is done. Well, we'll give the corresponding code, and the function of this method should be written in our pipe class. You can also write to a pipe management class in fact all the same.
public void Connectpipe (Pipe prepipe) { float relativerotation = random.range (0, pipesegmentcount) * 360f/pipes egmentcount;//First we define how many degrees of transform it needs to be rotated . SetParent (Prepipe.transform, false);//The first step is to determine the father-son relationship, that is, to make him a child of the last paragraph transform.localposition = Vector3.zero; Transform.localrotation = Quaternion.euler (0f, 0f,-prepipe.curveangle);//Then it is the Euler angle transform that sets its local coordinates . Translate (0f, Prepipe.curveradius, 0f);//start moving along the y-axis (local coordinate system) transform. Rotate (Relativerotation, 0f, 0f);//start moving around the x-axis (local coordinate system) transform. Translate (0f,-curveradius, 0f);//Then move back to the point transform of the response . SetParent (prePipe.transform.parent);//Last Reset Father node transform.localscale = Vector3.one; }
Then we make a management class that manages the ring.
public class pipesystem:monobehaviour{ [Serializefield] private int _pipecout; Private list<pipe> _pipes; private int _curindex = 0; void Awake () { _pipes = new list<pipe> (); } }
We generally set the number of this pipecount can not be too much, I personally think 4 is better. Although we are the map of the infinite splicing, but we only use 4 segments of the ring, when our players run to the second ring, we do not delete the first ring, but instead of the first ring to the fourth ring to the back so that you can not generate additional rings, For the generation of obstacles we use object pooling to do a little bit of optimization. For this management class he should provide some basic methods, for example, when our players go up to the second ring and then our management class should notify us that the first ring is connected to the fourth ring and then regenerate the block at the same time and return the reference to the ring, Also initialize the position of our object pool and the ring and the corresponding information in the Awake method.
<span style= "Background-color:rgb (255, 153, 0); > [Serializefield] Private gameobject _pipegameobject; [Serializefield] Private gameobject _cubeobs; [Serializefield] Private Gameobject _cylinderobs;</span>
<span Style= "Background-color:rgb (255, 153, 0); > Pools.managerecyclepool (New Objectpool () {Prefab = _cubeobs, instancestopreallocate = 50}); Pools.managerecyclepool (New Objectpool () {Prefab = _cylinderobs, instancestopreallocate = 50 });</span> _pipes = new list<pipe> (); <span style= "Background-color:rgb (255, 153, 0); > for (int i = 0; i < _pipecout; i++) {Gameobject OB = Gameobject.instantiate (_pipegameobject); Ob.transform.SetParent (This.transform); _pipes. ADD (ob. Getcomponent<pipe> ()); if (i! = 0) {_pipes[i]. Connectpipe (_pipes[i-1]); }}</span>
The next step is to add the toggle ring when our player is running to the second ring and the index in our current doughnut array needs to change. We know to do a horizontal version of the parkour game like that one big game company's parkour game as we do sometimes not necessarily let the player move, but let the background move. So this is the way to let the background move, but this is a lot of different angles of the ring composed of a map, then there is no easy mobile map AH. So our approach is to set all the rings to a parent node to manage it uniformly. We need to keep the first ring and our Father node relatively motionless at the beginning, and in the Update method we let our Father node its Euler angles on the z axis increase over time as we move our first ring. This way the player moves on the first ring. When the total offset of our Father's node is greater than the length of the ring (simulated by angle). This is our father. The total offset of the node is reset to 0, and if we move in the same way as before, then there is definitely an error. At this point we need to reset the position of our Father node, and we need to do a few steps to complete the job. 1 set all the ring nodes to empty their fathers node, then set the parent node position of the original ring to the position of our second ring. And then set all the rings back to the original Father node. The specific code is as follows.
private int _tempindex = 0; Public Pipe switchpipe () { _tempindex = _curindex; _curindex++; _curindex = _curindex%_pipecout; for (int i = 0; i < _pipecout; i++) { _pipes[i].transform.parent=null; } This.transform.localPosition = _pipes[_curindex].transform.position; This.transform.localEulerAngles = _pipes[_curindex].transform.eulerangles; for (int i = 0; i < _pipecout; i++) { _pipes[i].transform. SetParent (This.transform); } return _pipes[_curindex]; }
This method has a replacement for a barrier. So I re-wrote a method specifically to reset the obstacles.
public void Resetpipe () { _pipes[_tempindex]. Resetobstacle (); _pipes[_tempindex]. Connectpipe (_pipes[(_tempindex + _pipecout-1)% _pipecout]); }
The next step is to add the Player class, which is relatively simple, mostly an update method. I put the code directly.
Using system;using system.collections.generic;using system.linq;using system.text;using UnityEngine;public class player:monobehaviour{public Pipesystem Pipesystem; public float Velocity; Private Pipe _currentpipe; private float _totalangle = 0; [Serializefield] private float _speed; private void Start () {_currentpipe = Pipesystem.setupfirstpipe (this.transform); } private bool _resetpipe = false; private void Update () {Float Delta = time.deltatime * _SPEED; _totalangle + = Delta; if (_totalangle >= _currentpipe.curveangle) {delta = _currentpipe.curveangle-_totalangle + delta; PipeSystem.transform.localEulerAngles + = new Vector3 (0, 0, Delta); _currentpipe = Pipesystem.switchpipe (); _resetpipe = true; Delta = 0; _totalangle = 0; } if (_totalangle > && _resetpipe) {pipesystem.resetpipe (); _reSetpipe = false; } updateplayerrotation (); PipeSystem.transform.localEulerAngles + = new Vector3 (0, 0, Delta); } private void Updateplayerrotation () {if (Input.getmousebutton (0)) {Transform.localrotat Ion *= Quaternion.angleaxis (2, vector3.right); } }}
After the player has been added, we have some obstacles in the ring that have not yet been generated, so here we need to add a way to add obstacles. Then we go back to the pipe class and add the following code.
private void Createobstacleitem () {Float Pipeoffset = Mathf.pi * 2/PIPESEGMENTCOUNT; float Curveoffset = Curveangle * Mathf.deg2rad/(CURVESEGMENTCOUNT-1); for (int i = 0; i < CurvesegmentCount-1; i++) {int b = random.range (0, 10); if (b% 2 = = 0) {int index = random.range (0, Pipesegmentcount); int type = Random.range (0,10)% (int) Obscaleenum.cylinder; Gameobject obj = null; Switch (type) {Case 1:obj = creatawlobstacle (i, Index, pipeoffs ET, curveoffset); Break Case 0:obj = Createprimitivetypeobscale (obscaleenum.cube,i, Index, Pipeoffset, curveoffset); Break Case 2:obj = Createprimitivetypeobscale (Obscaleenum.cylinder, I, index, Pipeoffset, curveoffset); Break } _obstacleobjects.add (obj); } } }
Then add a few ways to create obstacles, first add the creation of the cone barrier, followed by the addition of creating cylinder and box obstacles.
Private Gameobject creatawlobstacle (int curvindex, int pipeindex, float pipeoffset, float curveoffset) {Lis t<vector3> pointlist = new list<vector3> (); int index = Pipeindex; int temp = (index + 1)% Pipesegmentcount; Vector3 T1 = GetVector3 (Pipeoffset * index, Curveoffset * curvindex); Vector3 t2 = GetVector3 (Pipeoffset * index, Curveoffset * (Curvindex + 1)); Vector3 t3 = GetVector3 (Pipeoffset * temp, Curveoffset * (Curvindex + 1)); Vector3 T4 = GetVector3 (Pipeoffset * temp, Curveoffset * curvindex); Vector3 mid = (T1 + t3)/2; float Raiuds = vector3.magnitude (T2-T1); Vector3 DirX = (t2-t1). normalized; Vector3 DirY = (t3-t2). normalized; for (int i = 0; I <=; i++) {float rad = I*18*mathf.deg2rad; var pos =raiuds* Dirx*mathf.sin (RAD)/2 +raiuds*diry*mathf.cos (RAD)/2; Pointlist.add (POS); } Vector3 normal = Vector3.cross(DirX, DirY). normalized; Vector3 T5 = normal * 0.5f; Pointlist.add (T5); var obj = _meshbuilder.createawlmeshobj (pointlist); Obj.transform.SetParent (This.transform,false); Obj.transform.localPosition = mid + normal * 0.01F; Pointlist.clear (); return obj; } private Gameobject Createprimitivetypeobscale (obscaleenum type,int curvindex, int pipeindex, float pipeoffset, float Curveoffset) {int index = Pipeindex; int temp = (index + 1)% Pipesegmentcount; Vector3 T1 = GetVector3 (Pipeoffset * index, Curveoffset * curvindex); Vector3 t2 = GetVector3 (Pipeoffset * index, Curveoffset * (Curvindex + 1)); Vector3 t3 = GetVector3 (Pipeoffset * temp, Curveoffset * (Curvindex + 1)); Vector3 mid = (T1 + t3)/2; Vector3 DirVector3 = (t3-t2). normalized; Vector3 normal = Vector3.cross (T2-t1, T3-T2); Gameobject cube = pools.spawn (type==obscaleenum.cube?_cube:_cyL); float ScaleY = Random.range (0.05f, 1.5f); float offset = random.range (0, 10)%2 = = 0? 0: -0.3f; Cube.transform.localScale = new Vector3 (0.1f, ScaleY, 0.1f); Cube.transform.up = normal; Cube.transform.SetParent (This.transform, false); Cube.transform.localPosition = mid + normal * ScaleY * 2 + DirVector3 * OFFSET; Cube. Getcomponent<meshrenderer> (). material = _meshmaterial; return cube; }
Then we give the basic method of grid creation.
public class meshbuilder{private Material _meshmaterial; public void intilized (Material mat) {_meshmaterial = Mat; } public Gameobject Createawlmeshobj (list<vector3> pointlist) {gameobject ob=new gameobject ("Obstacle" ); List<int> indicts=new list<int> (); Mesh mesh=new mesh (); Mesh.vertices = Pointlist.toarray (); int temp = pointlist.count-1; for (int i = 0; i < temp; i++) {indicts. ADD (i); indicts. ADD (temp); indicts. ADD ((i + 1)% temp); } mesh.triangles = indicts. ToArray (); var meshfilter = ob. Addcomponent<meshfilter> (); Meshfilter.mesh = mesh; Ob. Addcomponent<meshrenderer> (). material = _meshmaterial; return OB; } public Gameobject Createcubegameobject (list<vector3> pointlist) {gameobject ob=new gameobject (); list<int> indicts = new list<int> (); Mesh mesh = new mesh (); Mesh.vertices = Pointlist.toarray (); int onelinelength = POINTLIST.COUNT/4; for (int i = 0, i < 4; i++) {for (int j = 0; J < Onelinelength-1; J + +) { int temp = (i + 1)% 4; indicts. ADD (i * onelinelength + j); indicts. ADD (Temp * onelinelength + j); indicts. ADD (Temp * onelinelength + j + 1); indicts. ADD (i * onelinelength + j); indicts. ADD (Temp * onelinelength + j + 1); indicts. ADD (i * onelinelength + j + 1); }} indicts. ADD (0); indicts. ADD (0 + 2 * onelinelength); indicts. ADD (0 + 1 * onelinelength); indicts. ADD (0 + 3 * onelinelength); indicts. ADD (0 + 2 * onelinelength); indicts. ADD (0); indicts. ADD (3 * onelinelength-1); indicts. ADD (onelinelength-1); indicts. ADD (2 * onelinelength-1); indicts. ADD (onelinelength-1); indicts. ADD (onelinelength-1); indicts. ADD (3 * onelinelength-1); var meshfilter = ob. Addcomponent<meshfilter> (); Meshfilter.mesh = mesh; Mesh.vertices = Pointlist.toarray (); Mesh.triangles = indicts. ToArray (); Ob. Addcomponent<meshrenderer> (). material = _meshmaterial; return OB; }}
public enum obscaleenum{ None, Awl, Cube, Cylinder}
Back to the pipe. Awake method of response and some fields
Private list<vector2> _uvlist; <span style= "Background-color:rgb (255, 153, 0); >public float curveangle = 0; Private list<gameobject> _obstacleobjects; Private Meshbuilder _meshbuilder;</span>
_uvlist=new list<vector2> (); <span style= "Background-color:rgb (255, 153, 0); > _obstacleobjects=new list<gameobject> (); var mesh = Createmesh (Pipesegmentcount, Curvesegmentcount, Random.range ()); This. Getcomponent<meshfilter> (). Mesh = mesh; _meshbuilder=new Meshbuilder (); _meshbuilder.intilized (_meshmaterial); Createobstacleitem ();</span>
A basic SB and boring infinite parkour type of game almost finished, here is just a demo integrity is not high, the player's collider Ah, death judgment these I have not to do, if you have time to do, this is relatively simple, a little write a collision detection on the line, In addition, there is a part of the reason is that there is no UI art so do not have a complete small game. It would be easier for us to finish this and then go to a racing track. If there is anything you do not understand, you can see the following source code links. At the same time you can contact me QQ. qq:185076145 welcome everyone harassment haha. Http://pan.baidu.com/s/1mirn2be
Untiy second funny and boring parkour demo