2010年12月30日 星期四

iTunes Backup 路徑

今天把 iPad 升級到 iOS 4.2,但我的 Mac Mini 沒有空間,於是我把過往的 Backup 檔刪掉,竟然能騰空 14GB 的空間,實在驚人。Backup 檔存在於 /Library/Application Support/MobileSync/Backup

2010年12月24日 星期五

Robocon 73


世事真奇妙,今早心血來潮到 Ohmsha 網頁一看,發現有新一期 Robocon;回到家時,竟發現最一期已經到達。今期雜誌封面的機械人相當古怪,原來是第 18 回 Robo-one 的優勝機械人,真是奇妙。看到這些機體,不禁使我想起已經有一段長時間沒有接觸機體相關的開發...。

2010年12月17日 星期五

藍芽測試


今天,我的大女兒發燒,已經是連續第二天發燒,從星期日第一次發燒到今天,六日內發了三天燒,所以我特別請假照顧她。

就在今天的可調用時段中,我嘗試了 BlueTooth 連線。原來製作方法十分簡單。我把它製作成 BlueToothManager,方便調用:

//
// BlueToothManager.m
// Main
//
// Created by Pacess HO on 17/12/2010.
// Copyright 2010 Pacess. All rights reserved.
//

#import "MainViewController.h"
#import "BlueToothManager.h"
#import "HPNotification.h"

@implementation BlueToothManager

static BlueToothManager *sharedManager = nil;
static id parentDelegate = nil;

- (id)init {
self = [super init];

if (self == nil) {return nil;}

return self;
}

- (void)dealloc {
[super dealloc];
}

- (void)connect {
peerPickerController = [[GKPeerPickerController alloc] init];
peerPickerController.delegate = self;
peerPickerController.connectionTypesMask = GKPeerPickerConnectionTypeNearby;
[peerPickerController show];
}

- (void)disconnect {
[blueToothSession disconnectFromAllPeers];
[blueToothSession release];
blueToothSession = nil;
}

- (void)sendData:(NSData *)data {
if (blueToothSession == nil) {return;}

[blueToothSession sendDataToAllPeers:data withDataMode:GKSendDataReliable error:nil];
}

- (void)peerPickerControllerDidCancel:(GKPeerPickerController *)picker {
picker.delegate = nil;
[picker autorelease];
}

- (void)peerPickerController:(GKPeerPickerController *)picker didConnectPeer:(NSString *)peerID toSession:(GKSession *)session {
blueToothSession = [session retain];
session.delegate = self;
[session setDataReceiveHandler:self withContext:nil];

picker.delegate = nil;
[picker dismiss];
[picker autorelease];
}

- (void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state {
switch (state) {
case GKPeerStateConnected:
[HPNotification postNotificationCode:NOTIFICATION_BLUETOOTH_CONNECTED object:nil delegate:parentDelegate];
break;

case GKPeerStateDisconnected:
[HPNotification postNotificationCode:NOTIFICATION_BLUETOOTH_DISCONNECTED object:nil delegate:parentDelegate];
[blueToothSession release];
blueToothSession = nil;
break;
}
}

- (void)receiveData:(NSData *)data fromPeer:(NSString *)peer inSession:(GKSession *)session context:(void *)context {
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[HPNotification postNotificationCode:NOTIFICATION_BLUETOOTH_DATA_ARRIVED object:string delegate:parentDelegate];
}

+ (void)connect:(id )delegate {
if (sharedManager == nil) {
sharedManager = [[BlueToothManager alloc] init];
}
parentDelegate = delegate;
[sharedManager connect];
}

+ (void)disconnect {
[sharedManager disconnect];
}

+ (void)sendData:(NSData *)data {
[sharedManager sendData:data];
}

@end
暫時只處理兩機連線,至於四機連線,日後有時間再研究。我在 iPhone, iPhone 4, iPad 上測試,發現 iPhone iOS 3.1.3 並不支援這個連線,而 iPhone 4 及 iPad 則連線無誤。

2010年12月11日 星期六

不受 Status Bar 的影響

如果想在 App 內避免其他 ViewController 召喚 Status Bar 而受到影響的話,可以在 ViewController 內設定:
self.wantsFullScreenLayout = YES;

2010年12月9日 星期四

動畫中的動畫

我小組「Perception Studio」的第二個作品將會是一個解謎遊戲。名稱暫訂為「Adnap」。在今個作品中,我們希望呈現出細膩流暢的動畫,以吸引玩家們的注意。要達到如此目的,動畫的幀數一定不能少。但對於三合一版本及在 20MB 的限制下,是絕對不可能。如果以部件併合的方式製作動畫,相信問題能得到解決。我們唯有改變方式,把 Sprite 系統升級並支援「Animation of Animation」。好處是令到動畫不會太過「死板」而又能節省空間。舉例來說:現在要製作角色的等候動作。包含了呼吸及合眼動作。如果以全幀來演譯,至少要 8 幀。但問題來了,合眼的時間跟呼吸的動作會很規律;要是想做得好一點,便可能需要用上 40 幀。對於記憶體及儲存空間都是一個負荷,而且這裡只是一個動作,要是 30 個動作時,問題會呈幾何級數上升;對於製作人來說也不是好事。

而「Animation of Animation」的概念是把某些部份分開成個別動畫,以我們的例子,眼睛便可以獨立出來,只要 3 幀便能有很好的效果。至於呼吸動作,8 幀也很好。動畫可以各自有不同的時間設定。總數 11 幀便能組合出彈性,看起來不「死板」的動畫。

2010年12月3日 星期五

35 顆一星

今天用 AppSales Mobile 來看所有 AppStore 中玩家們對《銀行大亨》的評價。得出來的數據是有 35 顆一星,是一個相對失禮的數字,亦打擊了我的信心。雖然當中有大部份都是不幸因 SDK 4.2 而導致 iOS 3.1.3 的啟動問題;蹤使在開發時特別有在 iOS 3.1.3 上進行測試,但最終有這樣的結果,實在是極大的疏忽。不過,連 Apple 也沒有發現啟動問題,令我意識到 Apple 已沒有花時間在 iOS 3.x 上了;同時地,亦令我驚訝世上還有很多 iOS 3.x 的 iPhone。

在《銀行大亨》的開發上,我下了不少功夫,務求令遊戲比《帝國》穩定,Bug 比《帝國》少,遊戲性比《帝國》高。客觀來說,前兩項已經做到,至於最後那項,我個人亦比較喜歡《銀行大亨》。

結果是,很多玩家認為《銀行大亨》只是一個沒技術的 Drag & Drop 遊戲、玩法沉悶。其實當初有考慮過在每個服務點加入小遊戲,但我們小組認為「小遊戲」會導致「沉悶」的感覺,因而把方向設定為「手忙腳亂」的挑戰。現在,玩家們狠狠地打了我一下,說明了「小遊戲」是他們認為必須的部份,反而「手忙腳亂」對他們來說是得不到樂趣...。多謝這 35 顆一星。

2010年12月1日 星期三

AR Sample Code

今日找到了一個關於 AR 的 Sample Code。在我的 iPhone 3.1.3 上執行也不慢,算是接受到的程度。原來用官方 SDK 都能做 AR,很有興趣試試。

http://dl.dropbox.com/u/508075/augmented_reality/AugmentedRealitySample.zip
http://cmgresearch.blogspot.com/2010/10/augmented-reality-on-iphone-with-ios40.html

2010年11月26日 星期五

Indexed-Alpha

在《銀行大亨》中一直想加入多一點客人角色及其動作圖片;但礙於公司政策,遊戲必須在 20MB 以下,唯有將圖量減少。我的下一個作品亦不例外。作為一個創作人,追求更流暢細膩的動畫是我其中一個目標;因此在新作的初階,我嘗試研究一下相關技術。發現在 Adobe Fireworks 中能輸出一種新的 PNG 格式。它是 Indexed color 256 色的格式,但卻擁有 alpha 漸變透明的支援。

我把圖片放進 iPad 及 iPod Touch,確定真機能支援這個格式,效果十分好。而圖檔大小在 Device Build 中只增加了 4KB,比起原先 RGBA 細小很多,十分適合作為遊戲的開發。

2010年11月22日 星期一

AppSales Mobile 多帳號支援

AppSales Mobile Mulit-Accounts Support

一直以來都很愛用 AppSales Mobile,貪它功能齊全及方便;相對比起 Apple 那個,真是跑驘十條街。雖然如此,但它還有美中不足之處,就是對於中文的數據輸入會出現亂碼,以及沒有多帳號(Multiple Accounts)支援。既然作者提供了源碼,那就自己動手來改。最簡單的修改是把 App.m 中的 getDocPath 以帳號名來劃分,其改動如下:
NSString* getDocPath() {
// static NSString *documentsDirectory = nil;
// if (!documentsDirectory) {
// NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// documentsDirectory = [[paths objectAtIndex:0] retain];
// }
// return documentsDirectory;

// Pacess: Added username as prefix for multiple accounts support
NSString *documentsDirectory = nil;
NSString *username = [[NSUserDefaults standardUserDefaults] stringForKey:@"iTunesConnectUsername"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
documentsDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:username];
return documentsDirectory;
}
使用這個改動時,在設定頁面輸入帳號名稱後,需要重新啟動 AppSales Mobile 才進行更新;否則會出現問題。原因是源碼沒有登出機制,要是剛檢查了帳號 A 的報告再登入帳號 B 的話,系統會誤以為仍然是帳號 A。雖然使用上有點不便,但只憑這麼簡單的改動,方法算是接受到吧!

2010年11月21日 星期日

粒子系統 DEMO 程式

星期三會到廣州辦公室講課,主要教授國內同事在 iPhone 上開發「Particle System」的技巧。為了方便大家理解,亦能使美工看到效果,我決定製作一個簡單的「粒子系統示範程式」,讓同事可以即場修改相關數值並看到效果。

2010年11月20日 星期六

《親子王下載工具》發佈...失敗!

《親子王下載工具》的介面也完成了,正式成為一個製成品。原本打算製作一個 Jailbreak IPA 檔案,方便有需要的網友。可是在 iPhone iOS 3.1.3 上執行時,一點擊「立即下載」便彈回 SpringBoard;而在 iPhone 4 iOS 4.1 上是能執行,但下載到第 36 頁時亦發生彈回 SpringBoard 的情況;相信是由於記憶體不足所導致。一期《親子王》的 PDF 在原有解像度下大約需要 130MB,要花上這麼多記憶體,解決方法的而且確是一個大難題...。

暫時只能在有源碼的情況下,在模擬器上執行吧~

2010年11月19日 星期五

Banker 躍升第一位

上架兩天,終於都成為香港 AppStore 角色扮演類第一名!在美國 AppStore 角色扮演類亦得到第 16 名,直迫 SquareEnix 的 Final Fantasy 系列。多謝大家支持!

2010年11月18日 星期四

《親子王下載工具》製作方法(三)

取得 PictureLayer 及 TextLayer 圖片後,要做的是把它們合併,放進 PDF Context 並且建立 PDF 檔。一本《親子王》共有 76 頁,所以要下載 76 次。
- (void)download:(unsigned int)year month:(Byte)month day:(Byte)day  {
UIImage *pictureImage, *textImage, *finalImage;
NSString *string, *api, *filename;
BOOL contextCreated = NO;
NSData *data;
Byte page;

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableData *pdfData = [NSMutableData data];

for (page=1; page<=76; page++) {
NSLog(@"[download] Processing page %d...", page);
pictureImage = nil;
textImage = nil;

// Get text layer image
api = [NSString stringWithFormat:NETWORK_API_TEXT, year, month, day, page];
string = [NSString stringWithFormat:@"%@%@", NETWORK_SERVER, api];
data = [SmartParentsDownloaderViewController getDataFromFullURL:string];
if ([data length] > 10) {
textImage = [UIImage imageWithData:data];
}

// Get picture layer image
api = [NSString stringWithFormat:NETWORK_API_PICTURE, year, month, day, page];
string = [NSString stringWithFormat:@"%@%@", NETWORK_SERVER, api];
data = [SmartParentsDownloaderViewController getDataFromFullURL:string];
if ([data length] <= 10) {continue;}

pictureImage = [UIImage imageWithData:data];

if (contextCreated == NO) {
contextCreated = YES;
CGRect rect = CGRectMake(0, 0, pictureImage.size.width, pictureImage.size.height);
UIGraphicsBeginPDFContextToData(pdfData, rect, nil);
}

// Merge two uiimage
if (textImage != nil) {
UIGraphicsBeginImageContext(pictureImage.size);
[pictureImage drawAtPoint:CGPointZero];
[textImage drawAtPoint:CGPointZero];
finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
} else {
finalImage = pictureImage;
}

UIGraphicsBeginPDFPage();
[finalImage drawAtPoint:CGPointZero];
}

if (contextCreated == NO) {return;}

UIGraphicsEndPDFContext();

filename = [NSString stringWithFormat:@"SmartParents_%04d_%02d_%02d.pdf", year, month, day];
string = [SmartParentsDownloaderViewController getDocumentFilePath:filename];
[pdfData writeToFile:string atomically:YES];

[pool release];
}

2010年11月17日 星期三

《親子王下載工具》製作方法(二)

有了取得 PictureLayer 的網址及 TextLayer 的網址後,要做的是下載這兩張圖片。

+ (NSData *)getDataFromFullURL:(NSString *)fullURL {

// Create HTTP GET request
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:fullURL]];
[request setHTTPMethod:@"GET"];
[request setTimeoutInterval:5.0f];

// Get response
NSHTTPURLResponse *urlResponse = nil;
NSError *error = [[[NSError alloc] init] autorelease];
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&error];

// Response received
return responseData;
}

2010年11月16日 星期二

《親子王下載工具》製作方法(一)


要製作《親子王下載工具》,首先要了解如何取得《親子王》PDF 檔。

利用 Wireshark 進行封包欄截,發現了 PDF 檔名格式是 SMP_2010_11_11.PDF。可惜的是我無法找到它的路徑,所以無法直接儲存 PDF 檔案。既然讀者在《親子王》官網能免費一頁一頁地閱讀,即是說每一頁的圖案也會下載到電腦內。於是我利用 Internet Explorer 載入數頁後,到電腦內的 Temporary Internet Files,以「最後存取時間」逆排,很容易便看到《親子王》的檔案。原來《親子王》每一頁的圖案都是由一個 PNG 加一個 JPG 檔組成。JPG 是 PictureLayer 圖案,而 PNG 是 TextLayer 圖案。兩者合而為一便成為一頁的內容。在每個 PNG 或 JPG 檔名右方的內容,便是取得該檔案時的網址。這個網址對製作《親子王下載工具》十分重要。

2010年11月15日 星期一

File Sharing 設置


《親子王下載工具》會把下載到的每一頁圖片合成到一個 PDF 檔案,而檔案會儲存在 Documents 目錄下。為了方便存取 PDF,我特別在 plist 中加入了 Application supports iTunes file sharing。這樣便能在 iTunes > Apps 下的 File Sharing 項目下直接存取 PDF 到電腦內。

2010年11月14日 星期日

親子王 PDF


我一直有索取親子王雜誌的習慣,到目前為止已經有 34 本。所佔的空間也越來越多。為了方便搜尋資料,我很想能把它以數碼方式儲存,甚至是放進 iPad 內。可是,官方的網頁內沒有儲存 PDF 的選項,所以我撰寫了一個 iPhone 程式來擷取親子王成為 PDF。

2010年11月12日 星期五

《銀行大亨》高清版及免費版提交了

《銀行大亨》高清版及免費版都在昨天提交了。而標清版亦已經提交了一個星期,卻沒有得到 Apple 任何的回覆,看來 Steve Jobs 一個星期的承諾又變成廢話了。

2010年11月8日 星期一

iOS 4.2 引發的問題(三)

實驗過後,發現方法取得成功,能繼續讓 iPhone 4 及 iPad 共用同一組圖片。方法是利用 initWithCGImage 並加入一個自行設定的 scaleFactor:
//=====================================================================================================================
@interface UIImage (RetinaMod)
- (id)initWithContentsOfResolutionIndependentFile:(NSString *)path;
+ (UIImage*)imageWithContentsOfResolutionIndependentFile:(NSString *)path;
@end

//=====================================================================================================================
@implementation UIImage (RetinaMod)

//---------------------------------------------------------------------------------------------------------------------
- (id)initWithContentsOfResolutionIndependentFile:(NSString *)path {
NSString *systemVersionString = [Common getSystemVersion];
int systemVersion = [systemVersionString intValue];

// Check scale if OS support scale
CGFloat scaleFactor = 1.0f;
if ([[UIScreen mainScreen] respondsToSelector:NSSelectorFromString(@"scale")]) {
scaleFactor = [[UIScreen mainScreen] scale];
}

NSData *data = [NSData dataWithContentsOfFile:path];
if (systemVersion >= 4 && scaleFactor == 2.0f) {
UIImage *image = [UIImage imageWithData:data];
return [self initWithCGImage:[image CGImage] scale:2.0 orientation:UIImageOrientationUp];
}

return [self initWithData:data];
}

//---------------------------------------------------------------------------------------------------------------------
+ (UIImage *)imageWithContentsOfResolutionIndependentFile:(NSString *)path {
UIImage *image = [[[UIImage alloc] initWithContentsOfResolutionIndependentFile:path] autorelease];
return image;
}

@end

2010年11月7日 星期日

iOS 4.2 引發的問題(二)

今日找到一個方法,似乎能使 iPhone 4 及 iPad 共用圖檔資源之餘,亦能避免在 iPad 下出現 Retina 模式。明天會做一下實驗,希望能夠成功!

2010年11月5日 星期五

《銀行大亨》標清版已經提交


《銀行大亨》標清版已經提交。我們在 iOS 4.2 測試過沒有問題,相信十天後便能上架。

標清版主要是給 iPhone, iPhone 3G, iPhone 3GS, iPod Touch 等非 Retina Display 的裝置使用;而高清版則是所有機型均可使用,亦即是說包含了標清版在內。

2010年11月3日 星期三

Robocon 72


今日收到了最新一期 Robocon 雜誌。回顧最近幾期,沒有甚麼新鮮高技術的內容,都是 Line Trace Car、簡單機械人講解...等題材。開始覺得有點沉悶...。

2010年11月2日 星期二

iOS 4.2 引發的問題

昨天收到 Apple 的電郵,指 iOS 4.2 及 SDK 4.2 已開放給開發人員下載。這意味著 Apple 會把新提交的作品主力放在 iOS 4.2 上測試。由於我最新的作品會在星期五提交;所以機我把 iPod Touch 及 XCode 都升級至 iOS 4.2 及 SDK 4.2,以作最後測試。

可是,我發現在 iPad 4.2 會出現圖片比例問題。這是由於在設計上共用了 iPhone 4 及 iPad 版的圖片資源所導致。為了節省資源,iPad 版使用了 iPhone 4 的圖案,很多檔名都使用了 @2x 格式,導致在 iOS 4.2 上執行時,誤以為是 Retina 格式而令到圖案尺寸縮小了 50%。由於 UIImage 的 scaleFactor 是只能讀取而不能寫入。所以暫時的解決方法是把 iPad 及 iPhone 4 圖檔分開,不再共用資源...。

2010年10月30日 星期六

多點觸控試驗

今日做了 Multi-Touch 測試,原本拿 [touches Count] 來檢測觸控點的數目,可是在任何情況都是 1。於是我改用 [event allTouches] 後便成功了:
- (void)touches:(NSSet *)touches withEvent:(UIEvent *)event  {
NSSet *touchArray = [event allTouches];
NSMutableString *string = [[[NSMutableString alloc] init] autorelease];
[string appendFormat:@"Event count: %d\nTouch count: %d", [touchArray count], [touches count]];
[textView setText:string];
}

2010年10月29日 星期五

Barney 時鐘


回到家後,發現大廳的時鐘因為最近乾燥的天氣而開始變得破爛;對於追求完美的我,實在看不順眼,於是決定自製鐘面,把時鐘換一換面。

我決定製作一個大女喜愛的時鐘,於是在網上找一些 Barney 的圖片,作為鐘面的圖案。量度過鐘面的直徑後,在 Photoshop 解立了一張新圖,把選好了的 Barney 圖片放進去。可是,鐘面數字的位置就不知道如何能在 Photoshop 中準確量度;於是我用 Illustrator 來幫忙。在 Illustrator 中建立新文件,加入一個大圓型,目的是作為裁剪的參考線,以及拿圓心來對準鐘面上 12 個數字。首先加入置中的數字 12,然後點選旋轉工具,再按著 Option 鍵選圓心。角度 30 度,點選 Copy 複製。再按 10 次 Command-D。鐘面的數字便會齊全。新增一個圖層,畫一個小圓並放在數字 12 的中央,然後重覆以上的複製步驟。這個小圓是用作鐘面數字旋轉之用。把 12 個數字設定好數值,通常都是 1-12,也可以是分鐘數字,即 5, 10, 15,...60。

這時的鐘面數字基本上已經完成,但數字會比較難看得清楚。我們只需要點選數字,再點選旋轉工具,按著 Option 鍵選小圓心把旋轉軸設定為數字中央,輸入適當的角度。完成後,可以把沒有用的圖層隱藏。選擇所有內容並複製,在 Photoshop 內貼上便完成。

2010年10月28日 星期四

animationWithKeyPath

最近很常用 CAKeyframeAnimation 來製作動畫,但有時想找出 animationWithKeyPath 的值卻很困難,所以在這裡歸納一下:
transform.scale = 比例轉換
transform.scale.x = 闊的比例轉換
transform.scale.y = 高的比例轉換
transform.rotation.z = 平面圖的旋轉
opacity = 透明度

2010年10月26日 星期二

另一好用工具:NameChanger


在製作遊戲的過程,有時會用上 SpriteSheet;有時也會用上零零碎碎的圖檔。很多時候為了方做編程,檔名都會是由順序數字組成。由於很難做到徹底的溝通,美術人員很多時都會隨意把圖檔命名;當圖檔到達程序員手上時,往往需要重新命名。今次介紹的 NameChanger 便能幫到手!

2010年10月25日 星期一

解密至 NSString

昨天加密了的 NSString,其解密方法為:
+ (NSString *)decodeString:(NSString *)string withFormat:(NSStringEncoding)format  {

// Convert incoming string to ASCII data first
NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding];
const Byte *incomingArray = [data bytes];

// Allocate decode buffer
int charCount = [string length]/2;
Byte *byteArray = malloc(charCount);

int sourceOffset = 0;
int targetOffset = 0;
for (int i=0; i Byte highByte = incomingArray[sourceOffset+0];
Byte lowByte = incomingArray[sourceOffset+1];
sourceOffset += 2;

if (highByte >= '0' && highByte <= '9') {highByte -= '0';}
if (highByte >= 'a' && highByte <= 'f') {highByte -= ('a'-10);}

if (lowByte >= '0' && lowByte <= '9') {lowByte -= '0';}
if (lowByte >= 'a' && lowByte <= 'f') {lowByte -= ('a'-10);}

Byte charValue = (highByte<<4)|lowByte;

// Decryption
charValue ^= 117;

byteArray[targetOffset] = charValue;
targetOffset++;
}

// Convert byte array to NSString
NSString *decodedString = [[NSString alloc] initWithBytes:byteArray length:charCount encoding:format];
free(byteArray);

return decodedString;
}

2010年10月24日 星期日

把 NSString 加密

最近編寫了一個把數據傳送到服務器的程序。但由於包含了文字及字符,所以不方便直接傳送。於是便需要編寫一個加密功能:
+ (NSString *)encodeString:(NSString *)string  {
if (string == nil) {return string;}

NSMutableString *resultString = [[[NSMutableString alloc] init] autorelease];
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
const Byte *byteArray = [data bytes];
int length = [data length];

for (int i=0; i < length; i++) {
Byte byteValue = byteArray[i];
byteValue ^= 117;

[resultString appendFormat:@"%02x", byteValue];
}

return resultString;
}

判斷 Game Center 的方法

原本《超級銀行家》是加入公司自行推出的 LakooSNS 來進行玩家之間的交流;但礙於始終不夠 Apple Game Center 的競爭,所以決定改用 Game Center。而我一向追求「一個版本走天涯」及「支援最大機款」的前提下,iOS 3.0 版本的用家是無法使用 Game Center,所以在遊戲內便需要即時判斷是執行 Game Center 相關的功能。方法如下:
+ (BOOL)isGameCenterAvailable  {
// Check for presence of GKLocalPlayer API.
Class gcClass = (NSClassFromString(@"GKLocalPlayer"));

// The device must be running running iOS 4.1 or later.
NSString *reqSysVer = @"4.1";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);

return (gcClass && osVersionSupported);
}

2010年10月19日 星期二

Points App 消失...

今天收到 Apple 中國員工的通知,說 Apple 正在整頓所有包點的 App(就好像 Mafia War 那些分好幾個點數版本的 App),需要全面下架。並把購買點數改以 In-App Purchase 方式出現。而每次 In-App Purchase 最大價錢只能為 US$99。

2010年10月17日 星期日

PNG Crusher


今日找到了一個工具,能有效減少 PNG 的檔案大小,它就是「PNG Crusher」。用法也很簡單,只要把所需減肥的 PNG 拖拉到 PNG Crusher 圖示上,放手後便會進行瘦身。檔案會被 Overwrite 成為纖型版。經測試後,減肥率比 XCode 有效,能節省一點空間。

2010年10月16日 星期六

Machinarium


早幾天,同事阿魚介紹了一套超華麗的 WEB Game 給我。它的名字是「Machinarium」(http://machinarium.net/demo)。阿魚說得沒錯,真的是超華麗之作。

由於阿魚知道我的黑技術有點斤兩,所以向我要求把打它的圖黑下來作為參考學習之用。基於這個動機我十分認同,所以我答應了他。

要黑「Machinarium」最重要的工具是 Internet Explorer。沒錯,貪它隨手可得及白痴是也。啟動「Machinarium」一會後,到 Temporary Internet Files,以時間逆排,會看到幾個 101010.100 的檔案。把它們拷出來並改為 xxxxx.swf,再用 Flash Decompiler 反組譯便行。

2010年10月15日 星期五

突破 20MB 限制

《超級銀行家》是一套「支援所有版本」的遊戲;亦即是說付費一次,便能同時享受 iPhone, iPad 及 iPhone 4 版本。這個設計基於想方便有 iPhone 及 iPad 的玩家;而且亦有助下載點擊率、搜尋出現率、排行榜爬升能力。能集合這些優點是要付出代價,就是 IPA 大小已經突破了 Apple 規定在手機直接下載的 20MB 限制。由於這個限制會打擊「即興意慾」,所以我也得想辦法令 IPA 大小下降至 20MB 以下的水平。

方法不外乎是:
1) 使用相同資源,特別是 iPad 及 iPhone 4
2) 把音樂由雙聲軌轉換成單聲軌(是很有效的方法)
3) 減少圖片色深
4) 使用 JPG 代替 PNG
5) 把不常用的資源分拆到服務器,有需要時下載
6) 壓縮檔案

2010年10月8日 星期五

《超級銀行家》Banker 進入最後開發階段


小弟最新力作《超級銀行家》已經進入最後開發階段。它是我第一次嘗試的 Universal Binary 作品;一 App 同時支援 iPhone, iPhone 4 及 iPad。語言方面同時支援正體中文、簡體中文、英文及日文。

《超級銀行家》是一套「時間管理類」作品。今次擔任項目管理、遊戲設計、編程設計,花了很多時間及心血製作,希望能獲得玩家的喜愛。

2010年10月2日 星期六

Twitter API on iPhone

今日的研究題目為 Twitter API。網遊了一會,發現了三個 Sample Code,它們的共通點是使用 MGTwitterEngine。我曾嘗試 MGTwitterEngine 但久缺很多檔案,無法 Build 出一個版本來測試。原因是這個 Engine 是 Objective-C 系,能夠給 Snow Leopard 及 iPhone 平台使用,自然需要多一點內容。有網友把當中 iPhone 不用到的部份刪除,可惜我仍然無法令它正常運作。Twitter 官網不像 Facebook 那般體貼,清楚及有系統地指引開發人員使用官方 API,只好留待日後再花時間研究研究。

2010年10月1日 星期五

在應用程式中發電郵

要在 App 內發 eMail 十分簡單。在 iOS 3.0 之後,iPhone SDK 提供了 MFMailComposeViewController 給開發員來處理發放電郵。
- (void)sendEmail:(NSArray *)recipientsArray subject:(NSString *)subject message:(NSString *)message html:(BOOL)isHTML  {
MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init];
controller.mailComposeDelegate = self;
[controller setSubject:subject];
[controller setToRecipients:recipientsArray];
[controller setMessageBody:message isHTML:isHTML];
[self presentModalViewController:controller animated:YES];
[controller release];
}

2010年9月23日 星期四

Bin Packing 式的 Sprite 排列說明

在閱讀過 Bin Packing 的文件後,雖然不太明白工作方式,但根據示範圖片的排列,我也能略為理解,並用自己的方式演繹出來。

關於排序的技巧,首先是要取得所有 Sprite 的尺寸,然後由大至小排列好。我是以面積(闊x高)來決定大小;如果單用闊或高來決定大小,相信也沒有問題。有了大小順序之後,便開始排列。我把當前的圖案用「圖A」來表示,後來的圖案以「圖B」、「圖C」來表示。排列的方式是把「圖A」貼在左上角,然後檢查後來的圖案。如果「圖B」高度小於或等如「圖A」時,「圖B」會放在「圖A」的右方;若「圖B」高過「圖A」,便會嘗試放在「圖A」下方;要求是「圖B」的闊度不能大過「圖A」的闊度。在正常情況下,應該能處理得到。若然以上兩個情況都不乎合,或者其中一軸已經沒有空間時,「圖B」便會放在「圖A」的右下方。

按照以上方法,Sprite 將會以「倒L」形狀呈現。

2010年9月20日 星期一

SpriteSheet Maker


搏鬥了九個小時,終於完成了在 Snow Leopard 上運作的 SpriteSheet Maker。之前在做 PVR Reader 時,已經學習到少部份 Snow Leopard 的開發技巧;所以花了四個小時便完成了 Drag & Drog, Context to NSImage, NSImage to PNG, XML output...等部份。其餘的時間,全都花在「如何排列圖案」上。起初所使用的排列方法是自行創作,不過效果及速度都強差人意,便在網上學習學習。原來有一個名為 Bin Packing 的方法。在看過 C++ 及 Python 的代碼後,仍然不太理解運作,我只好憑著參考圖片自行編寫程序。最後能成功地輸出 PNG 及 XML。明天會再花時間用不同面積的圖案反複測試,看看有沒有大問題發生...。

2010年9月13日 星期一

EAGLView 的 iPad 處理

在支援了 iPhone 4 的高清圖像之後,iPad 版本會導致死機,原因是 iPad 目前最新的只是 3.2 版本;而我用上的功能則是 iOS 4.0 才有。在網上找到的方法,不外乎用 respondsToSelector 去檢查是否有 scale 這個 selector,從而斷定是否為 iPad。這個方法可行,但用在我的 EAGLView 內會造成死機。原來是 contentScaleFactor 惹的禍。這個也是 iOS 4.0 的東東,所以也要用 respondsToSelector 檢查過才能使用。
 //  Set UIView's content scale factor if support
if ([[UIScreen mainScreen] respondsToSelector:NSSelectorFromString(@"scale")]) {
if ([self respondsToSelector:NSSelectorFromString(@"contentScaleFactor")]) {
self.contentScaleFactor = [[UIScreen mainScreen] scale];
}
}

2010年9月12日 星期日

粒子系統教學


我最新的作品將會用上 Particle System,所以在網上學習一下製作方法,找到了這段不錯的教學片段。

2010年9月11日 星期六

OpenGL ES 在 iPhone 4 下的設定

我最新的作品是一個 Universal Binary。一個 IPA 適合 iPhone, iPod, iPad, iPhone 4 的遊戲。亦即是說顧客只需要購買一次,便能同時安裝到以上裝國。為了能滿足 20MB 下載限制的要求,我選擇 iPad 資源作為 iPhone 4 共同使用。由於 iPhone 4 使用高清圖案的方法有些特別,放到 OpenGL 環境下,圖案大小變成兩倍大。起初我把 Texture2D 圖案的 scaleFactor 設定為 0.5f,圖案大小亦變回原來大小,不過卻沒有高清效果。在網上找了很久也找不到以上的對策;最後在 Apple 的 iOS Application Programming Guide 找到解決方法。

- (id)initWithFrame:(CGRect)rect {
if ((self = [super initWithFrame:rect])) {
self.contentScaleFactor = 2.0;
...
}

2010年9月10日 星期五

十分好用的 XML Parser 語法

最近在做新遊戲時,很常用到 XML Parser;當中需要檢查指定的 XML Syntax 有沒有出現。我曾在家中網上學習時找到一段十分好用的語法,但在公司編寫相關程序時,往往都記不起這段語法。現記錄如下:

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
NSArray *elementsBeToParse = [[NSArray alloc] initWithObjects:
@"spritesheet", @"sprite", @"name", @"x", @"y", @"width", @"height", @"file", nil];

NSString *lowerElementName = [elementName lowercaseString];
BOOL parseIt = [elementsToParse containsObject:lowerElementName];
if (parseIt == NO) {return;}
...
}

2010年9月7日 星期二

iPhone 的電郵音效路徑

之前把 iPhone 4 收到電郵時的音效改為 Metal Gear Solid 的音效;用了一個星期,覺得效果不好,所以決家還原置換。只要把 caf 檔拷到以下路徑即可:

/System/Library/Audio/UISounds/new-mail.caf

2010年9月2日 星期四

iPhone BlueTooth 與 WiiMote 連線


最近在造新的六足機體,很想可以利用 iPhone 來操控;這需要利用 Open Toolchain 來達成。現在 Toolchain 已安裝,問題是要如何取得 BlueTooth 的數據?我想首先要確認一下是否真的能利用 iPhone 跟其他 BlueTooth 裝置連線。

在 Cydia 中有一個名為 WiiMote DEMO 的軟件可以作為測試之用。打開軟件後,在打開 WiiMote 手掣,按下 1 + 2 鍵後,iPhone 便會跟 WiiMote 連線。成功後,只要旋轉 WiiMote,相同的旋轉角度便會在 iPhone 內的 3D 模型表現出來,十分有趣吸引。

2010年9月1日 星期三

inCase Snap Case 到


等了好久(有一個半月了)的 iPhone 4 機套終於到了!比預計早到了五天。是一個硬膠機套,質感不錯,也不太厚。

至於另一個機套,我前幾天取消了訂購,目的是想轉換為 Apple Bumper。可是再次啟動 Case Program 都說已選擇了機套,無法繼續。我只好至電客服查詢;十分激氣的是撥通後等了半小時也沒有人回應,難道客服中心只有一位員工嗎?

2010年8月31日 星期二

UART WIFI 模塊產品

http://item.taobao.com/auction/item_detail-0db2-1618d47a746b3ed3919965b7ea341abe.htm

http://item.taobao.com/item.htm?id=3562579080

2010年8月30日 星期一

Open Toolchain 編程


Open Toolchain 3.0 雖然安裝好,但在編程時卻發現很多東西都沒有,亦有很多東西不同了。例如:UILabel 則變了 UITextLabel、CGColor 就變成了 float[] = {1, 1, 1, 1}...等;而且網上很難找到 Sample code。以下是今次的 Source:
 float textColor[] = {1, 0, 1, 1};
CGRect rect = CGRectMake(10, 100, 300, 50);
UITextLabel *textLabel = [[UITextLabel alloc] initWithFrame:rect];
[textLabel setColor:CGColorCreate(CGColorSpaceCreateDeviceRGB(), textColor)];
[textLabel setText:@"Sample using Open Toolchain!"];
[mainView addSubview:textLabel];
[textLabel release];

2010年8月29日 星期日

成功安裝 Open Toolchain 3.0


突然心血來潮,很想重試安裝 Open Toolchain for 3.0。在 Google 找到了網址 http://www.alexwhittemore.com/?p=281。之前也跟過來安裝但不成功,今次也再試結果也不成功。不過我發現到了問題是出於 Mac SDK。我到 Apple 嘗試下載 Mac SDK 10.4,卻引導我到了 XCode 3 的下載頁。這時一道閃光劃過。令我記起在安裝 XCode 時,Mac SDK 10.4 的預設值是關掉。要是重新安裝時把它打勾,便可能成功。結果如我所料,能順利安裝好了。但天色已晚,過了我上床時間快兩小時,所以還沒有嘗試編程及在真來上測試。留待日後繼續!

2010年8月28日 星期六

Amigo Hexapod 實作


為了能繼續機體的開發計劃,這個星期我每天都抽一點時間製作部件。一個星期的成果就是製成了六件部件。我把之前從女兒奶粉罐剪下來的底部,放上六件部件;可是似乎空間不夠,部件之間很容易發生碰撞。需要改一改設計了!否則連鋰電都放不下!

2010年8月27日 星期五

部件實作(二)


我家一直有儲鋁罐的習慣,待儲到一定數量後,便會拿去「黃鋁罐」處理。在查看過家中回收袋之後,發現有三罐用完的奶粉罐,於是乎我把它們剪下來作為機械人主體的鋁片之用。

2010年8月26日 星期四

iPhone 4 Case 付運

今日收到 Apple 的電郵通知,其中一個從 Case Program 訂購的 iPhone 4 套已經付運,不過到達時間為 9 月 6 日。

2010年8月24日 星期二

2010年8月23日 星期一

部件實作


昨天把第三設計的部件製作出來。首先是把紙樣貼到鋁條上,利用鋁剪把鋁條剪下。在上面鑽孔,分四個步驟把細孔鑽成大孔。利用「老虎鉗」把部件折曲 90 度,再把 Servo 安裝上。整個過程大約十五分鐘。製作出來的效果不錯,但稍為不夠穩固。

2010年8月22日 星期日

Amigo Hexapod Leg Design 03


這是再進一步簡化的設計,採用單臂方式來支撐機體。看來很不穩固,但網上有不少這類設計。

2010年8月21日 星期六

重拾工具


今日到四姨仔家探望她的初生寶貝時,順道看見馬路對面有一家五金舖,在心血來潮下走過去詢問有沒有「線鋸」出售。老闆拿出一把跟其他五金俑一樣的線鋸,粗粗的鋸片、固定的支架。這並不是我想要的那種,而且家裡也有一把。正想離開之時,隨口問了老闆一下,看看有沒有其他款式;他回答說:「還有一款,是套裝來的」後便隨手拿下來給我看。竟然跟我斷掉的是同一款式!是我找了很久也找不著的那款。看過價錢 HK$58 認為可以的價錢,便付款離開。

2010年8月20日 星期五

Amigo Hexapod Leg Design 02


今天嘗試了另一個較為簡潔接合方式,能利用長方形木條把兩個 Servo 連接起來。

2010年8月19日 星期四

Push Notifications


一直都很想試 Push Notifications,今天有點時間,先試試 Local 的做法,原來十分簡單。在 didFinishLaunchingWithOptions 加入以下代碼就能實現在十秒後發出推送通知:
 //  Local push notification test
UILocalNotification *localNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotification != nil) {

NSString *itemName = [localNotification.userInfo objectForKey:@"KEY_PUSH_NOTIFICATION"];
application.applicationIconBadgeNumber = localNotification.applicationIconBadgeNumber-1;
NSLog(@"has localNotification %@", itemName);
} else {

[[UIApplication sharedApplication] cancelAllLocalNotifications];

localNotification = [[UILocalNotification alloc] init];
if (localNotification == nil) {return YES;}

NSDate *now = [NSDate date];
NSDate *scheduled = [now dateByAddingTimeInterval:10];
NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:@"Local Push", @"KEY_PUSH_NOTIFICATION", @"Local Push received while running", @"KEY_MESSAGE", nil];

localNotification.fireDate = scheduled;
localNotification.timeZone = [NSTimeZone defaultTimeZone];
localNotification.alertBody = @"今天是鄭秀文的生日!";
localNotification.alertAction = @"View Details";
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.applicationIconBadgeNumber = 0;
localNotification.userInfo = infoDict;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
[localNotification release];
}

2010年8月18日 星期三

Futaba S3003 x22


最近買了很多物品,有 iPad 64GB WiFi, iPhone 4 x2, Apple iPad Case, Apple Magic TrackPad, 1TB Harddisk, Harddisk Dock...,加起上來的消費數字也有一定程度,而且很快就要還款。因此,在開發 Amigo Hexapod 時便需要節儉為上。我把以前的 Amigo 解拆下來後,得到 22 個 Futaba S3003 及 10 個 TowerPro MG995,足夠開發 Amigo Hexapod 有餘了。

2010年8月17日 星期二

Amigo Hexapod Leg Design


這是足部的軸心接合方法。至於 Servo 之間的接合方法,則要研究一下如何能簡單易製作為上。

2010年8月16日 星期一

Amigo Hexapod Design 01


最近網友 Keith 發起六足機體求生遊戲,他以快速的行動實現了機體的設計,並購買了相關的部件,相信很快便能見到真機的面世。

雖然距離年底的比賽還有三個月的時間,不過我也想分配一點時間進行製作。今次在技術上比起二足容易許多(容易但不是簡單),之前也處理過 BlueTooth 方面的控制,要理用電腦來操控機體,已經不是難度。問題是鏡頭方面選購及運用,將會花上不少時間。網友 Keith 選擇了 MiNi Wireless Surveillance Camera,售價為 HK$260,AV 輸出。而我在網上找到另一款式的 2.4G Wireless Camera 則要 HK$480 左右,USB 接收。現在正考慮購買後者。原因是我打算接收鏡頭畫面作為分析之用,要是 AV 輸出的話,我的手提電腦便需要外接電視盒;要是 USB 接收的話,便能直接使用。

2010年8月13日 星期五

Passing argument to selector

最近研究 Sally's Spa 時,發現它的 XML 中很常用到 這種格式。我想它的設計是凡出現 param 時,系統便會自動把由 type 所指定的 variable 設定為 value 內的數值。亦即是說,變量名稱及其數值都是一個字串,在 runtime 需要時把字串轉化為變量,以及把字串轉化為數值。後者十分簡單,只要 [NSString intValue] 便可以,問題是前者怎樣做?而且要把它程序化,確實有點難度。

在網上似乎也找不到關於「NSString to variable」及「Passing argument to selector」之類的資料;唯有自行研究。原來要實現這個設計不是太難,先看看以下代碼:
- (void)setVariable:(NSString *)variable value:(NSString *)value  {
// Prepare variables
NSString *firstCapChar = [[variable substringToIndex:1] capitalizedString];
NSString *variableBody = [variable substringFromIndex:1];
NSString *functionName = [NSString stringWithFormat:@"set%@%@:", firstCapChar, variableBody];

// Perform selector if exists
SEL mySelector = NSSelectorFromString(functionName);
if ([self respondsToSelector:mySelector]) {
[self performSelector:mySelector withObject:value];
}
}
假設 counter 是一個 Integer 類的 Class variable。要使用以上代碼,只需要解立一個功能:
- (void)setCounter:(NSString *)value  {
counter = [value intValue];
}
然後使用 [self setVariable:@"counter" value:@"117"]; 便能把 counter 設定為 117 了。為何是 117?是我的生日囉!夠方便吧!

2010年8月7日 星期六

Apple Magic Trackpad


最近很愛到豐澤,看看有沒有新的 Apple 產品上架。今天順路走過,便順路走進店內看看。發現有兩台 Apple Magic Trackpad,我猶豫了一會,拿出我的 HSBC Advance VISA,簽了一個名,便把它拿走了...。

2010年8月6日 星期五

self. 的技巧

我除了研習八字之外,也會參考風水方面的提示,雖然在風水上還沒有出現一件推算準確的事。亦由於有這方面的需要,一直都想製作一個 iPhone 版「羅庚」;由於 iPhone 沒有羅盤,所以一直到我擁有 iPad 及 iPhone 4 後才能實現。在 Apple 拿了一個例子參考。要取得羅盤數據十分簡單,但要做到我心目中的「羅庚」則需要下一點功夫。

在看 Apple 的例子時,學到一個技巧:
@property (nonatomic, retain) CLLocationManager *locationManager;

// setup the location manager
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
為甚麼一個 Class Variable 需要定義 Property?這個是因為方便將所有用 locationManager 的地方,能一下子由 Retain 轉為 Assign,相反亦然。這個我一直也有用。但看到 autorelease 的語法,頓時認為這個方法很好。我一直都是建立本地變量,alloc 及設定後才指派到類的變量。今次這個方法實在簡潔好用!

2010年7月31日 星期六

Apple iPad Case 到着


昨天除了新增了兩台 iPhone 4 之外,原裝 Apple iPad 套也送到家。盒子雖大,但內容很少。不過,原裝 Apple iPad 套的質地比想像中不同。面板內使用了金屬來增加強度,放入 iPad 後,整體感覺不錯!

iPhone Dock for iPhone 4


兩年多前買 iPhone 時,隨盒附送了一個底座。由於我的 iPhone 使用了「矽膠套」的緣故,一直都沒有使用這個底座。然而,新的 iPhone 4 已沒有底座附送,加上暫時沒套可用,於是我拿出底座嘗試與 iPhone 4 結合。我曾試過 iPhone 3 及 iPhone 3GS 都不能使用的 iPhone 底座,現在 iPhone 4 能順利而穩固地使用,實在太好了!

2010年7月30日 星期五

iPhone 4 Case Program - HK


終於都到香港地區有 iPhone 4 套送喇!
http://www.apple.com/hk/en/iphone/case-program/

9 月 30 日前發售的 iPhone 4 都被指有「天線門」,所以 Apple 就用送套來解決。至於送套的方式,是需要下載 Case Program 應用程式(必須在所屬地區的 AppStore 下載),然後在應用中選購套的款式。有 Apple Bumper 及十個左右其他廠的套可供選擇,全部都由 Apple 付費。

雖然我沒有發現「天線門」及「BlueTooth」問題,但既然 Apple 有套贈送,當然第一時間 Order 一個啦!不過款式就麻麻地,大部份都是「硬膠殼」,只有兩個「矽膠殼」,還要是很多透氣孔...

不過,大家要小心地選擇,只能選擇一次。選完之後再登入 Case Program 是不能再選購呢!

款式可以參考:
http://www.ilounge.com/index.php/backstage/comments/apple-iphone-4-case-program-here-are-the-best-worst-picks/

iPhone 4 到手!


今天很早便醒來,可能是因為 iPhone 4 首賣而興奮到睡不著。由於還未到 8:15am 出門上班的時間,於是打開報紙了解世界發生甚麼事情;然而竟給我發現 Smartone 在今天提早至 8:30am 開門,計一計時間,如果我能排在頭 5 名,便能在 9:30am 回到公司。於是原本打算放工出機的我,決定改為出機後才上班。幸好我排第 4,最終也能按照計算一樣 9:30am 回到公司。

2010年7月29日 星期四

訂購成功!


經過十五分鐘的攻勢後,終於都成功訂購 iPhone 4,收到確認信。信中寫了出機時間為 7 月 30 日至 8 月 2 日,若過了這個限期的話,便當作取消訂購。我打算明天到門市出機!

不過,我太太及一個朋友都有訂購 iPhone 4,但卻收不到確認信,可能是排在隊的最後,未能確認日期。事關有朋友的朋友,收到 Smartone 的確認信,但出機的時間為 8 月 6 日...。

2010年7月28日 星期三

Smartone 客戶優先訂機


明天(7 月 29 日)下午 2:00,Smartone 客戶可在網上優先訂購 iPhone 4。

香港各台的 iPhone 4 月費計劃

http://spreadsheets.google.com/pub?key=0Au_hDCvSI8ygdHctcDllWF9IbXJtZzFUcllrN3hlcFE

2010年7月27日 星期二

香港 Apple 網頁有 iPad 賣喇!


這幾天,我為了缺貨的原廠 iPad 套,日日到豐澤一會,也日日上 Apple 官網一看。今天發現 Apple 官網正在更新。原來更新過後是出現 iPad 及原廠套等周邊產品。最開心的是原廠套能在 24 小時內付運,我也急不及待立即訂購了一份。

2010年7月26日 星期一

破解 Sally's SPA 的 PVR 檔


由於工作的原固,我拿了 Sally's SPA 來參考,並破解了其 PVR 格式。首先我把 PVR 檔拖拉到 HexEdit,看看內容會否有很多重複的部份,如果有的話,代表了內容沒有被壓縮或編碼。確定沒有編碼後,我在 Photoshop 把檔案以 RAW 格式讀入,發現在 16-Bits 格式下,圖片偏移了 26 點,亦即是說 PVR 格式有 52 個字符的 Header。

利用 Photoshop 讀進來的檔案只是以灰階呈現,實在不能滿足我的需求,可惜的是我找不到合適的 Photoshop Plugin 來處理;我也不懂得 Snow Leopard 的編程方法;最後我決定使用 iPhone Simulator 來解決。因而開發出以下代碼:
+ (void)convertPVR2RAW:(NSString *)fileName  {
NSString *fileNameWithExt = [NSString stringWithFormat:@"%@.pvr", fileName];
NSString *mainPath = [[NSBundle mainBundle] bundlePath];
NSString *filePath = [NSString stringWithFormat:@"%@/%@", mainPath,
fileNameWithExt];
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
NSData *data = [fileHandle readDataToEndOfFile];
const Byte *dataByte = [data bytes];

// Convert RAGB-16bits to RGBA-32bits
int width = 512;
int height = 512;
int headerSize = 52;
int imageSize = width*height;
int totalSize = headerSize+imageSize*2;
int outputSize = imageSize*4;
Byte *rgbData = malloc(outputSize);
int rgbOffset = 0;
for (int i=headerSize; i< totalSize; i+=2) {
int channelValue = (dataByte[i]<<8)+(dataByte[i+1]);
int red = (channelValue>>12)&15;
int alpha = (channelValue>>8)&15;
int blue = (channelValue>>4)&15;
int green = (channelValue)&15;

rgbData[rgbOffset] = (blue<<4);
rgbData[rgbOffset+imageSize] = (green<<4);
rgbData[rgbOffset+imageSize+imageSize] = (red<<4);
rgbData[rgbOffset+imageSize+imageSize+imageSize] = (alpha<<4);
rgbOffset++;
}

// Get documents directory
NSArray *paths =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

// Generate output file
fileNameWithExt = [NSString stringWithFormat:@"%@.raw", fileName];
filePath = [NSString stringWithFormat:@"%@/%@", documentsDirectory,
fileNameWithExt];
NSData *outputData = [[NSData alloc] initWithBytes:rgbData length:outputSize];
[outputData writeToFile:filePath atomically:NO];
[outputData release];

// Release memory
free(rgbData);
[fileHandle closeFile];
}

最終都能成功解碼。說穿了,PVR 只不過是多了 52 Bytes Header 的 16-Bits RGBA RAW 格式。不過它的排列不是 RGBA,而是 RAGB。

2010年7月25日 星期日

iOS 4.0 下的 Icon 檔名

在 iPhone Developer Center 下載了一個 Sample Code。打開之後,發現了六個以 Icon 為首的檔名,分別為:
Icon.png / Icon@2x.png / Icon-72.png / Icon-Small.png / Icon-Small@2x.png / Icon-Small-50.png。

在好奇之下,搜尋了相關資料。Icon.png 就是原本 iPhone / iPod Home 畫面會用到的圖示;Icon@2x.png 是在 iPhone 4 Home 畫面顯示的圖示;Icon-72.png 是 iPad Home 畫面顯示的圖示;而有 Small 字眼的是在 Spotlight 下出現的圖示。

2010年7月24日 星期六

iPad 套也售罄

今天打算購買 Apple iPad Case,於是我走訪「灣仔電腦城」、「new:Vision」、「298 電腦商場」及「豐澤」。可惜不是沒有貨,就是有現貨但價錢卻要 HK$680! 是香港定價 HK$298 的一倍有多!聞說昨天 iPad 首賣時,每家店只得到 10 個原裝 iPad 套,難怪今天找了很多地方也沒有貨,而且價錢能飆升一倍。相信也只有 Apple 才能造出這樣的景象。

2010年7月23日 星期五

iPad 64GB WiFi 入手!


今天是香港首日發售 iPad 的日子,在日本買不成 iPad 的我,特地拜托了媽媽到我家商場的豐澤購買 iPad。在早上 8:15,我正在回公司的路途,已經看到約有 20 人在豐澤的門外排隊。到了 9:00 時左右,媽媽到了,這時她已經排第 50 左右了。到正式開門時,已經有過 100 人排隊。店員把顧客以 10 人分為一組,每人限購 2 台,分批進入店內購買。到了 12:00 時,終於到媽媽進場。這時的 16GB 及 32GB 都已經售罄,幸好我打算買的 iPad 64GB WiFi 版本仍然有貨。由於我媽不懂得試機,所以我委托店員幫忙,最終亦沒有問題,成功購買。今之真的辛苦媽媽!

在另一邊廂,我兩位朋友分別到 Apple 專賣店購買 iPad,他們都被迫地以 HK$500 及 HK$600 左右的價錢,購買 iPad 保護套、保護貼、清潔啫喱等指定的周邊產品。不過他們最終都能購買到 iPad 32GB WiFi 版本。

2010年7月20日 星期二

YouTube 問題


今天打算用我的元祖 iPhone 來看 YouTube,殊不知無法連接 YouTube 服務器,起初以為是 YouTube 的問題;但我的 Mac mini 又能正常使用 YouTube。於是乎我把 iPhone 重啟,可是仍然無法進入 YouTube,相信很可能是 JailBreak 的後遺症...

2010年7月18日 星期日

XCode 小秘技(四)


在編程時我有一個習慣,會把 #define 類的名稱全用英文大寫來表示。例如:TITLEVIEW_ACTION_LOADGAME。很多時名稱會由變數名稱演變出來。變數名稱則是以小寫為首,次字第一個字母為大寫。例如:loadGame。若遇上大量的名稱需要定義時,便會很花功夫。在 XCode 的 Preferences 內有一欄名為 Key Bindings,我們可以自定義「快捷鍵」及「功能」。今次我就加入了 Upper Case 的支援,使得編程時更加方便。

2010年7月17日 星期六

iOS 3.2 下的 setStatusBarHidden

進入 iOS 3.2 之後,原本設定狀態列的語法改變為:

// Hide status bar
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];

2010年7月15日 星期四

iPad 售價

2010年7月10日 星期六

再買 iPad


今日再次到「秋葉原」,嘗試到電器店購買 iPad。可惜店員指 WiFi 版本沒有貨,只有 3G 被本。對我來說,WiFi 版已經足夠,所以沒有購買 3G 版本。後來回到「新宿」,在大型百貨公司的電器樓層找到 Apple 攤位,一問之下,那裡也沒有 WiFi 版。但卻可以為我這些外國人訂貨,不過時間約為兩個星期...。我明天便回香港,再加上有消息指香港在 7 月 19 日也有 iPad 上市,所以沒有訂購。

2010年7月9日 星期五

iPad,只留給日本人


今日特別地到了涉谷,目的是到 Apple Store 購買 iPad。當我問日系店員時,他說沒有貨;而我問老外店員時,則說只能賣給本地人,外國客會說成沒有貨,所以我買不成 iPhone 4,也買不到 iPad。明天打算在新宿及秋葉原碰碰運氣。

2010年7月7日 星期三

體驗了 Retina Display 的魔力


今天是 2010 東京之旅的第一天,我到了秋葉原拜訪兩家機械人店之外,亦看看 iPhone 4。拿在手上看時,終於體驗到 Retina Display 的利害,真的很細緻。拍照時的速度很快,而且相片很美!不說的話,還以為自己拿著日系手機。可惜的是買不到...

2010年7月4日 星期日

SDK 4.0 下編譯 3.1 應用


今日我把 XCode 更新到 SDK4,嘗試最新版 XCode 能否編譯 3.1 應用,並在第一代 iPhone 上執行。結果証實能成功運行。只要在 Project Settings 裡的 Deployment 中 iPhone OS Deployment Target 選 iPhone OS 3.1 就行了。

2010年7月3日 星期六

PositionApp


今日試了一套名為「PositionApp」的軟件,它能顯示出一個軟件在不同地區的排名變化。我最欣賞它的不是排名的數據(因為我不懂得怎樣利用這些數據),而是它的介面設計。總括而言是:快、靚、正、爽、簡潔。比起我現在做的遊戲真是蚊脾對牛脾。有待國內同事好好學習。