Spritekit и OpenGL: плавный след дыма

Я хочу добиться этого эффекта в моей игре Spritekit, где за персонажем есть плавный след.

Посмотрите след за монетой в джетпаке: введите описание изображения здесь

И этот след за героем в «Прыжке Юпитера»: введите здесь описание изображения

Или этот супергладкий след за героем в Ski Safari: введите описание изображения здесь

Может быть, это стандартная функция в других игровых движках? Я думаю, что эмиттер частиц spritekit обеспечит только блочный / штампованный след, а не гладкий след. Должен ли я использовать какой-то пользовательский шейдер спрайтов? Есть еще какие-то изобретательские мысли?


person Patrick Collins    schedule 19.03.2015    source источник
comment
Подобный эффект определенно является пользовательским кодом. Вероятно, что-то вроде создания направленных под углом бок о бок прямоугольников с таймером продолжительности затухания альфа-канала.   -  person sangony    schedule 19.03.2015
comment
Кажется сложным избежать зазубренных краев таким образом :/   -  person hamobi    schedule 20.03.2015
comment
Согласованный. Должен ли я прибегать к CG, рисуя сплайн? Чувствуется очень неэффективно...   -  person Patrick Collins    schedule 20.03.2015


Ответы (1)


Ваш вопрос не включал ключевой вопрос, а именно тип движения, который следует использовать. Мой ответ основан на прикосновении к экрану в качестве точки назначения, но другой альтернативой является использование основного движения. Независимо от того, какой метод используется, основные принципы кода остаются неизменными. Менялась бы только реализация.

В моем примере я использовал прямоугольное изображение хвоста, потому что хотел, чтобы вы могли скопировать и запустить код примера. Вы должны заменить прямоугольник изображением/текстурой круга, чтобы придать хвосту более гладкие стороны.

Изменение значения fadeOutDuration приведет к более длинному или более короткому хвосту.

Изменение stepsDivider приведет к большему или меньшему количеству узлов в хвосте.

#import "GameScene.h"

@implementation GameScene {
    SKSpriteNode *playerNode;
    CGPoint destinationPoint;
    NSMutableArray *myArray;
    NSMutableArray *myDiscardArray;
    BOOL working;
    int numberOfSteps;
    float xIncrement;
    float yIncrement;
    float fadeOutDuration;
    int stepsDivider;
}

-(void)didMoveToView:(SKView *)view {
    self.backgroundColor = [SKColor blackColor];

    playerNode = [SKSpriteNode spriteNodeWithColor:[SKColor whiteColor] size:CGSizeMake(30, 30)];
    playerNode.position = CGPointMake(200, 200);
    [self addChild:playerNode];

    myArray = [[NSMutableArray alloc] init];
    myDiscardArray = [[NSMutableArray alloc] init];

    working = false;
    fadeOutDuration = 0.5;
    stepsDivider = 10;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    for (UITouch *touch in touches) {
        CGPoint location = [touch locationInNode:self];

        if(working == false) {
            destinationPoint = location;

            if(fabsf(location.x - playerNode.position.x) > fabsf(location.y - playerNode.position.y)) {
                numberOfSteps = fabsf(location.x - playerNode.position.x) / 10;
            } else {
                numberOfSteps = fabsf(location.y - playerNode.position.y) / 10;
            }

            xIncrement = (location.x - playerNode.position.x) / numberOfSteps;
            yIncrement = (location.y - playerNode.position.y) / numberOfSteps;

            working = true;
        }
    }
}

-(void)update:(CFTimeInterval)currentTime {

    if (working == true) {

        // create trail node at current player's position
        SKSpriteNode *myNode = [SKSpriteNode spriteNodeWithColor:[SKColor whiteColor] size:CGSizeMake(30, 30)];
        myNode.position = playerNode.position;
        [self addChild:myNode];
        [myArray addObject:myNode];
        [myNode runAction:[SKAction fadeOutWithDuration:fadeOutDuration]];

        // check array for any nodes with zero alpha
        for(SKSpriteNode *object in myArray) {
            if(object.alpha == 0) {
                [myDiscardArray addObject:object];
            }
        }

        // remove zero alpha nodes
        if([myDiscardArray count] > 0) {
            [myArray removeObjectsInArray:myDiscardArray];
            [myDiscardArray removeAllObjects];
        }

        // update player's new position
        playerNode.position = CGPointMake(playerNode.position.x+xIncrement, playerNode.position.y+yIncrement);

        // check if player has arrived at destination
        if(((int)playerNode.position.x == (int)destinationPoint.x) && ((int)playerNode.position.y == (int)destinationPoint.y)) {
            working = false;
        }
    }
}

@end
person sangony    schedule 22.03.2015
comment
Спасибо Сангони за усилия. Как вы сказали, я думаю, что это хорошее приближение к эффекту. Когда я смотрю на оригиналы, мне интересно, это какой-то цветовой градиент на пути Безье или, может быть, какой-то шейдер GLSL. - person Patrick Collins; 22.03.2015
comment
@PaddyCollins - Какой бы метод вы ни выбрали, принципы в значительной степени останутся прежними. В какой-то момент вам придется «нарисовать» форму/узел/пакет с периодом жизни альфа-затухания. Если вы в конечном итоге получите действительно хорошее разрешение, вы рискуете сильно нагрузить процессор только для этого эффекта и можете получить напряженный подсчет FPS. Возможно, этот эффект является стандартной частью других языков, таких как Unity, но я уверен, что SpriteKit не имеет такой встроенной функции. - person sangony; 23.03.2015
comment
Может ли кто-нибудь преобразовать это в быстрый код? Я плохо разбираюсь в Obj-C. Спасибо! - person Noah Covey; 17.03.2016
comment
@sangony Я с большим успехом адаптировал этот отличный код для своего проекта Xamarin iOS, большое спасибо. Только одна мелочь: есть ли небольшая утечка памяти? Если я правильно понял, как только playerNode прибудет к месту назначения, для работы будет установлено значение false, поэтому блок внутри Update больше не будет вызываться. Это означает, что последняя очистка исчезнувших узлов не будет выполнена, и они останутся без дела. Я вижу это на моем счетчике узлов SKView. Очищает ли Objective-C здесь, поэтому вам не нужно об этом беспокоиться, а мне нужно сделать отдельную утилизацию? - person Marakai; 24.05.2016