過去一週的網頁瀏覽次數

2011年12月13日 星期二

熊熊想到 - 10進為轉N進位

N可以為任意數,EX 8, 16, 15...等等


我們在將一個十進位的數P轉換成2進位時,通常會用除法和餘數組合
令一個int Quotient = P/2
在令一個int Remainder = P%2
Remainder 是我們要個別的結果,我們可以用一個東西把它裝起來
EX : char sum[i] = Remainder ;
而P = P/2 後,進行下一輪計算
依此類推,一直做到 Quotient <=0 在表示上我們用一個容器把它裝起來,陣列是最簡單的。因為列印時會從左而右,所以不失一般性我們從陣列的最後面開始存資料,這樣一來從頭列印的時後就可以顯示成一般人都能讀取的結果。 把這種想法轉成code: int end[SIZE]={0}; void tenToN(int a, int base) { int c=SIZE-1; int Remainder; //簡化code,Quotient省略 while(a > 0)
{
Remainder = a % base;
a = a / base;
end[c--] = Remainder;
}
}

但這code會遇到一個問題,如果要計算某個數的16進位表示法,結果可能有ABCDEFG...,int陣列就不能使用
陣列內容也不能單純的取Remainder的餘數,要轉換一下。

char end[SIZE]={0};
void tenToN(int a, int base)
{
int c=SIZE-1;
int Remainder; //簡化code,Quotient省略

while(a > 0)
{
Remainder = a % base;
a = a / base;

if(Remainder >= 10)
Remainder = (Remainder-10)+65; //'A' = 65
else
Remainder = Remainder+48; //'0' = 48

end[c--] = Remainder;
}
}

2011年12月10日 星期六

unity3D的網路資料傳輸 & 角色控制

本篇不說連線控制的部分,目前針對使用Network View屬性進行網路資料同步的架構 & 角色控制說明。


雖然說unity的腳本有支援.net 4的語法,實際上是可以用.net C#硬幹server、client(上次在資策會上課就有學員用這種方法做server端),當然這樣的難度就高了,可能也必須要懂運作理論,否則寫出來的server可能在壓力測試下就 "崩潰" 了....

前幾天因為研究他的官方範例,這邊寫一下結論。

基本上unity有內建一套他們自己的網路通訊方式,也可以透過他們建構出的API實做。在通訊基礎上是透過Network View 這個component當做通訊的傳送門。當你在某個元件上附加Network View 屬性後,這個物件就具有網路同步資料的功能。

Network View 中有個屬性叫做observed,在這邊丟入你已經寫好、內容包含同步資料功能的script,那這個物件在遊戲開始運作之後,就會去傳送 & 接收那些資料。一個物件身上可以被賦予多個Network View屬性,他們會同時運作。

大綱講完,重點在實做部分,跟傳輸運作有關係的script API主要為:

function OnSerializeNetworkView(stream : BitStream, info : NetworkMessageInfo) {}
這是在Network class中提供的一個func. 主要負責message sent / receive,他會同步被network view所關注的script中的資訊,也就是當你寫了一個script內含OnSerializeNetworkView(){},並且丟到observed屬性中,則OnSerializeNetworkView()裡的code就會開始運作。基本上他透過BitStream物件收發網路上的資訊,使用上不需要了解封包的問題,也不需要知道如何切割封包,官方有個簡單的範例code:
void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
{
    // Always send transform (depending on reliability of the network view) 
    if (stream.isWriting) 
    {
        Vector3 pos = transform.localPosition; 
        Quaternion rot = transform.localRotation; 
        stream.Serialize(ref pos); 
        stream.Serialize(ref rot); 
    }
    // When receiving, buffer the information 
    else {
        // Receive latest state information 
        Vector3 pos = Vector3.zero; 
        Quaternion rot = Quaternion.identity; 
        stream.Serialize(ref pos); 
        stream.Serialize(ref rot);
    }
}


BitStream有兩種型態可供參考isReadingisWriting,當isWriting = true 時表示可以寫入資料相反的就是可以讀取資料。這兩個型態應該是互斥的,一個是true,另一個就是false。再透過Serialize()這個func.收發。


Serialize()在官網的解釋為 : Serializes different types of variables,但實際上他並不是所有形態都能收發,例如你想送一個array[],就不行了...
function Serialize (ref value : boolean) : void
function Serialize (ref value : char) : void
function Serialize (ref value : short) : void
function Serialize (ref value : int) : void
function Serialize (ref value : float, maxDelta : float = 0.00001F) : void
function Serialize (ref value : Quaternion, maxDelta : float = 0.00001F) : void
function Serialize (ref value : Vector3, maxDelta : float = 0.00001F) : void
function Serialize (ref value : NetworkPlayer) : void
function Serialize (ref viewID : NetworkViewID) : void


至於第二個變數NetworkMessageInfo,這是一個結構,當封包送出時他會將一些資訊記錄在這個結構中,例如封包送出的時間戳記等等,這部分的運用我還不了解,之後再補。


再來就是game中角色在運作時的方式,本篇不講伺服器運作和玩家連線機制的部分。


假設遊戲中的所有玩家都長得一樣,那表示不管誰進入伺服器,遊戲中都會new一個一模一樣的物件。這個預先建構好的物件可能會帶有以下script:




其中:
1.PersonController:控制角色運作,與網路傳輸無關。
2.Animation:角色可能帶有動畫,這個腳本控制角色的動畫
3.NetworkTransformSync:透過Network View控制資訊的傳輸,EX:角色的位置,轉向等等。
4.NetworkAnimationSync:透過Network View控制角色同步動畫,因為透過Serialize機制並不能傳送動畫本身,只能傳送控制指令,例如一個字串。
5.ThirdPersonNetworkInit:這是最重要的,在遊戲開始後,控制哪個腳本可以運行,哪個腳本不能運行。


官方中ThirdPersonNetworkInit的內容如下:
function OnNetworkInstantiate (msg : NetworkMessageInfo) {
    // This is our own player
    if (networkView.isMine)
    {
        GetComponent(PersonController).enabled = true;
        GetComponent("NetworkTransformSync").enabled = false;
        GetComponent("NetworkAnimationSync").enabled = flase;
    }
    // This is just some remote controlled player
    else
    {
        name += "Remote";
        GetComponent(PersonController).enabled = false;
        GetComponent("NetworkTransformSync").enabled = true;
        GetComponent("NetworkAnimationSync").enabled = true;
    }
}
OnNetworkInstantiate()是Network class提供的一個func.,當一個物件是透過Network.Instantiate()這函式new出來的時候,則物件可以呼叫這個func。而networkView.isMine會判斷目前這個物件是不是本機操作,若是,則會關閉角色所帶的同步位置 & 動畫的腳本,這邊要注意,關閉腳本,是表示腳本中的Update()不會運作,但是腳本中的其他函式一樣會運作,包括OnSerializeNetworkView(),如此一來角色就可由本機控制,並且透過同步的腳本傳送資料。


相反的,若是場景中的角色是遠端玩家控制,則會關閉本機控制的功能,由網路接收資料來控制角色。

2011年12月8日 星期四

unity3d的一些bug

我想慢慢記錄一些unity使用上的bug,官方並沒有針對這些bug做說明,使用上就是要踩雷.....有時候會因為莫名其妙的bug卡好幾天Orz..目前用的版本是3.1版。




1.聲音檔loop play的bug:
如果將許多聲音檔個別放在不同的物件,選取play on awake & loop,在場景中理論上是可以聽到許多的聲音。
但是當聲音檔太短的話,他loop的頻率太高...聲音的播放會當掉...變成很短的那個聲音撥個幾次就當了,最後只剩下比較長的聲音存在...

2011年12月3日 星期六

Algo - Graham scan

最近遇到一個難題,就是在研究上我必須算出wiifit的xy資料包絡線。我曾經試著想自己搞出演算法,可是這比較麻煩,而且最重要的是我沒有時間這樣搞...

所以,任務可以簡化成為,我要在一個xy平面上尋找可以包圍這些所有點的最小多邊形。這是一個convex hull的問題,在wiki上這樣描述這演算法:
The Graham scan is a method of computing the convex hull of a finite set of points in the plane with time complexity O(n log n)
這演算法的命名是繼承提出者 Ronald Graham而命名,並且在1972年提出最初版本。


以下流程翻譯自wiki
1.Graham scan在xy平面上先找出y值最小的點,如果存在多個y值相同的點,則取x最小的點,並稱這點為P
點,時間複雜度為O(n)。
2.剩餘的點,會根據與 "P點的x軸夾角大小",由小到大sort。為了加速計算的過程,並不需要計算出他真正的夾角,取而代之,我們計算他的cosin值,這過程是一個單調遞減函數(monotonically decreasing function),因為cosin值從0~180。
3.接著,從第 i=2 個點開始取,每次取連接 i 、 i-1、i-2三點的向量,看看他們是不是所謂的"left turn"? 如果是"right turn"則表示第 i-1 個點並不是我們要的點,則將他捨去。此時會有一個例外,若是三點共線(colinear),則其中一個點可以考慮捨去,但是若有特別要求必須找出所有包圍的點,則不能捨棄( since in some applications it is required to find all points on the boundary of the convex hull)。
4.again,持續不斷的尋找直到找到最一開始的點。


tips:
所謂的"left turn" , "right turn",有個快速的判斷方法,就是取他們兩個向量的外積。假設有 (x1,y1), (x2,y2),(x3,y3)三點,會形成兩線段A,B,A,B線段的外積值會因為夾角而不同,就是所謂的右手定則,若值=0表共線。


input : pooints[] 記錄所有的點
output : after[] 記錄所有convex hull的點

program;


vector<struct point> points;
vector<struct point> after;



oid Graham_scan()
{
after.push_back(points[0]);

int m=1;
bool end = false;

for(unsigned int i=2; i<points.size();i++)
{
m = i-1;
int bm = m-1;
while( (ccw(points[m-1], points[m], points[i])<=0) )
{
i++;
m = i-1;
if(i >= points.size()){
i=0;
m = points.size()-1;
end = true;
}
}
if((ccw(points[bm], points[m], points[i])>0))
after.push_back(points[m]);
if(end)
break;
}
after.push_back(points[points.size()-1]);
}


int main(int argc, char** argv)
{
int lowesty;

//讀檔
readfile();

//找尋最小的y
lowesty = find_lowest_y();

cout <<"最小y在:" <<lowesty<<" y值:"<< points[lowesty].y << endl;
//將y插入第一個位置 & 計算每個point的角度
insert_and_caltheta(lowesty);

cout << endl << "做排序" << endl;

int high=points.size()-1;
int low=1;
Quick_sort(low,high);

//執行Graham scan
Graham_scan();

system("pause");
return 0;
}

2011年11月18日 星期五

first


這是我離開無名的第一個blog。

自從FB開始後,我越來越少在blog上寫日記

或許不能稱為日記,因為我根本沒有每天記錄阿...

或是偶爾無病呻吟一下,看起來有點糟..哈...


總結一下

36429人次,start from November 14 ,2005

google blog , I'm in 

and I called : NEW ARRIVAL