永利总站网址

向结点加多动作,依然个初级中学生

五月 23rd, 2019  |  永利总站网址

因为年轻时对于主角女友的背叛这点看的很重,导致直到现在才去回顾这部影片。众多明星
穿着各有风格。从车手的眼神 手上动作 脚部变换 水杯振动 乘客神态 以及
透视的气缸工作情况,描写出了车神的造诣,生动且专业。本片印象最深的要数杜汶泽搞怪的戏份,还有赛道解说那句“86上山了!”王者归来之感。周董的似乎就是在扮演他自己,表情动作都是他的招牌,对于26岁的他来说,能够带领观众走进该人物内心已足够

绘制精灵十分有用,但是静态的内容就是一幅画,而不是一个游戏。为了添加游戏元素,你需要让精灵在屏幕上移动并执行其他的一些逻辑。SpriteKit制作动画的主要机制就是动作,如果你是一路学到这里的那么你已经了解了动作子系统的一部分内容。现在,是时间深入了解一下动作是怎样构造并执行的了。
动作就是你想要对场景做的改变。大部分情况下,一个动作会将它的改变应用到执行它的结点上。比如说,如果我们想要移动一个精灵穿过屏幕,你要创建一个move动作并让精灵结点执行这个动作。SpriteKit会自动改变精灵的位置直到动画结束。

动作是自包含对象

每个动作都是描述如何改变场景的不透明的对象。所有的动作均继承自SKAction,它没有明确的实例,实际上,不同类型的动作都是使用类方法实例化的(工厂模式?)。例如,下面是一些动作可以完成的一些事情

  • 改变结点的位置或者方向
  • 改变结点的大小或者缩放属性
  • 改变结点的可见性或者使它变得透明
  • 改变节点的内容使得它可以执行贴图动画(一系列的贴图逐帧显示形成动画)
  • 给结点着色
  • 播放声音
  • 将一个结点从结点树中移除
  • 调用一个代码块(block)
  • 调用一个对象的方法

当你创建一个结点之后,它的类型就无法改变了,而且他的属性也很少能够改变。SpriteKit利用不可变对象的优势以提高执行效率。

提示:因为动作时高效的不可变对象,收益以你可以安全的将相同的动作同时应用在多个不同的结点上。基于这个缘故,如果在你的游戏中需要反复执行一个动作,你可以只只创建一个动作对象,然后在你需要的时候将它应用到结点上执行就可以了。

动作可以时即时的也可以不是即时的

  • 即时的动作在一个动画帧里开始和结束。例如,将一个结点从结点树中移除就是一个即时动作,因为结点不能部分的移除。当动作执行,结点就会立即移除。
  • 非即时动作进行动画效果时需要跨度一个时间段。动作执行时,会在每个动画帧处理动作知道动作结束。

用来创建动作的完整的类方法雷彪参见SKAction class
Reference,但是只有当你需要知道怎样配置某个动作的详细方法时才需要查阅。

结点执行方法

只有你让一个结点执行动作时动作才会执行。执行一个动作最简单的方法时让一个结点调用runAction:方法。代码
3-1 创建了一个move动作并让一个结点执行它。
代码 3-1 执行一个动作

SKAction *moveNodeUp = [SKAction moveByX:0.0 y:100.0 duration:1.0];
[rocketNode runAction: moveNodeUp];

move动作需要一段时间,所以这个动作会跨越很多动画帧直到时间到达动作的duration用完为止。当动作执行完后,动作会从结点移除
你可以在任何时候执行动作。当你添加一个动作到结点而这是场景正在处理动作时,动作也许不会立即执行知道下一帧开始。场景用来处理动作的步骤在
高级场景处理中描述
结点可以同时执行多个动作,即使这些动作是在不同时刻开始执行的。场景会保存每个动作是何时开始执行的并计算此刻该动作对结点造成的影响。例如,如果你执行两个动作移动同一个结点,两个动作会对每一帧造成改变。如果两个move动作相同但是方向相反,那么结点会呆在原点不动。
因为动作的处理是和场景绑定在一起的,所以只有在结点是当前显示的结点树的一部分时,动作才会被处理。你可以充分利用这个特性,创建一个结点并将动作赋给它,但是只有当该结点被添加到当前场景的结点上去时,动作才会执行。当结点添加到场景,它的动作会立即执行。当结点被拷贝时,动作正在执行的动作也一同被拷贝和打包,所以这个特性非常有用。
如果结点正在执行动作,那么hasActions属性会返回YES

取消正在执行的动作

要取消一个动作正在执行的动作,调用它的
removeAllActions方法,所有的动作都会立即从结点移除。如果被移除的结点有duration,动作对结点做的任何改变都会原封不动的保存,但会后续帧的改变不会执行。

动作完成时执行回调

runAction:completion:
方法和runAction:方法是一样的,但是当动作结束时,completion代码块会被执行。这个回调只有在动作结束时才会调用。如果动作完成前被移除了,那么这个回调不会执行。

使用命名的动作以便对动作精确控制

通常情况下,你不知道那些动作正在执行,如果你想移除动作,你必须移除所有的动作。如果你需要知道某一个动作是否正在执行或者想要移除某一个动作,你必须使用命名的动作。命名的动作使用一个唯一的key来标识该动作,你可以在结点上开始,移除,查找以及替换命名的动作
代码 3-2和 3-1 相似,但是动作使用一个key进行标识
代码 3-2 运行命名的动作

[SKAction *moveNodeRight = [SKAction moveByX:100.0 y:0.0 duration:1.0];
[spaceship runAction: moveNodeRight withKey:@"ignition"];

可以使用如下基于key的方法:

  • runAction:withKey:
    方法执行一个动作,如果具有相同key的动作正在执行,在新的动作被添加之前,旧的同key动作会被移除
  • actionForKey: 方法确定动作是否已经执行
  • removeActionForKey:方法移除动作

代码 3-3
演示如何使用命名动作控制精灵的移动。如果用户在场景中点击,方法被调用。这段代码确定点击发生的位置然后让精灵向那个位置移动。动作执行的时间会根据距离进行计算,因此精灵总是保持一个固定的速度。因为代码使用了runAction:withKey:
方法,所以如果精灵已经在运动,前一个运动会停止,新的运动会从当前点开始到达一个新的位置。
代码 3-3 移动精灵到当前点击位置

- (void)mouseDown:(NSEvent *)theEvent
{
    CGPoint clickPoint = [theEvent locationInNode:self.playerNode.parent];
    CGPoint charPos = self.playerNode.position;
    CGFloat distance = sqrtf((clickPoint.x-charPos.x)*(clickPoint.x-charPos.x)+
                             (clickPoint.y-charPos.y)*(clickPoint.y-charPos.y));

    SKAction *moveToClick = [SKAction moveTo:clickPoint duration:distance/characterSpeed];
    [self.playerNode runAction:moveToClick withKey:@"moveToClick"];
}

永利总站网址,创建执行其他动作的动作

SpriteKit提供了改变场景中结点属性的许多标准动作,但是只有将它们结合起来,动作才能发出巨大的威力。通过组合动作,执行单独一个动作就可以创建复杂的令人印象深刻的动画来。组合动作比起其他基本动作类型更简单。在脑中存有这些印象,我们开始学习序列动作,组动作以及重复动作。

  • 序列动作(动作序列)包含几个子动作,几个动作按照先后顺序依次执行
  • 组动作也是由几个动作组成,所有的动作保存在一个族中并且同时执行
  • 重复动作只有一个子动作,当子动作结束时,它会重新执行

动作序列串行执行

一个动作序列会依次连续的执行。当一个结点执行动作序列时,动作会连续的依次触发。当一个动作结束,下一个动作立即开始执行。当序列中的最后一个动作执行完毕,整个动作序列也就执行完毕了。
代码 3-4 演示使用动作的一个数组来创建动作序列
代码 3-4 创建动作序列

SKAction *moveUp = [SKAction moveByX:0 y:100.0 duration:1.0];
SKAction *zoom = [SKAction scaleTo:2.0 duration:0.25];
SKAction *wait = [SKAction waitForDuration: 0.5];
SKAction *fadeAway = [SKAction fadeOutWithDuration:0.25];
SKAction *removeNode = [SKAction removeFromParent];

SKAction *sequence = [SKAction sequence:@[moveUp, zoom, wait, fadeAway, removeNode]];
[node runAction: sequence];

这个例子中有以下事情需要注意:

  • wait动作是经常在动作序列中使用的一种特殊的动作。这个动作会等待一段时间,什么都不做。使用这个动作可以控制序列的时间
  • removeNode动作是一个即时动过,所以他执行时不会耗费时间。你可以看到尽管这个动作是序列的一部分,它并没有出现在图表3-1的时间线中。作为一个即时动作,它在fade动作完成后一开始就立即结束了,这个动作结束了序列。
![](https://upload-images.jianshu.io/upload_images/38765-4c8a13d30045d9a7.png)

图表 3-1 移动和zoom序列时间线

组动作并行执行

组动作是动作的集合,当组动作执行时,集合中的动作立刻同时执行。当你希望几个动作同步执行时,可以使用组动作。例如,代码
3-5
旋转并改变精灵的方向,来给人一种车轮在屏幕上滚动的错觉。使用组动作(而不是执行两个独立的动作)可以强调这两个动作是强相关的
代码 3-5 使用组动作模拟车轮滚动

SKSpriteNode *wheel = (SKSpriteNode *)[self childNodeWithName:@"wheel"];
CGFloat circumference = wheel.size.height * M_PI;

SKAction *oneRevolution = [SKAction rotateByAngle:-M_PI*2 duration:2.0];
SKAction *moveRight = [SKAction moveByX:circumference y:0 duration:2.0];

SKAction *group = [SKAction group:@[oneRevolution, moveRight]]; [wheel runAction:group];

尽管组动作中的动作是一起执行的,但是知道时间最长的那个动作(最后那个执行完毕的动作)执行完毕时,组动作才执行完毕。代码3-6演示了一个更加复杂的组动作,其中的动作具有不同的时间值。精灵执行它的贴图序列并在两秒内向屏幕下方移动,然而,在第1秒内,精灵不断放大,并且从完全透明变成完全不透明的。图表3-2
演示了组动作中其中两个动作使用一半的时间让精灵显示,组动作继续执行直到剩余两个动作执行完毕为止

[sprite setScale: 0];
SKAction *animate = [SKAction animateWithTextures:textures timePerFrame:2.0/numberOfTextures];
SKAction *moveDown = [SKAction moveByX:0 y:-200 duration:2.0];
SKAction *scale = [SKAction scaleTo:1.0 duration:1.0];
SKAction *fadeIn = [SKAction fadeInWithDuration: 1.0];

SKAction *group = [SKAction group:@[animate, moveDown, scale, fadeIn]];
[sprite runAction:group];

永利总站网址 1

图表 3-2 组动作中的动作同时开始,但不同时结束

重复动作多次执行另一个动作

重复动作循环执行另一个动作使得它能执行多次。当重复动作执行时,它会执行它包含的动作。当该动作执行完毕时,它会被重复动作重新开始。代码
3-7
演示了如何创建一个重复动作。你可以创建执行数次或者执行无数次的重复动作
代码 3-7 创建重复动作

SKAction *fadeOut = [SKAction fadeOutWithDuration: 1];
SKAction *fadeIn = [SKAction fadeInWithDuration: 1];
SKAction *pulse = [SKAction sequence:@[fadeOut,fadeIn]];

SKAction *pulseThreeTimes = [SKAction repeatAction:pulse count:3];
SKAction *pulseForever = [SKAction repeatActionForever:pulse];

图表3-3 显示了pulseThreeTimes
动作的时间序列,你可以看到动作完成后又重新执行

永利总站网址 2

图表 3-3 重复动作序列

当你重复执行一个组动作时,整个组必须执行完毕后才能重新开始执行。代码 3-8
创建一个组动作移动一个精灵并执行精灵贴图序列动画。但是在这个例子中,这两个动作具有不同的时间。图表3-4显示了组动作重复执行的时间表格。你可以看到贴图动作完成后就不再执行直到组动作开始重复执行

永利总站网址 3

重复组动作的时间表格

你可能需要每个动作以它自己自然的频率运行。要做到这一点,创建一组重复动作并使用他们创建一个组动作。代码
3-9演示了图表3-5 显示的时间序列

SKAction *animate = [SKAction animateWithTextures:textures timePerFrame:1.0/numberOfImages];
SKAction *moveDown = [SKAction moveByX:0 y:-200 duration:2.0];

SKAction *repeatAnimation = [SKAction repeatActionForever:animate];
SKAction *repeatMove = [SKAction repeatActionForever:moveDown];

SKAction *group = [SKAction group:@[repeatAnimation, repeatMove]];

永利总站网址 4

动作按照自己的自然频率运行

配置动作的时间

默然情况下,非即时动作会将改变线性的分配到整个时间线上。然而,你可以使用几个属性调整动作的时间线

  • 通常的,动作是线性执行的。但是,你可以使用动作的timingMode属性来选择一个非线性的动画时间。例如,你可以让动画一开始快速执行,然后再剩下的时间里速度慢下来
  • 动作的speed属性改变动画执行的频率(rate),你可以在原来的基础上加快或减慢。默认的频率是1.0,如果你设置动作的speed属性为2.0,当结点执行该动作时,他有两倍快。要暂停动作,将speed设为0
    如果你要调整包含其他动作的动作(例如组动作,序列动作,重复动作),这个属性会影响到它所包含的动作上。同时这些动作也会被它们自己的speed属性影响
  • 结点的speed属性和动作的speed属性的效果相同,不同的是效果会影响到应用到该结点的所有动作以及该结点的的子节点上

SpriteKit会计算应用到动作上的所有的speed并将他们相乘作为动作的speed

使用动作的小技巧

最好的做法是创建一次动作然后应用多次。如果可能的画,尽可能早的创建动作然后将它保存在易于访问和执行的地方
取决于动作的雷翔,下面是一些保存动作较好的地方

  • 结点的userData属性
  • 如果具有相同父节点的一系列子节点共享同一个动作,
    将其保存在父结点的userData属性
  • 如果贯穿整个场景一个动作被多个动作共享,
    将其保存在场景的userData属性
  • 如果是继承,保存在子类的某个属性上
    如果你需要设计者或者艺术家输入一个结点是怎样动画的,考虑将创建动作的代码移到你自顶一个设计工具上。然后对动作进行打包并将其加载到你的动画引擎中,更多的信息参见
    Sprite Best Practices

不应使用动作的场合

虽然动作很高效,但是创建和使用它们仍然有很多的开销。如果你想在每一帧动画中改变结点的属性,在每一帧重新计算结点的属性,
你最好直接应用这些改变而不用动作来实现。想要知道在哪儿可以做这些处理,参考
Advanced Secen Processing

标签:, , , ,

Your Comments

近期评论

    功能


    网站地图xml地图