[catlikecoding]:新坑,unity编程教程学习笔记
完全做翻译是不现实的了。但是终归还是应该做一下学习笔记,比如这个tutorial 2,我颠来倒去反复做了很多次,虽然每次都觉得学到了一些,但是到了自己写的时候还是够呛。原文在这里:
catlike coding的教程有一个好处,就是不会一次把所有的代码甩你脸上,而是一步一步的,中间还有不少反复修改的地方。(是的,中间有一些代码是故意写错的)所以一篇教程读下来就好像有个人在你面前live coding一样。这样子读起来确实不错,但是回头想要快速学习的时候就没有那么友好了。所以这群笔记算作一个补充,给自己也给有类似需要的人希望能有些帮助。
说句题外话,我以前主要的瞎编语言是javascript,虽然我也知道npm让人有些头痛,但是总是没法下决心转到python来。开始翻译automate the boring stuff with python 以后,再遇到有不懂的地方直接去自己专栏看就可以了。很多时候都能找到满意的答案。所以写专栏还是有些帮助的。成型后的graph类代码:
using UnityEngine;
public class Graph: MonoBehaviour{
public Transform pointPrefab;
Transform[] points;
public int resolution = 10;
void Awake(){
float step = 2f/resolution;
Vector3 scale = Vector3.one * step;
Vector3 position;
points = new Transform;
position.y = 0;
position.z = 0;
for(int i = 0;i<resolution;i++){
Transform point = Instantiate(pointPrefab);
position.x = (i+0.5f)*step-1f;
point.localPosition = position;
point.localScale = scale;
point.SetParent(transform,false);
points = point;
}
}
void Update(){
for (int i=0;i<points.Length;i++){
Transform point = points;
Vector3 position = point.localPosition;
position.y = Mathf.Sin(Mathf.PI * (position.x+Time.time));
point.localPosition = position;
}
}
}shader部分的代码先不放了,暂时讲不清。
上面graph类代码掰开来讲有这么几块:
生成一条线
用Instantiate(transform)生成若干个点,把这些点排成一排就成了线:
一般来说一个点(用cube表示)单位尺寸是1x1x1,所以10个cube排在一起长度就是10,在Awake里面生成这样的一条线,代码如下:
void Awake(){
for(int i = 0;i<10;i++){
Transform point = Instantiate(pointPrefab);
// set position for each point according to i:
point.localPosition = Vector3.right * i;
}
}现在如果要把直线的尺寸从10变成1,每个cube要缩小到0.1,同时中心位置也要缩小成下面这样:
>> 但是中心位置在0的话,cube的左半边会跑到y轴左边,所以要向右偏倚0.5,这个偏倚的量同样也要缩小,对应关系如下:
>> 计算表达式如下:
newX = (oriX+0.5)*shrinkRatio根据需求,现在从10缩小到2的长度,然后左偏倚1成为-1至1的范围:
newX = (oriX+0.5)/5 - 1所以for循环内部变成:
for(int i = 0; i<10;i++){
Transform point = Instantiate(pointPrefab);
point.localPosition = Vector3.right * ((i+0.5f)/5 -1f );
point.localScale = Vector3.one / 5f;
}将参数做进上面的过程里
在Graph类下面添加一个变量
public int resolution = 10;这个10 就是现在所取的分辨率,而我们需要将这么多的cube挤到-1到1这个长度为2的区间里,所以cube之间的间距(步长)就是:
float step = 2f/resolution;算起来在resolution是10的时候,step就是5,所以不仅适用于中心间距,也适用于scale。把两个vector3提到循环以外,程序变成下面这样:
float step = 2f / resolution;
// 这样直接就可以把sclVect后面的/resolution * 2变成step了
Vector3 posVect;
posVect.z = 0;
Vector3 sclVect =Vector3.one * step
for (int i = 0; i< resolution; i++){
Transform point = Instantiate(pointPrefab);
posVect.x = (i+0.5f) * step - 1f;
posVect.y = posVect.x;
//设置x和y之间的关系:y = x
point.localPosition = posVect;
point.localScale = sclVect;
} 顺便设置一下各个prefab的parent:
point.setParent(transform,false);设置Shader
Shader程序是GPU用来渲染三维物体的。Unity的材质可以选取Shader,通过Shader设置材质的各种属性。除了使用Unity自带的,还可以自建一个shader。Shader的建立:Assets/Create/Shader/Stander Surface Shader,命名为ColoredPoint。
打开ColoredPoint可以看到程序如下所示:
Shader &#34;Custom/ColoredPoint&#34; {
Properties {
_Color (&#34;Color&#34;, Color) = (1,1,1,1)
_MainTex (&#34;Albedo (RGB)&#34;, 2D) = &#34;white&#34; {}
_Glossiness (&#34;Smoothness&#34;, Range(0,1)) = 0.5
_Metallic (&#34;Metallic&#34;, Range(0,1)) = 0.0
}
SubShader {
Tags { &#34;RenderType&#34;=&#34;Opaque&#34; }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
UNITY_INSTANCING_CBUFFER_START(Props)
UNITY_INSTANCING_CBUFFER_END
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack &#34;Diffuse&#34;
}目前只修改Albedo相关的内容:
Shader &#34;Custom/ColoredPoint&#34; {
Properties {
_Glossiness(&#34;Smoothness&#34;,Range(0,1)) = 0.5
_Metallic (&#34;Metallic&#34;, Range(0,1)) = 0.0
}
SubShader {
Tags { &#34;RenderType&#34; = &#34;Opaque&#34; }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
struct Input {
};
half _Glossiness;
half _Metallic;
UNITY_INSTANCING_CBUFFER_START(Props)
UNITY_INSTANCING_CBUFFER_END
void surf (Input IN, inout SurfaceOutputStandard o) {
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
}
ENDCG
}
FallBack &#34;Diffuse&#34;
}目前shader还和Unity里面的东西没有任何关联,因为我们还没修改过Input,修改Input如下:
Struct Input {
float3 worldPos;
}然后让surf里的o属性跟Input关联起来:
o.Albedo.r = IN.worldPos.x;x的值从-1到1,但是实际上rgb是不能取负值的,所以左半边会一直保持黑色。作一下变换,让-1~1的值变成0~1的范围:
o.Albedo.r = IN.worldPos.x * 0.5 + 0.5;另外顺便把y也用进来设置g值:
o.Albedo.rg = IN.worldPos.xy * 0.5 + 0.5;Shader的写法还真是奇怪哦,可以这么把一个向量的两个分量放在一行写。
让线动起来
目前在Awake的时候设置了一下函数形态以后,接下来就没有然后了。如果这个函数能动起来那不是很好么?实际上让函数随时间变化,就是在x变量的基础再加一个t变量:f(x,t)。不过如果除了Awake的时候Instantiate一下,后面还想设置这些点的话,首先要用个数组跟踪他们。
points = new Transform;
for (int i=0;i<resolution;i++){
...
points = point因为现在y每帧都变,所以Awake就没必要设置y了,Awake部分的代码如下:
void Awake(){
float step = 2f/resolution;
Vector3 scale = Vector3.one * step;
Vector3 position;
points = new Transform;
position.y = 0;
position.z = 0;
for(int i = 0;i<resolution;i++){
Transform point = Instantiate(pointPrefab);
position.x = (i+0.5f)*step-1f;
point.localPosition = position;
point.localScale = scale;
point.SetParent(transform,false);
points = point;
}
}接下来是Update:
void Update () {
for (int i = 0; i < resolution; i++) {}
}除了用resolution,其实还可以用points.Length。接下来每次更新,都遍历points列表,设置point的y值:
for (int i = 0; i < points.Length; i++) {
Transform point = points;
Vector3 position = point.localPosition;
position.y = Mathf.Sin(position.x);
point.localPosition = position;
}之所以先把point.localPosition交给position,设置好以后又重新交给point是因为localPostion作为一个属性,只接受Vector3值。上面用Sine函数设置y,但是还是没看到时间的影子。另外如果像上面这样写,Sine函数参数取值范围只有-1到1,看起来一点也不像正弦函数。所以还需要给参数乘一个PI:
position.y = Mathf.Sin(Mathf.PI * position.x);上面说到现在还是没和时间扯上关系,让函数的形式变成:y = Sin( https://www.zhihu.com/equation?tex=%5Cpi * (x+t))就可以了。而t这个变量可以用Time.time获得。程序如下:
position.y = Mathf.Sin(Mathf.PI * (position.x + Time.time));
页:
[1]