本教程基於子龍山人翻譯的cocos2d的IPHONE教程,用cocos2d-x for XNA引擎重寫,加上我一些加工製作。教程中大多數文字圖片都是原作者和翻譯作者子龍山人,還有不少是我自己的理解和加工。感謝原作者的教程和子龍山人的翻譯。本教程僅供學習交流之用,切勿進行商業傳播。
子龍山人翻譯的Iphone教程地址:http://www.cnblogs.com/andyque/articles/1997820.html
Iphone教程原文地址:http://www.raywenderlich.com/692/rotating-turrets
旋轉炮塔來改變射擊的方向。許多遊戲都有這個功能,
在這個教程中,將會詳細地講解如何?這個功能,即如何把旋轉炮塔的功能添加到一個遊戲當中去。
準備工作
如果你看完並實踐了上一個教程,你可以繼續使用那個工程。如果沒有的話,那麼下載這個連結的代碼吧。
接下來,下載新的
player sprite 和
projectile sprite圖片,然後把它們加到Content工程的images目錄裡面。並修改原來的精靈初始化代碼。
// In the init methodCCSprite player = CCSprite.spriteWithFile(@"images/player2");// In the ccTouchesEnded methodCCSprite projectile = CCSprite.spriteWithFile(@"images/Projectile2");
編譯並運行你的工程,如果一切順利的話,你將會看到一個炮塔正在發射子彈。然後,這並不是很好,因為炮塔在射擊的時候並沒有面朝那個方向。因此,接下來讓我們來解決這個問題。
旋轉並射擊
在我們旋轉炮塔之前,首先,我們需要儲存Player精靈的引用,以便後面旋轉它的時候使用。把Player精靈的聲明提到類當中去:
CCSprite player;
修改init中的代碼為:
player = CCSprite.spriteWithFile(@"images/player2");
好了,現在讓我們取出player對象的引用並且旋轉它吧!為了旋轉它,我們首先需要計算出旋轉的角度。為瞭解決這個問題,想想我們在高中時候學過的三角代數吧。還記得sin cos tan嗎?為了便於理解,下面使用一張圖來解釋一下:tan = 對面/鄰邊。
如上所示,我們想要旋轉的角度是arctangent(angle),即對offY/offX求arctangent運算。
然而,這裡還有兩件事情,我們需要放在心上。首先,當我們計算actangent(offY/offX)的時候,這個結果是弧度,但是cocos2d使用的卻是角度。還好,cocosd2d提供了一個非常方便的宏,可以使得角度和弧度之間方便轉化。
第二點,我們假定上面的圖中angle的偏轉是正20度,但是,cocos2d裡面順時針方向為正(而不是所示的逆時針為正)。讓我們看到下面這個圖:
因此,為了得到正確的方向,我們把運算結果乘以一個-1就可以了。比如,如果我們把上面那幅圖片裡的角度乘以-1的話,我們就得夠得到-20度,這個角度其實就是逆時針方向的20度。(感覺老外說話好囉嗦啊,聰明的讀者恐怕早就明白了吧!:)
好了,講得夠多了!讓我們來寫一點代碼吧。在ccTouchesEnded裡面加入以下代碼,添加位置在你的projectile runAction之前。
//Determine angle to face float angleRadians = (float)Math.Atan(offRealY / offRealX); float angleDegrees = MathHelper.ToDegrees(angleRadians); float cocosAngle = -1 * angleDegrees;player.rotation = cocosAngle;
編譯並運行工程,現在我們的炮塔在射擊的時候可以改變方向了。
旋轉之後再射擊
目前來說還不錯,但是有一點點怪。因為,這個炮塔好像突然一下跳到一個方向射擊,有點不夠流暢。我們可以解決這個問題,但是在這之前,我們需要重構一下代碼。
首先,開啟GamePlayLayer類,然後在你的類裡添加如下成員變數:
CCSprite nextProjectile = null;
然後,修改你的ccTouchesEnded方法,並且添加一個新的方法,叫做finishShoot,如下所示:
public override void ccTouchesEnded(List<CCTouch> touches, CCEvent event_) { if (nextProjectile != null) return; CCTouch touch = touches.FirstOrDefault(); CCPoint location = touch.locationInView(touch.view()); location = CCDirector.sharedDirector().convertToGL(location); //set up initial location of projectile CCSize winSize = CCDirector.sharedDirector().getWinSize(); //CCSprite projectile = CCSprite.spriteWithFile(@"images/Projectile"); nextProjectile = CCSprite.spriteWithFile(@"images/Projectile2"); nextProjectile.position = new CCPoint(20, winSize.height / 2); //Determine offset of location to projectile float offX = location.x - nextProjectile.position.x; float offY = location.y - nextProjectile.position.y; //Bail out if we are shooting or backwards if (offX <= 0) { return; } //Determine where we wish to shoot the projectile to float realX = winSize.width + nextProjectile.contentSize.width / 2; float ratio = offY / offX; float realY = realX * ratio + nextProjectile.position.y; CCPoint realDest = new CCPoint(realX, realY); //Determine the length of how far we're shooting float offRealX = realX - nextProjectile.position.x; float offRealY = realY - nextProjectile.position.y; float length = (float)Math.Sqrt(offRealX * offRealX + offRealY * offRealY); float velocity = 480 / 1;//480pixls/lsec float realMoveDuration = length / velocity; //Determine angle to face float angleRadians = (float)Math.Atan(offRealY / offRealX); float angleDegrees = MathHelper.ToDegrees(angleRadians); float cocosAngle = -1 * angleDegrees; float rotateSpeed = (float)(0.5 / Math.PI);//Would take 0.5 seconds to rotate 0.5 radians ,or half a circle float rotateDuration = Math.Abs(angleRadians * rotateSpeed); player.runAction(CCSequence.actions(CCRotateTo.actionWithDuration(rotateDuration,cocosAngle), CCCallFunc.actionWithTarget(this, finishShoot))); //Move projectile to actual endpoint nextProjectile.runAction(CCSequence.actions(CCMoveTo.actionWithDuration(realMoveDuration, realDest), CCCallFuncN.actionWithTarget(this,spriteMoveFinished))); nextProjectile.tag = 2; //SimpleAudioEngine.sharedEngine().playEffect(@"resource/pew-pew-lei"); } void finishShoot() { // Ok to add now , we are finished rotation this.addChild(nextProjectile); _projectiles.Add(nextProjectile); nextProjectile = null; }
看上去好像有許多代碼,但是,實際上我們改動的並不多--大部分只是做一些小小的重構。下面是我們所修改的內容的一個列表:
- 在函數開頭檢查nextProjectile的值是否為nil。這意味著我們當前的touch事件正發生在射擊過程之中。也就是說,炮塔已經發射出一個子彈了。 .
- 之前,我們使用一個projectile的局部變數,並把它加入到了當前的情境中。在這個版本中,我們增加了一個nextProjectile的成員變數,但是並沒有馬上加到當前情境中。因為後要還要使用。
- .定義炮塔旋轉的角度,半秒鐘旋轉半個圓。記住,一個圓有2 PI個弧度。 .
- 計算旋轉特定的角度需要多長時間,這裡是拿弧度乘以速度。 .
- 接下來,我們使用一個sequence action來旋轉我們的炮塔。最後,調用一個函數,把projectile加入到當前情境當中去。
好,大功告成!編譯並運行工程,現在炮塔可以旋轉,並且很流暢地射擊了!本次工程下載:http://dl.dbank.com/c0cvo9f1gc
繼續學習:用cocos2d-x做一個簡單的windows phone 7遊戲:更猛的怪獸和更多的關卡(三)