2016年3月17日 星期四

頂點及索引

Vertex and Index

頂點 ->索引
v0 -> 0
v1 -> 1
v2 -> 2
v3 -> 3

index決定三角形構成方式
[0,1,2,2,3,0]
v0->v1->v2
v2->v3->v0
決定了兩個三角形構成,
也就是決定了兩個面

頂點代表多重意義,基本上是多種屬性的重疊
一般封裝的方式有兩種:

struct Vertex {
  Vector3 Position;
  Vector3 Normal;
  Vector3 Color;
}

Vector3[] Vertices;
Vector3[] normals;
Vector3[] colors;

Vertex Buffer

存放vertices的區域,稱為vertex buffer
在DX/OpenGL中,我們需要
1 申請vertex buffer
2 綁定buffer
3 上傳vertex資料
4 一個模型對應一個buffer

在unity中, 這些已經被封裝了

Gluint vertexbuffer;
glGenBuffer(1,&vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER,vertexbuffer);
glBufferData(GL_ARRAY_BUFFER,sizeof(g_vertex_buffer_data),g_vertex_buffer_data,GL_STATIC_DRAM);

Index Buffer

存放index資料的暫存區,稱為index buffer

不同的向量方向,構成的三角形不同

[0,1,2]構成 v0->v1, v1->v2兩個向量
[2,1,0]構成v2->v1,v1->v2兩個向量
兩者的法向量不同,所以是不同三角形


 三角形的正面及背面





Dx以情況A為正向,依據左手系統,法向量朝我方。

OpenGL以情況B為正向,依據右手系統,法向量朝我方。


索引的意義


1 每三個索引,決定一個三角形(面數)
2 索引順序不同,構成三角形不同
3 DX預設順時針為正向,OpenGL預設逆時針為正向
4 索引會決定三角形的正面、背面
5 索引可表示重複使用的頂點,可減少記憶體使用
6 索引是簡單整數,有些設備一個物件只支援65535個索引=21845個三角形

Unity物件頂點數限制65000個,也是從此而來。

物件頂點數超過65535個,可進行頂點資料及索引拆分,分成兩份資料。

7 合併緩衝區

用索引決定渲染

渲染整個模型,索引從0到最後
渲染某部分,更改索引起始位置及結束位置

合併緩衝區

就是把兩個物件的頂點緩衝區及索引緩衝區合併,當成一個物件處理。

假設三個物件的vertexBuffer及indexBuffer:
v0   v1  v2 => 0,1,2
m0 m1 m2 => 0,1,2
n0  n1   n2 => 0,1,2

合併後
[v0,v1,v2,m0,m1,m2,n0,n1,n2]
[0,1,2,(0+3),(1+3),(2+3),(0+6),(1+6),(2+6)]

0+3=4,表示是vertex的第四個=m0
1+4=5,表示是vertex的第五個=m2

這就是unity draw call batching的原理
合併緩衝區在物件是靜態時,效果最好,
如果非static卻一直合併緩衝區,就適得其反。
所以unity draw call batching 有分dynamic及static,
確保物件是static才使用static方法。

顏色

顏色表示法


假設使用向量表示顏色,最小值為0,最大值為1。
三原色可表示為:

r=(1,0,0)
g=(0,1,0)
b=(0,0,1)

8位元表示法,最小值為0,最大值為255
r=(255,0,0)
g=(0,255,0)
b=(0,0,255)

[0,1]對應到[0,255],只要每個分量除255即可。



顏色運算

加法

r+g=(1,0,0)+(0,1,0)=(1,1,0)

減法

(1,1,0)-(0,1,0)=(1,0,0)

數乘

0.5*(1,1,1)=(0.5,0.5,0.5)

分量乘法

(r0,r1,r2)*(g0,g1,g2)=(r0*g0,r1*g1,r2*g2)

分量乘法應用
假設c0=(1,1,1) 太陽光
c1=(0.5,0.25,0.5)  材質反射率

c0*c1=(0.5,0.25,0.5)
就是太陽光照射材質後,反射出來的光

向量點積、向量叉積對顏色無意義


顏色分量要限縮在0-1之間,超過這個範圍就無意義




2016年3月8日 星期二

射線 平面

射線

定義

定義射線需要起點,方向,長度
起點p0
方向=單位向量u
長度=t

向量相加為所形成平行四邊形的對角線
得到射線p(t)=p0+tu


Unity Raycast API

public static bool Raycast(Vector3 originVector3 direction, float maxDistance)
其實就和射線定義相同(起點,方向,長度)


平面

定義

p為平面上任意點,n為平面法向量,p1為法向量交於平面的點

p-p1向量=p-p1 假設為(ax,ay,az)
n假設為 (bx,by,bz)
p-p1與n垂直
向量點積公式 (a長)*(b長)*Cos(90)=0
向量點積算法(ax*bx+ay*by+cz*bz)
得到n.(p-p1)=0


射線與平面相交


射線
p=p0+tu

平面
n.(p-p1)=0

把射線p帶入平面

n.(p0+tu - p1)=0

n.p0+n.tu-n.p1=0

n.tu=n.p1-n.p0

t=n.p1-n.p0/n.u

這裡 (.)是點積

若t>=0,射線與平面相交,且交點為p0+tu
若t<0,兩不相交

矩陣

矩陣運算

矩陣相加   對應元素相加
矩陣相減   對應元素相減
矩陣相乘   每個元素就是兩個矩陣的行與列點積。所以兩個矩陣的行與列維度要相同。
矩陣數乘   每個元素乘上一個數
矩陣相等   對應的每個元素相等

矩陣相乘

條件

假設A矩陣*B矩陣
則A行向量的元素個數=B列向量元素個數
(大陸橫為行,直為列)
否則無法相乘


A=[1- 5 -4]
     [3   2  1]

B=[2  1  0 ]
     [0  -2  1]
     [-1  2   3]

AB=[(-1,5,-4).(2,0,-1)   (-1,5,-4).(1,-2,2)   (-1,5,-4).(0,1,3)]
        [(3,2,1).(2,0,1)       (3,2,1).(1,-1,2)      (3,2,1)(0,1,3)]

行.列  (點積)

矩陣乘法不滿足交換率

A*B != B*A

矩陣乘法滿足結合率

A(B+C)=AB+AB
(AB)C=A(BC)


矩陣除法

矩陣運算沒有除法

向量矩陣

行向量矩陣
A=[1 3 3]

列向量矩陣
B= [2]
      [2]
      [3]

向量-矩陣乘法

OpenGL 左乘方式--矩陣在左,向量在右    記憶法(O在左)
DirectX    右乘方式--矩陣在右,向量在左    記憶法(X在右)

因為矩陣乘法不反足交換率,所以在左在右結果不同。


轉置矩陣(Transpose)

把一個矩陣的行和列互換。mxn的轉置矩陣就是nxm。
原矩陣的行向量=轉置矩陣的列向量

A=[1  1  1 ]
     [2  2   2]

C=A.transpose
[1 2]
[1 2]
[1 2]


B=[a b c d]

D=B.transpose
[a]
[b]
[c]
[d]

A*B=(D*C).transpose

範例
A,B兩向量
C=A.transpose
D=B.transpose

A*B    ↘︎  C*D    =>  A*B=D*C      兩個剛好交錯
B*A   ↗︎   D*C    =>  B*A=C*D

矩陣乘以向量

1 矩陣維度與向量元素個數不需相同

2 矩陣*向量  =>變成向量

3 矩陣*向量=向量*矩陣轉置(要自己計算)

向量 X=(1,2,3,4);

A=[1  1  1 1]
     [2  2   2 2]
     [3  3  3  3]
     [4  4  4  4]

A*X=(1111.(1234)  2222.(1234)  3333.(1234)   4444.(1234))

A.transpose=
[1 2 3 4]
[1 2 3 4]
[1 2 3 4]
[1 2 3 4]

X*A.transpose
((1234)(1111)   (1234)(2222)  (1234)(3333)  (1234)(4444))


 Unity中矩陣乘以向量

可以直接矩陣*向量
不能向量*矩陣  (要自己計算)

矩陣A
向量V

A*V  =>ok
V*A  =>error


單位矩陣

1 是正方形矩陣

2 中心左上右下斜對角線(back slash)的元素都是1

3 其他元素都是0

4 任何矩陣*單位矩陣,結果不變

5 單位矩陣就是矩陣中的數字1(跟單位向量相同)

[1  0   0]
[0  1   0]
[0  0   1]

逆矩陣(Inverse)

1 矩陣運算沒有除法

2 只有正方形矩陣才能夠逆運算

3 一個nxn的矩陣的逆矩陣,仍然是nxn的矩陣

4 不是所有的矩陣都有逆矩陣,有逆矩陣的矩陣稱為可逆

5 如果有可逆矩陣,這個逆矩陣一定是唯一的

6 將一個矩陣與他的逆矩陣相乘,結果一定為單位矩陣

有矩陣A,B,C,且A=B*C,且D為C的逆矩陣
A=BC
AD=BDC (DC=1)
AD=B

範例

有一點a
一矩陣b
d=b的逆矩陣
轉換後的點c = a*b

cd=a*b*d  (bd=1)
cd=a

可找到原始的a點

常用來把點從局部空間轉換到世界空間,
或從世界空間轉換回局部空間。


矩陣計算平移

1 點的位移向量,使用DirectX要寫在矩陣第四行的前三個,
   因為如果寫在前三行,那會被x,y,z乘到
   只能寫在第四行的前三個,經過計算後就分別被x,y,z加

   使用OpenGL則分別寫在前三行的最後一元素

    directX與openGL可使用矩陣轉置來切換

2 矩陣的每個位置數字都有意義,都是經過設計的

3 因為3x3不夠用,所以擴充為4x4,自己新增w

4 當w=0,(x,y,z,0) 就是用來描述向量 =>可得到原始向量

5 當w=1,(x,y,z,1) 就是用來描述點    =>可算出位移後的點


DirectX計算(右乘,矩陣在右,向量在左)


A=[x,y,z,w] 行向量

M= 單位矩陣
       1  0  0  0
       0  1  0  0
       0  0  1  0
       0  0  0  1

A*M=

x*1+y*0+z*0+w*0=x
x*0+y*1+z*0+w*0=y
x*0+y*0+z*1+w*0=z
x*0+y*0+z*0+w*1=w

假設位移向量B=(tx,ty,tz),則A位移後的點為(x+tx,y+ty,z+tz)
tx,ty,tz寫在矩陣最下行靠左

m=
1  0   0   0
0  1   0   0
0   0  1   0
tx  ty tz  1

A*m=[x,y,z,w] *m=

x*1+y*0+z*0+w*tx=x+wtx
x*0+y*1+z*0+w*ty=y+wty
x*0+y*0+z*1+w*tz=z+wtz
x*0+y*0+z*0+w*1=w

當w=1,結果就是 x+tx,y+ty,z+tz,1
因為w是我們自己擴充的,所以可設為1

所以經由以上算法,點A的位移,利用矩陣運算可得到結果。
矩陣每個位置的數字都有固定意義,都事先設定好的。


OpenGL計算(左乘,矩陣在左,向量在右 =Unity的系統 )

A=[x,y,z,w] 行向量

M= 單位矩陣
       1  0  0  0
       0  1  0  0
       0  0  1  0
       0  0  0  1

M*A=

x*1+y*0+z*0+w*0=x
x*0+y*1+z*0+w*0=y
x*0+y*0+z*1+w*0=z
x*0+y*0+z*0+w*1=w

假設位移向量B=(tx,ty,tz),則A位移後的點為(x+tx,y+ty,z+tz)
tx,ty,tz寫在矩陣前三行最末位

m=
1  0   0   tx
0  1   0   ty
0   0  1   tz
0  0  0    1

m*A=m*[x,y,z,w] =

1*x+0*y+0*z+w*tx=x+wtx
0*x+1*y+0*z+w*ty=y+wty
0*x+0*y+1*z+w*tz=z+wtz
0*x+0*y+0*z+w*1=w

當w=1,結果就是 x+tx,y+ty,z+tz,1
因為w是我們自己擴充的,所以可設為1

矩陣計算縮放

1 縮放就是倍數的關係,所以(x,y,z)要能分別乘以縮放的數字

2 因此只能放在斜對角線的元素位置上

sx 0  0  0
0  sy 0  0
0  0  sz 0
0  0  0  0

假設x放大2,y所小0.5,z放大3,那就是

2     0  0  0
0  0.5  0  0
0     0  3  0
0     0  0  0

範例

矩陣A=
[1 0  0      0]
[0  2  0     0]
[0  0  0.5  0]
[0  0  0     1]

向量V=[x,y,z,w]

v*A=
x*1+y*0+z*0+w*0=x
x*0+y*2+z*0+w*0=2y
x*0+y*0+z*0.5+w*0=0.5z
x*0+y*0+z*0+w*0=w

=[x,2y,0.5z,0]
=x不變,y放大2,z縮小0.5


矩陣計算旋轉

1 旋轉要考慮軸

2 旋轉要考率角度(非弧度)


假設延n軸旋轉 n=(x,y,z)
轉 a角度
c=cos(a)
s=sin(a)

矩陣為:

c+(1-c)*x*x        (1-c)*x*y+s*z      (1-c)*x*z-s*y      0
(1-c)*x*y-s*z       c+(1-c)*y*y       (1-c)*y*z+s*x     0
(1-c)*x*z+s*y     (1-c)*y*z-s*x       c+(1-c)*z*z        0
0                           0                            0                          1

範例

假設延x軸旋轉 (1,0,0)

1   0             0             0
0  cos(a)   sin(a)      0
0  -sin(a)    cos(a)    0
0  0             0              1


矩陣組合


1 如果一次要計算三個矩陣(平移矩陣,縮放矩陣,旋轉矩陣),太消耗資源

2 利用矩陣乘法分配律,可以把三個矩陣先相乘,在跟向量相乘,可以少算2/3次

3 同時三個矩陣組成一個,也方便處理

4 組合方式是三個矩陣相乘



2016年3月6日 星期日

向量

向量的意義

向量是同時具有大小和方向的物理量

勢能:在某方向上施加一定的作用力
位移:在某方向上移動的一度距離
速度:速率和方向

用來表示方向

遊戲中角色方向
多邊形面對的方向
光線的方向
反射光的方向

向量幾何意義

兩個向量可以:
確定一個三角形
確定一個平行四邊形
確定一個平面

向量運算

向量相加  對應的元素相加
向量相減  對應的元素相減
向量數乘  每個元素乘上一個數
向量叉積  就是第三個同時垂直原向量的向量。不含自己,前減後,↘︎↙︎↘︎ 。|A||B|Sin(a)
向量點積  對應元素相乘,加起來。變成一個數。|A||B|Cos(a)
向量相等  方向相同,大小相等就是兩個相等向量


向量計算

向量計算一定是把向量頭部移到原點
也就是假設向量從原點出發

向量加法

定義

兩個同維度(2D和2D或3D和3D)的向量相加,
就是兩個向量的對應分量相加。
x+x
y+y
z+z

u=(ux,uy,uz)
v=(vx,vy,vz)
u+v=(ux+vx,uy+vy,uz+vz)

幾何意義

A=(xa,yz)
B=(xb,yb)
C=(xc,yc)

AB=(xb-xa,yb-ya)
BC=(xc-xb,yc-yb)
AC=(xc-xa,yc-ya)

AB+BC=(xb-xa+xc-xb,yb-ya+yc-yb)=(xc-xa,yc-ya)=AC
頭尾相連的兩個向量相加,結果從頭到尾的那個向量

AC就是平行四邊形的對角線
是兩向量行成的夾角內的對角線
因此兩個向量相加可以確定一個平行四邊形

AC也是兩向量頭尾相連的三角形的第三邊
所以可以求第三邊的長度及方向

平行四邊形面積= 底*高
所以向量可以算出平行四邊形面積

三角形面積=平行四邊形面積/2


結論

向量加法就是:兩項量移動成頭尾相連,所形成的第三邊。

因為向量有相等性,可以平移,
所以任何兩個向量,一定可以平移成頭尾相連。


向量減法


定義

兩個同維度(2D和2D或3D和3D)的向量相減,
就是兩個向量的對應分量相減。
x-x
y-y
z-z

u=(ux,uy,uz)
v=(vx,vy,vz)
u-v=(ux-vx,uy-vy,uz-vz)

幾何意義

AB=(xb-xa,yb-ya)
AC=(xc-xa,yc-ya)
BC=(xc-xb,yc-yb)


AB-AC=(xb-xa-xc+xa,yb-ya-yc+ya)=(xb-xc,yb-yc)=CB

CB是兩向量形成的平行四邊形的對角線(非兩向量形成的夾角內的對角線)
所以也可確定一個平行四邊形

CB是兩向量尾部相連形成的三角形的第三邊
CB可以求第三邊的長度及方向

結論

向量減法就是:兩向量移動成起點相連形成的第三邊
方向指向被減向量。(減號前方的是被減數)

向量減法就是向量加法加上負向量,
所以用向量加法來思考即可。


向量叉積 

向量叉積 Cross Product
向量外積 Outer Product
向量積Vector Product

A,B兩向量叉積(A X B) 會得到一個垂直於這兩個向量的第三個向量
B,A兩向量叉積(B X A) 會得到一個垂直於這兩個向量的第三個向量,但與A X B 方向相反

 B = - A ∧ B 
各自算出的結果是兩個方向相反,大小相同向量
就是反交換律

寫作

\vec{a}×\vec{b}(有時也被寫成\vec{a}\vec{b},避免和字母x混淆)

公式

A X B =|A||B|Sin𝛳
θ表示\vec{a}\vec{b}之間的角度(0°≤θ≤180°),它位於這兩個矢量所定義的平面上
\vec{n}是一個與\vec{a}\vec{b}所構成的平面垂直單位矢量
https://zh.wikipedia.org/wiki/%E5%90%91%E9%87%8F%E7%A7%AF

直接算法

A=(ax,ay,az)
B=(bx,by,bz)

A x B=(ay*bz - az*by) =x          記憶法 ↘︎  不含自己    前減後
            (az*bx - ax*bz) =y                       ↙︎
            (ax*by - ay*bx) =z                      ↘︎

幾何意義

叉積可得到垂直於AB平面的法向量。


向量點積

向量點積 Dot Product
向量內積 Inner Product
標向量 Scalar Product

 兩向量計算點積會得到一個純量,不帶方向只有大小

寫作

\vec{a}\vec{b}

公式

A.B=|A||B|cos𝛳

𝛳是AB間的最小夾角。即0<= 𝛳<=180
兩個向量必須共起點
當A,B都是單位向量時,A.B=1*1*cos𝛳=cos𝛳 , => -1<=cos𝛳<=1
cos𝛳一定大於等於-1,小於等於1

直接算法

A=(ax,ay,az)
B= (bx,by,bz)

A.B =ax*bx+ay*by+az*bz  =>只有大小  記憶法:分量相乘,加起來

AB都是單位向量,可推斷出角度

就可直接得到cos𝛳的值
若cos𝛳的值是已知的,那就可以知道角度是多少
ABcos𝛳=0.95 ,𝛳=18
因為cos(18)=0.95

幾何意義

無,但有一些性質

利用cos𝛳可得到一些性質
cos𝛳 0度=1
cos𝛳 90度=0
cos𝛳 180度=-1

所以 A.B=0  =>cos𝛳=0  => 𝛳 =90度 => A,B垂直
所以 A.B>0  =>cos𝛳>0  => 𝛳 <90度 =>A,B夾角小於90度
所以 A.B<0  =>cos𝛳<0  => 𝛳 >90度 =>A,B夾角大於90度

平面法線跟光的角度越小,光越強
平面法線跟光的角度越大,光越弱
正好符合cos,所以可以用cos(𝛳)來計算光的強弱
光最後的顏色=光的顏色*cos(𝛳)


向量大小(長度)

向量長度符號
||U||=√a² +b²

單位向量符號
U戴帽子∧

都是從勾股定理,商高定理(畢氏定理)來的
Pythagorean theorem

平面上的直角三角形的兩條直角邊的長度平方和等於斜邊長平方
勾長 直邊
股長 底邊
弦長 斜邊

來源:
根據「周髀算經」記載,
商高對周公說 將一根直尺折成一個直角,
兩端連結成一個直角三角形,
如果勾是 3,股是 4, 那麼弦就是 5。

2D distance

√(a²   + b²)
根號(a²+b²)

3D distance

√(x²+y²+z²)
根號(x²+y²+z²)

空間中的兩個點距離
A(ax,ay,az)
B(bx,by,bz)
√((ax-bx)²+(ay-by)²+(az-bz)²)
 根號((ax-bx)²+(ay-by)²+(az-bz)²)

a 是利用兩次勾股定理算出

b 或是利用向量平移,把其中一個點移到原點,
然後另一個點扣掉平移量之後,跟原點計算距離

其實就是向量減法的幾何意義

單位向量驗證
把長度/長度
√(x²+y²+z²)/向量長=1/1=1

C# Coroutine

Unity Coroutine

使用的地方

必須在MonoBehaviour或繼承MonoBehaviour的class中使用

不可使用的地方

yield不可在Update
不可在FixedUpdate中使用

啟動Coroutine

StartCoroutine(string methodName)
StartCoroutine(IEnumeratorroutine)

停止Coroutine

StopCoroutine(string methodName)
StopAllCoroutines()

三角函數

三角函數定義


直角三角形邊的比例關係
sin
cos
tan
cot
sec
csc


          0°90°180°270°
sinθ   0   1  0   -1
cosθ  1   0   -1  0
tanθ  0   x    0   x
cotθ  x   0    x   0
secθ 1   x    -1   x
cscθ  x  1    x   -1

http://ms1.hcvs.kh.edu.tw/hc3331/pbook/scr/work-1/page3.htm

假設斜邊為1


sinθ=對邊/斜邊 =對邊/1=對邊  =>sinθ就是對邊長
cosθ=對邊/鄰邊=鄰邊/1=鄰邊  => cosθ就是鄰邊長


正弦比例

假設一三角形三個角A,B,C對應三邊a,b,c
a邊的高為ha
三角形面積=a*ha/2
sinC=ha/b => ha=b*sinC

三角形面積= a*b*sinC/2
同理 a*b*SinC/2 =b*c*SinA/2 =a*c*SinB/2
=> abSinC=bsSinA = acSinB   (全部*2)
=>  SinC/c=SinA/a=SinB/b       (全部除abc)
=>  c/SinC=a/SinA =b/SinB     (反過來寫)

結論  
每個角的(sin值與對邊)的比相等
a/sinA=b/SinB=c/SinC
sinA/a=sinB/b=sinc/C




直線與圓

直線斜率

斜率=y/x


兩垂直的直線,斜率相成=-1

圓方程式

圓上形成直徑的兩點(x1,y1), (x2,y2)
與圓上的任意一點(x,y),
可得到圓方程式為:


(x-x1)(x-x2)+(y-y1)(y-y2)=0

證明:

(x,y)與(x1,y1)線段
(x,y)與(x2,y2)線段,
兩者的斜率相成=-1。
(圓周上的三角形對直徑的角是直角)


(x,y),(x1,y1)斜率=(y-y1)/(x-x1)

(x,y),(x2,y2)斜率=(y-y2)/(x-x2)

((y-y1)/(x-x1) ) * ((y-y2)/(x-x2))=-1
=>(y-y1)*(y-y2)=-1*(x-x2)*(x-x1)
=>(y-y1)(y-y2)+(x-x1)(x-x2)=0

2016年3月4日 星期五

C# delegete


C# delegete

就像是把函式包裝過然後傳給別的函式使用。
有點像是變數的泛型,因為不確定使用時的什麼類型,所以訂一個通用的類型來用。

Unity Delegate

//delegate 定義
//以下是一個參數為string, 回傳值為int的函式
  delegate int Action(string amount);

//宣告成一個物件,以便使用
  Action action;

//接受傳入delegate物件
private void UseAction(int money,Action action)
{
  int n=action(money);
}
//假設有三個同參數同回傳值的函式
//一開始不知道要用哪一個
//就可使用delegate
private int TestA(int num) {}
private int TestB(int num) {}
private int TestC(int num) {}

private void UseA()
{
  //TestA指定給action,傳進UseAction
  //UseAction接受為派類型為參數
  //TestA必須是int為參數,回傳值為int的函式
  action=TestA;
  //action+=TestA;  也可以這樣用
  UseAction(3,action);
}

private void UseB()
{
  //TestA指定給action,傳進UseAction
  action=TestB;
  //action+=TestB;  也可以這樣用
  UseAction(3,action);
}

private void UseC()
{
  //TestA指定給action,傳進UseAction
  action=TestC;
 //action+=TestC;  也可以這樣用
  UseAction(3,action);
}

可以把A的函式委託給B

或是把B的函式委託給A



多重呼叫delegate

delegate一次註冊多個方法,呼叫時所有註冊的都會一起呼叫
using UnityEngine;
using System.Collections;

public class MulticastScript : MonoBehaviour 
{
    delegate void MultiDelegate();
    MultiDelegate myMultiDelegate;
    

    void Start () 
    {
        myMultiDelegate += PowerUp;
        myMultiDelegate += TurnRed;
        
        if(myMultiDelegate != null)
        {
            myMultiDelegate();
        }
    }
    
    void PowerUp()
    {
        print ("Orb is powering up!");
    }
    
    void TurnRed()
    {
        renderer.material.color = Color.red;
    }
}

C# 泛型修飾詞

C# in out 泛型修飾詞

out 修飾詞

MSDN
out 關鍵字會指定型別參數為 Covariant
可在泛型介面可使用
可在委派中可使用
// Covariant interface.
interface ICovariant { }

// Extending covariant interface.
interface IExtCovariant : ICovariant { }

// Implementing covariant interface.
class Sample : ICovariant { }

class Program
{
    static void Test()
    {
       // ICovariant iobj = new Sample();
       // ICovariant istr = new Sample();

        // You can assign istr to iobj because
        // the ICovariant interface is covariant.
       // iobj = istr;
    }
}

in 修飾詞

MSDN
in 關鍵字會指定型別參數為 Covariant
可在泛型介面可使用
可在委派中可使用

C# 方法參數修飾詞 out ref params

C# in , out , ref , params


params 修飾詞

MSDN
當傳入array時可用,此時參數的數目不定
可傳入陣列
可傳入元素
可以什麼都不傳,此時陣列長度為0
在params之後不許有其他參數(params要放在最後一個參數)
且只能有一個params

//當有這樣一個陣列
Object[] ary = new object[3] { 300, 'x', "abc" };

//這個方法的參數宣告為params
private void TestFunc(params Object[] ary)
//我們可以直接傳入一組物件
TestFunc(200,'d',"xxx");

完整範例
public class MyClass
{
    public static void UseParams(params int[] list)
    {
        for (int i = 0; i < list.Length; i++)
        {
            Console.Write(list[i] + " ");
        }
        Console.WriteLine();
    }

    public static void UseParams2(params object[] list)
    {
        for (int i = 0; i < list.Length; i++)
        {
            Console.Write(list[i] + " ");
        }
        Console.WriteLine();
    }

    static void Main()
    {
        // You can send a comma-separated list of arguments of the 
        // specified type.
        UseParams(1, 2, 3, 4);
        UseParams2(1, 'a', "test");

        // A params parameter accepts zero or more arguments.
        // The following calling statement displays only a blank line.
        UseParams2();

        // An array argument can be passed, as long as the array
        // type matches the parameter type of the method being called.
        int[] myIntArray = { 5, 6, 7, 8, 9 };
        UseParams(myIntArray);

        object[] myObjArray = { 2, 'b', "test", "again" };
        UseParams2(myObjArray);

        // The following call causes a compiler error because the object
        // array cannot be converted into an integer array.
        //UseParams(myObjArray);

        // The following call does not cause an error, but the entire 
        // integer array becomes the first element of the params array.
        UseParams2(myIntArray);
    }
}
/*
Output:
    1 2 3 4
    1 a test

    5 6 7 8 9
    2 b test again
    System.Int32[]
*/

ref 修飾詞

MSDN
把參數用傳參考的方式傳入函式
在宣告和呼叫的參數前都加上ref
參數在傳遞前一定要初始化
被傳入的參數,在函式被修改,也會改到他原本的值
屬性不能做為ref的參數

這樣子不構成重載,會報錯
public void SampleMethod(out int i) { }
public void SampleMethod(ref int i) { }
這樣子可以
public void SampleMethod(int i) { }
public void SampleMethod(ref int i) { }

out 修飾詞

MSDN
把參數用傳參考的方式傳入函式
參數在傳遞錢不一定要初始化
在宣告和呼叫的參數前都加上out
被傳入的參數,在函式被修改,也會改到他原本的值
函式可宣告多個out參數
在函式中一定要對out參數附值,否則報錯
直接在函式中修改out參數,就等於是回傳值

private void Test(out int a, out int b)
{
     a =3;
     b=5;
}
private void Call()
{
  int x=0;
  int y=4;
}
Test(out x, out y);

參數原本的值會被更動
class OutReturnExample
{
    static void Method(out int i, out string s1, out string s2)
    {
        i = 44;
        s1 = "I've been returned";
        s2 = null;
    }
    static void Main()
    {
        int value;
        string str1, str2;
        Method(out value, out str1, out str2);
        // value is now 44
        // str1 is now "I've been returned"
        // str2 is (still) null;
    }
}

int 修飾詞

傳入的參數不能被更動