2012年2月16日星期四

死神學神

構思到一個故事大崗,如果有機會能拍成電影就好了。

在冥界中有一種叫「死神」的職業,祂們每天都會接到「柯打」準時到達死亡地點,靜待死亡的發生,引領靈魂到達輪迴的地方。時間對於祂們來說十分重要。若然錯過引領靈魂的時間,祂們要因此而付出代價。「死神」這種職業並不是依照自己的意願選擇;而是生前種下的罪孽太深,需要成為「死神」經歷多次死亡來獲得救贖。當祂們看淡生死之時,便能重返輪迴之道。這是剛剛成為「死神」的阿一接到「柯打」,由上級帶領之下執行第一次任務,及之後發生的一連串故事與面對多次生死的領悟。

2012年2月15日星期三

After Effects 下的影片加速

Fast Forward in After Effects

很久很久沒有剪片了。今個星期接了一個項目,替客人製作兩段影片作為報告之用。雖然這個項目沒有難度,但步驟很多。總算完成了第一段。要在 iPad 中為動畫編程,然後錄下模擬器的動畫,把影片裁好後,還要將原來 2 分 17 秒的影片濃縮到 8 秒。否則看報告的人必然睡著。花了一點時間,才找到影片加速的方法。在 Composite 中點右鍵,選 Time 內的 Time Stretch 後,輸入最終想要的時間長度。

2012年2月14日星期二

raw2UIImage

以下是 raw2UIImage 的源碼:
+ (UIImage *)raw2UIImage:(unsigned char *)rawData width:(int)width height:(int)height  {
 if (rawData == NULL)  {return nil;}
 if (width < 0)  {return nil;}
 if (height < 0)  {return nil;}
 
 //  Link up raw data to context
 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
 CGContextRef contextRef = CGBitmapContextCreate(rawData, width, height, 8, width*4, colorSpace, kCGBitmapByteOrder32Little|kCGImageAlphaPremultipliedFirst);
 
 //  Create a UIImage from raw data
 CGImageRef imageRef = CGBitmapContextCreateImage(contextRef);
 CGContextRelease(contextRef);
 free(rawData);
 UIImage *outputImage = [UIImage imageWithCGImage:imageRef];
 CGImageRelease(imageRef);
 CGColorSpaceRelease(colorSpace);
 
 return outputImage;
}

把這段源碼跟 obm 檔的解密源碼(http://pacess.blogspot.com/2011/07/street-fighter-iv-volt-obm.html)一齊使用,便能輕易製作出 obm 轉 png 的工具。

2012年2月13日星期一

關於 Activity 之間的數據傳送

About Data Passing Between Activities

習慣了 Objective-C 的想法,在開發 Android 應用時,很自然把 Activity 當成 UIViewController 來使用。然而,有些時候需要在兩個 View 之間傳送數據。在 Android 上可使用 startActivityForResult 及 onActivityResult。
public class MainActivity extends Activity  {
     ...
     @Override
     private void userLogin()  {
          Intent intent = new Intent(this,LoginViewActivity.class);
          intent.putExtra("defaultUsername", "Pacess");
          intent.putExtra("defaultPassword", "Password");
          startActivityForResult(intent, 1);
     }

     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent intent)  {
          if (resultCode != RESULT_OK)  {return;}

          switch (requestCode)  {
               default:  return;

               case 1:  {
                    int userLevel = intent.getExtras().getInt("userLevel");
                    String sessionID = intent.getExtras().getString("sessionID");
               }  break;
          }
     }
}

public class LoginViewActivity extends Activity implements OnClickListener  {
     ...
     @Override
     public void onCreate(Bundle savedInstanceState)  {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.main);

          Intent intent = getIntent();
          String username = intent.getStringExtra("defaultUsername");
          String password = intent.getStringExtra("defaultPassword");
     }

     @Override
     public void onClick(View view)  {
          Intent intent = getIntent();
          intent.putExtra("userLevel", 1);
          intent.putExtra("sessionID", "s01234");

          setResult(RESULT_OK, intent);
          finish();
     }
}

2012年2月12日星期日

關於 Android 的 onClick 寫法

About Android onClick
在看《The Android Developer's Cookbook》講解 Button.onClick 時,它的寫法是:
public class MainActivity extends Activity  {
     ...
     @Overrider
     public void onCreate(Bundle savedInstanceState)  {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.main);

          Button button = (Button)this.findViewById(R.id.button);
          button.setOnClickListener(new View.OnClickListener()  {
               public void onClick(View view)  {
                    doSomething();
               }
          });
     }
}
然而,我比較喜歡另一個寫法:
public class MainActivity extends Activity implements OnClickListener  {
     ...
     @Override
     public void onCreate(Bundle savedInstanceState)  {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.main);
        
          Button button = (Button)this.findViewById(R.id.button);
          button.setOnClickListener(this);
     }

     @Override
     public void onClick(View view)  {
          doSomething();
     }
}
這個寫法代碼的 Indent 層次亦較淺,看起來比較清楚明確。如果在同一個 Activity 內有多於一個 Button 時,亦能共用同一個 onClick函數。

2012年2月11日星期六

情.尋朱古力

Catch Moses If You Can
半年前,針對 iPhone 及 Android 利用 HTML5 開發了不用下載 Apps 來即場玩遊戲的技術。2011 年 8 月時曾向 YSL 推銷這個尖端技術,我建議開發一個釣魚遊戲。嘉賓只需要即場打開瀏覽器便能進行遊戲,而且是在游泳池旁邊。沒錯,在游泳池旁釣虛擬魚!很吸引吧?她們對 BeyondZ 這項技術及遊戲都很感興趣。我自己很滿意這個構想與技術,亦很希望能成為香港第一家實現這個技術的公司。可惜的是,經過現場觀察及專業器材人員的意見,實現起來的工程浩大之餘,效果亦不如理想,最終只能放棄收場。

半年後的今天,路過尖沙咀海港城時,看到有公司已經把這種技術應用出來了。效果做得不錯,而且獎品有朱古力及 iPhone 4 套呢。

2012年2月10日星期五

在 Mac OS X 上安裝 Eclipse 及 Android SDK

Install Eclipse on Mac OS X with Android SDK

為了開發 Android 項目,需要在我的 MacBook Air 內安裝 Eclipse。之前已經在公司的 iMac 上安裝好,但安裝過程已經不想記起了。跟 http://developer.android.com/sdk/installing.html 再做一次。

1. 到 http://www.eclipse.org/downloads/ 下載 Eclipse Classics 3.7.1
2. 到 http://developer.android.com/sdk/index.html 下載 Android SDK
3. 解壓 Eclipse 並拷到 Applications 內
4. 解壓 Android SDK 到 Eclipse 目錄下
5. 啟動 Eclipse。選 Help 中的 Install New Software,按 Add
6. 在 Name 格輸入 ADT Plugin
7. 在 Location 格輸入 https://dl-ssl.google.com/android/eclipse/
8. 按 OK
9. 點選 Developer Tools 內所有插件後點 Next
10. 按畫面指示接受所有條款
11. 安裝完成後重啟 Eclipse
12. 在 Android SDK 畫面選剛才安裝 SDK 的目錄
13. 選 Applications - eclipse - android-sdk-macosx - tools - android
14. Android SDK Manager 會自動選擇需要安裝的元件
15. 安裝所有元件

2012年2月9日星期四

Linux 指令筆記(二)

關於公司的服務器設定,除了我就沒有其他人懂得去做。可是我自己對這方面沒有興趣,只有頂硬上來做。所以很多指令都記不清楚,要做記錄才行。

重啟 CentOS 6 的 Apache
/sbin/service httpd restart

重啟 QNAP TS-210 的 Apache
/etc/init.d/Qthttpd.sh restart

關閉 CentOS 6 的目錄清單功能
1. vi /etc/httpd/conf/httpd.conf
2. 找尋位於 Directory "/var/www/html" 下方的 Options Indexes
3. 把 Indexes 刪除
4. 儲存改動,並重啟 Apache

2012年2月8日星期三

寫給有心轉行的人

時值轉工季節,收到的求職信亦多起來了。今天剛好約了一位應徵者面試。我選擇約見這位應徵者,原因是他的履歷中包含了兩個 iPhone App 的截圖。

每一位應徵者來到 BeyondZ 都需要花 30 分鐘去完成一份問卷。編程崗位如是、美術崗位如是。這份問卷的作用是篩選出態度及技術較好的人。30 分鐘過去了,我收到的問卷機乎空白。回答的都是風馬牛不相及的東西。這刻我充滿疑問,為何有 iPhone 應用開發經驗的人,連 Objective-C 的題目都無法回答?這個謎團將在面見時解開。

試題時間之後便是 30 分鐘的面見時間。我們會向應徵者提問各式各樣的問題,應徵者亦能發問關於 BeyondZ 的問題。在溝通的過程中,我瞭解到他完全沒有任何編程方面的底子,只是因為想轉行而報讀了為期 4 個月的 iPhone 開發課程。他在履歷中的作品是由幾位同學加上 Open Source 而製成。我頓時明白為何問卷的答案會是這個樣子。本來還對自學編程有一絲希望,畢竟我的編程也是自學而來。對於應徵者的求變態度,我是肯定的。可惜 BeyondZ 付不起由零開始去訓練新人成為編程人員所需的資源。我不禁反問,那家教育機構在想甚麼?難度他們相信 4 個月能把零編程經驗的人訓練成為有編程思考能力的人嗎?縱使應徵者交出了一個 iPhone 應用,只是因為他們把老師所教的整合而成,卻沒有編程方面的思維,根本無法編寫別的程式。校方亦沒有給他們後續的支援。校方的心態根本在賺他們的錢,而不是在教育他們。真是氣憤!香港的教育機構都忘記了教育的本義嗎?從應徵者口中得知沒有一個同學能順利轉行。我希望那位有緣的應徵者能繼續努力,打好編程的底子,這樣才有能力跨越未來的障礙。

2012年2月7日星期二

Icon 0x0


最近在提交《中國神馬天氣》事宜中,不論是 App Store 還是 SmartMAD 都遇到阻滯,但總算跨過了。

在 iTunesConnect 建立新版本,把版本 2.00 錯誤地輸入了 1.20,之後惡夢來了。本來能簡單地改回 2.00,可是介面卻指沒有上傳屏幕截圖而無法更新。問題是那個介面只有圖示可上傳,而且截圖在 1.00 時已經存在,怎可能沒有截圖?原來這樣的改動,是需要把舊有截圖刪除後,重新上傳圖檔,即使上傳一模一樣的截圖。

好了,版本號搞好了,又到上傳 App 那邊出問題。指 Icon.png 大小是 0x0 像素,無法提交。翻查過無數次,圖檔的尺寸都是正常。最終的解決辦法是在 Xcode Build Settings 內的 Compress PNG Files 設定為 NO 才能成功上傳。

至於 SmartMAD 方面,指引中說明需要加入測試機的 UDID,可是找遍 SDK 文件都找不到。發電郵給他們,三天也沒有回覆。改用 MSN 查詢才得悉現在不用 UDID 了 >_<。好在事先已經提交一份,不過又遇到審批拒絕。拒絕歸拒絕,也在介面中給我原因好嗎?追問之下,原因已經電郵寄出,可是電郵卻是客戶的地址。我只好向客戶查詢。一問之下,拒絕的原因竟然是應用超過 20 天...。天呀!過了 20 天就拒絕,是甚麼道理?還好,得到 SmartMAD 親切員工的協助,終於可以重新上傳。