国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

SpriteBuilder學(xué)習(xí)筆記二

2019-11-14 19:35:54
字體:
供稿:網(wǎng)友

Chapter 3  Controlling and Scrolling

@implementation GameScene {    __weak CCNode *_levelNode;    __weak CCPhysicsNode *_physicalNode;    __weak CCNode *_playerNode;    __weak CCNode *_backgroundNode;}

注意__weak關(guān)鍵字。總的來說,聲明一個obejct pointer 變量而不是由類created 或者說owned的時候,最好都使用__weak,尤其是在cocos2d中,應(yīng)該總是聲明一個引用,當這個引用不是parent或者node的“兄弟”(sibling)時。如果沒有__weak關(guān)鍵字,默認生成一個strong引用。

通過名字找到Player Node

在GameScene中添加代碼:

- (void)didLoadFromCCB {    NSLog(@"GameScene created!");    // 使得可以接受輸入的事件 (enable receiving input events)    // 這句話允許GameScene類去接受觸摸事件    self.userInteractionEnabled = YES;    // load the current level 載入當前l(fā)evel    [self loadLevelNamed:nil];}
- (void)loadLevelNamed:(NSString*)levelCCB {    // 在scene中獲取當前l(fā)evel的player,遞歸尋找    _playerNode = [self getChildByName:@"player" recursively:YES];    // 如果沒有找到,NSAssert會拋出一個異常    NSAssert1(_playerNode, @"player node not found in level:%@", levelCCB);}

下面的代碼用于實現(xiàn)通過觸摸移動物體到觸摸的位置

- (void)touchBegan:(CCTouch*)touch withEvent:(UIEvent*)event {    _playerNode.position = [touch locationInNode:self];}

NOTE:書中第一個參數(shù)類型為UITouch* 報錯,改為CCTouch后即可實現(xiàn)功能。

查閱API,摘抄如下:

touchBegan:withEvent:

Called when a touch began. Behavior notes:

- (void)touchBegan:(CCTouch *)touch withEvent:(CCTouchEvent *)event

Parameters

touch

Contains the touch.

event

Current event information.

Discussion

  • If a touch is dragged inside a node which does not claim user interaction, a touchBegan event will be generated.
  • If node has exclusive touch, all other ongoing touches will be cancelled.
  • If a node wants to handle any touch event, the touchBegan method must be overridden in the node subclass. Overriding just touchMoved or touchEnded does not suffice.
  • To pass the touch further down the Cocos2D responder chain, call the super implementation, ie [super touchBegan:withEvent:].

Declared In

CCResponder.h

 

分配Level-Node變量

在SPRiteBuilder中分配變量和通過名字獲取一個node是一樣的,僅僅是個人習(xí)慣問題。但是不推薦頻繁使用getChildByName:方法在schedule methond中(不太懂這是什么方法)和updata:方法中,特別是遞歸查找和a deep-node hierarch。

Caution:在SpriteBuilder中分配一個變量僅僅適用于CCB文件的直接descendants(后代--不知如何翻譯),不可以對通過Sub File(CCBFile)導(dǎo)入的另一個CCB指定node為變量或者properties。這也是為何player node通過名字獲取。

打開GameScene.ccb,

 

note:A doc root var assigns a node to a correspondingly named ivar or property declared in the CCB root node's custom class

Doc root var:分配一個node,為在CCB根node的自定義類中聲明的相對應(yīng)名字的變量或者屬性。

做完這步后,_levelNode變量會在它發(fā)送didLoadFromCCB消息之前被CCBReader分配,這是創(chuàng)建一個在CCB中包含的node的最簡單,最有效的方法。

 

用CCActionMoveTo移動Player

為了平滑的移動player到指定位置,可以修改如下代碼:

- (void)touchBegan:(CCTouch*)touch withEvent:(UIEvent*)event {   // _playerNode.position = [touch locationInNode:self];    CGPoint pos = [touch locationInNode:_levelNode];    CCAction *move = [CCActionMoveTo actionWithDuration:0.2 position:pos];    [_playerNode runAction:move];}

觸摸點根據(jù)_levelNode轉(zhuǎn)化。這一點很重要,保證了player可以在整個_levelNode上移動,而不是被禁錮在屏幕空間中。但是這一點目前還看不出來,因為還沒有添加滾動(scrolling)。

但是此時,如果增加duration(持續(xù)時間),會發(fā)現(xiàn)移動的動作并沒有疊加,player也不會停在你最后一次點擊的地方。所以必須添加一個tag,有了這個tag,可以在執(zhí)行新的動作之前,停止當前動作,代碼更改如下:

- (void)touchBegan:(CCTouch*)touch withEvent:(UIEvent*)event {   // _playerNode.position = [touch locationInNode:self];    [_playerNode stopActionByTag:1];    CGPoint pos = [touch locationInNode:_levelNode];    CCAction *move = [CCActionMoveTo actionWithDuration:20.2 position:pos];    move.tag = 1;    [_playerNode runAction:move];}

 

滾動Level(Scrolling the Level)

在2D游戲中,更普遍的做法是相反方向移動content layer,已達到滾動效果。

在Cocos2D和OpenGl中,沒有camera的概念,只有device screen(設(shè)備屏幕).

 

 Scheduling Updates(調(diào)度更新)

如果player移動到右邊和上邊,那我們要做的事情實際上是移動_levelNode向左邊和下邊方向移動。player的位置限定在level node中,左下角左邊為(0,0),在這個程序中,范圍是4000*500 points。

在GameScene中添加如下代碼:

// the updata:method is automatically called once per frame// update方法在每一幀都被自動調(diào)用- (void)update:(CCTime)delta {    // update scroll node position to player node, with offset to center player in the view    [self scrollToTarget:_playerNode];}

update:方法自動被Cocos2d調(diào)用,在底層,每一幀,node出現(xiàn)在屏幕之前,都回被調(diào)用。

不像之前的Cocos2d版本,你不再需要去明確調(diào)度更新(you no longer have to explicitly schedule the update:method.)

你可以使用node schedule和unschedule方法調(diào)度其他的方法或blocks.(you can still schedule other methods or blocks using the node schedule and unschedule methods)

例如:延遲運行一個selector,可以寫為:

[self scheduleOnce:@selector(theDelayedMethod:)delay:2.5]:

然后再相同的類中實現(xiàn)對應(yīng)的selector。這個selector必須使用一個CCTime參數(shù):

-(void)theDelayedMethod:(CCTime)delta {

//your code

}

Caution:永遠不要使用NSTimer等。這些時間方法在node或者Cocos2d暫停時候不會自動暫停。

delta參數(shù)是delta time,或者difference in time。

在60幀每秒時,delta時間經(jīng)常取大約0.0167,單位是秒。

delta time通常用作以相同的速度移動nodes,而忽略幀速率。我們在這本書中不使用delta time,因為我們使用Cocos2d的物理引擎。

 

Moving the level Node in the Opposite Derection

向相反方向移動Level Node

在GameScene.m中添加scrollToTarget方法以完成滾動:

- (void)scrollToTarget:(CCNode*)target {    CGSize viewSize = [CCDirector sharedDirector].viewSize;    CGPoint viewCenter = CGPointMake(viewSize.width / 2.0,viewSize.height / 2.0);    CGPoint viewPos = ccpSub(target.positionInPoints, viewCenter);    CGSize levelSize = _levelNode.contentSizeInPoints;    viewPos.x = MAX(0.0, MIN(viewPos.x, levelSize.width - viewSize.width));    viewPos.y = MAX(0.0,MIN(viewPos.y, levelSize.height - viewSize.height));    _levelNode.positionInPoints = ccpNeg(viewPos);}

前兩行的作用是指定view的尺寸到viewSize,值為屏幕以points為單位的值。

然后計算view的中心點。

viewPos變量被初始化為目標的positionInPoints減去中心點viewCenter。

這個使用ccpSub做的減法是為了保持目標node保持中心位置,如果不做這一步,目標node會消失在屏幕的左下角。

levelSize變量被定義為_lovelNode.contentSizeInPoints,在下面兩行中,它用于夾住viewPos。

因為屏幕永遠不應(yīng)該比viewCenter滾動的更接近于level的邊界,所以使用減法。每個邊界的距離相加等于viewSize。或者換句話說,可以滾動的區(qū)域是viewCenter的兩倍或者一個viewSize   ???

level區(qū)域和可滾動區(qū)域的關(guān)系圖:箭頭表示可滾動區(qū)域。注意player在接近level邊界的時候已經(jīng)不在中心位置了。

 

Parallax Scrolling 視差滾動

有很多種實現(xiàn)視差滾動的方法,最簡單的方法是給每個layer不同的速度,并移動layers。但是這種方法有一個缺點,就是你永遠也不可能知道每個layer到底需要多大,而且很難判斷當player到達一個level中的點時,背景的哪一個部分會是可見的。

Working with Images

 

如果你只有2x規(guī)模的圖片版本,可以適配retina iphone和non-retina ipad,你可以改變“Scale from”設(shè)置,從Default改變?yōu)?x。這不能起到節(jié)省內(nèi)存的作用,SpriteBuilder會創(chuàng)建一個低規(guī)模的1x和一個高規(guī)模的4x版本。這意味著4x版本的圖片和原始的2x版本的圖片有一樣程度的細節(jié)。你也可以為各種規(guī)模的圖片采用不同的圖片。強烈建議創(chuàng)建所有images。

SpriteBuilder給你兩個選擇:要么創(chuàng)建所有圖片,4x和568x384,以便在ipads上運行,要么創(chuàng)建2x,568x320,然后只為了在iphone設(shè)備上運行。

舉例來說,填滿ipads Retina屏幕需要最少2048x1536個像素(defult 4x),如果是2272x1536更好,可以更好的覆蓋4-inch的iphon屏幕。如果你的app只為了在iphone上運行,(對所有圖片使用2x規(guī)模),那么覆蓋整個retina屏幕的圖片需要1136x640像素點(568X320 points)。但是如果你希望稍后添加ipad版本,那么就需要你為ipad retina屏幕設(shè)計你所有的圖片了。

Project Setting

如果你開發(fā)一個僅在Iphone上運行的app,SpriteBuilder給了你改變默認4x規(guī)模的選擇。File-Project-Setting dialog

 

如果你想你的iPad版本顯示對應(yīng)的更大的游戲世界,你可以改變“Default scaling from”,設(shè)定為2x(phonehd),注意設(shè)置不會應(yīng)用在已經(jīng)存在的圖片上,只有伺候新的添加進SpriteBuilder的images會改變。

注意:Apple要求app開發(fā)者支持Retina。強烈建議使用設(shè)置:Scale from setting 1x for any images,因為在iPad Retina屏幕上的顯示質(zhì)量會很低。

設(shè)置:phone:僅在iPhone3GS上適用

     phonehd:在iPhone4和更新的設(shè)備上適用

   tablet:對非Retina ipads:Ipad1 和2,iPad mini1上適用

    tablethd:對iPad3和iPadmini2和更新的設(shè)備上適用

其他的選項:

    Audio quality:影響發(fā)布的音頻文件的大小和質(zhì)量。level 1創(chuàng)建最小但是質(zhì)量最差的文件。

    Screen mode:主要的應(yīng)用情況是當你的游戲僅僅是iPhone上運行時,并且你希望把3.5和4英寸iphone作為同一個設(shè)備,這種情況下可以考慮使用fixed mode,但是通常不建議使用因為這會使得屏幕的布局很困難。

   Orientation:橫屏或者豎屏設(shè)置。

 

Adding Addition Background Layers

很明顯,需要更多的layers去達到景深效果。距離觀察者更近的layer,它的size就需要更大。

 

Prepare to Parallax in 3 2 1...

使得背景l(fā)ayers視差滾動需要一些初始化步驟。

首先,引進physics node,并使得player node變成physics node的子node。現(xiàn)在,physics node是level的內(nèi)容容器,而不是level.ccb本身。

讓player作為另一個node的子node的主要原因是為了能夠在視差背景中獨立移動level內(nèi)容。如果繼續(xù)使用Level.ccb做為player的父node,the changes made to the player's parent position would offset all of the level1.ccb child nodes,including the background .這會使得背景滾動的代碼復(fù)雜得多,并且很難添加一個靜止不動的node,比如暫停按鈕。

NOTE:不能把player node拖拽到background node下。因為background node 是Sub File node,不可以接受子nodes。還有其他幾類無法擁有子nodes的:Particle System,Label TTF, Label BM-Font,Button,Text Field,Slider,Scroll View。

現(xiàn)在需要分配CCPhysicsNode引用_physicsNode變量。因為getChildByName:返回一個CCNode類的引用,所以必須強轉(zhuǎn)返回的node。

_physicsNode = (CCPhysicsNode*)[_levelNode getChildByName:@"physics" recursively:NO];

 

- (void)loadLevelNamed:(CCNode*) levelCCB {    _physicsNode = (CCPhysicsNode*)[_levelNode getChildByName:@"physics" recursively:NO];    _background = [_levelNode getChildByName:@"background" recursively:NO];    _playerNode = [_physicsNode getChildByName:@"player" recursively:NO];    NSAssert1(_playerNode, @"not found %@", levelCCB);    NSAssert1(_physicsNode, @"not found %@", levelCCB);    NSAssert1(_background, @"not found %@", levelCCB);}

現(xiàn)在,需要對scrollToTarget方法進行修改:

_levelNode.positionInPoints = ccpNeg(viewPos);

改為

_physicsNode.positionInPoints = ccpNeg(viewPos);

這樣,_backgroundNode的位置就會被解放,可以根據(jù)_physicsNode的位置獨立的更新。

現(xiàn)在,scrollToTarget方法的代碼為:

- (void)scrollToTarget:(CCNode*)target {    // 屏幕大小 480 * 320    CGSize viewSize = [CCDirector sharedDirector].viewSize;    // player的中心位置 (240,160)    CGPoint viewCenter = CGPointMake(viewSize.width / 2.0, viewSize.height / 2.0);    // levelNode的size 4000 * 500;    CGSize levelSize = _levelNode.contentSizeInPoints;    //    CGPoint viewPos = ccpSub(target.positionInPoints, viewCenter);    viewPos.x = MAX(0.0, MIN(viewPos.x, levelSize.width - viewSize.width));    viewPos.y = MAX(0.0, MIN(viewPos.y, levelSize.height - viewSize.height));    _physicsNode.positionInPoints = ccpNeg(viewPos);}

現(xiàn)在,必須獲取每個背景l(fā)ayers在_physisNode的位置更新時視差滾動的位置。

因為你想要每個layers的位置對應(yīng)_physicsNode的位置,(在這個方法中是viewPos),那么考慮到viewPos(view的中心)應(yīng)該與level的邊界保持一定的距離就很重要了。這個最小的距離必須至少在水平方向是viewCenter.width,在垂直方向是viewCenter.height.這樣才可以阻止可視區(qū)域出現(xiàn)在level邊界的外面.

這幅圖可以幫助理解,想象viewPos是每個Viewable Area的中心,那么實際的Scrollable Area矩形(比如,viewPos合法位置)必須比Level Area的左下角大,必須比右上角小。

這樣,每個背景l(fā)ayer相對應(yīng)的位置不能和整個尺寸的level相比,也就是4000x500個points。

例子:在Iphone5上,viewCenter是284x160.這樣的話,scrollable area就是:

284x160 到( 4000 - 284) x (500 - 160) = 3716 x 340 points.

換句話說,這個Scrollable Area是level的尺寸減去view的尺寸。這樣,通過scrollable area(levelSize - viewSize)分割viewPos給了你_physicsNode當前位置在scrollable area上的百分比:

CGPoint viewPosPercent = CGPointMake(viewPos.x / (levelSize.width - viewSize.width),viewPos.y / (levelSize.height - viewSize.height));

現(xiàn)在,得到了_physicsNode的位置的范圍(0.0到1.0之間),0.0指的是scrollalbe area的左下角的位置,284x160,1.0指的是右上角的位置 3716x340.下一步必須運用這個百分比在每一個layer上,把每個layer自己的尺寸算進去。

試著計算: 使用568x384作為layerSize的寬和高,并且用568x320作為viewSize的寬和高,計算當viewPosPercent是0.5,0.5的時候,layerPos是多少。

- (void)scrollToTarget:(CCNode*)target {    // 屏幕大小 480 * 320    CGSize viewSize = [CCDirector sharedDirector].viewSize;    // player的中心位置 (240,160)    CGPoint viewCenter = CGPointMake(viewSize.width / 2.0, viewSize.height / 2.0);    // levelNode的size 4000 * 500;    CGSize levelSize = _levelNode.contentSizeInPoints;    //    CGPoint viewPos = ccpSub(target.positionInPoints, viewCenter);    viewPos.x = MAX(0.0, MIN(viewPos.x, levelSize.width - viewSize.width));    viewPos.y = MAX(0.0, MIN(viewPos.y, levelSize.height - viewSize.height));    _physicsNode.positionInPoints = ccpNeg(viewPos);    CGPoint viewPosPercent = CGPointMake(viewPos.x / (levelSize.width - viewSize.width),viewPos.y / (levelSize.height - viewSize.height));    for (CCNode *layer in _backgroundNode.children) {        CGSize layerSize = layer.contentSizeInPoints;        CGPoint layerPos = CGPointMake(viewPosPercent.x * (layerSize.width - viewSize.width), viewPosPercent.y * (layerSize.height - viewSize.height));        layer.positionInPoints = ccpNeg(layerPos);    }}
- (void)touchBegan:(CCTouch *)touch withEvent:(CCTouchEvent *)event {    //_playerNode.position = [touch locationInNode:self];    [_playerNode stopActionByTag:1];    CGPoint pos = [touch locationInNode:_physicsNode];    CCAction *move = [CCActionMoveTo actionWithDuration:0.2 position:pos];    move.tag = 1;    [_playerNode runAction:move];}

 

試著計算

 

未完待續(xù)d 

 


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 和顺县| 闸北区| 永城市| 礼泉县| 乌恰县| 萝北县| 临洮县| 通城县| 綦江县| 太原市| 财经| 启东市| 京山县| 甘泉县| 邹城市| 河间市| 扎囊县| 德江县| 墨脱县| 潼关县| 金溪县| 怀安县| 林芝县| 安义县| 神农架林区| 安化县| 遂宁市| 鲁山县| 井陉县| 南京市| 济源市| 酒泉市| 渝中区| 岑溪市| 灌云县| 福海县| 车致| 界首市| 安泽县| 延津县| 南漳县|