找回密码
 立即注册
查看: 328|回复: 0

[简易教程] [catlikecoding]:新坑,unity编程教程学习笔记

[复制链接]
发表于 2020-11-25 09:16 | 显示全部楼层 |阅读模式
完全做翻译是不现实的了。但是终归还是应该做一下学习笔记,比如这个tutorial 2,我颠来倒去反复做了很多次,虽然每次都觉得学到了一些,但是到了自己写的时候还是够呛。
原文在这里:
catlike coding的教程有一个好处,就是不会一次把所有的代码甩你脸上,而是一步一步的,中间还有不少反复修改的地方。(是的,中间有一些代码是故意写错的)所以一篇教程读下来就好像有个人在你面前live coding一样。这样子读起来确实不错,但是回头想要快速学习的时候就没有那么友好了。所以这群笔记算作一个补充,给自己也给有类似需要的人希望能有些帮助。
说句题外话,我以前主要的瞎编语言是javascript,虽然我也知道npm让人有些头痛,但是总是没法下决心转到python来。开始翻译automate the boring stuff with python 以后,再遇到有不懂的地方直接去自己专栏看就可以了。很多时候都能找到满意的答案。所以写专栏还是有些帮助的。
成型后的graph类代码:
  1. using UnityEngine;
  2. public class Graph: MonoBehaviour{
  3.         public Transform pointPrefab;
  4.         Transform[] points;
  5.         [Range(10,100)] public int resolution = 10;
  6.         void Awake(){
  7.                 float step = 2f/resolution;
  8.                 Vector3 scale = Vector3.one * step;
  9.                 Vector3 position;
  10.                 points = new Transform[resolution];
  11.                 position.y = 0;
  12.                 position.z = 0;
  13.                 for(int i = 0;i<resolution;i++){
  14.                         Transform point = Instantiate(pointPrefab);
  15.                         position.x = (i+0.5f)*step-1f;
  16.                         point.localPosition = position;
  17.                         point.localScale = scale;
  18.                         point.SetParent(transform,false);
  19.                         points[i] = point;
  20.                 }
  21.         }
  22.         void Update(){
  23.                 for (int i=0;i<points.Length;i++){
  24.                         Transform point = points[i];
  25.                         Vector3 position = point.localPosition;
  26.                         position.y = Mathf.Sin(Mathf.PI * (position.x+Time.time));
  27.                         point.localPosition = position;
  28.                 }
  29.         }
  30. }
复制代码
shader部分的代码先不放了,暂时讲不清。
上面graph类代码掰开来讲有这么几块:
生成一条线

用Instantiate(transform)生成若干个点,把这些点排成一排就成了线:
一般来说一个点(用cube表示)单位尺寸是1x1x1,所以10个cube排在一起长度就是10,在Awake里面生成这样的一条线,代码如下:
  1. void Awake(){
  2.     for(int i = 0;i<10;i++){
  3.         Transform point = Instantiate(pointPrefab);
  4.         // set position for each point according to i:
  5.         point.localPosition = Vector3.right * i;
  6.     }
  7. }
复制代码
现在如果要把直线的尺寸从10变成1,每个cube要缩小到0.1,同时中心位置也要缩小成下面这样:
  1. [0, 1,2, 3 ..., 9] >> [0, 0.1, 0.2, 0.3 ..., 0.9]
复制代码
但是中心位置在0的话,cube的左半边会跑到y轴左边,所以要向右偏倚0.5,这个偏倚的量同样也要缩小,对应关系如下:
  1. [0.5, 1.5, 2.5, 3.5 ..., 9.5] >> [0.05, 0.15, 0.25, 0.35 ..., 0.95]
复制代码
计算表达式如下:
  1. newX = (oriX+0.5)*shrinkRatio
复制代码
根据需求,现在从10缩小到2的长度,然后左偏倚1成为-1至1的范围:
  1. newX = (oriX+0.5)/5 - 1
复制代码
所以for循环内部变成:
  1. for(int i = 0; i<10;i++){
  2.     Transform point = Instantiate(pointPrefab);
  3.     point.localPosition = Vector3.right * ((i+0.5f)/5 -1f );
  4.     point.localScale = Vector3.one / 5f;
  5. }
复制代码
将参数做进上面的过程里

在Graph类下面添加一个变量
  1. [Range(10,100)] public int resolution = 10;
复制代码
这个10 就是现在所取的分辨率,而我们需要将这么多的cube挤到-1到1这个长度为2的区间里,所以cube之间的间距(步长)就是:
  1. float step = 2f/resolution;
复制代码
算起来在resolution是10的时候,step就是5,所以不仅适用于中心间距,也适用于scale。把两个vector3提到循环以外,程序变成下面这样:
  1.         float step = 2f / resolution;
  2.         // 这样直接就可以把sclVect后面的/resolution * 2变成step了
  3.         Vector3 posVect;
  4.         posVect.z = 0;
  5.         Vector3 sclVect =Vector3.one * step
  6.         for (int i = 0; i< resolution; i++){
  7.             Transform point = Instantiate(pointPrefab);
  8.             posVect.x = (i+0.5f) * step - 1f;
  9.             posVect.y = posVect.x;
  10.             //设置x和y之间的关系:y = x
  11.             point.localPosition = posVect;
  12.             point.localScale = sclVect;
  13.         }
复制代码
顺便设置一下各个prefab的parent:
  1. point.setParent(transform,false);
复制代码
设置Shader

Shader程序是GPU用来渲染三维物体的。Unity的材质可以选取Shader,通过Shader设置材质的各种属性。除了使用Unity自带的,还可以自建一个shader。Shader的建立:Assets/Create/Shader/Stander Surface Shader,命名为ColoredPoint。
打开ColoredPoint可以看到程序如下所示:
  1. Shader "Custom/ColoredPoint" {
  2.         Properties {
  3.                 _Color ("Color", Color) = (1,1,1,1)
  4.                 _MainTex ("Albedo (RGB)", 2D) = "white" {}
  5.                 _Glossiness ("Smoothness", Range(0,1)) = 0.5
  6.                 _Metallic ("Metallic", Range(0,1)) = 0.0
  7.         }
  8.         SubShader {
  9.                 Tags { "RenderType"="Opaque" }
  10.                 LOD 200
  11.                
  12.                 CGPROGRAM
  13.                 #pragma surface surf Standard fullforwardshadows
  14.                 #pragma target 3.0
  15.                 sampler2D _MainTex;
  16.                 struct Input {
  17.                         float2 uv_MainTex;
  18.                 };
  19.                 half _Glossiness;
  20.                 half _Metallic;
  21.                 fixed4 _Color;
  22.                 UNITY_INSTANCING_CBUFFER_START(Props)
  23.                 UNITY_INSTANCING_CBUFFER_END
  24.                 void surf (Input IN, inout SurfaceOutputStandard o) {
  25.                         fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
  26.                         o.Albedo = c.rgb;
  27.                         o.Metallic = _Metallic;
  28.                         o.Smoothness = _Glossiness;
  29.                         o.Alpha = c.a;
  30.                 }
  31.                 ENDCG
  32.         }
  33.         FallBack "Diffuse"
  34. }
复制代码
目前只修改Albedo相关的内容:
  1. Shader "Custom/ColoredPoint" {
  2.     Properties {
  3.         _Glossiness("Smoothness",Range(0,1)) = 0.5
  4.         _Metallic ("Metallic", Range(0,1)) = 0.0
  5.     }
  6.     SubShader {
  7.         Tags { "RenderType" = "Opaque" }
  8.         LOD 200
  9.                 CGPROGRAM
  10.                 #pragma surface surf Standard fullforwardshadows
  11.         struct Input {
  12.         };
  13.                 half _Glossiness;
  14.                 half _Metallic;
  15.         UNITY_INSTANCING_CBUFFER_START(Props)
  16.         UNITY_INSTANCING_CBUFFER_END
  17.         void surf (Input IN, inout SurfaceOutputStandard o) {
  18.             o.Metallic = _Metallic;
  19.             o.Smoothness = _Glossiness;
  20.         }
  21.         ENDCG
  22.     }
  23.     FallBack "Diffuse"
  24. }
复制代码
目前shader还和Unity里面的东西没有任何关联,因为我们还没修改过Input,修改Input如下:
  1. Struct Input {
  2.     float3 worldPos;
  3. }
复制代码
然后让surf里的o属性跟Input关联起来:
  1. o.Albedo.r = IN.worldPos.x;
复制代码
x的值从-1到1,但是实际上rgb是不能取负值的,所以左半边会一直保持黑色。作一下变换,让-1~1的值变成0~1的范围:
  1. o.Albedo.r = IN.worldPos.x * 0.5 + 0.5;
复制代码
另外顺便把y也用进来设置g值:
  1. o.Albedo.rg = IN.worldPos.xy * 0.5 + 0.5;
复制代码
Shader的写法还真是奇怪哦,可以这么把一个向量的两个分量放在一行写。
让线动起来

目前在Awake的时候设置了一下函数形态以后,接下来就没有然后了。如果这个函数能动起来那不是很好么?实际上让函数随时间变化,就是在x变量的基础再加一个t变量:f(x,t)。不过如果除了Awake的时候Instantiate一下,后面还想设置这些点的话,首先要用个数组跟踪他们。
  1. points = new Transform[resolution];
  2. for (int i=0;i<resolution;i++){
  3. ...
  4. points[i] = point
复制代码
因为现在y每帧都变,所以Awake就没必要设置y了,Awake部分的代码如下:
  1.         void Awake(){
  2.                 float step = 2f/resolution;
  3.                 Vector3 scale = Vector3.one * step;
  4.                 Vector3 position;
  5.                 points = new Transform[resolution];
  6.                 position.y = 0;
  7.                 position.z = 0;
  8.                 for(int i = 0;i<resolution;i++){
  9.                         Transform point = Instantiate(pointPrefab);
  10.                         position.x = (i+0.5f)*step-1f;
  11.                         point.localPosition = position;
  12.                         point.localScale = scale;
  13.                         point.SetParent(transform,false);
  14.                         points[i] = point;
  15.                 }
  16.         }
复制代码
接下来是Update:
  1.         void Update () {
  2.                 for (int i = 0; i < resolution; i++) {}
  3.         }
复制代码
除了用resolution,其实还可以用points.Length。接下来每次更新,都遍历points列表,设置point的y值:
  1.                 for (int i = 0; i < points.Length; i++) {
  2.                         Transform point = points[i];
  3.                         Vector3 position = point.localPosition;
  4.                         position.y = Mathf.Sin(position.x);
  5.                         point.localPosition = position;
  6.                 }
复制代码
之所以先把point.localPosition交给position,设置好以后又重新交给point是因为localPostion作为一个属性,只接受Vector3值。
上面用Sine函数设置y,但是还是没看到时间的影子。另外如果像上面这样写,Sine函数参数取值范围只有-1到1,看起来一点也不像正弦函数。所以还需要给参数乘一个PI:
  1. position.y = Mathf.Sin(Mathf.PI * position.x);
复制代码
上面说到现在还是没和时间扯上关系,让函数的形式变成:y = Sin( * (x+t))就可以了。而t这个变量可以用Time.time获得。程序如下:
  1. position.y = Mathf.Sin(Mathf.PI * (position.x + Time.time));
复制代码
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-6-5 16:52 , Processed in 0.123680 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表