本游戏中最为核心的算法在于绘制轨道曲线的算法,实现如下:
每帧获取屏幕上的鼠标的位置设置为终点vt,之前上一帧设置的起点为v0,则这一帧所生成的轨道就是从v0到vt。在轨道生成完成后,将v0设置为vt,以便于下一帧继续进行轨道的生成。
流程图:
图1 核心算法流程图
伪代码:
1.检测是否按住鼠标左键,是则进入2,否则进入8。
2.获取鼠标在屏幕上的当前位置并转换为世界坐标temp_pos。
3.如果是第一次按下(first为true),将当前temp_pos赋给begin_pos,将first设为false。
4.如果当前的temp_pos到begin_pos的距离在最小限度以外,且线条总长totalLength在总长限制maxLength内则进入5,否则进入8。
5.将temp_pos赋给end_pos,若当前totalLength加上begin_pos和end_pos之间的距离tempLength超过了maxLength则对线段[begin_pos,end_pos]截取,改变end_pos的位置,使totalLength加上tempLength等于maxLength。将totalLength+tempLength赋给totalLength。
6.在线段[begin_pos,end_pos]的终点new_pos的位置创建一个轨道元素,并调整其角度和缩放值,使其从begin_pos连接到end_pos。
7.将end_pos赋给begin_pos。
8.检测是否放开鼠标左键,是则进入9,否则进入10。
9.将first设置为true,将totalLength设置为maxLength(即松开左键后不得继续创建轨道元素;clear()函数可将totalLength清零,之后可以继续画线)。
10.回到1。
详细代码:
if (Input.GetMouseButton(0)){//如果按住左键 temp_pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);//屏幕坐标转世界坐标 temp_pos.z = 0;//调整世界坐标z轴 if (first == true){//如果是第一次按左键 begin_pos = temp_pos;//当前位置作为开始位置 first = false;//设置第一次标志为假 } //当前位置和开始位置有一定间隔&&未超出长度限制 else if ((Vector3.Magnitude(temp_pos - begin_pos) >= 0.1) && (totalLength < maxLength)){ end_pos = temp_pos;//当前位置作为结束为止 float tempLength = Vector3.Magnitude(end_pos - begin_pos);//记录当前长度 if (totalLength + tempLength > maxLength) {//若在本次超出长度限制 //调整end_pos并且进行截取 end_pos = begin_pos + (end_pos - begin_pos) * (maxLength - totalLength) / tempLength; totalLength = maxLength;//总长度等于最大长度 } else{//本次未超出长度限制 totalLength += tempLength;//总长度加上当前长度 } //新的轨道物体的位置为开始位置和结束位置连线的中点 Vector3 new_pos = (begin_pos + end_pos) / 2; //新的轨道物体的x方向的缩放因子为开始位置和结束位置之间的线段长度 //(因为轨道物体的x方向长度为1) Vector3 new_scale = new Vector3(Vector3.Magnitude(end_pos - begin_pos), 1, 1); //新的轨道物体的旋转角度 float new_degree = 0; if (begin_pos.y > end_pos.y){//终点在起点下方 //旋转角度为向量Vector3.right和end_pos-begin_pos的夹角的相反数 new_degree = -Vector3.Angle(Vector3.right, end_pos - begin_pos); //因为Angle方法只能返回0到180度的角 } else{//终点在起点上方 //旋转角度为向量Vector3.right和end_pos-begin_pos的夹角 new_degree = Vector3.Angle(Vector3.right, end_pos - begin_pos); //因为Angle方法只能返回0到180度的角 } //实例化track_element物体 track_element_new=(GameObject)Instantiate(track_element, new_pos, Quaternion.identity); //更改其缩放因子 track_element_new.transform.localScale = new_scale; //更改其旋转角度(绕z轴正方向旋转new_degree度) track_element_new.transform.Rotate(Vector3.forward, new_degree); //更新下一次的开始位置为这一次的结束位置 begin_pos = end_pos; } } if (Input.GetMouseButtonUp(0)){//如果放开鼠标左键 totalLength = maxLength;//将当前总长度设为最大,即松开左键就不可再画(一笔画) first = true;//设置first标志为真 }