{"id":9433,"date":"2012-09-04T12:01:51","date_gmt":"2012-09-04T10:01:51","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=9433"},"modified":"2012-09-04T12:01:51","modified_gmt":"2012-09-04T10:01:51","slug":"7-come-creare-uno-sfondo-con-scrolling-infinito-con-cocos2d","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/7-come-creare-uno-sfondo-con-scrolling-infinito-con-cocos2d\/","title":{"rendered":"7. Come creare uno sfondo con scrolling infinito con cocos2d"},"content":{"rendered":"<p><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/09\/corso-programmazione-videogame-cocos2d-07.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/09\/corso-programmazione-videogame-cocos2d-07.jpg\" alt=\"corso-programmazione-videogame-cocos2d-07\" title=\"corso-programmazione-videogame-cocos2d-07\" width=\"200\" height=\"100\" class=\"alignleft size-full wp-image-9435\" \/><\/a> Nella lezione di oggi sfrutteremo le competenze teoriche che abbiamo acquisito nella lezione precedente e realizzeremo uno sfondo in finto 3D, con la particolarit\u00e0 che potr\u00e0 scrollare all&#8217;infinito. Lo scroll infinito \u00e8 una componente fondamentale in tantissimi tipi di giochi, dal classico &#8220;space shooter&#8221; ai pi\u00f9 recenti &#8220;endless run&#8221;, ci sono delle enormi differenze tra i vari giochi, ma alla fine la tecnica \u00e8 sempre la stessa, in pratica quello che si deve fare \u00e8 gestire correttamente gli elementi grafici che &#8220;scompaiono&#8221; da una parte dello schermo e crearne di nuovi pronti ad essere visualizzati dalla parte opposta,  come in un moderno Zoetrope (<a href=\"http:\/\/en.wikipedia.org\/wiki\/Zoetrope\" target=\"_blank\">http:\/\/en.wikipedia.org\/wiki\/Zoetrope<\/a>).<!--more--><\/p>\n<p><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/09\/scrolling-infinito-corso-cocos2d-01.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/09\/scrolling-infinito-corso-cocos2d-01.jpg\" alt=\"scrolling-infinito-corso-cocos2d-01\" title=\"scrolling-infinito-corso-cocos2d-01\" width=\"80\" height=\"88\" class=\"alignright size-full wp-image-9443\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/09\/scrolling-infinito-corso-cocos2d-01.jpg 550w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/09\/scrolling-infinito-corso-cocos2d-01-274x300.jpg 274w\" sizes=\"auto, (max-width: 80px) 100vw, 80px\" \/><\/a> Per gli elementi che scompaiono da una parte dello schermo c&#8217;\u00e8 poco da inventarsi, possono al massimo finire in una cache per essere ripescati velocemente qualora il giocatore tornasse indietro, tutta l&#8217;inventiva invece sta nel come creare i nuovi elementi.<br \/>\nIl caso pi\u00f9 semplice \u00e8 quello in cui, banalmente, gli elementi non vengno creati&#8230; si crea cio\u00e8 un fondale lunghissimo in cui il personaggio si pu\u00f2 muovere avanti e indietro a piacimento. Questo, bench\u00e9 possibile, \u00e8 estremamente costoso in termini di memoria occupata quidi risulta di fatto inutilizzabile.<br \/>\nI giochi pi\u00f9 complessi, invece, creano le nuove immagini in maniera random o in base allo stato del gioco, per esempio un classico &#8220;endless run&#8221; mostra livelli via via pi\u00f9 complessi man mano che avanza il gioco (avete mai provato canabalt? \u00e8 opensource! <a href=\"http:\/\/adamatomic.com\/canabalt\/\" target=\"_blank\">http:\/\/adamatomic.com\/canabalt\/<\/a>).<\/p>\n<p>L&#8217;esempio che vedremo in questa lezione \u00e8 una via di mezzo, in pratica creeremo due &#8220;sfondi&#8221; completi, quando un&#8217;immagine scompare da una parte la faremo istantaneamente riapparire dall&#8217;altra parte.<\/p>\n<p><center><br \/>\n<a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/09\/scrolling-infinito-corso-cocos2d-02.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/09\/scrolling-infinito-corso-cocos2d-02.png\" alt=\"scrolling-infinito-corso-cocos2d-02\" title=\"scrolling-infinito-corso-cocos2d-02\" width=\"550\" height=\"141\" class=\"aligncenter size-full wp-image-9449\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/09\/scrolling-infinito-corso-cocos2d-02.png 1432w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/09\/scrolling-infinito-corso-cocos2d-02-300x77.png 300w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/09\/scrolling-infinito-corso-cocos2d-02-1024x263.png 1024w\" sizes=\"auto, (max-width: 550px) 100vw, 550px\" \/><\/a><br \/>\n<\/center><\/p>\n<p>In questa immagine potete vedere anche un piccolo trucco&#8230; una delle due schermate \u00e8 stata flippata per farle combaciare correttamente, altrimenti si sarebbe visto un brutto stacco.<br \/>\nMa veniamo alle brutte notizie, purtroppo la classe CCParallaxNode che abbiamo visto nel precedente articolo non \u00e8 utlizzabile per questo genere di operazioni, perch\u00e9 la posizione del singolo livello non \u00e8 modificabile una volta che viene inserito dentro un CCParallaxNode, non ci resta quindi che darci da fare e scrivere un p\u00f2 di codice per gestire noi stessi il tutto.<\/p>\n<p>Iniziamo dichiarando alcune variabili nell&#8217;header del nostro layer:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@interface HelloWorldLayer : CCLayer\r\n{\r\n    NSArray *spriteArray;\r\n    int numSprite;\r\n    NSArray *speedFactors;\r\n}\r\n<\/pre>\n<p>spriteArray conterr\u00e0 i puntatori alle sprite che andremo a creare, numSprite \u00e8 un intero che useremo per memorizzare il numero di sprite che andremo a gestire. L&#8217;ultimo array, speedFactors, memorizza il moltiplicatore che andremo ad utilizzare per muovere ogni livello di una percentuale dello spostamento totale.<\/p>\n<p>La creazione delle sprite avviene come negli altri esempi all&#8217;interno del metodo init<\/p>\n<p>Il metodo completo \u00e8 questo:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n-(id) init\r\n{\r\n        if( (self=[super init])) {\r\n        CGSize screensize = [[CCDirector sharedDirector] winSize];\r\n        numSprite = 4;\r\n       \r\n        CCSprite *sprite0 = [CCSprite spriteWithFile:@\"liv_0.png\"];\r\n        [sprite0 setAnchorPoint:CGPointMake(0,0)];\r\n        [sprite0 setPosition:CGPointMake(0,0)];\r\n        [self addChild:sprite0];\r\n       \r\n        CCSprite *sprite0b = [CCSprite spriteWithFile:@\"liv_0.png\"];\r\n        [sprite0b setAnchorPoint:CGPointMake(0,0)];\r\n        [sprite0b setPosition:CGPointMake(screensize.width - 1, 0)];\r\n        [self addChild:sprite0b];\r\n       \r\n        CCSprite *sprite1 = [CCSprite spriteWithFile:@\"liv_1.png\"];\r\n        [sprite1 setAnchorPoint:CGPointMake(0,0)];\r\n        [sprite1 setPosition:CGPointMake(0,80)];\r\n        [self addChild:sprite1];\r\n       \r\n        CCSprite *sprite1b = [CCSprite spriteWithFile:@\"liv_1.png\"];\r\n        [sprite1b setAnchorPoint:CGPointMake(0,0 )];\r\n        [sprite1b setPosition:CGPointMake(screensize.width - 1 , 80)];\r\n        sprite1b.flipX = YES;\r\n        [self addChild:sprite1b];\r\n       \r\n        CCSprite *sprite2 = [CCSprite spriteWithFile:@\"liv_2.png\"];\r\n        [sprite2 setAnchorPoint:CGPointMake(0, 0)];\r\n        [sprite2 setPosition:CGPointMake(0, 60)];\r\n        [self addChild:sprite2];\r\n       \r\n        CCSprite *sprite2b = [CCSprite spriteWithFile:@\"liv_2.png\"];\r\n        [sprite2b setAnchorPoint:CGPointMake(0, 0)];\r\n        [sprite2b setPosition:CGPointMake(screensize.width -1 , 60)];\r\n        sprite2b.flipX = YES;\r\n        [self addChild:sprite2b];\r\n       \r\n        CCSprite *sprite3 = [CCSprite spriteWithFile:@\"liv_3.png\"];\r\n        [sprite3 setAnchorPoint:CGPointMake(0, 0)];\r\n        [sprite3 setPosition:CGPointMake(0, 0)];\r\n        [self addChild:sprite3];\r\n       \r\n        CCSprite *sprite3b = [CCSprite spriteWithFile:@\"liv_3.png\"];\r\n        [sprite3b setAnchorPoint:CGPointMake(0, 0)];\r\n        [sprite3b setPosition:CGPointMake(screensize.width - 1, 0)];\r\n        sprite3.flipX = YES;\r\n        [self addChild:sprite3b];\r\n       \r\n        spriteArray = [[NSArray arrayWithObjects:sprite0,sprite1, sprite2, sprite3, sprite0b,sprite1b, sprite2b, sprite3b, nil ] retain];\r\n       \r\n        speedFactors = [[NSArray arrayWithObjects:\r\n                         [NSNumber numberWithFloat:0.1],\r\n                         [NSNumber numberWithFloat:0.4],\r\n                         [NSNumber numberWithFloat:0.8],\r\n                         [NSNumber numberWithFloat:1.0],\r\n                         \r\n                         [NSNumber numberWithFloat:0.1],\r\n                         [NSNumber numberWithFloat:0.4],\r\n                         [NSNumber numberWithFloat:0.8],\r\n                         [NSNumber numberWithFloat:1.0],\r\n                         nil] retain];\r\n       \r\n        [self scheduleUpdate];\r\n    }\r\n        return self;\r\n}\r\n<\/pre>\n<p>Non dovrebbe essere difficile capire cosa abbiamo fatto&#8230; in pratica per ciascuna delle quattro slide abbiamo aggiunto alla scena la sprite e una sua &#8220;copia&#8221; ribaltata. Queste sprite sono poi state aggiunte in un array (spriteArray) per agevolare l&#8217;operazione che vedremo tra un attimo.<\/p>\n<p>All&#8217;interno dell&#8217;array speedFactors sono memorizzati i fattori di spostamento, sono 8 perch\u00e9 le sprite in gioco sono 8, ma sono solo 4 livelli diversi, quindi sono 4 valori.<\/p>\n<p>Infine utilizziamo [self scheduleUpdate]; per aggiornare la nostra scena ad ogni iterazione.<\/p>\n<p>Il metodo che ho utilizzato \u00e8 questo:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n-(void)update:(ccTime)delta {\r\n    CGSize screensize = [[CCDirector sharedDirector] winSize];    \r\nfor (CCSprite *sprite in spriteArray) { \/\/1\r\n        int index = [spriteArray indexOfObject:sprite]; \/\/2\r\n       \r\n        CGPoint position = sprite.position;\r\n        position.x -= 100 * (delta) * [[speedFactors objectAtIndex:index]floatValue]; \/\/3\r\n       \r\n        if ( position.x < -screensize.width ) {  \/\/4\r\n            position.x += ( screensize.width * 2 ) - 2; \/\/5\r\n        }\r\n        sprite.position = position; \/\/6\r\n    }\r\n}\r\n<\/pre>\n<p>#1 Eseguo un ciclo su tutte le sprite che sono inserite nell'array spriteArray<br \/>\n#2 recupero anche l'indice dell'array alla quale si trova<br \/>\n#3 calcolo la nuova posizione moltiplicando per 100 il delta, in pratica il tempo che \u00e8 intercorso dalla precedete chiamata, moltiplicato per il fattore di moltiplicazione che \u00e8 memorizzato nell'arry speedFactors<br \/>\n#4 Verifico se la posizione di questa sprite \u00e8 completamente fuori dallo schermo, in tal caso...<br \/>\n#4 calcolo la nuova posizione, aggiungendo due volte la larghezza della sprite.<br \/>\n#5 Setto la posizione della sprite.<\/p>\n<h4>Flickering<\/h4>\n<p>Se avete fatto caso ci sono alcuni piccoli valori correttivi quando creo le sprite \"gemelle\", in realt\u00e0 le posiziono leggermente sovrapposte alle prime, nello specifico di un 1 pixel.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n[sprite0b setPosition:CGPointMake(screensize.width - 1, 0)];\r\n<\/pre>\n<p>Inoltre, quando devo risposizionare la sprite, devo mantenere questa differenza, quindi la sposto di <code>( screensize.width * 2 ) - 2<\/code> pixel.<\/p>\n<p>Questo \u00e8 dovuto al fatto che posizionare la sprite proprio affiancata a quella precedente potrebbe causare problemi di flickering, quindi aggiungiamo un pixel di sovrapposizione per essere pi\u00f9 tranquilli.<\/p>\n<p>Vi lascio con il video del risultato finale ottenuto:<\/p>\n<p><center><br \/>\n<iframe loading=\"lazy\" width=\"480\" height=\"360\" src=\"http:\/\/www.youtube.com\/embed\/aFiea57WGco?rel=0\" frameborder=\"0\" allowfullscreen><\/iframe><br \/>\n<\/center><\/p>\n<p>e con il link a github con il <a href=\"https:\/\/github.com\/ignazioc\/Cocos2d.Lesson7-InfiniteScroll\" target=\"_blank\">progetto completo<\/a>.<\/p>\n<p>PS: il mio scopo qui \u00e8 quello di raccontare cocos2d, non di fornirvi la pappa pronta... per\u00f2 se una volta studiato il tutto vorreste usare qualche classe scritta da altri potete provare a guardare <a href=\"http:\/\/ak.net84.net\/iphone\/creating-a-repeating-backgroundinfinite-parallax-effect-in-cocos2d\/\" target=\"_blank\">questo progetto<\/a> \ud83d\ude09<\/p>\n<p>Buona programmazione a tutti!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Nella lezione di oggi sfrutteremo le competenze teoriche che abbiamo acquisito nella lezione precedente e realizzeremo uno&#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":[1151,1180,1182,1120,1179,1183,1181],"class_list":["post-9433","post","type-post","status-publish","format-standard","hentry","category-corso-programmazione-videogiochi","tag-corso-cocos2d-italiano","tag-creare-giochi-per-iphone-e-ipad","tag-creare-un-platform-game-iphone","tag-creare-videogame-iphone-ipad","tag-guida-cocos2d","tag-manuale-italiano-cocos2d","tag-scrolling-infinito-cocos2d"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/9433","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=9433"}],"version-history":[{"count":24,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/9433\/revisions"}],"predecessor-version":[{"id":9461,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/9433\/revisions\/9461"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=9433"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=9433"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=9433"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}