ARKIT

一、ARKit Demo (一只你永远不能逮到的飞机)

2017-07-11  本文已影响299人  Dosun

研究 ARKit 有一段时间,发现 Xcode 的坑不少,同样的代码和逻辑,加载 .scn 文件总是失败,另外相同的代码,在公司的 6 plus 上不能捕捉到水平面,而在朋友的机子上运行 6666 的。what's hell!! 下面展示的 demo 是点击飞机 3D ,飞机会自动消失了,然后飞机会在其他地方出现,你永远不能逮到它,气死你,哈哈。

抱歉,如下和动态图片质量的待进一步提高!!

ARTouchMove.gif

一、核心代码

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    UITouch *fistTouch =  [touches allObjects].firstObject;
  //拿到第一个点击的对象,
    if (fistTouch) {
        CGPoint location = [fistTouch locationInView:self.arSCNView];
        NSArray<SCNHitTestResult *> *hitResults = [self.arSCNView hitTest:location options:nil];
        SCNHitTestResult *hitObject = hitResults.firstObject;
        
        if (hitObject) {
            SCNNode *node = hitObject.node;
            //飞机有唯一的标识,如下图
            if ([node.name isEqualToString:@"shipMesh"]) {
                
                [node removeFromParentNode];
                
                self.account += 1; //Xcode have a bug, just igore it .
                self.addPlaneNumber.text = [NSString stringWithFormat:@"added %zd �times",self.account];
                NSLog(@"%@", [NSString stringWithFormat:@"added %zd �times",self.account]);
                [self addObject];
            }
        }
    }
}

飞机模型的唯一标识如下图。


Snip20170711_1.png

二、完整代码

在 ViewContoller.m 中

#import "ViewController.h"
#import "ARSCNViewViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (IBAction)showAR:(id)sender {
    [self presentViewController:[ARSCNViewViewController new] animated:YES completion:nil];
}


@end

在 SpaceShip.h 中

#import <ARKit/ARKit.h>

@interface SpaceShip : SCNNode

-(void)loadSpaceShipModel;

@end

在 SpaceShip.m 中

#import "SpaceShip.h"

@implementation SpaceShip
-(void)loadSpaceShipModel{
    SCNScene *spaceshipScene = [SCNScene sceneNamed:@"Models.scnassets/ship/ship.scn"];
    
    SCNNode *wraperNode = [SCNNode node];
    for (SCNNode *childNode in spaceshipScene.rootNode.childNodes) {
        [wraperNode addChildNode:childNode];
    }
    [self addChildNode:wraperNode];
}
@end

在 ARSCNViewViewController.m 中

#import "ARSCNViewViewController.h"
#import <SceneKit/SceneKit.h>
#import <ARKit/ARKit.h>
#import "SpaceShip.h"

#define _W self.view.bounds.size.width
#define _H self.view.bounds.size.heigh

@interface ARSCNViewViewController ()<ARSCNViewDelegate,ARSessionDelegate>
#pragma mark - ARSCNViewViewController
//AR视图
@property(nonatomic,strong)ARSCNView *arSCNView;

//AR会话
@property(nonatomic,strong)ARSession *arSession;

//会话追踪配置
@property(nonatomic,strong)ARSessionConfiguration *arSessionConfiguration;

// mode names
@property(nonatomic, strong)NSArray *modelNames;

//is add Model
@property(nonatomic, strong)UILabel* addPlaneNumber;

@property(nonatomic, assign)int *account;

@end
#pragma mark - ARSCNViewViewController
@implementation ARSCNViewViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
}


-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    //将AR视图添加到当前视图
    [self.view addSubview:self.arSCNView];
    //开启AR会话(此时相机开始工作)
    [self.arSession runWithConfiguration:self.arSessionConfiguration];
    
    [self addDissmissBtn];
    [self addTouchTimeLabel];
    
    //add spaceship
    [self addObject];
    
}


-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    UITouch *fistTouch =  [touches allObjects].firstObject;
    if (fistTouch) {
        CGPoint location = [fistTouch locationInView:self.arSCNView];
        NSArray<SCNHitTestResult *> *hitResults = [self.arSCNView hitTest:location options:nil];
        SCNHitTestResult *hitObject = hitResults.firstObject;
        
        if (hitObject) {
            SCNNode *node = hitObject.node;
            if ([node.name isEqualToString:@"shipMesh"]) {
                
                [node removeFromParentNode];
                
                self.account += 1; //Xcode have a bug, just igore it .
                self.addPlaneNumber.text = [NSString stringWithFormat:@"added %zd �times",self.account];
                NSLog(@"%@", [NSString stringWithFormat:@"added %zd �times",self.account]);
                [self addObject];
            }
        }
        
    }
    
    
}
#pragma mark - private method
//add back btn
-(void)addDissmissBtn{
    //添加返回按钮
    int cornerR = 25;
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
    [btn setTitle:@"返回" forState:UIControlStateNormal];
    [btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    btn.frame = CGRectMake(20, self.view.bounds.size.height - 70, 130, 50);
    btn.backgroundColor = [UIColor greenColor];
    [btn addTarget:self action:@selector(back:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
    btn.layer.cornerRadius = cornerR;
    btn.layer.masksToBounds = YES;
}
- (void)back:(UIButton *)btn
{
    [self.arSession pause];
    [self dismissViewControllerAnimated:YES completion:nil];
}

//add touch time Label
-(void)addTouchTimeLabel{
    //add label
    self.addPlaneNumber = [[UILabel alloc]initWithFrame: CGRectMake(self.view.bounds.size.width - 150, self.view.bounds.size.height- 70, 130, 50)];
    self.addPlaneNumber.backgroundColor = [UIColor greenColor];
    [self.view addSubview:self.addPlaneNumber];
    self.addPlaneNumber.text = @"add 0 times";
    
    self.addPlaneNumber.textAlignment = NSTextAlignmentCenter;
    self.addPlaneNumber.layer.cornerRadius = 25;
    self.addPlaneNumber.layer.masksToBounds = YES;
    
}
//add node
-(void)addObject{
    
    SpaceShip *ship = [SpaceShip new];
    [ship loadSpaceShipModel];
    
    CGFloat x = [self randomFloatWithMin:-1.5 andMax:1.5];
    CGFloat y = [self randomFloatWithMin:-1.5 andMax:1.5];
    
    ship.position = SCNVector3Make(x, y, -1);
    

    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC));
    dispatch_after(time, dispatch_get_main_queue(), ^
    {
        // 此任务被延迟提交到队列中
        [self.arSCNView.scene.rootNode addChildNode:ship];
    });
    
}

-(CGFloat)randomFloatWithMin:(CGFloat) min andMax:(CGFloat)max{
    return ((1.0 * (arc4random())/0xFFFFFFFF) * (max - min) + min);
}

#pragma mark - lazy load property
- (ARSessionConfiguration *)arSessionConfiguration
{
    if (_arSessionConfiguration == nil) {
        _arSessionConfiguration = [[ARWorldTrackingSessionConfiguration alloc] init];
        _arSessionConfiguration.lightEstimationEnabled = YES;
    }
    return _arSessionConfiguration;
}


- (ARSession *)arSession
{
    if(_arSession == nil)
    {
        _arSession = [[ARSession alloc] init];
        _arSession.delegate = self;
    }
    
    return _arSession;
}


- (ARSCNView *)arSCNView
{
    if (_arSCNView == nil) {
        _arSCNView = [[ARSCNView alloc] initWithFrame:self.view.bounds];
        _arSCNView.delegate = self;
        _arSCNView.session = self.arSession;
        _arSCNView.showsStatistics = YES;
        _arSCNView.automaticallyUpdatesLighting = YES;
    }
    
    
    return _arSCNView;
}

//show model name
-(NSArray *)modelNames{
    
    if (_modelNames == nil) {
        _modelNames = @[@"Models.scnassets/ship/ship.scn",@"Models.scnassets/candle/candle.scn",@"Models.scnassets/cup/cup.scn",@"Models.scnassets/chair/chair.scn",@"Models.scnassets/vase/vase.scn",@"Models.scnassets/lamp/lamp.scn"];
    }
    return _modelNames;
    
}

#pragma mark - ARSCNViewDelegate

//添加节点时候调用
- (void)renderer:(id <SCNSceneRenderer>)renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor
{
        NSLog(@"捕捉到平地");
    
}

//刷新时调用
- (void)renderer:(id <SCNSceneRenderer>)renderer willUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor
{
    NSLog(@"刷新中");
}

//更新节点时调用
- (void)renderer:(id <SCNSceneRenderer>)renderer didUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor
{
    NSLog(@"节点更新");
    
}

//移除节点时调用
- (void)renderer:(id <SCNSceneRenderer>)renderer didRemoveNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor
{
    NSLog(@"节点移除");
}

#pragma mark - ARSessionDelegate

//会话位置更新(监听相机的移动),此代理方法会调用非常频繁,只要相机移动就会调用,如果相机移动过快,会有一定的误差,具体的需要强大的算法去优化
- (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame
{
    
    //NSLog(@"相机移动");
    
}
- (void)session:(ARSession *)session didAddAnchors:(NSArray<ARAnchor*>*)anchors
{
    NSLog(@"添加锚点");
    
}


- (void)session:(ARSession *)session didUpdateAnchors:(NSArray<ARAnchor*>*)anchors
{
    NSLog(@"刷新锚点");
    
}


- (void)session:(ARSession *)session didRemoveAnchors:(NSArray<ARAnchor*>*)anchors
{
    NSLog(@"移除锚点");
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];

}

@end

若发现,加载模型时,不能加载,其路径也是正确的,不妨可以用 xcode8.3 打开工程,然后重新添加模型资料 .scnassets。也可以去看看下面的勾是否已经勾选(大多数情况不会勾选,但是手动勾选,,可能加载成功)。

Snip20170712_4.png
上一篇下一篇

猜你喜欢

热点阅读