|
完全做翻译是不现实的了。但是终归还是应该做一下学习笔记,比如这个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;
- [Range(10,100)] public int resolution = 10;
- void Awake(){
- float step = 2f/resolution;
- Vector3 scale = Vector3.one * step;
- Vector3 position;
- points = new Transform[resolution];
- 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[i] = point;
- }
- }
- void Update(){
- for (int i=0;i<points.Length;i++){
- Transform point = points[i];
- 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, 1,2, 3 ..., 9] >> [0, 0.1, 0.2, 0.3 ..., 0.9]
复制代码 但是中心位置在0的话,cube的左半边会跑到y轴左边,所以要向右偏倚0.5,这个偏倚的量同样也要缩小,对应关系如下:- [0.5, 1.5, 2.5, 3.5 ..., 9.5] >> [0.05, 0.15, 0.25, 0.35 ..., 0.95]
复制代码 计算表达式如下:- newX = (oriX+0.5)*shrinkRatio
复制代码 根据需求,现在从10缩小到2的长度,然后左偏倚1成为-1至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类下面添加一个变量- [Range(10,100)] 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[resolution];
- for (int i=0;i<resolution;i++){
- ...
- points[i] = point
复制代码 因为现在y每帧都变,所以Awake就没必要设置y了,Awake部分的代码如下:- void Awake(){
- float step = 2f/resolution;
- Vector3 scale = Vector3.one * step;
- Vector3 position;
- points = new Transform[resolution];
- 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[i] = 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[i];
- 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( * (x+t))就可以了。而t这个变量可以用Time.time获得。程序如下:- position.y = Mathf.Sin(Mathf.PI * (position.x + Time.time));
复制代码 |
|