如何在没有 Cocos2d 的情况下为 Xcode 中的精灵表设置动画?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9185102/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 23:15:22  来源:igfitidea点击:

How do I animate a sprite sheet in Xcode without Cocos2d?

iosxcodeanimationsprite

提问by MyNameIsKo

I'm developing a simple application that animates an image as the user moves a slider. This could easily be done with individual images, but for obvious reasons that method is inefficient.

我正在开发一个简单的应用程序,它可以在用户移动滑块时为图像设置动画。这可以通过单个图像轻松完成,但由于显而易见的原因,该方法效率低下。

Currently, I have the animation broken up into 14 sprite sheets with 16 images per sheet. I created a method that uses CGImageCreateWithImageInRect to find the current image dictated by the slider and update the image view with that image. This works, but not fluidly. I think I understand why, but I have no clue what to do otherwise. While I could use Cocos2d or OpenGL ES, I am stubborn and convinced that this is possible without them. I just want to know how.

目前,我将动画分解为 14 个精灵表,每张图 16 个图像。我创建了一个方法,它使用 CGImageCreateWithImageInRect 来查找滑块指定的当前图像并使用该图像更新图像视图。这有效,但不流畅。我想我明白为什么,但我不知道该怎么做。虽然我可以使用 Cocos2d 或 OpenGL ES,但我很固执并坚信没有它们也是可能的。我只想知道怎么做。

Here's some example code:

下面是一些示例代码:

- (void)setUp{

NSString *string;
NSString *bundleString = [[NSBundle mainBundle] bundlePath];
dsRedPathArray = [[NSMutableArray alloc] initWithCapacity:15];
for (int i = 0; i < 14; i++)
{
  string = [bundleString stringByAppendingFormat:@"/dsRedAni_%d.png", i];
  [dsRedPathArray addObject:string];
}

//initial image starts at (0, 1) of image dsRedAni_9
currentImage = [UIImage imageWithContentsOfFile:[dsRedPathArray objectAtIndex:9]];  
currentRef = CGImageCreateWithImageInRect(currentImage.CGImage, CGRectMake(495, 0,     kModelWidth, kModelHeight));   

modelView.image = [UIImage imageWithCGImage:currentRef];
 }

- (IBAction)sliderMoved:(UISlider*)sender
{
  [self animateModel:sender.value];
}

- (void)animateModel:(int)index
{
  index += 1;
  imageIndex = (index / 16) + 9;
  if (imageIndex > 13)
  {
    imageIndex = -14 + imageIndex;
  }
  currentX = kModelWidth * (index % 4);
  currentY = kModelHeight * ((index / 4) % 4);

  currentRect = CGRectMake(currentX, currentY, kModelWidth, kModelHeight);
  currentImage = [UIImage imageWithContentsOfFile:[dsRedPathArray objectAtIndex: (imageIndex)]];    
  currentRef = CGImageCreateWithImageInRect(currentImage.CGImage, currentRect);

  modelView.image = [UIImage imageWithCGImage:currentRef];
}

Thanks in advance for any help.

在此先感谢您的帮助。

回答by MyNameIsKo

I finally found a rather quick and efficient, if unconventional, way of cycling through each section of my sprite sheets without any outside help from APIs. Rather than spending time and power cutting up the image into contexts or individual files, I found that it was more efficient for me to create a UIView in the size of the image I needed and then adding the entire sprite sheet to the view as a UIImageView.

我终于找到了一种相当快速和有效的方式,即使是非常规的,也可以在没有任何来自 API 的外部帮助的情况下循环浏览我的精灵表的每个部分。与其花时间和精力将图像切割成上下文或单个文件,我发现创建一个我需要的图像大小的 UIView 然后将整个精灵表作为 UIImageView 添加到视图中对我来说更有效.

With view.clipsToBounds set to YES, the view acts as a mask for my sprite sheet, limiting the visible portion to the size of each image I want to cycle through on the sheet. In order to give the effect of animation, I simply use imageview.center to move the sprite sheet around the view to the desired image coordinates. So, in a sprite-sheet with 16 images I only have to make one call to show the image, then move it around per each frame in the animation.

view.clipsToBounds 设置为 YES 时,视图充当我的精灵表的蒙版,将可见部分限制为我想要在工作表上循环的每个图像的大小。为了给出动画效果,我简单地使用 imageview.center 将视图周围的精灵表移动到所需的图像坐标。因此,在包含 16 个图像的精灵表中,我只需调用一次即可显示图像,然后在动画中的每一帧中移动它。

- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
  [self setUp];
  self.backgroundColor = [UIColor clearColor];
  self.clipsToBounds = YES;
}
return self;
}

- (void)setUp
{
  //assign |lastImageIndex| a number not equal to |imageIndex|
  lastImageIndex = -1;

  modelImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1924, 1708)];

  NSString *string;
  NSString *bundleString = [[NSBundle mainBundle] bundlePath];
  UIImage *image;
  if (!imageArray)
  {
    imageArray = [[NSMutableArray alloc] init];
    NSLog(@"Init egfp |imageArray|");
  }

  for (int i = 0; i < 10; i++)
  {
    string = [bundleString stringByAppendingFormat:@"/moleculeAni_%d.png", i];
    image = [UIImage imageWithContentsOfFile:string];
    [imageArray addObject:image];
    if (i == 9)
    {
      NSLog(@"Filled egfp |imageCache|");
    }
  }

  [self addSubview:modelImageView];  
}

- (void)animateModel:(int)index
{
  if (index != 1)
  {
   index -= 1;
  }
  imageIndex = (index / 16);

  if (imageIndex < 9)
  {
    currentX = 962 - (481 * (index % 4));
    currentY = 854 - (427 * ((index / 4) % 4));
  }
  else
  {
    currentX = 962 - (481 * (index % 4));
    currentY = 427 - (427 * ((index / 4) % 4));
  }

  if (imageIndex != lastImageIndex)
  {
    if (imageIndex < 9 && onLastFrame)
    {
      modelImageView.frame = CGRectMake(0, 0, 1924, 1708);
      onLastFrame = NO;
    }
    else if (imageIndex == 9 && !onLastFrame)
    {
      modelImageView.frame = CGRectMake(0, 0, 1924, 854);
      onLastFrame = YES;
    }


    NSLog(@"Image: %d", imageIndex);
    tempImage = [imageArray objectAtIndex:imageIndex];
    modelImageView.image = tempImage;
  }

  modelImageView.center = CGPointMake(currentX, currentY);

  lastImageIndex = imageIndex;
}

Most of the code here is spent determining where the imageview needs to move in order to display the correct image. This is taking the method I explained above and is spreading it across 10 sprite sheets each with 16 images spread evenly (except the last which has 8). The only slowdown came when the program was swapping between images every 16th frame. To get around this, my controller has the view quickly cycle through all the images beneath another view (like a curtain) so the user is only privy to a loading screen. Once the images have been cycled, the curtain view is removed and the animation (controlled by the user with a slider) moves like butter.

这里的大部分代码都用于确定 imageview 需要移动到哪里才能显示正确的图像。这是采用我上面解释的方法,并将其分布在 10 个精灵表中,每个精灵表均匀分布 16 个图像(最后一个有 8 个图像除外)。唯一的减速是程序每 16 帧在图像之间交换。为了解决这个问题,我的控制器让视图快速循环浏览另一个视图(如窗帘)下的所有图像,因此用户只知道加载屏幕。一旦图像被循环,窗帘视图将被移除,动画(由用户使用滑块控制)像黄油一样移动。

回答by Bryan Cimo

One way to do this would be to cut up the images with CGImageCreateWithImageInRect, like you started to above. Then, add them to an array:

一种方法是使用 CGImageCreateWithImageInRect 剪切图像,就像上面开始的那样。然后,将它们添加到数组中:

myArray addObject:[UIImage imageWithCGImage:currentRef];

Next use a UIImageView with setAnimationImages, like this:

接下来使用带有 setAnimationImages 的 UIImageView,如下所示:

UIImageView *myAnimationImageView;
[myAnimationImageView setAnimationImages:myArray];

Then You can start the animation.

然后你就可以开始动画了。

There is a good project doing this here: https://github.com/r3econ/UIImage-Sprite-Additions

这里有一个很好的项目:https: //github.com/r3econ/UIImage-Sprite-Additions