Sprite Kit Actions(一)

So far, you have learned how to move or rotate Sprite Kit nodes – a node being anything that appears onscreen – by manually setting their position and rotation over time.


截至目前,我們學會了如何通過不斷的手動調節來移動或者旋轉屏幕上的節點節點.


This do-it-yourself approach works and is quite powerful, but Sprite Kit provides an easier way to move sprites incrementally: actions.


這些通過DIY來實現效果的方式非常有效,也非常強大,但是SpriteKit提供了一種更簡單的實現方式:actions(動作,感覺還是保留原文更方便).


Actions are great because they allow you to do things like rotate, scale or change a sprite’s position over time with just one line of code! You can also chain actions together to create some neat movement combinations quite easily.


Actions非常強大,緊緊一句話!就可以讓你的sprite轉身,縮放,或者慢慢移動到某一個位置~還可以非常簡單的把一系列action連起來,讓你的sprite在屏幕上翩翩起舞~


Move action

移動


Right now your zombie’s life is a bit too carefree, so let’s add some action into this game by introducing some enemies to dodge – crazy cat ladies!


現在我們的殭屍似乎有點太悠然自得了,試試通過action來給他點東西去躲—瘋貓奶奶(圖片確實是個老奶奶…爲什麼咱就想不出萬磁王,魔形女這種名字呢…理科生的腦子傷不起啊~)


Open MyScene.m and create the start of a new method to spawn an enemy:


打開MyScene.m文件,創建一個生成敵人的方法:


- (void)spawnEnemy 
{
    SKSpriteNode *enemy = [SKSpriteNode spriteNodeWithImageNamed:@"enemy"]; 
    enemy.position = CGPointMake(self.size.width + enemy.size.width/2, self.size.height/2); 
    [self addChild:enemy];
}


Now, you’d like to move the enemy from the right of the screen to the left. If you were doing this manually, you might update the enemy’s position each frame according to a velocity.


現在我們需要把她從屏幕的右邊往左邊移動.如果要手動實現,我們需要根據速率來更新她每一幀的位置.


No need to trouble yourself with that this time! Simply add these two lines of code to the bottom of spawnEnemy:


但是這次就沒有這麼麻煩啦!只需要在spawnEnemy:方法最後添加兩行代碼:


SKAction *actionMove = [SKAction moveTo:CGPointMake(-enemy.size.width/2,enemy.position.y) duration:2.0]; 
[enemy runAction:actionMove];


To create an action in Sprite Kit, you call one of several static constructors on the SKAction class, such as the one you see here, moveTo:duration:. This particular constructor returns an action that moves a sprite to a specified position, over a specified duration (in seconds).


想要通過SpriteKit創建一個action,需要調用SKAction類的一個靜態方法,比如說moveTo:duration:.它返回了一個在一定持續時間(秒)內讓sprite移動到指定位置的簡單action.


Here you set up the action to move the enemy along the x-axis at whatever speed is necessary to take it from its current position to just off the left side of the screen in two seconds.


這樣就設定了一個讓老太太在輛秒內移到屏幕外側的action,至於什麼速度?管他呢...


Give it a try! For now, just call this method inside initWithSize:, right after calling [self addChild:_zombie]:


試一下,現在只需要在initWithSize:方法調用加剛纔的方法:


[self spawnEnemy];


Build and run, and you should see the crazy cat lady race across the screen:


運行以下,你可以看到老太太在屏幕上慢慢走過了~


Not bad for just two lines of code, eh? You could have even done it with a single line of code if you didn’t need to use the actionMove variable for anything else.


只需要兩行代碼,是不是感覺不錯?如果你不需要actionMove做其他的什麼操作的話,只需要一行代碼就夠了.


Here you saw an example of moveTo:duration:, but there are a few other move action variants:


這裏我們演示了moveTo:duration:方法的例子,其實還有很多其他的類似的方法:


moveToX:duration: and moveToY:duration: These allow you to specify a change in only the x or y position – the other is assumed to remain the same. You could have used moveToX:duration: in the example above, to save a bit of typing.


moveToX:duration:和moveToY:duration:這兩個方法允許你只指定x或者y軸的座標,未指定的軸座標將保持不變,上邊的例子就可以使用moveToX:duration.


moveByX:y:duration: The “move to” actions move the sprite to a particular point, but sometimes it’s convenient to move a sprite as an offset from its current position, wherever that may be. You could have used moveByX:y:duration for this example, passing -(self.size.width + enemy.size.width) for x and 0 for y.


moveByX:y:duration:剛纔”移動到”的動作讓sprite移動到一個確定的點,但是有些時候讓sprite根據當前的位置進行偏移會更加方便.向x傳遞-(self.size.width+enemy.size.width),給y一個0,就可以用這個方法替代上邊的方法了.


You’ll see this pattern of “[action] to” and “[action] by” variants for other action types as well. In general, you can use whichever of these is more convenient for you – but keep in mind that if either works, the “[action] by” actions are preferable because they are reversible. 


你會看到在其他的action中看到很多同樣的”to”和”by”模式,通常來講,哪個對你更方便就可以用哪個,但是需要注意的是如果兩個方法都可以用,推薦使用”by”,因爲他是可逆的.(恩...跟結紮手術差不多的道理…).


Sequence action

動作隊列


The real power of actions lies in how easily you can chain them together. For example, say you want the cat lady to move in a V – down toward the bottom of the screen, then up to the goal position.


actions真正的強大之處在於關聯他們的方式簡單到令人髮指!舉個例子,你希望老太太走一個V字形,移動到屏幕的底端,然後再往指定的位置走.


To do this, replace the lines that create and run the move action in spawnEnemy with the following:


只需要把spawnEnemy方法中創建移動action的代碼替換一下就搞定了:


// 1
SKAction *actionMidMove =
[SKAction moveTo:CGPointMake(self.size.width/2,
enemy.size.height/2)
duration:1.0];
// 2
SKAction *actionMove =
[SKAction moveTo:CGPointMake(-enemy.size.width/2,
enemy.position.y) duration:1.0];
// 3
SKAction *sequence =
[SKAction
sequence:@[actionMidMove, actionMove]];
// 4
[enemy runAction:sequence];


The sequence action is one of the most useful and commonly used actions – chaining actions together is just so powerful! You will be using the sequence action many times in this chapter and throughout the rest of this book.


動作序列是actions最有用,最常用的功能 — 把一些列動作串起來是如此的強大!本章和本書的剩餘部分都會大量的使用動作序列.


Wait for duration action

動作等待


The wait for duration action does exactly what you’d expect: makes the sprite wait for a period of time, during which the sprite does nothing.


就像你想的那樣,動作等待讓sprite就跟那呆一段時間.(矮人直升機的導彈…dota還是很有用的...)

“What’s the point of that?”, you may be wondering. Well, wait for duration actions only truly become interesting when combined with a sequence action.


你可能會想”這東西有什麼用?”.恩...只有當把它放到動作序列裏的時候纔會變的有趣.


For example, let’s make the cat lady briefly pause when she reaches the bottom of the V-shape. To do this, simply modify your list of actions in spawnEnemy:, like so (changes highlighted):


舉個例子,你希望讓老太太移動到屏幕底端的時候思考一下人生~只需要把spawnEnemy:方法中控制動作序列的方法稍作更改(這裏只貼出更改部分的代碼):

SKAction *wait = [SKAction waitForDuration:0.25];
SKAction *sequence = [SKAction sequence:@[actionMidMove, wait, actionMove]];


Run block and selector actions

block和選擇器調用動作


At times when you’re running a sequence of actions, you’ll want to run your own block of code at some point. For example, say you want to log out a message when the cat lady reaches the bottom of the V.


有些時候當你運行動作序列時需要在某一個時間點調用一些block.舉個例子,讓老太太移動到屏幕底端的時候闡述一下對人生的思考...


To do this, just modify your list of actions in spawnEnemy: like so (changes highlighted):


再次修改以下spawnEnemy:方法中的代碼(同上):


SKAction *logMessage = [SKAction runBlock:^{
    NSLog(@"Reached bottom!”);
}];
SKAction *sequence = [SKAction sequence: @[actionMidMove, logMessage, wait, actionMove]];


Build and run, and when the cat lady reaches the bottom of the V, you should see the following in the console:


編譯運行,這次當老太太思考的時候就會看到這麼一句話:


ZombieConga[9644:70b] Reached bottom!


Of course, you can do far more than just log a message here – since it’s an arbitrary code block, you can do anything you want!


當然,除了打印記錄信息,你能做的事多了去了~發揮一下野馬一樣的思維...


You should be aware of a few other actions related to running blocks of code:


可能你已經意識到了肯定還有其他根block調用相關的動作:


runBlock:queue: Allows you to run the block of code on an arbitrary dispatch queue instead of in the main Sprite Kit event loop. You can learn more about this in Chapter 25, “Performance.”


runBlock:queue:方法允許你通過多線程來調用block(該部分內容涉及到GCD的相關知識,此處不展開討論,有興趣的讀者可以到以下連接獲取相關資料…http:www.google.com.hk...  =.=||).

performSelector:onTarget: Allows you to run any method on a target object, rather than having a block of code inline. Whether you use this or the runBlock: action to execute code is a matter of personal preference – mostly. You’ll see an example where your choice actually matters in Chapter 16, “Saving and Loading Games.”


performSelecton:onTarget:允許你調用一個對象的方法來替代block.絕大多數情況下這兩種方法的選擇都是個人喜好.16章的時候我們會討論究竟什麼情況下才有明顯的區別.


Reversed actions

反轉動作


Let’s say you want to make the cat lady go back the way she came – after she moves in a V to the left, she should move in a V back to the right.


現在我們想讓這個老太太從哪來回哪去,當她移動到屏幕的左邊時原路返回(走的可能也是貓步…)


You can reverse certain actions in Sprite Kit simply by calling reversedAction on them. This results in a new action that is the opposite of the original action.


在SpriteKit中,你可以非常簡單的通過reversedAction方法來反向一個現有的動作序列.


Not all actions are reversible – for example, moveTo:duration: is not.


並非所有的動作都是可逆的,舉例來說,moveTo:duration:就不行.


Let’s try this out. Modify your list of actions in spawnEnemy:


試一下,把spawnEnemy方法修改成這個樣子:


SKAction *actionMidMove = [SKAction moveByX:-self.size.width/2-enemy.size.width/2 y:-self.size.height/2+enemy.size.height/2 duration:1.0];

SKAction *actionMove = [SKAction moveByX:-self.size.width/2-enemy.size.width/2 y:self.size.height/2+enemy.size.height/2 duration:1.0];

SKAction *wait = [SKAction waitForDuration:1.0]; 

SKAction *logMessage = [SKAction runBlock:^{
    NSLog(@"Reached bottom!"); 
}];

SKAction *reverseMid = [actionMidMove reversedAction];
SKAction *reverseMove = [actionMove reversedAction];

SKAction *sequence = [SKAction sequence:@[actionMidMove, logMessage, wait, actionMove, reverseMove, logMessage, wait, reverseMid]]; 

[enemy runAction:sequence];


One last thing about reversible actions: if an action is not reversible, then it will return the same action. And because sequence actions are also reversible, you can simplify the above code as follows. Remove the lines where you create the reversed actions and replace the sequence creation with the following lines:


關於反向動作還有最後一件事:如果一個action是不可逆的,那麼他會返回相同的action.另外,動作序列是可逆的,你可以把上面創建反向動作的代碼改寫成這樣:


SKAction *sequence = [SKAction sequence:@[actionMidMove, logMessage, wait, actionMove]];
sequence = [SKAction sequence:@[sequence,[sequence reversedAction]]];


Astute observers may have noticed that the first half of this action logs a message as soon as it reaches the bottom of the screen, but on the way back the message is not logged until after the sprite has waited at the bottom for one second.


細心的讀者可能發現了,這次動作序列的前半部分會在老太太剛剛到達屏幕底部的時候打印信息,但是回來的時候卻要等一秒纔會打印.


This is because the reversed sequence is the exact opposite of the original, unlike how you wrote the first version. Later in this chapter you’ll read about the group action, which you could use to fix this.


這是由於反向序列是對原序列的純粹反轉,並不向我們之前寫的那個版本一樣.本章稍後的內容會使用動作組來解決這一問題.


時間問題,今天先寫到這了,明天繼續~



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章