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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

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

蘋果的應用講究用戶體驗

有的時候仔細想想

的確,很多細節決定了用戶體驗

比如說慣性拖動

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

?

Cocos2D中實現能夠慣性拖動的選擇界面

完成的效果:

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

?

首先新建一個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;//所有節點圖標的父節點NSMutableArray *s_icons;//節點圖標清單CCNode *s_selectedIcon;//選定的節點 BOOL isDragging;//是否在拖拽狀態CGPoint lastTouchPoint;//上一個觸摸點float lastx;//上一個圖層內容x坐標float xvel;//內容在x軸上的速度int maxX;//內容可以自然移動到的最大極限x坐標int minX;//內容可以自然移動到的最小極限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;

?在圖層選擇的節點發生改變時將會發送此消息給代理,如果改變為沒有選擇節點也會發送此消息

?

初始化

在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];//關閉計時器 [self removeChild:s_content cleanup:YES];[s_content release];s_content = nil;s_selectedIcon = nil;[super onExit]; }@end

?

以上代碼實現了初始化&內存釋放以及onEnter和onExist方法

在選擇器被添加到某一個節點中時,將會自動創建一個內容節點s_content,用來存放所有的節點,并一起移動

?

布局節點

在onEnter方法中布局視圖,并實現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作為每兩個節點之間的間距,同時第一個節點在s_content中的位置應該是(0,0)所以計算得出位置的公式(間距和初始位置可以根據需要更改)

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

之后添加節點到s_content,并且設置最后一個為初始選定的節點,最后通知代理選定節點發生更改

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

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

maxX = 100;

?

觸摸記錄?

布局完成,接下來我們需要實現觸摸事件消息來記錄數據供模擬物理使用

在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;
在方法中我們清空了選擇的節點并通知代理選擇的節點改變,標記自身狀態為拖拽中

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

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

標記自身狀態為未拖拽

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

標記自身狀態為未拖拽


這樣我們已經能夠辨別自身是否在拖拽狀態以及正確拖拽內容

?

模擬物理計算

首先說明一下思路:

我們在udpate方法中我們需要檢測圖層的狀態:

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

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

?

在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方法中,我們已經啟用了計時器,所以udpate方法將會在每個最小時間間隔被調用?

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

內容受到的力分為

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

F1 = - xvel * 0.1;

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

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

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

F3 = - distance;

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

fabsf(xvel) < 100,當移動速度小于100時,才產生吸力,這樣你會發現拖拽順暢多了,并且也能夠在選定了節點后短時間內變為靜止

?

還有什么?

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

在原有代碼內添加以下內容:

-(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模擬器,你應該看到以下效果:

?

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

?

測試代碼

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

SimplePhysicsDragSelectorTest.zip

?

轉載于:https://www.cnblogs.com/sawyerzhu/archive/2012/08/13/2636275.html

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。