游戏效果解密----水果忍者刀划痕效果

============================================================
博文原创,转载请声明出处
电子咖啡(原id蓝岩)
============================================================
闲来无事,开始研究opengl在cocos2d中图像渲染时候的底层用法,及除了CCScene,CCSprint普通接口以外,底层opengl图形纹理渲染部分的代码。github有不少很棒的demo,拿过来研究一下。在这里感谢开源社区的所有贡献者。


水果忍者刀划痕效果,github地址:https://github.com/hiepnd/CCBlade

该例子主要用一下两个文件:

CCBlade.m//划痕效果的顶点存储,opengl的渲染
TouchTrailLayer.m //继承CCLayer,接收touch的begin、end点,传递给CCBlade,进行动画显示

下面主要介绍CCBlade。由于代码较多,难度不一,因此我将我的理解注释和测试代码写入了文件中,你可以直接阅读代码,里面关键的地方我已经写上了注释。有助于理解,当然,我的理解肯定有错误,还请多多指正,共同进步。

效果图:

CCBlade.h

#import <Foundation/Foundation.h>
#import "cocos2d.h"


#define USE_STL_LIST    0

inline float fangle(CGPoint vect);
inline float lagrange1(CGPoint p1, CGPoint p2, float x);

inline void CGPointSet(CGPoint *v, float x, float y);
inline void f1(CGPoint p1, CGPoint p2, float d, CGPoint *o1, CGPoint *o2);

@interface CCBlade : CCNode {
    //轨迹所有点array
    NSMutableArray *path;
	unsigned int pointLimit;
	int count;
    //opengl中存储vertices & coordinate数组
	CGPoint *vertices;
	CGPoint *coordinates;
	BOOL reset;
	CCTexture2D *_texture;	
	float width;
    BOOL _finish;
    BOOL _willPop;
}
@property (readonly)unsigned int pointLimit;
@property(retain) CCTexture2D *texture;
@property(nonatomic) float width;
@property (nonatomic, assign) BOOL autoDim;

+ (id) bladeWithMaximumPoint:(int) limit;
- (id) initWithMaximumPoint:(int) limit;
- (void) push:(CGPoint) v;
- (void) pop:(int) n;
- (void) clear;
- (void) reset;
- (void) dim:(BOOL) dim;
- (void) finish;
@end


CCBlade.m

#import "CCBlade.h"

//计算旋转角度
inline float fangle(CGPoint vect){
    //相同,不旋转
	if (vect.x == 0.0 && vect.y == 0.0) {
		return 0;
	}
	//y不同,旋转90度
	if (vect.x == 0.0) {
		return vect.y > 0 ? M_PI/2 : -M_PI/2;
	}
	//x逆向
	if (vect.y == 0.0 && vect.x < 0) {
		return -M_PI;
	}
	//出去上面的特殊情况,剩下的atan计算角度
	float angle = atan(vect.y / vect.x);
    
	return vect.x < 0 ? angle + M_PI : angle;
}

//p1,p2 原点
//o1,o2 目标点
//d 宽度
inline void f1(CGPoint p1, CGPoint p2, float d, CGPoint *o1, CGPoint *o2){
	float l = ccpDistance(p1, p2);
    //计算转角角度
	float angle = fangle(ccpSub(p2, p1));
    //将p1点的width两端进行旋转
    //这里只取p1,是因为下一次p2会迭代为p1进行计算
	*o1 = ccpRotateByAngle(ccp(p1.x + l,p1.y + d), p1, angle);
	*o2 = ccpRotateByAngle(ccp(p1.x + l,p1.y - d), p1, angle);
}

inline float lagrange1(CGPoint p1, CGPoint p2, float x){
	return (x-p1.x)/(p2.x - p1.x)*p2.y + (x-p2.x)/(p1.x - p2.x)*p1.y ;
}

inline void CGPointSet(CGPoint *v, float x, float y){
	v->x = x;
	v->y = y;
}

@implementation CCBlade
@synthesize texture = _texture;
@synthesize pointLimit;
@synthesize width;
@synthesize autoDim;

+ (id) bladeWithMaximumPoint:(int) limit{
    return [[[self alloc] initWithMaximumPoint:limit] autorelease];    
}

- (id) initWithMaximumPoint:(int) limit{
    self = [super init];
        
    pointLimit = limit;
	self.width = 5;
	
    vertices = (CGPoint *)calloc(2*limit+5, sizeof(vertices[0]));
    coordinates = (CGPoint *)calloc(2*limit+5, sizeof(coordinates[0]));
    
    CGPointSet(coordinates+0, 0.00, 0.5);
    reset = NO;
    
    path = [[NSMutableArray alloc] init];

    return self;
}

- (void) dealloc{
    [_texture release];
    free(vertices);
    free(coordinates);
    
    [path release];	
	[super dealloc];
}

//move (populater) vertices
- (void) populateVertices{
    vertices[0] = [[path objectAtIndex:0] CGPointValue];
    //get the header point ,then iterate it
    CGPoint pre = vertices[0];
    
    unsigned int i = 0;
    
    /**
     glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
     glVertexPointer(2, GL_FLOAT, 0, vertices);
     */
    
    //get second point ,then iterate it
    CGPoint it = [[path objectAtIndex:1] CGPointValue];
	float dd = width / [path count];
    //as "pre"&"it" cost 2 item ,so "while" is "n-2"
//    printf("%s:%d sizeof CGPoing:%ld\n",__FUNCTION__,__LINE__,sizeof(CGPoint));
	while (i < [path count] - 2){
        //change the width (height) of the blade
        //"width - i * dd",dd不变 i 越大,越宽
		f1(pre, it, width - i * dd , vertices+2*i+1, vertices+2*i+2);
//		f1(pre, it, 7, vertices+2*i+1, vertices+2*i+2);
        //all coordinates are same value , never change value
		CGPointSet(coordinates+2*i+1, .5, 1.0);
		CGPointSet(coordinates+2*i+2, .5, 0.0);
		
		i++;
		pre = it;
		
		it = [[path objectAtIndex:i+1] CGPointValue];
	}
    
    CGPointSet(coordinates+1, 0.25, 1.0); 
	CGPointSet(coordinates+2, 0.25, 0.0);
	
	vertices[2*[path count]-3] = it;
	CGPointSet(coordinates+2*[path count]-3, 0.75, 0.5);
}
//移动点坐标,未用
- (void) shift{
	int index = 2 * pointLimit - 1;
	for (int i = index; i > 3; i -= 2) {
		vertices[i] = vertices[i-2];
		vertices[i-1] = vertices[i-3];
	}
}

- (void) setWidth:(float)width_{
    width = width_ * CC_CONTENT_SCALE_FACTOR();
}

#define DISTANCE_TO_INTERPOLATE 10 //允许的线条最大长度

//insert a point at the header of the path
- (void) push:(CGPoint) v{
    _willPop = NO;
    
	if (reset) {
		return;
	}
    //retina screen
    if (CC_CONTENT_SCALE_FACTOR() != 1.0f) {
        v = ccpMult(v, CC_CONTENT_SCALE_FACTOR());
    }


    if ([path count] == 0) {
        [path insertObject:[NSValue valueWithCGPoint:v] atIndex:0];
        return;
    }
    
    CGPoint first = [[path objectAtIndex:0] CGPointValue];
    if (ccpDistance(v, first) < DISTANCE_TO_INTERPOLATE) {
        //如果小于最大长度,则insert at the header
        [path insertObject:[NSValue valueWithCGPoint:v] atIndex:0];
        if ([path count] > pointLimit) {
            [path removeLastObject];
        }
    }else{
        //移除最后的vertices
        int num = ccpDistance(v, first) / DISTANCE_TO_INTERPOLATE;
        CGPoint iv = ccpMult(ccpSub(v, first), (float)1./(num + 1));
		for (int i = 1; i <= num + 1; i++) {
            [path insertObject:[NSValue valueWithCGPoint:ccpAdd(first, ccpMult(iv, i))] atIndex:0];
		}
		while ([path count] > pointLimit) {
			[path removeLastObject];
		}
    }



	
	[self populateVertices];
}
//移除最后点
- (void) pop:(int) n{
    while ([path count] > 0 && n > 0) {
        [path removeLastObject];
        n--;
    }
    
    if ([path count] > 2) {
        [self populateVertices];
    }
}

- (void) clear{
    [path removeAllObjects];
	reset = NO;
    if (_finish)
        [self removeFromParentAndCleanup:YES];
} 

- (void) reset{
	reset = TRUE;
}

- (void) dim:(BOOL) dim{
	reset = dim;
}

- (void) draw{
    if ((reset && [path count] > 0) || (self.autoDim && _willPop)) {
        [self pop:1];
        if ([path count] < 3) {
            [self clear];
        }
    }
    
    if ([path count] < 3) {
        return;
    }
    
    _willPop = YES;
	
    glDisableClientState(GL_COLOR_ARRAY);
    NSAssert(_texture, @"NO TEXTURE SET");
    
    /**
     f1(pre, it, width - i * dd , vertices+2*i+1, vertices+2*i+2);
     //		f1(pre, it, 7, vertices+2*i+1, vertices+2*i+2);
     //all coordinates are same value , never change value
     CGPointSet(coordinates+2*i+1, .5, 1.0);
     CGPointSet(coordinates+2*i+2, .5, 0.0);
     */
    
    glBindTexture(GL_TEXTURE_2D, _texture.name);
//    glVertexPointer(2, GL_FLOAT, 0, vertices);
    glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
    glVertexPointer(2, GL_FLOAT, 0, vertices);
//    glTexCoordPointer(2, GL_FLOAT, 0, vertices);
//    glVertexPointer(2, GL_FLOAT, 0, coordinates);

    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 2*[path count]-2);
    //不是blade,是fans
//    glDrawArrays(GL_TRIANGLE_FAN, 0, 2*[path count]-2);
	glEnableClientState(GL_COLOR_ARRAY);
}

- (void) finish
{
    _finish = YES;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值