<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                《Learn IPhone andiPad Cocos2d Game Delevopment》第6章(原文中部分無關緊要的內容沒有進行翻譯)。 ## 一、 CCSpriteBatchNode 在屏幕上貼圖時,圖形硬件需要經過準備、渲染、清除等步驟。每次貼圖都會重復這個過程。如果圖形硬件能事先知道有一組擁有相同紋理的Sprite需要渲染,則這個過程會被簡化。比如,一組Sprite的準備和清除動作總共只需要執行一次。 下圖的例子使用了CCSpriteBacthNode。屏幕上同時有幾百顆子彈飛過。如果一次只渲染一顆,那么幀率馬上降到85%。使用CCSpriteBatchNode,可以避免這種情況: ![](https://box.kancloud.cn/2016-05-04_572a0b9beb669.gif) ? 通常我們這樣創建一個CCSprite: ~~~ CCSprite*sprite=[CCSprite spriteWithFile:@”bullet.png”]; [selfaddChild:sprite]; ~~~ 而使用CCSpriteBatchNode則需要修改為: ~~~ CCSpriteBatchNode*batch=[CCSpriteBatchNode batchNodeWithFile:@”bullet.png”]; [selfaddChild:batch]; for(inti=0;i<100;i++){ CCSprite* sprite=[CCSpritespriteWithFile:@”bullet.png”]; [batch addChild:bullet]; } ~~~ 注意,CCSpriteBatchNode需要一個圖片文件名作為參數,哪怕它根本用不著這個圖片(進行顯示)。可以把它看做是一個Layer,你可以用它來加入一些CCSprite節點。由于它使用了一個圖片文件作為構造參數,所以在后面加入的CCSprite中必須使用相同的文件作為構造參數,否則會導致如下錯誤: ~~~ SpriteBatches[13879:207]*** Terminating app due to uncaught exception'NSInternalInconsistencyException', reason: 'CCSprite is not using the sametexture id' ~~~ 當采用相同紋理的CCSpite越多,則采用CCSpriteBatchNode的好處越明顯。 但這有一個限制,所有的CCSprite節點都會位于同一個Z坐標(深度)上。如果子彈是“擊穿”敵人并向后飛,你得使用兩個Z軸不同的CCSpriteBatchNode。 另外一個限制是,CCSpriteBatchNode和加入其中的CCSprite必須使用相同的貼圖。這一點在使用TextureAtlas時尤其顯得重要。一個Texture Atlas可以畫多個不同的圖片,并且這些圖片使用同一個CCSpriteBatchNode,以提高渲染速度。 Z軸的問題可以通過指定CCSpriteBatchNode中單個CCSprite的Z值來解決。如果你所有的圖片都放到了一個TextureAtlas(紋理集),則你完全可以只使用一個CCSpriteBatchNode。 把CCSpriteBatchNode看成一個簡單的CCLayer,它只接受使用相同圖片的CCSprite,這樣,你就知道怎么用它了。 在下面代碼中,隱藏有一個致命的陷阱: ~~~ -(id)init{ ??? If ((self = [superinitWithFile:@"ship.png"])) { [self scheduleUpdate]; ??? } ??? return self; } ~~~ 由于-(id)init方法是默認的初始化方法,它會被其他初始化方法比如initWithFile調用。在-(id)init方法中調用了[super initWithFile…]方法,[super initWithFile…]會調用[super init], 該類覆蓋了-(id)init方法,于是又會調用-(id)init方法,無限循環。 解決辦法是修改方法名,比如修改為-(id)initWithShipImage。 這個教訓告訴我們,在默認初始化方法-(id)init中,除了[superinit]之外,永遠不要調用其他東西(其他的初始化方法)。如果你必須在初始化方法中調用[super initWith…]方法,你應當把方法名命名為initWith…。 ## 二、示例代碼 1、ship類 ~~~ #import <Foundation/Foundation.h> #import "cocos2d.h" @interface Ship : CCSprite { } +(id) ship; @end #import "Ship.h" #import "Bullet.h" #import "GameScene.h" @interface Ship (PrivateMethods) -(id) initWithShipImage; @end @implementation Ship +(id) ship { return [[[selfalloc] initWithShipImage] autorelease]; } -(id) initWithShipImage { if ((self = [superinitWithFile:@"ship.png"])) { [selfscheduleUpdate]; } returnself; } -(void) dealloc { [superdealloc]; } -(void) update:(ccTime)delta { [[GameScenesharedGameScene] shootBulletFromShip:self]; } @end ~~~ ship類很簡單,除了update方法。該方法調用了GameScene的shootBulletFromShip方法外([GameSceneshareGameScene]實際上只是獲取GameScene 的單實例)。 2、GameScene類 ~~~ #import <Foundation/Foundation.h> #import "cocos2d.h" #import "Ship.h" typedefenum { GameSceneNodeTagBullet = 1, GameSceneNodeTagBulletSpriteBatch, } GameSceneNodeTags; @interface GameScene : CCLayer { intnextInactiveBullet; } +(id) scene; +(GameScene*) sharedGameScene; -(void) shootBulletFromShip:(Ship*)ship; @property (readonly) CCSpriteBatchNode*bulletSpriteBatch; @end #import "GameScene.h" #import "Ship.h" #import "Bullet.h" @interface GameScene(PrivateMethods) -(void) countBullets:(ccTime)delta; @end @implementation GameScene static GameScene*instanceOfGameScene; +(GameScene*) sharedGameScene { NSAssert(instanceOfGameScene != nil, @"GameSceneinstance not yet initialized!"); returninstanceOfGameScene; } +(id) scene { CCScene *scene = [CCScenenode]; GameScene *layer = [GameScenenode]; [scene addChild: layer]; return scene; } -(id) init { if ((self = [superinit])) { instanceOfGameScene = self; CGSize screenSize = [[CCDirectorsharedDirector] winSize]; CCColorLayer* colorLayer = [CCColorLayerlayerWithColor:ccc4(255, 255, 255, 255)]; [selfaddChild:colorLayer z:-1]; CCSprite* background = [CCSpritespriteWithFile:@"background.png"]; background.position = CGPointMake(screenSize.width / 2, screenSize.height / 2); [selfaddChild:background]; Ship* ship = [Shipship]; ship.position = CGPointMake(ship.texture.contentSize.width / 2, screenSize.height / 2); [selfaddChild:ship]; CCSpriteBatchNode* batch = [CCSpriteBatchNodebatchNodeWithFile:@"bullet.png"]; [selfaddChild:batch z:1tag:GameSceneNodeTagBulletSpriteBatch]; for (int i = 0; i < 400; i++) { Bullet* bullet = [Bulletbullet]; bullet.visible =NO; [batch addChild:bullet]; } [selfschedule:@selector(countBullets:) interval:3]; } returnself; } -(void) dealloc { instanceOfGameScene = nil; //don't forget to call "super dealloc" [superdealloc]; } -(void) countBullets:(ccTime)delta { CCLOG(@"Number ofactive Bullets: %i", [self.bulletSpriteBatch.childrencount]); } -(CCSpriteBatchNode*) bulletSpriteBatch { CCNode* node = [selfgetChildByTag:GameSceneNodeTagBulletSpriteBatch]; NSAssert([node isKindOfClass:[CCSpriteBatchNodeclass]], @"not aCCSpriteBatchNode"); return (CCSpriteBatchNode*)node; } -(void) shootBulletFromShip:(Ship*)ship { CCArray* bullets = [self.bulletSpriteBatchchildren]; CCNode* node = [bullets objectAtIndex:nextInactiveBullet]; NSAssert([node isKindOfClass:[Bulletclass]], @"not abullet!"); Bullet* bullet = (Bullet*)node; [bullet shootBulletFromShip:ship]; nextInactiveBullet++; if (nextInactiveBullet >= [bullets count]) { nextInactiveBullet = 0; } } @end ~~~ 現在你應該看到了,在init方法中使用CCSpriteBatchNode加入了400顆子彈(被設置為不可見了)。然后在接下來的shootBulletFromShip方法(在ship的update方法中調用)中,依次調用每一顆子彈的shootBulletFromShip方法。 3、Bullet類 ~~~ #import <Foundation/Foundation.h> #import "cocos2d.h" #import "Ship.h" @interface Bullet : CCSprite { CGPointvelocity; floatoutsideScreen; } @property (readwrite, nonatomic) CGPoint velocity; +(id) bullet; -(void) shootBulletFromShip:(Ship*)ship; @end #import "Bullet.h" @interface Bullet(PrivateMethods) -(id) initWithBulletImage; @end @implementation Bullet @synthesize velocity; +(id) bullet { return [[[selfalloc] initWithBulletImage] autorelease]; } -(id) initWithBulletImage { if ((self = [superinitWithFile:@"bullet.png"])) { } returnself; } -(void) dealloc { [superdealloc]; } // Re-Uses the bullet -(void) shootBulletFromShip:(Ship*)ship { float spread = (CCRANDOM_0_1() - 0.5f) * 0.5f; velocity = CGPointMake(1, spread); outsideScreen = [[CCDirectorsharedDirector] winSize].width; self.position = CGPointMake(ship.position.x + ship.contentSize.width * 0.5f, ship.position.y); self.visible = YES; [selfscheduleUpdate]; } -(void) update:(ccTime)delta { self.position = ccpAdd(self.position, velocity); if (self.position.x > outsideScreen) { self.visible = NO; [selfunscheduleAllSelectors]; } } @end ~~~ shootBulletFromShip方法實現了子彈的射擊。Spread變量計算了一個擴散值,使從飛船中射出的子彈有1/2的機率會向上/下兩邊擴散。Velocity是一個每幀移動的位置偏移量。然后設置子彈的初始位置位于飛船右邊。在把子彈可視狀態設置為顯示后,調度運行update方法(每幀調用一次)。 在update方法中,讓子彈移動velocity的偏移量。這種方式,比CCMoveXX方法效率更高一些。而且這里用了一個技巧,當子彈飛出屏幕后,我們并沒有立即將Bullet對象清除(為了節省資源),而是子彈設置為不可視緩存起來,方便再次使用以提高程序性能。出于這個原因,我們在GameScene類中設計了一個nextInactiveBullet變量,以此來記錄已經使用掉(射出去)的子彈(設置為可視的子彈)。等所有子彈都射出去以后,nextInactiveBullet重置為0。 ## 三、增加角色動畫 以下代碼為ship對象增加角色動畫。Ship對象的角色動畫是5張連續的幀圖像,以表現飛船尾部不斷噴射并變化的火焰。 ~~~ -(id) initWithShipImage { if ((self = [superinitWithFile:@"ship.png"])) { // 把5張圖片裝入動畫幀數組 NSMutableArray* frames = [NSMutableArrayarrayWithCapacity:5]; for (int i = 0; i < 5; i++) { NSString* file = [NSStringstringWithFormat:@"ship-anim%i.png", i]; // 使用貼圖緩存構造2D貼圖 CCTexture2D* texture = [[CCTextureCachesharedTextureCache] addImage:file]; CGSize texSize = texture.contentSize; CGRect texRect = CGRectMake(0, 0, texSize.width, texSize.height); // 用2D貼圖構造動畫幀 CCSpriteFrame* frame = [CCSpriteFrameframeWithTexture:texture rect:texRect offset:CGPointZero]; // 把動畫幀放入數組 [frames addObject:frame]; } // 用動畫幀數組構造動畫對象,幀率:0.08秒/幀,標識名:move CCAnimation* anim = [CCAnimationanimationWithName:@"move"delay:0.08fframes:frames]; // 如果你把anim存儲到CCSprite,則可以通過名稱move來訪問CCAnimation //[self addAnimation:anim]; // 構造Action:無限重復 CCAnimate* animate = [CCAnimateactionWithAnimation:anim]; CCRepeatForever* repeat = [CCRepeatForeveractionWithAction:animate]; [selfrunAction:repeat]; [selfscheduleUpdate]; } returnself; } ~~~ 為求簡便,上面的代碼我們也可以封裝為一個新的類別Category。 1、類別AnimationHelper 利用OC中的Category,我們可以擴展CCAnimation類。Category提供了一種不需要修改類的源代碼即可為類增加新方法的途徑(有點象AOP?),但它不能增加新的成員變量。下面的代碼為CCAnimation增加了一個Category,名為Helper(新建Class,名為CCAnimationHelper.h): ~~~ #import <Foundation/Foundation.h> #import "cocos2d.h" @interface CCAnimation (Helper) +(CCAnimation*) animationWithFile:(NSString*)name frameCount:(int)frameCount delay:(float)delay; +(CCAnimation*) animationWithFrame:(NSString*)frame frameCount:(int)frameCount delay:(float)delay; @end #import "CCAnimationHelper.h" @implementation CCAnimation (Helper) // 通過圖片文件名創建CCAnimation對象. +(CCAnimation*) animationWithFile:(NSString*)name frameCount:(int)frameCount delay:(float)delay { //把前面的代碼移到這里來了 NSMutableArray* frames = [NSMutableArrayarrayWithCapacity:frameCount]; for (int i = 0; i < frameCount;i++) { NSString* file = [NSStringstringWithFormat:@"%@%i.png", name, i]; CCTexture2D* texture = [[CCTextureCachesharedTextureCache] addImage:file]; CGSize texSize = texture.contentSize; CGRect texRect = CGRectMake(0, 0, texSize.width, texSize.height); CCSpriteFrame* frame = [CCSpriteFrameframeWithTexture:texture rect:texRect offset:CGPointZero]; [framesaddObject:frame]; } return [CCAnimationanimationWithName:namedelay:delayframes:frames]; } // 通過sprite frames創建CCAnimation. +(CCAnimation*) animationWithFrame:(NSString*)frame frameCount:(int)frameCount delay:(float)delay { //未實現 returnnil; } @end ~~~ 現住,在Ship類的初始化方法里,可以通過CCAnimation的類別Helper這樣簡單地創建動畫對象了: ~~~ -(id) initWithShipImage { if ((self = [superinitWithFile:@"ship.png"])) { // 使用類別Helper來創建動畫對象 CCAnimation* anim = [CCAnimationanimationWithFile:@"ship-anim"frameCount:5delay:0.08f]; // 創建Action:無限循環播放動畫 CCAnimate* animate = [CCAnimateactionWithAnimation:anim]; CCRepeatForever* repeat = [CCRepeatForeveractionWithAction:animate]; [selfrunAction:repeat]; [selfscheduleUpdate]; } returnself; } ~~~ ## 四、 Texture Atlas 貼圖集(或譯作紋理集) 1、定義 貼圖集Texture Atlas僅僅是一張大的貼圖。通過使用CCSpriteBatchNode,你可以一次性渲染所有的圖片。使用TextureAtlas不但節約了內存也提升了性能。 貼圖的大小(寬和高)總是2的n次方——例如1024*128或256*512。由于這個規則,貼圖尺寸有時候是大于圖片的實際尺寸的。例如,圖片大小140*600,當加載到內存時,貼圖尺寸是256*1024。這顯然是一種內存浪費,尤其是你有幾個這樣的單獨的Texture時。 因此有了Texture Atlas的概念。它是一張包含了多個圖片的圖片,并且它的尺寸已經是對齊的。所謂對齊,即是根據前面提到的那個規則,指它的長和寬都已經是2的n次方。貼圖集每一個角色幀(spriteframe)都定義為貼圖集中的一部分(一個矩形區域)。這些角色幀的CGrect則定義在單獨的一個.plist文件里。這樣cocos2d就可以從一張大的貼圖集中單獨渲染某個指定的角色幀。 2、Zwoptex Zwoptex是一個2D貼圖工具,付費版需要$24.95。有一個7天的試用版,下載地址[http://zwoptexapp.com](http://zwoptexapp.com/)。但Zwoptex提供了一個flash版,沒有時間限制:[http://zwoptexapp.com/flashwersion](http://zwoptexapp.com/flashwersion),也基本夠用(僅僅有一些限制,比如2048*2048貼圖限制,角色旋轉等)。 如果你不想試用Zwoptex,那么有一個可以替換的工具是TexturePacker: [http://texturepacker.com/](http://texturepacker.com/) 這里以Zwoptex 0.3b7版本為例(這個是免費版)。打開Zwoptex,默認是一個叫做Untitled的空白畫布。選擇菜單:SpriteSheet ——>Import Sprites,會彈出文件選擇對話框,選擇你需要的角色幀圖片文件,點擊import,于是所有的圖片會導入到Zwoptex。 ? 選擇菜單:Sprite Sheet——>Settings,會彈出布局窗口: ![](https://box.kancloud.cn/2016-05-04_572a0b9c17d8f.gif) ? 你可以更改設置,包括畫布大小,排序規則、行間距、列間距等,目的是用最小的貼圖集容納全部所需的圖片。然后點擊save去應用。 注意,除非單獨為3GS、iPad和iPhone4開發,否則不要使用2048*2048的畫布尺寸,因為老的型號最大只支持1024*1024。 改變畫布大小時要當心,因為有時候圖片會疊在一起——由于空間不足。 建議不要手動更改圖片(如移動、旋轉),因為這個版本并不支持,它是自動布局的。 Zwoptex會自動截掉圖片中透明邊沿,所以本來一樣大小的圖片在Zwoptex中會顯得大小不一。 ![](https://box.kancloud.cn/2016-05-04_572a0b9c2d3a6.gif) ? 不用擔心,cocos2d會自動計算這些誤差并正確顯示(不用擔心,這些數據都記載在.plist里)。 點擊File——>save菜單,編輯結果保存為.zss文件格式(Zwoptex格式)。 點擊Sprite Sheet——>Export——>Texture,編輯結果保存為.png格式。 點擊Sprite Sheet——>Export——>Cordinates,編輯結果保存為.plist格式。 后兩者,正是cocos2d所需要的。 3、Cocos2d中使用貼圖集 首先,將Zwoptex生成的.png和.plist文件加入到項目的Resource組中。然后在代碼中使用貼圖集: ~~~ -(id) initWithShipImage { //用CCSpriteFrameCache加載貼圖集,用.plist文件而不是.png文件做參數 CCSpriteFrameCache* frameCache = [CCSpriteFrameCachesharedSpriteFrameCache]; [frameCache addSpriteFramesWithFile:@"ship-and-bullet.plist"]; ? //從貼圖集中加載名為ship.png的sprite,注意ship.png是.plist中定義的key,而不是真正意義的文件名 if ((self = [superinitWithSpriteFrameName:@"ship.png"])) { // 從貼圖集中加載sprite幀,注意用.plist中的key值做參數而非文件名 NSMutableArray* frames =[NSMutableArray arrayWithCapacity:5]; for (inti = 0; i < 5; i++) { NSString* file = [NSString stringWithFormat:@"ship-anim%i.png", i]; CCSpriteFrame* frame = [frameCache spriteFrameByName:file]; [frames addObject:frame]; } CCAnimation* anim = [CCAnimationanimationWithName:@"move"delay:0.08fframes:frames]; CCAnimate* animate = [CCAnimateactionWithAnimation:anim]; CCRepeatForever* repeat = [CCRepeatForeveractionWithAction:animate]; [selfrunAction:repeat]; [selfscheduleUpdate]; } returnself; } ~~~ [CCSpriteFrameCachesharedSpriteFrameCache]是一個單例對象,其 addSpriteFramesWithFile 方法用于加載貼圖集(需要以.plist文件名作為參數)。對于大文件貼圖集(超過512*512),加載過程可能會花費數秒,應當在游戲開始前就加載。 CCSprite的initWithSpriteFrameName方法可以從貼圖集中獲取貼圖,并設置Sprite的顯示圖片,但它需要以貼圖集(.plist)中的幀名(framename,實際上是<frames>中的一個<key>)為參數。 當然,如果要從貼圖集中得到一個幀,可以用CCSpriteFrameCache的spriteFrameByName方法。這同樣需要用貼圖集中的幀名為參數。 如果你加載了多個貼圖集,但只要名為ship.png的幀只有1個,那么cocos2d就可以找到正確貼圖的。 ? 其他代碼沒有任何改變。但出現了一個奇怪的現象:飛船的位置莫名其妙地向屏幕中心靠近了一點,盡管不是很明顯。這個問題很容易解決,之前Ship的初始化代碼是這樣的: ~~~ Ship* ship = [Shipship]; ship.position = CGPointMake(ship.texture.contentSize.width / 2, screenSize.height / 2); [selfaddChild:ship]; ? ~~~ 這個地方需要改變: ~~~ ship.position = CGPointMake(ship.contentSize.width / 2, screenSize.height / 2); ~~~ 問題解決了。導致這個現象的原因是,ship對象的texture的contentSize要比ship對象的contentSize大(Ship的Texture現在用的是貼圖集——具體說就是ship-and-bullet.png 這張圖,尺寸為256*256,而原來的ship.png才128*64)。 4、修改CCAnimation類別Helper 現在是實現類別Helper中的animationWithFrame方法的時候了: ~~~ +(CCAnimation*)animationWithFrame:(NSString*)frame frameCount:(int)frameCount delay:(float)delay { //構造一個frame數組 NSMutableArray* frames = [NSMutableArray arrayWithCapacity:frameCount]; //通過CCSpriteFrameCache從貼圖集中加載frame,并將frame加到數組中 for (inti = 0; i < frameCount; i++) { NSString* file = [NSStringstringWithFormat:@"%@%i.png", frame, i]; CCSpriteFrameCache* frameCache = [CCSpriteFrameCachesharedSpriteFrameCache]; CCSpriteFrame* frame = [frameCache spriteFrameByName:file]; [framesaddObject:frame]; } //用frame數組構建animation對象并返回 return [CCAnimationanimationWithName:frame delay:delayframes:frames]; } ~~~ 現在,可以修改ship類中initWithShipImage方法,把CCAnimation的初始化修改為: ~~~ CCAnimation* anim = [CCAnimationanimationWithFrame:@"ship-anim"frameCount:5delay:0.08f]; ~~~ ? 5、弱水三千,只取一瓢飲 只要你愿意,你可以把所有游戲圖片都加到一個貼圖集里。用3個1024*1024的貼圖集跟用20個更小的貼圖集效率更高。 對于程序員而言,應當把代碼“分離”成不同的邏輯組件。于此不同,對于貼圖集來說,我們的目標就是盡可能多地把圖片放到一個貼圖集里,盡可能降低內存空間的浪費。 用一個貼圖集放入玩家圖片,用另外的貼圖集放怪物A、B、C的圖片——這好像更符合邏輯些,但這僅僅有助于你有大量的圖片,而且每次你只是有選擇地加載一部分圖片的時候。 當你的圖片只需要3-4個1024*1024貼圖集的時候,你應當把所有圖片只放在這些貼圖集里進行預加載。這需要12-16MB的內存。程序代碼和其他資源如音頻則不會占用這么多內存,你可以把這些貼圖集都保留在內存,這樣哪怕只有128MBRAM的老IOS設備也可以承受。 如果超過這個內存,就應該采取特別的策略了。比如,可以把游戲圖片進行分類,并且只在當前地圖中加載必要的貼圖集。這可以減少地圖加載時的延時。 因為cocos2d會自動緩存所有圖片,需要一種卸載貼圖的機制。絕大部分情況下你可以使用cocos2d提供的: ~~~ [[CCSpriteFrameCachesharedSpriteFrameCache]removeUnusedSpriteFrames]; [[CCTextureCachesharedTextureCache] removeUnusedTextures]; ~~~ 顯然應當在某些貼圖不再使用的時候調用這些方法。比如轉場景完成后。游戲正在進行時就不行了。注意,僅僅在新場景初始化結束后,前面的場景才會被deallocated。意即在一個Scenen的初始化方法中你不能調用removeUnusedxxx方法——除非你在兩個scene轉換中使用了第5章的LoadingScene類,這樣你要擴展LoadingScene使它在加載新場景替時remove所有未使用的貼圖。 如果要絕對清除所有內存中的貼圖以加載新的貼圖,應當使用: ~~~ [CCSpriteFrameCachepurgeSharedSpriteFrameCache]; [CCTextureCachepurgeSharedTextureCache]; ~~~ 6、美工和藝術 圖片的編輯你可以使用免費軟件Searshore:[http://seashore.sourceforge.net](http://seashore.sourceforge.net/) 對于動畫和位圖,你可以使用Pixen:[http://opensword.org/Pixen](http://opensword.org/Pixen),這是一個簡單易用和強大的動畫編輯工具。 如果你想自己制作音樂音效,可以使用Mac自帶的GarageBand。插上一只麥克風開始記錄一些聲音,花幾分鐘編輯并在任何適當的地方使用這些聲音。如果不想自己制作聲效,也可以從網上搜索一些免費或便宜的音頻,例如[www.soundsnap.com](http://www.soundsnap.com/)或者類似網站。 警告:對于使用源自互聯網的音頻和圖片要尤其小心。你可以下載到一些“免費”的資源用于早期的游戲制作。但簡單的“免費”并不意味著你可以再分發這些文件,或者用于商業用途,例如在蘋果商店中銷售。除非獲得作者特別的授權或者這些資源附有一個正式的協議。 ? ? ?
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看