日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

在Cocos2d中实现能够惯性拖动的选择界面

發(fā)布時間:2023/12/9 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 在Cocos2d中实现能够惯性拖动的选择界面 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

蘋果的應(yīng)用講究用戶體驗

有的時候仔細想想

的確,很多細節(jié)決定了用戶體驗

比如說慣性拖動

可以說之前沒有任何一家廠商能把觸摸慣性拖動做的像蘋果的UI那么流暢

?

Cocos2D中實現(xiàn)能夠慣性拖動的選擇界面

完成的效果:

制作一個簡單的圖層,通過傳入許多的節(jié)點,圖層自動將節(jié)點排版,并能夠通過物理拖拽來選擇其中的某一個節(jié)點,并通知節(jié)點的代理來處理

?

首先新建一個cocos2d項目,我用的版本是2.0,命名為SimplePhysicsDragSelectorTest

新建一個objective-c class,我這里命名為SZSimplePhysicsDragSelector

在SimplePhysicsDragSelector.h文件里添加以下代碼:

?

#import "cocos2d.h"@class SZSimplePhysicsDragSelector; @protocol SZSimplePhysicsDragSelectorDelegate <NSObject> @optional // call when the selected icon changes -(void)onSelectedIconChanged:(SZSimplePhysicsDragSelector*)selector; @end@interface SZSimplePhysicsDragSelector : CCLayer {CCNode *s_content;//所有節(jié)點圖標的父節(jié)點NSMutableArray *s_icons;//節(jié)點圖標清單CCNode *s_selectedIcon;//選定的節(jié)點 BOOL isDragging;//是否在拖拽狀態(tài)CGPoint lastTouchPoint;//上一個觸摸點float lastx;//上一個圖層內(nèi)容x坐標float xvel;//內(nèi)容在x軸上的速度int maxX;//內(nèi)容可以自然移動到的最大極限x坐標int minX;//內(nèi)容可以自然移動到的最小極限x坐標float acceleration;//加速度float f;//合外力id<SZSimplePhysicsDragSelectorDelegate> s_delegate;//代理 }@property (nonatomic, readonly) NSMutableArray *Icons; @property (nonatomic, readonly) CCNode *SelectedIcon; @property (nonatomic, assign) id<SZSimplePhysicsDragSelectorDelegate> Delegate;- (id)initWithIcons:(NSArray*)icons;@end

?

?

?

這里聲明了SZSimplePhysicsDragSelector需要使用到的變量和方法,同時聲明了SZSimplePhysicsDragSelector代理的方法

變量的作用如注釋里描述的,后面將會詳細說到

解釋下代理方法:

-(void)onSelectedIconChanged:(SZSimplePhysicsDragSelector*)selector;

?在圖層選擇的節(jié)點發(fā)生改變時將會發(fā)送此消息給代理,如果改變?yōu)闆]有選擇節(jié)點也會發(fā)送此消息

?

初始化

在SZSimplePhysicsDragSelector.m文件中添加以下代碼:

@implementation SZSimplePhysicsDragSelector@synthesize Delegate = s_delegate; @synthesize Icons = s_icons; @synthesize SelectedIcon = s_selectedIcon;- (id)initWithIcons:(NSArray *)icons {self = [super init];if (self) {s_icons = [[NSMutableArray alloc] initWithArray:icons];s_content = nil;s_selectedIcon = nil;isDragging = NO;lastTouchPoint = CGPointZero;lastx = 0.0f;xvel = 0.0f;minX = 0;maxX = 0;acceleration = 0.0f;f = 0.0f;self.isTouchEnabled = true;// 啟用接收觸摸事件 s_delegate = nil;}return self; }- (void)dealloc {[s_icons release];[super dealloc]; }#pragma mark Override methods-(void) onEnter {[super onEnter];s_content = [[CCSprite alloc]init];[self addChild:s_content];[self scheduleUpdate];//開啟計時器 }-(void) onExit {[self unscheduleUpdate];//關(guān)閉計時器 [self removeChild:s_content cleanup:YES];[s_content release];s_content = nil;s_selectedIcon = nil;[super onExit]; }@end

?

以上代碼實現(xiàn)了初始化&內(nèi)存釋放以及onEnter和onExist方法

在選擇器被添加到某一個節(jié)點中時,將會自動創(chuàng)建一個內(nèi)容節(jié)點s_content,用來存放所有的節(jié)點,并一起移動

?

布局節(jié)點

在onEnter方法中布局視圖,并實現(xiàn)layout方法-(void) onEnter

-(void) onEnter {[super onEnter];s_content = [[CCSprite alloc]init];[self addChild:s_content];[self layout];[self scheduleUpdate]; }-(void) layout {int i = 1;for (CCNode *icon in s_icons) {CGPoint position = ccp((i-1) * 180, 0);float distance = fabsf(icon.position.x)/100;icon.position = position;if (![s_content.children containsObject:icon]) {[s_content addChild:icon];}i++;}s_selectedIcon = [s_icons lastObject];if ([s_delegate respondsToSelector:@selector(onSelectedIconChanged:)]) {[s_delegate onSelectedIconChanged:self];}minX = - (i-1) * 180 - 100;maxX = 100; }

?

解釋下layout方法

將180pt作為每兩個節(jié)點之間的間距,同時第一個節(jié)點在s_content中的位置應(yīng)該是(0,0)所以計算得出位置的公式(間距和初始位置可以根據(jù)需要更改)

position = ccp((i-1) * 180, 0)

之后添加節(jié)點到s_content,并且設(shè)置最后一個為初始選定的節(jié)點,最后通知代理選定節(jié)點發(fā)生更改

關(guān)于極限位置(minX,maxX)是這樣設(shè)定的,前面說到180作為間距,(0,0)為初始節(jié)點位置,所以最后一個節(jié)點的x坐標為(i-1) *?180(i為節(jié)點個數(shù)),當(dāng)需要選擇右邊的節(jié)點時實際上是將s_content的位置向左移動,所以選擇到最后一個節(jié)點時s_content的位置應(yīng)該是-(i-1) *?180,同理第一個選擇到第一個節(jié)點時s_content的位置應(yīng)該是(0,0),此外我希望極限位置能夠比頭尾節(jié)點位置的范圍稍大,所以最終我設(shè)定

minX = - (i-1) * 180 - 100;

maxX = 100;

?

觸摸記錄?

布局完成,接下來我們需要實現(xiàn)觸摸事件消息來記錄數(shù)據(jù)供模擬物理使用

在SZSimplePhysicsDragSelector.m文件中添加以下代碼:

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; {s_selectedIcon = nil;if ([s_delegate respondsToSelector:@selector(onSelectedIconChanged:)]) {[s_delegate onSelectedIconChanged:self];}UITouch *touch = [touches anyObject];CGPoint position = [self convertTouchToNodeSpace:touch];lastTouchPoint = position;isDragging = true; }- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; {UITouch *touch = [touches anyObject];CGPoint position = [self convertTouchToNodeSpace:touch];CGPoint translate = ccpSub(position, lastTouchPoint);translate.y = 0;s_content.position = ccpAdd(s_content.position, translate);lastTouchPoint = position; }- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; {isDragging = false; }- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; {isDragging = false; }

這里分開說下4個觸摸事件

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
在方法中我們清空了選擇的節(jié)點并通知代理選擇的節(jié)點改變,標記自身狀態(tài)為拖拽中

- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
在方法中根據(jù)此刻觸摸點與上一次觸摸點的位置差,來移動s_content的位置,從而使內(nèi)容跟隨觸摸移動,最后在記錄下此刻的位置為上一次觸摸位置,供下一次計算使用

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;

標記自身狀態(tài)為未拖拽

- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

標記自身狀態(tài)為未拖拽


這樣我們已經(jīng)能夠辨別自身是否在拖拽狀態(tài)以及正確拖拽內(nèi)容

?

模擬物理計算

首先說明一下思路:

我們在udpate方法中我們需要檢測圖層的狀態(tài):

若圖層在被拖拽狀態(tài),則不需要模擬物理,只需要計算出用戶觸摸拖拽內(nèi)容在x軸上的速度

若圖層在未拖拽狀態(tài),則根據(jù)已經(jīng)記錄下的x軸移動速度,和通過受力計算出的加速度,改變x軸移動速度,最后在根據(jù)計算出的移動速度來計算實際位移

?

在SZSimplePhysicsDragSelector.m文件中添加以下代碼:

-(void) update:(ccTime)dt; {[self updateMove:dt]; }- (void) updateMove:(ccTime)dt {if ( !isDragging ){// *** CHANGE BEHAVIOR HERE *** // float F1 = 0.0f;float F2 = 0.0f;float F3 = 0.0f;CGPoint pos = s_content.position;//F1// frictionF1 = - xvel * 0.1;//F2// prevent icons out of rangeif ( pos.x < minX ){F2 = (minX - pos.x);}else if ( pos.x > maxX ){F2 = (maxX - pos.x);}//F3// suck planetif (fabsf(xvel) < 100 && !s_selectedIcon) {CCNode *nearestIcon = nil;for (CCNode *icon in s_icons) {if (nearestIcon) {CGPoint pt1 = [icon.parent convertToWorldSpace:icon.position];float distance1 = fabsf(pt1.x - [CCDirector sharedDirector].winSize.width/2);CGPoint pt2 = [nearestIcon.parent convertToWorldSpace:nearestIcon.position];float distance2 = fabsf(pt2.x - [CCDirector sharedDirector].winSize.width/2);if (distance1 < distance2) {nearestIcon = icon;}}else {nearestIcon = icon;}}if (nearestIcon) {s_selectedIcon = nearestIcon;if ([s_delegate respondsToSelector:@selector(onSelectedIconChanged:)]) {[s_delegate onSelectedIconChanged:self];}}}if (s_selectedIcon) {CGPoint pt = [s_selectedIcon.parent convertToWorldSpace:s_selectedIcon.position];;float distance = pt.x - [CCDirector sharedDirector].winSize.width/2;F3 = - distance;}//CALCULATEf = F1 + F2 + F3;acceleration = f/1;xvel += acceleration;pos.x += xvel*dt;s_content.position = pos;}else{xvel = ( s_content.position.x - lastx ) / dt;lastx = s_content.position.x;} }

在onEnter方法中,我們已經(jīng)啟用了計時器,所以udpate方法將會在每個最小時間間隔被調(diào)用?

其他就如同剛才整理的那樣,沒什么問題,主要使這個受力問題,這個受力是我經(jīng)過了好多數(shù)值的嘗試后,得出的比較能符合要求的效果

內(nèi)容受到的力分為

F1阻力:方向與內(nèi)容移動速度方向相反,大小與移動速度快慢呈正比

F1 = - xvel * 0.1;

F2超出邊界的額外受力:方向與超出邊界的方向相反,大小與超出邊界的距離呈正比

F2 = (minX - pos.x);或者F2 = (maxX - pos.x);

F3將選定節(jié)點吸至屏幕中央的吸力:方向從選定節(jié)點指向屏幕中央,大小與選定節(jié)點到屏幕中央的距離呈正比:

F3 = - distance;

此外有個細節(jié),如果我們不斷的施加吸力,會出現(xiàn)一種情況:很難將選定的節(jié)點拖拽出去,因為吸力太大了,所以在代碼中添加了一個條件

fabsf(xvel) < 100,當(dāng)移動速度小于100時,才產(chǎn)生吸力,這樣你會發(fā)現(xiàn)拖拽順暢多了,并且也能夠在選定了節(jié)點后短時間內(nèi)變?yōu)殪o止

?

還有什么?

最后在添加一個隨著移動而變化節(jié)點大小的效果,讓拖拽看起來更加舒服

在原有代碼內(nèi)添加以下內(nèi)容:

-(void) layout {int i = 1;for (CCNode *icon in s_icons) {CGPoint position = ccp((i-1) * 180, 0);float distance = fabsf(icon.position.x)/100;float scale = 1/(1+distance);icon.position = position;icon.scale = scale;//初始化縮放比例if (![s_content.children containsObject:icon]) {[s_content addChild:icon];}i++;}s_selectedIcon = [s_icons lastObject];if ([s_delegate respondsToSelector:@selector(onSelectedIconChanged:)]) {[s_delegate onSelectedIconChanged:self];}minX = - (i-1) * 180 - 100;maxX = 100; }-(void) update:(ccTime)dt; {[self updateMove:dt]; [self updateScale:dt];//更新縮放比例
}
-(void) updateScale:(ccTime)dt; {for (CCNode *icon in s_icons) {CGPoint pt = [self convertToNodeSpace:[icon.parent convertToWorldSpace:icon.position]];float distance = fabsf(pt.x)/100;icon.scale = 1/(1+distance);} }

?

測試

好了,代碼完成了,接下來測試一下效果

把HelloWorldLayer的初始化方法替換為以下代碼:

// create and initialize a LabelCCLabelTTF *label = [CCLabelTTF labelWithString:@"Sawyer's Test" fontName:@"Marker Felt" fontSize:64];// ask director for the window sizeCGSize size = [[CCDirector sharedDirector] winSize];// position the label on the center of the screenlabel.position = ccp( size.width /2 , size.height/2 );// add the label as a child to this Layer [self addChild: label];// add the test selector to the layerNSMutableArray *icons = [NSMutableArray array];int i = 10;while (i) {[icons addObject:[CCSprite spriteWithFile:@"Icon@2x.png"]];i--;}SZSimplePhysicsDragSelector *selector = [[[SZSimplePhysicsDragSelector alloc] initWithIcons:icons] autorelease];selector.position = self.anchorPointInPoints;
selector.Delegate = self;[self addChild:selector];

運行ios模擬器,你應(yīng)該看到以下效果:

?

還算滿意,希望大家能夠用到各位的游戲中

?

測試代碼

測試代碼可以在以下鏈接下載

SimplePhysicsDragSelectorTest.zip

?

轉(zhuǎn)載于:https://www.cnblogs.com/sawyerzhu/archive/2012/08/13/2636275.html

總結(jié)

以上是生活随笔為你收集整理的在Cocos2d中实现能够惯性拖动的选择界面的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。