<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>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ? Learn IPhoneand iPad Cocos2d Game Delevopment》第7章(原文中有部分無關緊要的內容未進行翻譯)。 對于射擊類游戲,使用重力感應進行游戲控制是不可接受的,采用虛擬手柄將會更恰當。出于“不重新發明輪子”的原則,我們將采用開源庫SneakyInput。 控制玩家的飛船進行移動只是其中一件事情。我們還需要讓背景能夠滾動,以造成在某個方向上“前進”的感覺。為此必須自己實現背景滾動。由于CCParallaxNode的限制,它不能無限制地滾動卷軸式背景。 ## 一、高級平行視差滾動 在這個射擊游戲中,我們將使用ParallaxBackground節點。同時,我們將使用CCSpriteBatchNode以提高背景圖片的渲染速度。 1、創建背景層 下圖顯示了我用Seashore繪制背景層。 ? ![](https://box.kancloud.cn/2016-05-04_572a0b9f373c0.gif) ? 每個背景層位于Seashore的單獨的圖層中,每一層可以保存為單獨的文件,分別命名為bg0-bg6。 以這種方式創建背景層的原因在于:你既可以把各個層的背景放在一起,也可以分別把每一層存成單獨的文件。所有文件大小都是480*320,似乎有點浪費。但不需要把單獨把每個文件加到游戲里,只需要把它們融合在一個貼圖集里。由于Zwoptex會自動去除每個圖片的透明邊沿,它會把這些背景層緊緊地放到一起沒有絲毫空間的浪費。 把背景分層的原因不僅是便于把每一層放在不同的Z軸。嚴格講,bg5.png(位于最下端)和bg6.png(位于最上端)應該是相同的Z坐標,因為它們之間沒有交疊,所以我把他們存在分開的文件里。這樣Zwoptex會把兩者上下之間的空白空間截掉。 此外,把背景分層有利于提高幀率。iOS設備的填充率很低(每1幀能繪制的像素點數量)。由于不同圖片之間常存在交疊的部分,iOS設備每1幀經常需要在同1點上繪制多次。比如,最極端的情況,一張全屏圖片位于另一張全屏圖片之上。你明明只能看到最上面的圖片,但設備卻不得不兩張圖片都繪制出來。這種情況叫做overdraw(無效繪制)。把背景分層可以盡量地減少無效繪制。 2、修改背景的繪制 ~~~ #import <Foundation/Foundation.h> #import "cocos2d.h" @interface ParallaxBackground :CCNode { CCSpriteBatchNode* spriteBatch; intnumStripes; CCArray* speedFactors; // 速度系數數組 floatscrollSpeed; } @end ~~~ 我把CCSpriteBatchNode引用保存在成員變量里,因為它在后面會用得比較頻繁。采用成員變量訪問節點比通過getNodeByTag方式訪問要快一點,每1幀都會節約幾個時鐘周期(但保留幾百個成員變量就太夸張了)。 ? ~~~ #import "ParallaxBackground.h" @implementation ParallaxBackground -(id) init { if ((self = [superinit])) { CGSize screenSize = [[CCDirectorsharedDirector] winSize]; // ?把game_art.png加載到貼圖緩存 CCTexture2D* gameArtTexture = [[CCTextureCachesharedTextureCache] addImage:@"game-art.png"]; // 初始化CCSpriteBatchNodespritebatch spriteBatch = [CCSpriteBatchNodebatchNodeWithTexture:gameArtTexture]; [selfaddChild:spriteBatch]; numStripes = 7; // 從貼圖集中加載7張圖片并進行定位 for (int i = 0; i < numStripes; i++) { NSString* frameName = [NSStringstringWithFormat:@"bg%i.png", i]; CCSprite* sprite = [CCSpritespriteWithSpriteFrameName:frameName]; sprite.position = CGPointMake(screenSize.width / 2, screenSize.height / 2); [spriteBatchaddChild:sprite z:i tag:i]; } // 再加7個背景層, 將其翻轉并放到下一個屏幕位置的中心for (int i = 0; i < numStripes; i++) { NSString* frameName = [NSStringstringWithFormat:@"bg%i.png", i]; CCSprite* sprite = [CCSpritespriteWithSpriteFrameName:frameName]; //放到下一屏的中心 sprite.position = CGPointMake(screenSize.width / 2 + screenSize.width, screenSize.height / 2); //水平翻轉 sprite.flipX =YES; [spriteBatchaddChild:sprite z:i tag:i + numStripes]; } // 初始化速度系數數組,分別定義每一層的滾動速度speedFactors = [[CCArrayalloc] initWithCapacity:numStripes]; [speedFactorsaddObject:[NSNumbernumberWithFloat:0.3f]]; [speedFactorsaddObject:[NSNumbernumberWithFloat:0.5f]]; [speedFactorsaddObject:[NSNumbernumberWithFloat:0.5f]]; [speedFactorsaddObject:[NSNumbernumberWithFloat:0.8f]]; [speedFactorsaddObject:[NSNumbernumberWithFloat:0.8f]]; [speedFactorsaddObject:[NSNumbernumberWithFloat:1.2f]]; [speedFactorsaddObject:[NSNumbernumberWithFloat:1.2f]]; NSAssert([speedFactorscount] == numStripes, @"speedFactors count does notmatch numStripes!"); scrollSpeed = 1.0f; [selfscheduleUpdate]; } returnself; } -(void) dealloc { [speedFactorsrelease]; [superdealloc]; } -(void) update:(ccTime)delta { CCSprite* sprite; CCARRAY_FOREACH([spriteBatchchildren], sprite) { NSNumber* factor = [speedFactorsobjectAtIndex:sprite.zOrder]; CGPoint pos = sprite.position; pos.x -= scrollSpeed * [factor floatValue]; sprite.position = pos; } } @end ~~~ 在GameScene中,我們曾經加載了貼圖集game-art.plist: ~~~ CCSpriteFrameCache* frameCache = [CCSpriteFrameCachesharedSpriteFrameCache]; [frameCacheaddSpriteFramesWithFile:@"game-art.plist"]; ~~~ 因此,實際上game-art.png已經加載。當我們在init方法中再次加載game-art.png時(我們需要獲得一個CCTexture2D以構造CCSpriteBatchNode),實際上并不會再次加載game-art.png,CCTextureCache會從緩存中返回一個已經加載的CCTexture2D對象。我們沒有其他辦法,因為cocos2d沒有提供一個getTextureByName 的方法。 接下來,初始化了CCSpriteBatchNode對象,并從貼圖集中加載了7張背景圖。 在update方法中,每一層背景圖的x位置每播放一幀,就減去了一點(從右向左移動)。移動的距離由scrollSpeed*一個速度系數(speedFactors數組中相應的一個數值)來計算。 這樣,每1層的背景會有不同的速度系數,從而會以不同的速度移動: ![](https://box.kancloud.cn/2016-05-04_572a0b9f51467.gif) ? 由于各層移動速度不同,所以最終背景的右邊沿會呈現出不整齊的現象: ![](https://box.kancloud.cn/2016-05-04_572a0b9f7108d.gif) ? 3、無限滾動 在 ParallaxBackground 類的init方法中,我們再次添加了7張背景圖并進行水平翻轉。目的是讓每一層背景圖片的寬度在水平方向上延伸,翻轉的目的則是使拼在一起的時候兩張圖片的對接邊沿能夠對齊。 同時,把第2幅圖片緊挨著放在第1幅圖右邊,從而把兩張相同但互為鏡像的圖片拼接在一起。 這是第1幅圖的位置擺放: ~~~ sprite.position = CGPointMake(screenSize.width / 2, screenSize.height / 2); ? ~~~ 這是第2幅圖的位置擺放: ~~~ // 放到下一屏的中心 sprite.position = CGPointMake(screenSize.width / 2 + screenSize.width, screenSize.height / 2); ~~~ 通過比較很容易就得以看出二者的x坐標相差1個屏幕寬度:screenSize(這同時也是圖片寬度,我們的圖片是嚴格按照480*320的屏幕尺寸制作的)。 下面我們可以用另外一種方式來擺放圖片(更直觀),把相應的代碼修改為: 第1幅圖的擺放: ~~~ sprite.anchorPoint = CGPointMake(0, 0.5f); sprite.position = CGPointMake(0, screenSize.height / 2); ~~~ 第2幅圖的擺放: ~~~ sprite.anchorPoint = CGPointMake(0, 0.5f); sprite.position = CGPointMake(screenSize.width, screenSize.height / 2); ~~~ 我們改變了圖片的anchorPoint屬性。anchorPoint就是一個圖形對象“錨點”或“對齊點”,這個屬性對于靜止不動的對象是沒有意義的。但對于可以移動的對象來說,意味著位置移動的參考點。也就是說物體移動后錨點應該和目標點對齊(定點停車?)。如果命令一個物體移動到a點,真實的意思其實是把這個物體的錨點和a點對齊。錨點用一個CGPoint表示,不過這個CGPoint的x和y值都是0-1之間的小數值。一個物體的錨點,如果不改變它的話, 默認 是(0.5f, 0.5f)。這兩個浮點數所代表的含義是:該錨點位于物體寬度1/2和高度1/2的地方。即物體(圖形)的正中心: ![](https://box.kancloud.cn/2016-05-04_572a0b9f8eccc.gif) ? 而代碼sprite.anchorPoint = CGPointMake(0, 0.5f); 實際上是把圖片的錨點移到了圖片左中部的位置: ![](https://box.kancloud.cn/2016-05-04_572a0b9fa662e.gif) ? 這樣我們擺放第1張圖時候可以從橫坐標0開始擺,而不必要計算屏幕寬度。 而擺放第2張圖的時候直接從第2屏的起始位置(即1個屏幕寬度)開始擺。 接下來,我們可以修改update的代碼,讓兩幅圖交替移動以模擬出背景圖無限滾動的效果: ~~~ -(void) update:(ccTime)delta { CCSprite* sprite; CCARRAY_FOREACH([spriteBatchchildren], sprite) { NSNumber* factor = [speedFactorsobjectAtIndex:sprite.zOrder]; CGPoint pos = sprite.position; pos.x -= scrollSpeed * [factor floatValue]; // 當有一副圖移出屏幕左邊后,把它挪到屏幕右邊等待再次滾動—無限滾動 if (pos.x < -screenSize.width) { pos.x+= screenSize.width * 2 - 1; } sprite.position = pos; } } ~~~ 實際上,飛船是不動的,動的是背景,以此模擬出飛船在游戲世界中前進的效果。 ? 4、防止抖動 仔細觀察,你會發現畫面上有時會出現一條黑色的豎線。這是由于圖片之間拼接位置出現湊整的問題。幀與幀之間,由于小數點上的誤差,有時會出現1個像素寬度的縫隙。對于商業品質的游戲,應該解決這個小問題。 最簡單的辦法,讓圖片之間微微交疊1個像素。 在擺放第2幅圖時: ~~~ sprite.position = CGPointMake(screenSize.width-1, screenSize.height / 2); ~~~ 在update方法中: ~~~ // 當有一副圖移出屏幕左邊后,把它挪到屏幕右邊等待再次滾動—無限滾動 if (pos.x < -screenSize.width) { pos.x+= screenSize.width * 2 - 2; } sprite.position = pos; ~~~ 為什么是減2個像素?因為1個像素是上次拼接時“用掉”的(一開始我們在init的時候就拼接過一次)。而在update方法中,已經是第2次拼接了。1次拼接需要1個像素,兩次拼接自然要2個像素。 ? 5、重復貼圖 在這一章沒有其他值得注意的技巧了。你可以讓同一個貼圖在任意一個空間里重復。只要這個空間夠大,你能讓這個貼圖沒完沒了地重復。至少成千上萬像素或成打的屏幕上能夠用一張貼圖貼滿,而不會給性能和內存帶來不良影響。 這個技巧就是使用OpenGL 的GL_REPEAT參數。只不過,要重復的對象只能是邊長為2的n次方的正方形。如32*32,128*128。 ~~~ CGRect repeatRect = CGRectMake(-5000, -5000, 5000,5000); CCSprite* sprite = [CCSpritespriteWithFile:@”square.png” rect:repeatRect]; ccTexParams params ={ GL_LINEAR, GL_LINEAR, GL_REPEAT, ?GL_REPEAT }; [sprite.texture setTexParameters:&params]; ~~~ 這里,CCSprite必須用一個CGRect構造,這個CGRect描述了要重復貼圖的矩形范圍。ccTexParams參數是一個GL_REPEAT結構,這個參數用于CCTexture2D的setTexParameters方法。 這將使整個指定的矩形區域被square.png圖片鋪滿(橫向平鋪,縱向平鋪)。當你移動CCSprite時,整個貼圖局域也被移動。你可以用這個技巧把最底層的背景刪除,然后用一張簡單的小圖片替代。 ## 二、虛擬手柄 由于iOS設備沒有按鈕(除了Home鍵),虛擬手柄(或D-pads)在游戲中就顯得很有用。 1、SneakyInput介紹 SneakyInput的作者是Nick Pannuto,示例代碼由CJ Hanson提供。這是一個免費的開源項目,它接受自愿捐助:[http://pledgie.com/campaigns/9124](http://pledgie.com/campaigns/9124) 該項目源碼托管于github庫: [http://github.com/sneakyness/SneakyInput](http://github.com/sneakyness/SneakyInput). 源碼下載后,解包,打開該項目,編譯運行。你可以在模擬器中看到一個虛擬手柄。 SneakyInput中集成了cocos2d,但可能不是最新版本。如果出現”base SDKmissing”錯誤,你可以修改Info面板中的base SDK。 2、集成SneakyInput 對于源代碼項目,有這樣一個問題:當我們需要和其他項目集成時,哪些文件是必須的?每個源碼項目都不一樣,答案也不盡相同。 但我會告訴你SneakyInput的哪些文件是必須的,包括5個核心的類: SneakyButton 和 SneakyButtonSkinnedBase SneakyJoystick 和 SneakyJoystickSkinnedBase ColoredCircleSprite(可選的) 其他文件不是必須的,但可作為一些參考。 使用Add Existing Files對話框加入上述5個類(5個.m文件,5個.h文件)。 ? 3、射擊按鈕 首先,我們需要在GameScene的scene方法中加入一個InputLayer(繼承自CCLayer): InputLayer* inputLayer = [InputLayernode]; [scene addChild:inputLayer z:1tag:GameSceneLayerTagInput]; 在枚舉GameSceneLayerTags中添加GameSceneLayerTagInput定義,用于InputLayer層的tag: ~~~ typedefenum { GameSceneLayerTagGame = 1, GameSceneLayerTagInput, } GameSceneLayerTags; ~~~ ? 然后新建類InputLayer: ~~~ #import <Foundation/Foundation.h> #import "cocos2d.h" // SneakyInputheaders #import "ColoredCircleSprite.h" #import "SneakyButton.h" #import "SneakyButtonSkinnedBase.h" #import "SneakyJoystick.h" #import "SneakyJoystickSkinnedBase.h" #import "SneakyExtensions.h" @interface InputLayer : CCLayer { SneakyButton* fireButton; } @end #import "InputLayer.h" #import "GameScene.h" @interface InputLayer(PrivateMethods) -(void) addFireButton; @end @implementation InputLayer -(id) init { if ((self = [superinit])) { [selfaddFireButton]; [selfscheduleUpdate]; } returnself; } -(void) dealloc { [superdealloc]; } -(void) addFireButton { float buttonRadius = 80; CGSize screenSize = [[CCDirector sharedDirector] winSize]; fireButton = [[[SneakyButton alloc] initWithRect:CGRectZero]autorelease]; fireButton.radius = buttonRadius; ?fireButton.position =CGPointMake(screenSize.width - buttonRadius,buttonRadius); [self addChild:fireButton]; } -(void) update:(ccTime)delta { if(fireButton.active) { CCLOG(@"FIRE!!!"); } } @end ~~~ 在頭文件中,我們定義了一個Sneakbutton成員變量。然后我們通過addFireButton方法創建發射按鈕。 因為SneakyButton的initWithRect方法的CGRect參數其實并沒有用到,所以我們可以簡單地傳遞一個CGRectZero給它。實際上SneakyButton使用radius屬性代表觸摸所能響應的圓形半徑,我們通過簡單計算(屏幕寬度-按鈕半徑)把射擊按鈕緊湊地放到屏幕的右下角。 接下來,[self shceduleUpdate]調用了update方法。 在update方法里,我簡單地在Log里輸出一句話,以代替射擊動作。 ? 4、訂制按鈕外觀 我用了一個特殊的類別(Category),為SneakyButton增加了一個兩個特殊的靜態初始化方法,以防止你忘記alloc或者autorelease對象。如SneakyExtensions.h和SneakyExtensions.m所示: ~~~ #import "ColoredCircleSprite.h" #import "SneakyButton.h" #import "SneakyButtonSkinnedBase.h" #import "SneakyJoystick.h" #import "SneakyJoystickSkinnedBase.h" @interface SneakyButton(Extension) +(id) button; +(id) buttonWithRect:(CGRect)rect; @end interfaceSneakyButtonSkinnedBase (Extension) +(id) skinnedButton; @end #import "SneakyExtensions.h" @implementation SneakyButton(Extension) +(id) button { return [[[SneakyButtonalloc] initWithRect:CGRectZero] autorelease]; } +(id) buttonWithRect:(CGRect)rect { return [[[SneakyButtonalloc] initWithRect:rect] autorelease]; } @end @implementation SneakyButtonSkinnedBase(Extension) +(id) skinnedButton { return [[[SneakyButtonSkinnedBasealloc] init] autorelease]; } @end ~~~ 我導入了所有.h文件,因為在這個類別中,我打算對每個SneakyInput都進行擴展。 用于SneakyButton的initWithRect方法的CGRect參數其實并沒有用到,所以我們可以用button方法來替代SneakyButton的初始化方法: ~~~ fireButton=[SneakyButton button]; ~~~ 現在開始訂制SneakyButton的外觀。首先制作4張100*100大小的圖片,分別表示按鈕的4個狀態:默認,按下,激活,失效。默認狀態即按鈕未被按下時的外觀,于此相反的是按下狀態。激活狀態僅發生在切換按鈕的時候,此時按鈕被激活,或獲得焦點。失效狀態表示按鈕此時是無效的。例如,當武器過熱時,你會有幾秒鐘無法射擊,此時應該讓按鈕失效并讓按鈕顯示失效狀態的圖片。當然,在這里,我們僅需要使用默認圖片和按下圖片。 修改InputLayer的addFireButton 方法為: ~~~ -(void) addFireButton { float buttonRadius = 50; CGSize screenSize = [[CCDirectorsharedDirector] winSize]; ? fireButton = [SneakyButtonbutton]; fireButton.isHoldable = YES; SneakyButtonSkinnedBase* skinFireButton = [SneakyButtonSkinnedBaseskinnedButton]; skinFireButton.position = CGPointMake(screenSize.width - buttonRadius, buttonRadius); skinFireButton.defaultSprite = [CCSpritespriteWithSpriteFrameName:@"button-default.png"]; skinFireButton.pressSprite = [CCSpritespriteWithSpriteFrameName:@"button-pressed.png"]; skinFireButton.button = fireButton; [selfaddChild:skinFireButton]; } ? ~~~ 這里設置了isHoldable屬性,這意味著當你按下按鈕不放時會導致子彈不停地發射。現在,不需要設置radius屬性,因為接下來的SneakyButtonSkinnedBase中的圖片的大小就決定了radius的值。SneakyButtonSkinedBase的靜態初始化方法skinnedButton是我們在Extension這個類別中定義過的。 現在,我們用SneakyButtonSkinnedBase替代了SneakyButton,用設置SneakyButtonSkinnedBase的位置替代了設置SneakyButton的位置。并且設置了SneakyButtonSkinnedBase的狀態圖片。 注意最后兩句代碼, SneakyButtonSkinnedBase的button屬性持有了SneakyButton對象引用,這樣fireButton對象隱式地被加到了InputLayer。 ? update方法也修改了,這次調用了GameScene的射擊方法: ~~~ -(void) update:(ccTime)delta { totalTime += delta; if (fireButton.active && totalTime > nextShotTime) { nextShotTime = totalTime + 0.5f; GameScene* game = [GameScenesharedGameScene]; [game shootBulletFromShip:[game defaultShip]]; } //Allow faster shooting by quickly tapping the fire button. if (fireButton.active == NO) { nextShotTime = 0; } } ~~~ 變量totalTime和nextShortTime限制了子彈射擊的速度為2發/秒。如果發射按鈕的active狀態為NO(意味著它未被按下),nextshortTime變量被設置為0,從而保證你下一次按下發射鍵時,子彈不再判斷時間,直接發射。快速點擊發射鍵導致子彈的射速會更快(超過連續發射)。 ? 5、動作控制 我們需要使用SneakyJoystick來生成一個虛擬搖桿。首先,增加一個SneakyJoystick成員變量:SneakyJoystick* joystick; 增加一個addJoystick方法,這次我們直接使用了SneakyJoystickSkinnedBase,以定制其外觀,: ~~~ -(void) addJoystick { float stickRadius = 50; ? joystick = [SneakyJoystickjoystickWithRect:CGRectMake(0, 0, stickRadius,stickRadius)]; joystick.autoCenter = YES; joystick.hasDeadzone = YES; joystick.deadRadius = 10; SneakyJoystickSkinnedBase* skinStick = [SneakyJoystickSkinnedBaseskinnedJoystick]; skinStick.position = CGPointMake(stickRadius * 1.5f, stickRadius * 1.5f); skinStick.backgroundSprite = [CCSpritespriteWithSpriteFrameName:@"button-disabled.png"]; skinStick.backgroundSprite.color = ccMAGENTA; skinStick.thumbSprite = [CCSpritespriteWithSpriteFrameName:@"button-disabled.png"]; skinStick.thumbSprite.scale = 0.5f; skinStick.joystick = joystick; [selfaddChild:skinStick]; } ~~~ ? 同樣的,我們在extension類別中為SneakyJoystickSkinnedBase增加了新的靜態方法skinnedJoystick: ~~~ @implementationSneakyJoystickSkinnedBase (Extension) +(id) skinnedJoystick { return [[[SneakyJoystickSkinnedBasealloc] init] autorelease]; } @end ~~~ SneakyJoystick的初始化方法需要一個CGRect參數,與SneakyButton不同,這里CGRect的確能決定搖桿的半徑。autoCenter設置為YES可以使搖桿自動回到中心位置。hasDeadZone和deadRadius屬性決定了你能移動的最小半徑,在此范圍內的移動視作無效。如果hasDeadZone=NO,你幾乎不可能讓搖桿穩定保持在中心位置。 搖桿與屏幕邊緣稍微空出了一些距離,對于游戲而言搖桿的位置和尺寸不是最恰當的,但用來演示足夠了。 如果搖桿過于靠近屏幕邊緣,手指很容易移出屏幕從而失去對飛船的控制。 我決定讓搖桿使用button-disabled.png作為背景圖,同時搖桿大小縮放為原來的一半。這里backgroundSprite和thumbSprite使用的圖片都是同一張。二者的區別是: 搖桿的手柄(thumbSprite)半徑僅為按鈕背景(backgroundSprite)半徑的一半。button-disabled.png圖片是一個灰色的圓形按鈕。這樣的將導致虛擬搖桿由兩個灰色的正圓構成,在一個圓形的中心還有一個一半大小的小圓。 而且,把背景圖選取為灰色圖片是特意的。因為backgroundSprite的color屬性被設置為品紅,于是backgroundSprite的灰色圖片被著色為品紅了!通過把color屬性設置為不同的顏色:紅色、綠色、黃色,你可以輕易地為backgroundSprite染上不同的顏色! 當然,控制飛船移動的代碼是在update方法中: ? ~~~ GameScene* game = [GameScenesharedGameScene]; Ship* ship = [game defaultShip]; CGPoint velocity = ccpMult(joystick.velocity, 200); if (velocity.x != 0 && velocity.y != 0) { ship.position = CGPointMake(ship.position.x + velocity.x * delta, ship.position.y + velocity.y * delta); } ~~~ 我們在GameScene中增加了defaultShip方法,以便在這里訪問ship對象。搖桿的velocity屬性用于改變飛船的位置,但需要根據比例放大,這使得搖柄能在控制上能夠有一個放大效果。放大比例是一個經驗值,在游戲中感覺可以就行了。 萬一出現update方法調用不規律的情況,為確保飛船平滑移動的效果,我們必須利用update方法的delta參數。Delta參數傳遞了從上次update調用以來到本次調用之間的時間值。另外,飛船可能被移出屏幕區域外——你肯定不希望這樣。你可能想把代碼直接加在InputLayer中ship位置被改變的地方。這會有一個問題:你是為了防止搖柄把飛船移到屏幕外?還是為了讓飛船根本就沒有移到屏幕外的能力?無疑,后者更為優雅——這樣,你就要覆蓋Ship類的setPosition方法了: ~~~ -(void) setPosition:(CGPoint)pos { CGSize screenSize = [[CCDirectorsharedDirector] winSize]; float halfWidth = contentSize_.width * 0.5f; float halfHeight = contentSize_.height * 0.5f; ? //防止飛船移出屏幕 if (pos.x < halfWidth) { pos.x = halfWidth; } elseif (pos.x > (screenSize.width - halfWidth)) { pos.x = screenSize.width - halfWidth; } if (pos.y < halfHeight) { pos.y = halfHeight; } elseif (pos.y > (screenSize.height - halfHeight)) { pos.y = screenSize.height - halfHeight; } //一定要調用父類的同名方法 [supersetPosition:pos]; } ~~~ ? 每當飛船的位置發生改變,上面的代碼會對飛船的位置進行一個邊界檢測。如果飛船x,y坐標移出了屏幕外,它將被保持在屏幕邊沿以內。 由于position是屬性,下面語句會調用setPosition方法: ~~~ ship.position=CGPointMake(200,100); ~~~ 點語法比發送getter/setter消息更簡短,當然我們也可以用發送消息的語法: ~~~ [shipsetPosition:CGPointMake(200,100)]; ~~~ 通過這種方法,你可以重寫其他基類的方法,以改變游戲對象的行為。例如,如果要限定一個對象只能旋轉0-180度,你可以重寫setRotation(float)rotation方法在其中加入限制旋轉的代碼。 ? 6、數字控制 如果你的游戲不適合采用模擬控制,你可以把SneakyJoystick類轉換成數字控制,即 D-pad。這需要改動的代碼很少: ~~~ joystick=[SneakyJoystick joystickWithRect:CGRectMake(0,0,stickRadius,stickRadius)]; joystick.autoCenter=YES; // 減少控制方向為8方向 joystick.isDPad=YES; joystick.numberOfDirections=8; ~~~ ? dead zone屬性被刪除了——在數字控制中他們不再需要了。isDPad屬性設置為YES,表明采用數字控制。同時你可以定義方向數。如果你想讓D-pads在上下左右4個方向的同時,增加斜角方向(同時按下兩個方向將使角色沿斜角移動),你只需要把numberOfDirections設置為8。SneakyJoystick自動把模擬控制的方向轉換成8個方向。當然,如果你把方向數設置成6,你會得到一些怪異的結果。 ? 7、GP Joystick SneakyInput不是僅有的解決方案。還有GP Joystick,一個付費的商業產品,不過費用很低: [http://wrensation.com/?p=36](http://wrensation.com/?p=36) ? 如果你想知道GPJoystick和SneakyInput有什么區別,你可以觀看GP Joystick的YouTube視頻: [http://www.youtube.com/user/SDKTutor](http://www.youtube.com/user/SDKTutor) 在這里也提供了幾個cocos2d的視頻教程。 ? ## 三、結論 這章你學習了背景平行視差滾動效果:背景無限循環滾動(去除抖動),如何將背景拆分成不同的圖層以便Zwoptex能去掉透明區域,同時讓這些圖片保持正確的位置。 ? 接下來是指定屏幕分辨率。假設你想創建一個iPad的版本,除了必需創建1024*768的圖片外,你可以使用相同技術。這個工作你可以自己嘗試一下。 后半章介紹了SneakyInput,一個開源項目,可以在cocos2d游戲中加入虛擬搖桿和按鈕。它并不是最好的,但對大多數游戲來說已將足夠,無論如何,總勝過你自己去寫虛擬搖桿的代碼。 現在,飛船已經能控制了并且不再能飛出屏幕邊緣了。通過按下發射按鈕,它也能進行射擊了。但這個游戲仍然還有許多東西要做。如果沒有什么東西給你射擊,那么射擊游戲就不能成為射擊游戲了。下一章繼續。 ? ? ? ? ?
                  <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>

                              哎呀哎呀视频在线观看