接下来要出场的几个类的确很酷,你会发现很多游戏都在使用他们。
本章用到了以下几个类:
CCProgressTimer
CCParallaxNode
CCRibbon
CCMotionStreak
先看效果吧
CCParallaxNode(视差节点)
从名字上看得出来,此类继承自CCNode。cocos2D官方是这样说的:
A node that simulates a parallax scroller
The children will be moved faster / slower than the parent according the the parallax ratio.
此类能容纳一些孩子们,这些孩子们一般情况而言都是CCSprite。这些孩子们会按照事先设定好的比例来进行不同速度的移动。最终造成一种视觉上的景深感。
看代码吧:
-(id)init
{
if (self = [super init]) {
CCParallaxNode *para = [CCParallaxNode node];
CGSize sizeOfWin = [[CCDirector sharedDirector] winSize];
//用4个精灵来显示4个层次
CCSprite *parallax1 = [CCSprite spriteWithFile:@"parallax1.png"];
CCSprite *parallax2 = [CCSprite spriteWithFile:@"parallax2.png"];
CCSprite *parallax3 = [CCSprite spriteWithFile:@"parallax3.png"];
CCSprite *parallax4 = [CCSprite spriteWithFile:@"parallax4.png"];
//设置孩子们的定位点
parallax1.anchorPoint = CGPointMake(0, 1);
parallax2.anchorPoint = CGPointMake(0, 0.5);
parallax3.anchorPoint = CGPointMake(0, 0.5);
parallax4.anchorPoint = CGPointMake(0, 0);
CGPoint topOffset = CGPointMake(0, sizeOfWin.height);
CGPoint midOffset = CGPointMake(0, sizeOfWin.height * 0.5);
CGPoint zeroOffset = CGPointZero;
//注意,这里并不是真正的坐标点,而是用来控制速度的。
CGPoint piont1 = CGPointMake(0.5f, 0);
CGPoint piont2 = CGPointMake(1, 0);
CGPoint piont3 = CGPointMake(1.5, 0);
CGPoint piont4 = CGPointMake(2, 0);
//添加4个孩子,注意z轴、速度差别和基于定位点的Offset
[para addChild:parallax1 z:1 parallaxRatio:piont1 positionOffset:topOffset];
[para addChild:parallax2 z:2 parallaxRatio:piont2 positionOffset:midOffset];
[para addChild:parallax3 z:3 parallaxRatio:piont3 positionOffset:midOffset];
[para addChild:parallax4 z:4 parallaxRatio:piont4 positionOffset:zeroOffset];
[self addChild:para];
//动起来
CCMoveTo *moveTo = [CCMoveTo actionWithDuration:5 position:CGPointMake(-330, 0)];
CCMoveTo *moveBack = [CCMoveTo actionWithDuration:5 position:CGPointMake(0, 0)];
CCSequence *se = [CCSequence actions:moveTo,moveBack, nil];
CCRepeatForever *repeat = [CCRepeatForever actionWithAction:se];
[para runAction:repeat];
}
return self;
}
关键地方都写上注释了。不难,但是很有用的一个类。
注意,因为移动速度不一、各个层的的定位点和Offset不一样,在moveTo的时候边界的计算要综合考虑,避免移出边界。
CCProgressTimer(进度条)
时间进度类可用于很多地方,比如加载进度条,或者用于展示处于失效状态的按钮恢复到激活状态所需要的时间。比如WOW中技能的CD冷却效果,想象一下如何实现这个效果,一个显示技能图标的精灵,一个半透明的起蒙版作用的遮罩精灵。
恩,我尝试着模仿了一下游戏中的技能冷却效果,还不错。这里特别提一下像素的【刀剑2】一个很值得一玩的游戏。
先看cocos2D官方的一句话定义:
CCProgresstimer is a subclass of CCNode. It renders the inner sprite according to the percentage. The progress can be Radial, Horizontal or vertical.
其实有了这句话,不用多解释了,足够清晰!想象一下你接触过的游戏,很多效果用CCprogressTimer都可以实现。
我的例子中因为考虑到代码的可扩展性,定义了一个继承自CCNode的类。此类中其中一个CCSprite成员显示技能,一个CCProgressTimer用来起蒙版作用,另外也将技能CD冷却时间作为属性加入了类中。下面是SkillSprite的代码:
//
// SkillSprite.m
// CH07
//
// Created by phc on 11-12-17.
// Copyright (c) 2011年 hxsoft. All rights reserved.
//
#import "SkillSprite.h"
@implementation SkillSprite
-(void)dealloc
{
//这个Scheduler必须手动释放
[[CCScheduler sharedScheduler] unscheduleAllSelectorsForTarget:self];
[super dealloc];
}
//向外部提供的静态初始化方法
+(id)skillWithParent:(CCNode *)parentNode spriteFile:(NSString *)fileName
{
return [[[self alloc] initWithParent:parentNode spriteFile:fileName] autorelease];
}
-(int)cdTime
{
return cdTime_;
}
-(void)setCdTime:(int)cdTime
{
cdTime_ = cdTime;
}
-(id)initWithParent:(CCNode *)parentNode spriteFile:(NSString *)fileName
{
if (self = [super init]) {
CCSpriteFrameCache *frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];
[frameCache addSpriteFramesWithFile:@"skill.plist"];
//根据传递进来的fileName参数显示相应的图片给skillSprite
skillSprite = [CCSprite spriteWithSpriteFrame:[frameCache spriteFrameByName:fileName]];
//构造蒙版层,mask.png本身带有透明效果。
maskSprite = [CCProgressTimer progressWithFile:@"mask.png"];
//技能图标在下,蒙版在上
[self addChild:skillSprite z:0];
[self addChild:maskSprite z:1];
}
return self;
}
//在onEnter中注册目标触摸事件委托,注意不要漏掉[super onEnter]
-(void)onEnter
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
[super onEnter];
}
//在onExit中注销目标触摸事件委托
-(void)onExit
{
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
[super onExit];
}
//为了检测是否命中,需要获取自身的rect,这里需要好好琢磨。
-(CGRect)getRect
{
CGRect tmp = [maskSprite boundingBox];
return CGRectMake(-tmp.size.width * 0.5, -tmp.size.height * 0.5, tmp.size.width, tmp.size.height);
}
-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
//获得触摸点坐标
CGPoint touchPos = [self convertTouchToNodeSpaceAR:touch];
if (CGRectContainsPoint([self getRect], touchPos)) {
if (CDLock) {
return YES;
}
//如果命中了技能
maskSprite.percentage = 100;
//让蒙版转起来
[[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:0 paused:NO];
timePicker = 0;
CDLock = YES;
return YES;
}
return NO;
}
-(void)update:(ccTime)dt
{
//把时间片累加起来,来对应显示进度。
timePicker += dt*10;
float percent =100 - timePicker / cdTime_*100;
if (percent <= 0) {
percent = 0;
maskSprite.percentage = 0;
[[CCScheduler sharedScheduler] unscheduleAllSelectorsForTarget:self];
CDLock = NO;
}else
{
maskSprite.percentage = percent;
}
}
@end
1. 注册了目标触摸,实现了触摸的began方法。
2. 在初始化方法中将fileName作为参数传了进来,当然,也可以把CDTime作为参数加进去。
3. 根据CDTime,在update方法中percentage的增加速度。
CCRibbon(链条)和CCMotionStreak(拖尾链条)
官方的,总是最权威的:
A CCRibbon is a dynamically generated list of polygons drawn as a single or series of triangle strips. The primary use of CCRibbon is as the drawing class of Motion Streak, but it is quite useful on it's own. When manually drawing a ribbon, you can call addPointAt and pass in the parameters for the next location in the ribbon. The system will automatically generate new polygons, texture them accourding to your texture width, etc, etc.
从其中可以看出来CCRibbon和CCMotionStreak似乎密切相关(其实在CCMotionStreak中CCRibbon是作为一个属性出现的)。addPoiontAt是最重要的一个方法,它的作用是在该坐标点上添加一个新的链条。
//创建链条对象
-(void)createRibbon
{
CCRibbon *rib = [CCRibbon ribbonWithWidth:32 image:@"sl.png" length:32 color:ccc4(255, 255, 255, 255) fade:125];
[self addChild:rib z:3 tag:TAGRIBBON];
}
//移除链条对象,本例中在touchEnd的时候使用
-(void)removeRibbon
{
[self removeChildByTag:TAGRIBBON cleanup:YES];
}
//创建拖尾链条,我写错了单词了,:-)
-(void)createMotionSpeak
{
CCMotionStreak *mot = [CCMotionStreak streakWithFade:0.4f minSeg:100 image:@"sl.png" width:16 length:16 color:ccc4(255, 0, 255, 255)];
[self addChild:mot z:3 tag:TAGMOTIONSTREAK];
}
//移除拖尾链条
-(void)removeMotionStreak
{
[self removeChildByTag:TAGMOTIONSTREAK cleanup:YES];
}
//本例同时展示了链条和拖尾链条,没有办法,奇数次使用CCRibbon,偶数次使用CCMotionStreak
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchPosition = [[CCDirector sharedDirector] convertToGL:[touch locationInView:touch.view]];
if (isMotionStreak) {
[self createMotionSpeak];
CCMotionStreak *mot = [self getChildByTag:TAGMOTIONSTREAK];
[mot.ribbon addPointAt:touchPosition width:16];
} else {
[self createRibbon];
CCRibbon *rib = [self getChildByTag:TAGRIBBON];
[rib addPointAt:touchPosition width:8];
}
}
//在触摸点移动的时候,addPointAt。
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchPosition = [[CCDirector sharedDirector] convertToGL:[touch locationInView:touch.view]];
if (isMotionStreak) {
CCMotionStreak *mot = [self getChildByTag:TAGMOTIONSTREAK];
[mot.ribbon addPointAt:touchPosition width:16];
} else {
CCRibbon *rib = [self getChildByTag:TAGRIBBON];
[rib addPointAt:touchPosition width:8];
}
}
//触摸结束的时候,清除链条
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (isMotionStreak) {
[self removeMotionStreak];
} else {
[self removeRibbon];
}
isMotionStreak = !isMotionStreak;
}