Hao-Cher Hong
Hao-Cher Hong
2017年4月14日 星期五

哭哭北科

No automatic alt text available.
這是「哭哭北科」,北科的匿名靠北哭哭板,以下是原始碼的連結。
https://github.com/HaoCherHong/CCBC

哭哭北科是一個完全向靠北工程師抄襲致敬的發文系統,只要使用者上網頁填個發文內容,幾秒鐘後文章就會匿名的出現在Facebook粉絲專頁上,以純文字、圖片或文字圖的形式。你甚至可以用匿名的角色到文章下面留言。

Image may contain: one or more people, people sitting and outdoor
小女孩 匿名角色

幾個月前,我注意到了學校的靠北板「靠北北科」關板的事情。起初我覺得很奇怪,為什麼會突然關板?後來我聽到了傳言,說是學校上級向學生施壓,不允許這種可能會傷害校譽的匿名平台存在,所以要求學生關板的(我並不知道這是不是事實,但我聽到這個說法了)。我聽到後非常生氣,認為這是學校打壓言論自由的作為!兩天之後,哭哭北科上線了。

言論自由?


這是我對學校的抗議,也是自己對平台經營的一種訓練,純粹就是一把火,就把它做出來了。打著言論自由的口號,丟上全體板,果然大家就進來了。後來就是每天想辦法宣傳這個平台,例如到Dcard發文、弄一些話題,思考什麼樣的內容會吸引更多的使用者。我甚至每天都找點人少的時間,到學校找一些標誌性雕像、地標去拍照,做成匿名回覆的角色。

沒想到的是,人數都還沒上來(大約2、300個人),就開始出現問題了。開始出現人身攻擊(非常惡毒那種)、謾罵,各種不尊重別人的發言出現。我身為平台的管理者,這種東西當然不該讓他出現在板上,內容是真的會傷害一個人那種,我不得已只好刪文。這讓我非常矛盾,原本打著言論自由、不審核言論的口號,這下我一刪,不就是反其道而行了嗎?果然我馬上得到使用者的抗議,但我也無可奈何。這個平台很快就淪為讓人躲在螢幕後面對別人人身攻擊、排擠、謾罵與誹謗的平台,我就取消了直接發文,改成審核後才會發文的機制了。

這其實讓我很失望,人只要把自己的身分藏起來,就不會尊重別人了。以前看到別人寫這種話,聽了都沒什麼感覺,現在則是真正的感受到它的發生。

開板後幾個月,「靠北北科」就復活了,由於大家本來就都是靠北北科的關注者,這個板只是新興的一個小平台,使用者人數有懸殊的差距。我想說我的階段性任務完成,我就把哭哭北科關板了。



隱姓埋名


我一直沒有告訴別人我是這個平台的作者。因為我害怕,哪怕只是告訴一個人,學校只要查到是我,我就沒辦法繼續為學生提供這個平台發言。所以之前有人在問,我都裝傻帶過而已。現在靠北北科復活幾個月都沒事,我想說應該不會再被關板了,所以就直接把這件事寫出來吧。

如果哪天靠北北科又被關了,我就直接說這是我做的再把它復活吧!
Hao-Cher Hong
Hao-Cher Hong
2017年3月26日 星期日

BigSushi - 在 HTML 5 Canvas 設計出 2D 座標 Render 系統 (Part I)



這學期的物件導向程式設計實習課,老師要我們用實驗室做的HTML 5基本遊戲Framework做一款遊戲出來。這個Framework做出了基本的Game Loop,但有很多東西是不完整的。因為我之前長時間使用Unity遊戲引擎的關係,我看了Framework的基礎架構後馬上就想把他改到用起來跟Unity幾乎一樣。

這篇文章會寫到我如何在 HTML5 的 Canvas 實作像 Unity 一樣的 Render 方式。也就是場景中的每個物件都自成一個座標系統,而每個物件都可以包含多個子物件。只要將物件的父子關係設定好,系統就會自動算出物件的世界座標World Space Coordinate)並且根據物件與攝影機的相對位置在螢幕的螢幕座標Screen Space Coordinate)上繪圖。

GameObject 與 Transform

首先講到實作物件的父子關係系統,這在 Unity 裡面稱作 Transform,Transform包含座標、旋轉、縮放還有父子物件。而表示場景中物件的叫做GameObject。GameObject包含許多Components(以後會介紹到,有空的話😄)和一個 Transform。由於也只有 GameObject 會有 Transform,所以我直接將 Transform 跟 GameObject 做在一起,就叫做 GameObject

所有的 GameObject 都會有:
  • Parent (父 GameObject)
  • Children (子 GameObjects)
  • Position / AbsolutePosition (相對座標 / 絕對座標)
  • Rotation / AbsoluteRotation (相對旋轉 / 絕對旋轉)
  • Scale / AbsoluteScale (相對縮放 / 絕對縮放)
絕對是指GameObject相對整個世界原點的值,而相對則是它相對於其Parent的值。例如你在我右邊7公尺(你對我的相對 x 是1m),而我在世界原點(?)的右邊9410公尺,所以你的絕對位置就是 x: 9417m
座標、旋轉及縮放都是直接以相對位置儲存的,而他們的絕對數值則是透過計算計算出來的(這對我來說有點違反直覺,不過由於這個Framework原本就是存相對座標,所以我就沿用了)。

計算旋轉與縮放比起座標簡單的多,我先來說說絕對的旋轉與縮放是怎麼計算的。

絕對旋轉的計算

這個Framework儲存旋轉值都是以角度來儲存的(通常好像是用徑度,我就沿用了,反正兩種都行)。絕對旋轉的算法是 GameObject的相對旋轉 + 其Parent的絕對旋轉。就這麼簡單,由於Parent的絕對旋轉也會需要計算其Parent的絕對旋轉,所以這個計算會遞迴一直算到最上層為止。簡單的說,就是一路往最上層,把所有相對旋轉加起來

absoluteRotation = relativeRotation + parent.absoluteRotation

絕對縮放的計算

縮放跟旋轉幾乎一樣,就是 + 變成 *(乘) 而已。
絕對旋轉的算法是 GameObject的相對縮放 * 其Parent的絕對縮放

absoluteScale = relativeScale * parent.absoluteScale

絕對位置的計算

由於相對位置是基於其Parent的座標系統,所以如果Parent有旋轉,我的x軸是往Parent的x軸延伸的。例如:Parent的旋轉是45度,我的x是1m,那我的絕對位置的x會是在Parent絕對座標的45度角方向增加1m的位置。所以這會運用到三角函數的計算。

先把Parent的絕對旋轉以徑度 rad 表示(由於我們是以角度儲存,這邊要做 乘 180 除 PI 的計算)。
Parent的絕對縮放以 scale 表示 (自身的縮放不會影響自身的位置)。

absoluteX = ( relativeX * cos(rad)  - relativeY * sin(rad) ) * scale + parent.x
absoluteY = ( relativeX * sin(rad) + relativeY * cos(rad)) * scale + parent.y

Camera

Camera的概念是,把所有GameObject的位置,透過其相對一個 Camera(攝影機)的位置,來計算它應該出現在螢幕上的哪個位置。如果沒有這個概念,我們就只能畫出 x位置在 0 ~ 螢幕寬度 和 y 位置在 0 ~ 螢幕高度 的東西了哦~

GameObject在螢幕位置的計算是這樣的:

x = gameObject.absoluteX - camera.x + Screen.width / 2
y = gameObject.absoluteY - camera.y + Screen.height / 2

其實就是GameObject跟camera的相對位置而已。 由於我們在繪圖時會希望以Camera為中心繪圖。所以假設GameObject跟camera在同一點上,它其實應該在螢幕的中間而非 x: 0, y: 0的位子上,所以我們在 x 跟 y 上分別加上 螢幕寬度 / 2 與 螢幕高度 / 2 。

Canvas

到這裡才講到真正在 HTML5 Canvas上繪圖的部分。但整個繪圖的系統都是基於上面的幾個概念建構出來的。

Canvas的繪圖,首先要知道的是:

  • 原點 (x: 0, y: 0) 在左上角
  • x是往右延伸,y是往下延伸
這跟我們在數學上慣用的座標系統是有差別的,所以要注意不要搞錯了。

由於我們在做的是 2D Game,這邊假設我們要繪圖的東西全部都是圖片(Image),我下面就用 canvas 的 drawImage來解說。

context.drawImage(image, x, y)
context.drawImage(image, x, y, width, height)

這個函數是在 canvas 上,從左上角往右數 x 像素,往下數 y 像素的位置畫出 image 圖片。如果你沒有指定 width 跟 height,系統會幫你預設畫出該圖片的原始大小。若指定的話,則會幫你把整張圖縮放到指定的大小繪圖。這個就是在 HTML5 Game裡面最常用到的繪圖函數了!

我們現在只要把 image 設為GameObject指定的圖片,x, y 設為GameObject經過 Camera 轉置成的螢幕座標,並把 width 與 height 設成原始大小乘以 scale 值。 就可以在 Canvas 上正確的位置畫出遊戲中的物件了!

那旋轉怎麼辦呢?Canvas 有個旋轉函數可以使用:

context.rotate(angle)

在繪圖之前執行這項指令設定好angle後(注意 angle 是徑度,所以要先轉換哦),接下來畫的東西都會旋轉這個角度囉!

這下就可以在 Canvas 上畫出場景上所有的物件了,而且包括位移、旋轉跟縮放。

進階

其實 Canvas 還提供了各種轉置函數,例如: context.translate(x, y), context.scale(x, y) ,translate是位移,scale則是縮放。由於這又牽扯到了 stack 的概念,這些我就留在 Part II 來講吧。


Canvas 可以用的功能其實非常多,只要觀念正確,你也可以在網頁上做出很多華麗特效的遊戲。想要深入了解的話,可以先參考 MDN 的 Canvas Reference。這篇就先講到這邊吧!

Hao-Cher Hong
Hao-Cher Hong
2017年3月1日 星期三

一年的創業

這學期修的創新及創業第二次上課要交一篇自傳及創業看法。根據我那一年的創業經驗,我寫完創業看法後,順便貼在這裡。

創業看法

我在大一下的時候休學,朋友找我去創業,要做台北夜生活的手機社交應用程式。花了一年時間,沒有任何成果,只浪費了一堆錢。我從大學開始就常常看別人的創業經驗跟許多創業故事。我相信大部分的年輕人都會覺得創業是很酷又充滿光彩的,但是我覺得是完全相反。創業是一條錯綜複雜且紊亂無章的道路,沒有人可以告訴你你現在做的是對的還是錯的,因為也沒有人知道。你只能相信你, 跟相信你相信的人,相信你們的想法是正確的。但在沒有任何有強力背景的人的支持下,你們就像一群無頭蒼蠅在亂飛。你以為你有專業知識背景,但在現實下執行,一切都跟你想得不一樣。你以為很酷的點子,別人一句「我為什麼要買?」「我用這個方法就好了啊」就可以把你變成像白癡一樣。對路人來說,一款連聽都沒聽過的產品,還不如路上被發的傳單吸引人。

我們當初是由一個來自香港的朋友召集,找了財務、設計師跟幾個工程師來做我們的應用程式。大家一開始都不是熟識,才開始沒有幾個月,內部就開始對彼此保持高度戒心。這樣的情況,別說專心工作了,連要保持大家不要變成不歡而散都有問題。接著,我們需要找iOS系統的應用開發工程師。花了三個月,結論是,沒有有經驗的人會想到一間還看不到前景的公司工作,而你也不可能就這樣請一個菜鳥來做,於是我們開始尋找外包商,我想這是一大步錯誤。

首先,外包商不懂我們的理念,不懂我們的設計,而且也許不只不懂,更不在乎!再來,我們不再同一個環境下工作,溝通非常不便,外包商也不會完全只專心做一個案子,兩個團隊坐在不同地方但是一起同時同心協力在做你們的案子這種情況是不存在的。新創團隊的專案最常見的就是"更動",而外包商最難接受的也是,只要合約跟規格訂好後,要做一切更動都會造成很大的問題,而很多問題也都是開始做下去之後才會發現。我想,新創團隊最不該做的就是找外包商來做你們的核心產品。
把錢浪費光後,接下來就是開始士氣低落,互相指責。因為大家有一半的人除了這裡還有自己的另一份工作,有些人要上學,有些人要上班。大家不再同一個地點、時間下工作的話,最需要的就是良好的溝通。很不幸的,我們沒有好好的溝通,我們不知道對方在做什麼,因為溝通很麻煩,我們"懶得"溝通。換來的就是士氣更加低落、更多的爭吵、更糟的工作情緒。

一年過去了,什麼都沒有。有些人還留下,但我離開了。一年不眠不休的努力,連個"開始"都沒換到。要說我學到了什麼,我不知道。但我知道我把那堆不該踩的雷踩過了一次:

  • 不要找你還沒有足夠的認識的人來做你的核心產品
  • 不要找外人來做你的核心產品
  • 不要找沒辦法只專心做你的核心產品的人來做你的核心產品
  • 隨時好好溝通

只能說如果我還有機會再試一次,那些錯誤,我不會再去做一次。
Hao-Cher Hong
Hao-Cher Hong
2017年2月5日 星期日

PUPY噗比 開發日記

前言

這是無名小站關站以來第一次寫Blog,想說開始來記錄一下我的開發跟學習過程。這篇裡面寫到了很多技術上的名詞,寫得內容不深也不淺,也不知道寫給誰看,不過就記錄一下吧。

PUPY噗比(https://pupy.tw/) 是一個2015年我跟我的好朋友開始一起做的寵物認養平台,目的是建立一個快速方便的寵物送養、認養平台,給大家可以非常快速的找到適合自己的寵物。

前一年(2015)我用Angular.js(第一版)做了電腦版的網站。後來發現Angular在手機上的效能不佳,所以打算整個用現在最流行的React.js來重作。但是怕整個全部重做花太長時間,網站會難產,所以先做手機板部分,之後再慢慢把整個全部改成用React.js實作。

正文開始啦

環境(?)

如前文所述,寫這篇文章的時候,給電腦版的網頁早就做好了,但是手機板的網頁因為各種原因遲遲沒有出來。為了不要讓網頁難產,也讓以後開發網頁更快速更有系統,我開始學習用React.js來做前端,Node.js來做後端。不過這樣說有點太簡略,這邊先來寫寫整個環境。

React.js使用一個叫做JSX的JS擴充語言,用來更快速的表示React的Component。這當然不是瀏覽器原生支援的語言,所以要先經過babel編譯才能在瀏覽器正常使用。
在React裡面又有很多地方需要用到ES6, ES7的功能,寫起來才會比較簡潔快速,所以也是要經過babel編譯回ES5。目前前端沒有用太多額外的Library,所以大概就先這樣。

後端的話如剛剛說到的是用Node.js來寫,而資料庫則是使用MongoDB(因為他的資料儲存格式可以讓開發很方便快速)。網頁伺服器是用很流行的express.js Module。為了解決JS Callback Hell的問題,我前陣子花了時間把幾乎所有的Async Call都改寫成Promise的作法,搭配ES7的Async, Await語法,整個寫起來變得非常的乾淨。這邊很多東西也是Node.js還沒有完全支援的,所以我也是乾脆都透過babel把他編譯回ES5來執行。

資料庫(MongoDB)其實不是用原來的Driver,而是使用mongoose這個ODM(Object Document Mapping)Module來寫,整體的操作都變得跟JS的物件一樣,非常淺顯易懂。

可以看到很多東西都是透過babel編譯後才能執行,我當然不是每次都寫完手動拿去編譯再來執行啦。後面其實是用gulp這套系統來管理發布流程。每次有更動都會自動叫babel去編譯更動後的檔案,然後有個livereload的module來幫我自動重新整理前端。伺服器端就是在手動關掉開起來就好。也許以後可以再特別寫一篇講解整個gulp腳本。有興趣的可以看看gulp這個Module。

開發

目前手機板的進度已經做到大概一半了,剩下帳號設定、問題回報還有新增文章沒做。目前做下來其實都蠻快的,因為Angular跟React都一樣是把一個一個Component分出來實作的,只是資料傳遞的方式不同。跟React一樣,Angular的每個Controller都有自己的State,但是React沒有Angular的Service,所以要共用整個App的最上層State時會變得很麻煩,不太可能都一層一層的把State用Props往下傳。所以就去爬文發現了有Flux這個開發模式,配合Provider配合React的Context功能會讓資料傳遞變得非常容易,基本上就是只要有用connect的Component都可以輕鬆存取到Redux的唯一State樹。

Redux的開發模式有提到一個原則,整個App只有唯一一個State樹。但我覺得這是有點不切實際的。有很多小Component其實可以自成一個完全獨立的Component,但依照這個開發模式,它們都必須把自己的State放在App唯一的State樹底下。這讓整個開發過程變得很繁瑣。所以我還是讓很多Component如果沒有跟App狀態相依,都讓他們有自己的State。這個問題可能之後還要花時間爬個文看看大家是怎麼處理的。

幾個問題

現在正在思考幾個問題。
首先,上傳圖片的功能要怎麼實作,因為手機的硬體資源比較寶貴,如果像網頁版一樣在客戶端做自動切圖然後順便製作預覽圖放在畫面上,低階手機大概會受不了吧?也許這些工作都應該丟給伺服器去做。

用網頁伺服器Serve圖片似乎會影響整體的效能,也許應該找個CDN服務來儲存使用者上傳的圖片,這也是我第一次從頭到尾設計一整套系統,對這種東西也是一知半解,感覺還有很多東西要去摸索呢。

因為之前小測試後發現許多人不喜歡使用Facebook帳號登入,而我們當初又是設計只有用Facebook登入。所以之後還要放入使用Email申請帳號,這就要來做Email驗證功能,要在Godaddy提供的虛擬主機跟網域上做發信功能似乎很麻煩。也許可以用Gmail代發,但是發信網域不是來自@pupy.tw就覺得不太舒服呢...

P.S.

這個網站的所有設計(像是上面那個可愛的Logo)都是出自設計師XunYu之手呦,歡迎到她的blog看看 XUN DESIGN ~