一、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