今天的基础练习来源教程如下:
Unity数字表面
另外,本次基础练习使用的是上次视图基础练习的场景继续开发,所以请先看看之前的练习。
创建一个委托C#脚本GraphFunction以及枚举脚本GraphFunctionName
为的是之后更方便存储不同的函数,实现不同函数之间的切换。
Graph脚本代码:
using UnityEngine;
public class Graph : MonoBehaviour
{
const float pi = Mathf.PI;//使用局部常量获取PI值
public Transform pointPrefab;
static GraphFunction[] functions = {//创建索引数组
//因为这个数组不论在哪总是相同的,所以没有必要为每个视图实例创建一个
SineFunction,Sine2DFunction,MultiSineFunction,MultiSine2DFunction,
Ripple,Cylinder,Sphere,Torus
};
Transform[] points;
[Range(10,100)]
public int resolution = 50;//使用Range控制立方体数量
public GraphFunctionName function;//枚举类型
void Awake() {
float step = 2f/resolution;//调整步长
Vector3 scale = Vector3.one*step;
points = new Transform[resolution*resolution];//新的函数生成方式不再依赖于原始位置,所以重写生成函数
for(int i=0;i<points.Length;i++){
Transform point = Instantiate(pointPrefab);
point.localScale = scale;
point.SetParent(transform,false);
points[i] = point;
}
}
void Update() {//实现动态效果
float t = Time.time;//缓存以节约性能空间
GraphFunction f = functions[(int)function];//使用委托类型获取值
float step = 2f/resolution;
for(int i=0,z=0;z<resolution;z++){
float v = (z+0.5f)*step -1f;
for(int x=0;x<resolution;x++,i++){
float u = (x+0.5f)*step -1f;
points[i].localPosition = f(u,v,t);//使用此种方式代替原来的赋值方法
}
}
}
static Vector3 SineFunction(float x,float z,float t){//用static让方法和实例脱离联系,让别的脚本也可以直接调用
Vector3 p;
p.x = x;
p.y = Mathf.Sin(pi * (x+t));
p.z = z;
return p;
}
static Vector3 MultiSineFunction(float x,float z,float t){
Vector3 p;
p.x = x;
p.y = Mathf.Sin(pi*(x+t));
p.y +=Mathf.Sin(2f*pi*(x+2f*t))/2f;
p.y *=2f/3f;
p.z= z;
return p;
}
static Vector3 Sine2DFunction(float x,float z,float t){
Vector3 p;
p.x = x;
p.y = Mathf.Sin(pi*(x+t));
p.y += Mathf.Sin(pi*(z+t));
p.y *=0.5f;
p.z = z;
return p;
}
static Vector3 MultiSine2DFunction(float x,float z,float t){
Vector3 p;
p.x = x;
p.y = 4f*Mathf.Sin(pi*(x+z+t/2f));
p.y += Mathf.Sin(pi*(x+t));
p.y += Mathf.Sin(2f*pi*(z+2f*t))*0.5f;
p.y *=1f/5.5f;
p.z = z;
return p;
}
static Vector3 Ripple(float x,float z,float t){//涟漪函数
Vector3 p;
float d = Mathf.Sqrt(x*x+z*z);
p.x = x;
p.y = Mathf.Sin(pi*(4f*d-t));
p.y /=1f+10f*d;
p.z = z;
return p;
}
static Vector3 Cylinder(float u,float v,float t){//圆柱体函数
Vector3 p;
float r = 0.8f + Mathf.Sin(pi*(6f*u+2f*v+t))*0.2f;
p.x = r*Mathf.Sin(pi*u);
p.y = v;
p.z = r*Mathf.Cos(pi*u);
return p;
}
static Vector3 Sphere(float u,float v,float t){
Vector3 p;
float r = 0.8f+ Mathf.Sin(pi*(6f*u+t))*0.1f;
r += Mathf.Sin(pi*(4f*v+t))*0.1f;
float s = r*Mathf.Cos(pi*0.5f*v);
p.x = s*Mathf.Sin(pi*u);
p.y = r*Mathf.Sin(pi*0.5f*v);
p.z = s*Mathf.Cos(pi*u);
return p;
}
static Vector3 Torus(float u,float v,float t){
Vector3 p;
float r1 = 0.65f + Mathf.Sin(pi*(6f*u+t))*0.1f;
float r2 = 0.2f + Mathf.Sin(pi*(4f*v+t))*0.05f;
float s =r2*Mathf.Cos(pi*v)+r1;
p.x = s*Mathf.Sin(pi*u);
p.y = r2*Mathf.Sin(pi*v);
p.z = s*Mathf.Cos(pi*u);
return p;
}
}
委托脚本GraphFunction
枚举类脚本
实现效果比较复杂,这里就不贴出了。
今天的练习需要大量的数学知识,但是除去这些数学知识,我们还能学到哪些东西呢?
首先就是委托类:
委托类获取实际函数的实现:
这里的functions是从静态数组function中获取的
而索引数是从枚举类中获得的
这种方法在加载数目大的函数或者资源时十分好用。我记得我上半年做的一个JAVA游戏坦克大战中,也是使用类似的方式去加载敌人,主角,子弹等不同的类装载到游戏中。
在这里我们创建了一个委托类GraphFunction,使用索引+枚举+委托这种方式加载函数,去定向获取不同的函数。这样写可以使得我们的脚本更加简洁,而且更不容易出错。
其它的?
其它的就是这次使用这些数学函数去生成方块的动态位置让我思路开阔了很多。比如说我觉得这种方法可以用来动态控制场景的变换,或者是编程化实现不同的场景。(因为这次练习的场景生成没有创建任何一个立方体,都是由脚本控制生成的)
还有吗?
数学真是太重要了!!!!!