{"id":9367,"date":"2012-07-31T10:19:19","date_gmt":"2012-07-31T08:19:19","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=9367"},"modified":"2012-07-31T10:19:19","modified_gmt":"2012-07-31T08:19:19","slug":"5-le-action-in-cocos2d","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/5-le-action-in-cocos2d\/","title":{"rendered":"5. Le action in Cocos2D"},"content":{"rendered":"<p><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/corso-programmazione-videogame-cocos2d-05.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/corso-programmazione-videogame-cocos2d-05.jpg\" alt=\"corso-programmazione-videogame-cocos2d-05\" title=\"corso-programmazione-videogame-cocos2d-05\" width=\"200\" height=\"100\" class=\"alignleft size-full wp-image-9369\" \/><\/a> In questa nuova lezione del nostro <strong>corso di programmazione videogame in cocos2d<\/strong> approfondiremo un concetto che abbiamo gi\u00e0 incontrato nelle precedenti lezioni ma che, vista l&#8217;importanza che riveste, necessita di una lezione tutta sua: le action!<\/p>\n<p>Le action pemettono di far compiere delle azioni ai CCNode presenti nella nostra applicazione, come rotazioni, ridimensionamenti a molto altro. \u00c8 interessante notare che le action si applicano ai CCNode attraverso il metodo runAction: quindi possiamo indifferentemente applicare un&#8217;azione ad una sprite, ad un layer o ad un&#8217;intera scena!<!--more--><\/p>\n<h4>La gerarchia delle classi<\/h4>\n<p>Per capire bene come gestire ed eseguire le action \u00e8 necessario capire quali sono le classi interessate e la loro gerarchia.<\/p>\n<p>La radice di tutto \u00e8 la classe CCAction, che mappa il concetto generico di action. CCACtion, infatti, presenta pochissime propriet\u00e0, in quanto viene specializzata da un gran numero di sottoclassi e saranno proprio queste classi che andremo ad utilizzare.<br \/>\nIn questo grafico vediamo il primo livello di gerarchia, con la classe CCAction e le prime sottoclassi.<\/p>\n<p><center><br \/>\n<a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/ccaction-corso-cocos2d-01.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/ccaction-corso-cocos2d-01.png\" alt=\"ccaction-corso-cocos2d-01\" title=\"ccaction-corso-cocos2d-01\" width=\"411\" height=\"140\" class=\"aligncenter size-full wp-image-9373\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/ccaction-corso-cocos2d-01.png 411w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/ccaction-corso-cocos2d-01-300x102.png 300w\" sizes=\"auto, (max-width: 411px) 100vw, 411px\" \/><\/a><br \/>\n<\/center><\/p>\n<p>Il grafo completo pu\u00f2 essere visualizzato a <a href=\"http:\/\/www.cocos2d-iphone.org\/api-ref\/1.0.0\/interface_c_c_action.html\" target=\"_blank\">questo indirizzo<\/a>.<\/p>\n<p>Quasi tutte le azioni che andremo ad utilizzare saranno subClass di <strong>CCFiniteTimeAction<\/strong>, che mappa il concetto di un&#8217;azione e che ha una durata finita nel tempo, come ad esempio uno spostamento oppure una rotazione. In particolare CCFiniteTimeAction ha due sottoclassi, la prima \u00e8 <strong>CCActionInterval<\/strong>, mentre la seconda \u00e8 <strong>CCActionInstant<\/strong> e dai loro nomi \u00e8 chiaro che si riferiscono alle azioni che durano un certo intervallo di tempo e le animazioni che sono invece instantanee.<\/p>\n<p>Le rimanenti tre classi (CCFollow, CCRepeatForever, CCSpeed) sono invece delle azioni un p\u00f2 particolari, e le vedremo pi\u00f9 avanti con degli esempi.<\/p>\n<h4>Prepariamo il progetto per gli esempi<\/h4>\n<p>Per provare alcune azioni ci servir\u00e0 un progetto cocos2d dove fare un p\u00f2 di esperimenti. Questo non dovrebbe pi\u00f9 presentare problemi, se avete qualche dubbio tornate a leggere il capilo due di questo corso.<\/p>\n<p>Io ho creato il mio progetto cocos2d e l&#8217;ho chiamato &#8220;learningCocosAction&#8221;.<\/p>\n<p>Ho deciso in questo caso di rimuovere completamente la classe HelloWorldLayer per crearne una mia da zero, ho quindi rimosso i due file HelloWordLayer.h e .m ed ho cancellato la riga<br \/>\n#import &#8220;HelloWorldLayer.h&#8221; dal file AppDelegate.m.<\/p>\n<p>A questo punto ho creato un nuovo file e dalla finestra che permette di scegliere il tipo di file ho scelto &#8220;cocos -> CCNode class&#8221;. Saremmo potuti tranquillamente partire anche con un NSObject, infatti partendo da questo template non \u00e8 che ci sia molto in pi\u00f9, ci risparmiamo giusto l&#8217;import della libreria di cocos.<\/p>\n<p>Ho chiamato il file BasicActionLayer  e l&#8217;ho creato come subclass di CCNode.<\/p>\n<p>Ora come abbiamo gi\u00e0 visto nel progetto &#8220;Hello world&#8221; \u00e8 consuetudine inserire all&#8217;interno del layer un metodo di istanza che restituisca una CCScene gi\u00e0 allocata e con il CCLayer gi\u00e0 inserito. Personalmente trovo questa soluzione molto comoda, ma forse un p\u00f2 anomala dal punto di vista logico, preseguiamo comunque con questa consuetudine e aggiungiamo questo codice nel file BasicActionLayer.m:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n+(CCScene *)scene {\r\n    CCScene *scene = [CCScene node];\r\n    BasicActionLayer *layer = [BasicActionLayer node];\r\n    [scene addChild:layer];\r\n    return scene;\r\n}\r\n<\/pre>\n<p>Non dimentichiamoci di dichiarare il metodo anche nel file BasicActionLayer.h.<\/p>\n<p>Fatto questo possiamo tornare al file appDelegate.m e importare la nostra nuova classe inserendo:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n#import \"BasicActionLayer.h\"\r\n<\/pre>\n<p>e sostituendo l&#8217;ultima riga del metodo applicationDidFinishLaunching: con questa:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n[[CCDirector sharedDirector] runWithScene: [BasicActionLayer scene]];\r\n<\/pre>\n<p>Se provate ad eseguire l&#8217;applicazione a questo punto, dovreste vedere solamente lo schermo nero e il contatore del framerate, questo perch\u00e9 nella classe BasicActionLayer non abbiamo ancora inserito nulla.<\/p>\n<p>Provvediamo quindi immediatamente a caricare una semplice sprite a cui assegneremo delle action.<br \/>\nImportiamo la seguente immagine nel nostro progetto e modifichiamo il metodo init del BasicActionLayer in modo che crei la sprite dall&#8217;immagine:<\/p>\n<p><center><br \/>\n<a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/ccaction-corso-cocos2d-02.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/ccaction-corso-cocos2d-02.png\" alt=\"ccaction-corso-cocos2d-02\" title=\"ccaction-corso-cocos2d-02\" width=\"85\" height=\"64\" class=\"aligncenter size-full wp-image-9374\" \/><\/a><br \/>\n<\/center><\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n-(id) init\r\n{\r\n        if( (self=[super init])) {\r\n                        CCSprite *alien = [CCSprite spriteWithFile:@\"alien01.png\"];\r\n                CGSize size = [[CCDirector sharedDirector] winSize];\r\n                alien.position =  ccp( size.width \/2 , size.height\/2 );\r\n                [self addChild: alien];\r\n        }\r\n        return self;\r\n}\r\n<\/pre>\n<p>Questo codice dovrebbe esservi gi\u00e0 familiare: abbiamo creato una sprite da file e l&#8217;abbiamo posizionata al centro dello schermo, il risultato sar\u00e0 questo:<\/p>\n<p><center><br \/>\n<a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/ccaction-corso-cocos2d-03.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/ccaction-corso-cocos2d-03.png\" alt=\"ccaction-corso-cocos2d-03\" title=\"ccaction-corso-cocos2d-03\" width=\"550\" height=\"293\" class=\"aligncenter size-full wp-image-9375\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/ccaction-corso-cocos2d-03.png 744w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/ccaction-corso-cocos2d-03-300x159.png 300w\" sizes=\"auto, (max-width: 550px) 100vw, 550px\" \/><\/a><br \/>\n<\/center><\/p>\n<h4>Proviamo le action<\/h4>\n<p>Non ci resta adesso che provare alcune action, guardiamo la lista di tutte le animazioni presenti <a href=\"http:\/\/www.cocos2d-iphone.org\/api-ref\/1.0.0\/interface_c_c_action.html\" target=\"_blank\">alla pagina<\/a> che ho linkato poco fa e decidiamo di provare una tra quelle che useremo pi\u00f9 spesso: <strong>CCMoveTo<\/strong>.<\/p>\n<p>Dalla documentazione possiamo vedere che ha un metodo di istanza <strong>actionWithDuration:position:<\/strong> e questo ci \u00e8 sufficiente per creare la nostra action, modifichiamo quindi il metodo init aggiungendo semplicemente queste due righe di codice:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n-(id) init\r\n{\r\nif( (self=[super init])) {\r\nCCSprite *alien = [CCSprite spriteWithFile:@\"alien01.png\"];\r\nCGSize size = [[CCDirector sharedDirector] winSize];\r\nalien.position =  ccp( size.width \/2 , size.height\/2 );\r\n[alien setTag:100];\r\n        [self addChild: alien];\r\n       \r\nCCMoveTo *moveAction = [CCMoveTo actionWithDuration:2 position:ccp(0, 0 )];\r\n[alien runAction:moveAction];\r\n        }\r\n        return self;\r\n}\r\n<\/pre>\n<p>Il codice \u00e8 molto semplice, abbiamo creato una istanza della classe CCMoveTo specificando la durata in secondi e la posizione da raggiungere, provate ad eseguire il progetto e vedrete la sprite che si porter\u00e0 a velocit\u00e0 costante verso l&#8217;angolo in basso a sinistra dello schermo:<\/p>\n<p><center><br \/>\n<a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/ccaction-corso-cocos2d-04.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/ccaction-corso-cocos2d-04.png\" alt=\"ccaction-corso-cocos2d-04\" title=\"ccaction-corso-cocos2d-04\" width=\"550\" height=\"293\" class=\"aligncenter size-full wp-image-9376\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/ccaction-corso-cocos2d-04.png 744w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/ccaction-corso-cocos2d-04-300x159.png 300w\" sizes=\"auto, (max-width: 550px) 100vw, 550px\" \/><\/a><br \/>\n<\/center><\/p>\n<p>Allo stesso modo possiamo eseguire una lunga serie di animazioni, come ad esempio il <a href=\"http:\/\/www.cocos2d-iphone.org\/api-ref\/1.0.0\/interface_c_c_blink.html\" target=\"_blank\">blink<\/a> in cui specifichiamo la durata dell&#8217;animazione e il numero di &#8220;lampeggi&#8221;.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\nCCBlink *blinkAction = [CCBlink actionWithDuration:2 blinks:10];\r\n[alien runAction:blinkAction];\r\n<\/pre>\n<p>oppure il <a href=\"http:\/\/www.cocos2d-iphone.org\/api-ref\/1.0.0\/interface_c_c_fade_in.html\" target=\"_blank\">fadein<\/a>, per far apparire l&#8217;oggetto modificando la sua opacit\u00e0:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\nCCFadeIn *fadein = [CCFadeIn actionWithDuration:2];\r\n[alien runAction:fadein];\r\n<\/pre>\n<p>Una molto carina \u00e8 <a href=\"http:\/\/www.cocos2d-iphone.org\/api-ref\/1.0.0\/interface_c_c_jump_to.html\" target=\"_blank\">CCJumpTo<\/a>, che permette di far eseguire alla nostra sprite dei salti che simulano la presenza della gravit\u00e0. Niente di eccezionale per carit\u00e0, ma l&#8217;accellerazione diminuisce man mano che la sprite raggiunge il punto pi\u00f9 in alto per poi cambiare verso e ricominciare a crescere. Pensate solo per un attimo a quanto codice avremmo dovuto scrivere per ottenere questo risultato, mentre grazie a cocos2d il tutto si riduce a due righe di codice:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\nCCJumpTo *jumpto = [CCJumpTo actionWithDuration:5 position:ccp(0, 0 ) height:100 jumps:8];\r\n[alien runAction:jumpto];\r\n<\/pre>\n<p>I tre parametri della funzione sono rispettivamente:<\/p>\n<ul>\n<li>il tempo dell&#8217;animazione in secondi<\/li>\n<li>la posizione da raggiungere<\/li>\n<li>quanto alti saranno i salti e in quanti salti verr\u00e0 raggiunta la posizione finale<\/li>\n<\/ul>\n<p>Un aspetto molto interessante \u00e8 che le animazioni possono anche essere eseguite contemporanemante&#8230; se volte spostare la vostra sprite mentre ruota e intanto diventa trasparente non dovete far altro che creare tre azioni e poi eseguirle: la sprite (che si aggiorna ogni 1\/60 di secondo) eseguir\u00e0 tutte le animazioni contemporaneamente.<\/p>\n<p>Ci sono alcune azioni che hanno un effetto immediato sulla sprite e sono subclass di CCActionInstant, come ad esempio CCFlipY:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\nCCFlipY *fliyp = [CCFlipY actionWithFlipY:TRUE];\r\n[alien runAction:fliyp];\r\n<\/pre>\n<p>che una volta eseguita ribalta l&#8217;immagine lungo l&#8217;asse Y.<\/p>\n<h4>Modificatori<\/h4>\n<p>Alcune subclass di CCAction sono un p\u00f2 particolari nel senso che operano come se fossero dei modificatori alle altre azioni, vediamo ad esempio CCRepeatForever. Questa classe deriva direttamente da CCAction, ma da sola \u00e8 inutile. Dalla documentazione notiamo infatti che il suo metodo principale \u00e8:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n+ (id) actionWithAction:(CCActionInterval *) action;\r\n<\/pre>\n<p>Quindi dobbiamo prima avere un&#8217;azione di tipo CCActionInterval e sar\u00e0 poi questa azione che verr\u00e0 eseguita indefinitivamente. Per avere un esempio basta digitare:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\nCCRotateBy *rotate = [CCRotateBy actionWithDuration:2 angle:360];\r\nCCRepeatForever *rotateForever = [CCRepeatForever actionWithAction:rotate];\r\n[alien runAction:rotateForever];\r\n<\/pre>\n<p>In questo esempio creiamo prima un&#8217;animazione per ruotare la sprite di 360 gradi, poi creiamo un&#8217;animazione di tipo CCRepeatForever e gli passiamo l&#8217;animazione appena creata&#8230; in questo modo abbiamo creato una sprite che ruota indefinivamente alla velocit\u00e0 di mezzo giro al secondo.<\/p>\n<p>Tra i modificatori uno molto utile \u00e8 sicuramente <strong>CCSequence<\/strong>, che ha la particolarit\u00e0 di ricevere un numero di azioni e di eseguirle in sequenza, vediamolo nell&#8217;esempio:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\nCCBlink *blinkAction = [CCBlink actionWithDuration:2 blinks:10];\r\nCCSequence *actionSequence = [CCSequence actionsWithArray:[NSArray arrayWithObjects:blinkAction,rotate, nil]];\r\n[alien runAction:actionSequence];\r\n<\/pre>\n<p>Provate adesso a guardare la pagina che ho linkato all&#8217;inizio di questo articolo, dove sono presenti tutte le subclass di CCAction e provate a scrivere un p\u00f2 di codice per implementarle tutte.<\/p>\n<p>Buona programmazione!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In questa nuova lezione del nostro corso di programmazione videogame in cocos2d approfondiremo un concetto che abbiamo&#8230;<\/p>\n","protected":false},"author":53,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1119],"tags":[1172,1165,1167,1166,1169,1170,1164,1168,1171,1121],"class_list":["post-9367","post","type-post","status-publish","format-standard","hentry","category-corso-programmazione-videogiochi","tag-action-cocos2d","tag-ccaction","tag-ccactioninstant","tag-ccactioninterval","tag-ccblink","tag-ccfadein","tag-ccfinitetimeaction","tag-ccmoveto","tag-ccrepeatforever","tag-corso-cocos2d"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/9367","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/users\/53"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/comments?post=9367"}],"version-history":[{"count":16,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/9367\/revisions"}],"predecessor-version":[{"id":9388,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/9367\/revisions\/9388"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=9367"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=9367"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=9367"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}