废话开篇:简略的操作杆旋转完成

一、作用完成

简略完成了一个消除病毒的小作用,画面略显粗糙,多多见谅

iOS 简单的操作杆旋转实现

操控球复位

iOS 简单的操作杆旋转实现

二、操作杆完成

完成拖动小球,获取当时小球的旋转方向,将旋转的方向传递出去,旋转“坦克”进行进犯“病毒”。

#import "DirectionOptionView.h"
@interface DirectionOptionView()
//方向指示器滚珠
@property(nonatomic,strong) UIView * ball;
@end
@implementation DirectionOptionView
- (instancetype)initWithFrame:(CGRect)frame changeDirectionBlock:(ChangeDirectionBlock)changeDirectionBlock
{
  if (self = [super initWithFrame:frame]) {
    self.changeDirectionBlock = changeDirectionBlock;
    [self makeView];
    [self addPan];
  }
  return self;
}
//增加拖拽手势
- (void)addPan{
  UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action: @selector(panAction:)];
  [self addGestureRecognizer:pan];
}
- (void)panAction:(UIPanGestureRecognizer *)pan
{
  switch (pan.state) {
    case UIGestureRecognizerStateBegan:
    {
      CGPoint point = [pan locationInView:**self**];
      self.ball.alpha = 1;
      [self moveWithPoint:point];
    }
      break;
    case UIGestureRecognizerStateChanged:
    {
      CGPoint point = [pan locationInView:self];
      [self moveWithPoint:point];
    }
      break;
    case UIGestureRecognizerStateEnded:
    {
      CGPoint point = [pan locationInView:self];
      [self resetBallPositionWithEndPoint:point];
    }
      break;
    default:
      break;
  }
}
//小球复位
- (void)resetBallPositionWithEndPoint:(CGPoint)point
{
  self.changeDirectionBlock(0);
  [UIView animateWithDuration:0.2 animations:^{
    self.ball.center = CGPointMake((self.frame.size.width / 2.0), (self.frame.size.height / 2.0));
    self.ball.alpha = 0.4;
  }];
}
//依据操控球方位获取当时旋转视点
- (void)moveWithPoint:(CGPoint)point
{
  CGFloat distanceCircle = (self.ball.frame.size.width / 2.0);
  CGFloat x = point.x;
  CGFloat y = point.y;
  CGFloat dx = x - self.frame.size.width / 2.0;
  CGFloat dy = y - self.frame.size.height / 2.0;
  CGFloat rotation = atan2(dx,dy);
  CGFloat r = self.frame.size.width / 2.0;
  CGFloat rx = (r - distanceCircle) * sin(rotation) + r;
  CGFloat ry = (r - distanceCircle) * cos(rotation) + r;
    //防止操控球越界
  if ((sqrt((dx * dx) + (dy * dy))) > (r - distanceCircle)) {
    x = rx;
    y = ry;
  }
    //用block形式向外界暴露当时操控球相对于屏幕上方的视点
  self.changeDirectionBlock(-rotation + M_PI);
  self.ball.center = CGPointMake(x, y);
}
- (void)makeView{
  //进行倒角
  self.layer.masksToBounds = YES;
  self.layer.cornerRadius = self.frame.size.width / 2.0;
  self.backgroundColor = [[UIColor groupTableViewBackgroundColor] colorWithAlphaComponent:0.7];
  //增加操控球
  [self addSubview:self.ball];
}
//操控球
- (UIView *)ball
{
  if (!_ball) {
    CGSize size = CGSizeMake(45, 45);
    _ball = [[UIView alloc] initWithFrame:CGRectMake((self.frame.size.width - size.width) / 2.0, (self.frame.size.height - size.height) / 2.0, size.width, size.height)];
    _ball.alpha = 0.4;
    _ball.layer.masksToBounds = YES;
    _ball.layer.cornerRadius = _ball.frame.size.width / 2.0;
    _ball.backgroundColor = [UIColor lightGrayColor];
  }
  return _ball;
}
@end

三、发射子弹及磕碰检测

1、发射子弹
//依据当时视点,预判子弹动画的结束方位
- (NSArray *)prepareBulletPath
{
  CGPoint center = self.center;
  CGFloat maxLength = sqrt(([UIScreen mainScreen].bounds.size.width * [UIScreen mainScreen].bounds.size.width) + ([UIScreen mainScreen].bounds.size.height * [UIScreen mainScreen].bounds.size.height));
  CGFloat endY = sin(self.angle - M_PI / 2.0) * maxLength + center.y;
  CGFloat endX = cos(self.angle - M_PI / 2.0) * maxLength + center.x;
  CGPoint endPoint = CGPointMake(endX, endY);
  return @[@(center),@(endPoint)];
}
- (void)fir
{
  CGFloat bulletWidth = 10;
  BulletView * lastBulletView = self.bulletArr.count > 0 ? self.bulletArr.lastObject : nil;
  if (!lastBulletView) {
    BulletView * view = [[BulletView alloc] initWithFrame:CGRectMake((self.frame.size.width - bulletWidth) / 2.0, 0,bulletWidth,bulletWidth)];
    view.points = [self prepareBulletPath];
    [self.superview insertSubview:view belowSubview:self];
    [self.bulletArr addObject:view];
    view.center = [view.points[0] CGPointValue];
    [UIView animateWithDuration:1 animations:^{
      view.center = [view.points[1] CGPointValue];
    } completion:^(BOOL finished) {
      [view removeFromSuperview];
      [self.bulletArr removeObject:view];
    }];
  }
}

fir 本身是一个定时器事情,在里面增加一些创立子弹的逻辑,方位移动仍是用了最简略的 UIViewanimateWithDuration 办法,但是注意这儿面经过 frame 进行磕碰检测是获取不到,所以,增加了 CADisplayLink 屏幕改写事情来检测子弹视图的 layer.presentationLayer.frame来进行与病毒的 frame 进行检测屏幕方位是否包括。

2、检测磕碰

bool CGRectIntersectsRect(CGRect rect1, CGRect rect2) 检测磕碰的办法

//增加屏幕改写事情监听
- (void)addScreenRefreshAction
{
  self.displayLink = [CADisplayLink displayLinkWithTarget:self selector: @selector(update)];
  [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)update
{
  [self.bulletArr enumerateObjectsUsingBlock:^(BulletView * bulletView, NSUInteger idx, BOOL * _Nonnull stop) {
    [self.virusArr enumerateObjectsUsingBlock:^(VirusView * virusView, NSUInteger idx, BOOL * _Nonnull stop) {
            //检测磕碰
      if (CGRectIntersectsRect(bulletView.layer.presentationLayer.frame, virusView.frame)) {
        [bulletView removeFromSuperview];
        [self.bulletArr removeObject:bulletView];
        [virusView attacked];
        if ([virusView isDie]) {
          [virusView removeFromSuperview];
          [self.virusArr removeObject:virusView];
        }
      }
    }];
  }];
  if (!self.virusArr.count) {
    [self createVirusView];
  }
}

四、增加病毒及消除动画

1、随机创立病毒
- (void)createVirusView
{
  int width = arc4random() % 30 + 50;
  int x = arc4random() % ([[NSNumber numberWithFloat:[UIScreen mainScreen].bounds.size.width] integerValue] - width);
  int y = arc4random() % ([[NSNumber numberWithFloat:[UIScreen mainScreen].bounds.size.height / 2.0] integerValue]);
  VirusView * virusView = [[VirusView alloc] initWithFrame:CGRectMake(x, y, width, width)];
  [self.superview addSubview:virusView];
  [self.virusArr addObject:virusView];
}
2、消除动画

增加了一点粒子作用,显现病毒消散动画

- (void)fireExplode
{
  CAEmitterLayer * emitter = [CAEmitterLayer layer];
  emitter.frame = self.frame;
  [self.superview.layer addSublayer:emitter];
  emitter.renderMode = kCAEmitterLayerAdditive;
  emitter.emitterPosition = CGPointMake(emitter.frame.size.width*0.5,  emitter.frame.size.height*0.5);
  CAEmitterCell *cell = [[CAEmitterCell alloc] init];
  cell.contents = ( __bridge id)[UIImage imageNamed:@"v4"].CGImage;
  cell.birthRate = 1;//出生率
  cell.lifetime = 0.7;//生命周期
  cell.emissionLongitude = - M_PI_2;
  cell.emissionRange = M_PI_2;
  cell.alphaSpeed = -0.2;
  cell.velocity = 10;//速度
  cell.scale = 0.15;//缩放倍数
  emitter.emitterCells = @[cell];
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [emitter removeFromSuperlayer];
  });
}

五、考虑与总结

增加 UIViewanimateWithDuration 办法后,这儿用的是屏幕改写检测,来获取当时控件的 layer.presentationLayer.frame 来检测磕碰,其他逻辑都相对简略。代码拙劣,大神勿笑[抱拳][抱拳][抱拳]