Ответ 1
К сожалению, SKShapeNode не так хорош для того, что вы пытаетесь сделать. Тем не менее, есть способ оптимизировать это, хотя и с некоторыми оговорками.
Первая из самых больших проблем с fps заключается в том, что счетчик обращений становится чрезвычайно высоким, потому что каждый сегмент линии, который вы добавляете, является другой ничьей. Если вы установите showsDrawCount
на свой экземпляр SKView
, вы увидите, что я имею в виду.
В этом ответе Несколько skshapenode в одной ничьей? вы можете получить дополнительную информацию о том, как вы можете использовать свойство shouldRasterize
для SKEffectNode
для решения проблемы если вы рисуете что-то один раз. Если вы этого не сделаете, у вас будет время процессора, затрачиваемое на многочисленные розыгрыши каждого кадра.
Итак, вы можете видеть, что розыгрыши являются основной проблемой, когда вы не получаете желаемого результата. Тем не менее, вы, похоже, хотите со временем последовательно рисовать, поэтому то, что я собираюсь предложить, может быть жизнеспособным решением для вас.
Логика решения, которое я предлагаю, такова:
1 - Создайте SKSpriteNode
, который мы можем использовать в качестве холста.
2 - Создайте один SKShapeNode
, который будет использоваться, чтобы нарисовать ТОЛЬКО текущий сегмент линии.
3 - Сделайте SKShapeNode
дочерний элемент холста.
4 - Нарисуйте новый сегмент линии через SKShapeNode
5 - Используйте метод SKView
`textureFromNode, чтобы сохранить то, что было нарисовано на холсте.
6 - установите текстуру холста на эту текстуру.
Возвратитесь к # 4 и создайте новый путь для SKShapeNode
для следующего сегмента линии.
Повторите при необходимости.
Результат должен состоять в том, что ваш счетчик ничья никогда не будет выше, чем 2 ничьи, что позволило бы решить проблему с большим количеством фишек.
В принципе, вы сохраняете то, что ранее было нарисовано в текстуре, поэтому требуется только один SKShapeNode
draw для последнего сегмента линии и один ничья для SKTexture
.
Опять же, я еще не пробовал этот процесс, и если есть какое-либо отставание, то в этом случае textureFromNode
вызывается каждый кадр. Если бы это было вашим узким местом, это было бы так!
Я могу попробовать эту теорию некоторое время сегодня, так как мне нужно textureFromNode
для другой проблемы, которую я пытаюсь решить, и поэтому я точно узнаю, насколько быстрым/медленным этот метод! ха
UPDATE
Это не полный код, но важная часть для достижения желаемой производительности чертежа (60 кадров в секунду):
Основными элементами node являются:
container → SKNode, содержащий все элементы, которые необходимо кэшировать
canvas → SKSpriteNode, который отображает кэшированную версию отрисованных сегментов
пул сегментов → используется для извлечения сегментов изначально и при необходимости повторно используется
Сначала создайте пул SKShapeNodes:
pool = [[NSMutableArray alloc]init];
//populate the SKShapeNode pool
// the amount of segments in pool, dictates how many segments
// will be drawn before caching occurs.
for (int index = 0; index < 5; index++)
{
SKShapeNode *segment = [[SKShapeNode alloc]init];
segment.strokeColor = [SKColor whiteColor];
segment.glowWidth = 1;
[pool addObject:segment];
}
Далее создайте метод для получения SKShapeNode из пула:
-(SKShapeNode *)getShapeNode
{
if (pool.count == 0)
{
// if pool is empty,
// cache the current segment draws and return segments to pool
[self cacheSegments];
}
SKShapeNode *segment = pool[0];
[pool removeObjectAtIndex:0];
return segment;
}
Затем создайте метод для получения сегмента из пула и рисования строки:
-(void)drawSegmentFromPoint:(CGPoint)fromPoint toPoint:(CGPoint)toPoint
{
SKShapeNode *curSegment = [self getShapeNode];
CGMutablePathRef path = CGPathCreateMutable();
curSegment.lineWidth = 3;
curSegment.strokeColor = [SKColor whiteColor];
curSegment.glowWidth = 1;
curSegment.name = @"segment";
CGPathMoveToPoint(path, NULL, fromPoint.x, fromPoint.y);
CGPathAddLineToPoint(path, NULL, toPoint.x, toPoint.y);
curSegment.path = path;
lastPoint = toPoint;
[canvas addChild:curSegment];
}
Далее приведен метод создания текстуры и возврата существующих сегментов в пул:
-(void)cacheSegments
{
SKTexture *cacheTexture =[ self.view textureFromNode:container];
canvas.texture = cacheTexture;
[canvas setSize:CGSizeMake(canvas.texture.size.width, canvas.texture.size.height)];
canvas.anchorPoint = CGPointMake(0, 0);
[canvas enumerateChildNodesWithName:@"segment" usingBlock:^(SKNode *node, BOOL *stop)
{
[node removeFromParent];
[pool addObject:node];
}];
}
Наконец, обработчики касаний:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self cacheSegments];
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInNode:self];
lastPoint = location;
[self drawSegmentFromPoint:lastPoint toPoint:location];
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInNode:self];
[self drawSegmentFromPoint:lastPoint toPoint:location];
}
}
Как я уже сказал, это не весь инклюзивный код, я предполагаю, что вы достаточно понимаете концепцию, которую вы можете реализовать в своем приложении. Это всего лишь примеры моей реализации barebones.