转载请注明出处:ARKit从入门到精通(10)-ARKit让飞机绕着你飞起来
1.1-ARKit物体围绕相机旋转流程介绍 1.2-完整代码1.3-代码下载地址- 废话不多说,先看效果
- 由于是晚上,笔者选择的是一个台灯
- 其实是会一直围着你转圈的,只不过笔者不好意思暴露家里的场景,所以请读者朋友们见谅~
1101.gif
1.1-ARKit物体围绕相机旋转流程介绍#pragma mark- 点击屏幕添加飞机- (void)touchesBeganNSSet<UITouch *> *)touches withEventUIEvent *)event{ [self.planeNode removeFromParentNode]; //1.使用场景加载scn文件(scn格式文件是一个基于3D建模的文件,使用3DMax软件可以创建,这里系统有一个默认的3D飞机)--------在右侧我添加了许多3D模型,只需要替换文件名即可 SCNScene *scene = [SCNScene sceneNamed:@"Models.scnassets/lamp/lamp.scn"]; //2.获取台灯节点(一个场景会有多个节点,此处我们只写,飞机节点则默认是场景子节点的第一个) //所有的场景有且只有一个根节点,其他所有节点都是根节点的子节点 SCNNode *shipNode = scene.rootNode.childNodes[0]; self.planeNode = shipNode; //台灯比较大,适当缩放一下并且调整位置让其在屏幕中间 shipNode.scale = SCNVector3Make(0.5, 0.5, 0.5); shipNode.position = SCNVector3Make(0, -15,-15); ; //一个台灯的3D建模不是一气呵成的,可能会有很多个子节点拼接,所以里面的子节点也要一起改,否则上面的修改会无效 for (SCNNode *node in shipNode.childNodes) { node.scale = SCNVector3Make(0.5, 0.5, 0.5); node.position = SCNVector3Make(0, -15,-15); } self.planeNode.position = SCNVector3Make(0, 0, -20); //3.绕相机旋转 //绕相机旋转的关键点在于:在相机的位置创建一个空节点,然后将台灯添加到这个空节点,最后让这个空节点自身旋转,就可以实现台灯围绕相机旋转 //1.为什么要在相机的位置创建一个空节点呢?因为你不可能让相机也旋转 //2.为什么不直接让台灯旋转呢? 这样的话只能实现台灯的自转,而不能实现公转 SCNNode *node1 = [[SCNNode alloc] init]; //空节点位置与相机节点位置一致 node1.position = self.arSCNView.scene.rootNode.position; //将空节点添加到相机的根节点 [self.arSCNView.scene.rootNode addChildNode:node1]; // !!!将台灯节点作为空节点的子节点,如果不这样,那么你将看到的是台灯自己在转,而不是围着你转 [node1 addChildNode:self.planeNode]; //旋转核心动画 CABasicAnimation *moonRotationAnimation = [CABasicAnimation animationWithKeyPath:@"rotation"]; //旋转周期 moonRotationAnimation.duration = 30; //围绕Y轴旋转360度 (不明白ARKit坐标系的可以看笔者之前的文章) moonRotationAnimation.toValue = [NSValue valueWithSCNVector4:SCNVector4Make(0, 1, 0, M_PI * 2)]; //无限旋转 重复次数为无穷大 moonRotationAnimation.repeatCount = FLT_MAX; //开始旋转 !!!:切记这里是让空节点旋转,而不是台灯节点。 理由同上 [node1 addAnimation:moonRotationAnimation forKey:@"moon rotation around earth"];}1.2-完整代码#import "ARSCNViewViewController.h"//3D游戏框架#import <SceneKit/SceneKit.h>//ARKit框架#import <ARKit/ARKit.h>@interface ARSCNViewViewController ()<ARSCNViewDelegate,ARSessionDelegate>//AR视图:展示3D界面@property(nonatomic,strong)ARSCNView *arSCNView;//AR会话,负责管理相机追踪配置及3D相机坐标@property(nonatomic,strong)ARSession *arSession;//会话追踪配置:负责追踪相机的运动@property(nonatomic,strong)ARSessionConfiguration *arSessionConfiguration;//飞机3D模型(本小节加载多个模型)@property(nonatomic,strong)SCNNode *planeNode;@end@implementation ARSCNViewViewController- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view.}- (void)backUIButton *)btn{ [self dismissViewControllerAnimated:YES completion:nil];}- (void)viewDidAppearBOOL)animated{ [super viewDidAppear:animated]; //1.将AR视图添加到当前视图 [self.view addSubview:self.arSCNView]; //2.开启AR会话(此时相机开始工作) [self.arSession runWithConfiguration:self.arSessionConfiguration]; //添加返回按钮 UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; [btn setTitle:@"返回" forState:UIControlStateNormal]; btn.frame = CGRectMake(self.view.bounds.size.width/2-50, self.view.bounds.size.height-100, 100, 50); btn.backgroundColor = [UIColor greenColor]; [btn addTarget:self action:@selector(back forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btn];}#pragma mark- 点击屏幕添加飞机- (void)touchesBeganNSSet<UITouch *> *)touches withEventUIEvent *)event{ [self.planeNode removeFromParentNode]; //1.使用场景加载scn文件(scn格式文件是一个基于3D建模的文件,使用3DMax软件可以创建,这里系统有一个默认的3D飞机)--------在右侧我添加了许多3D模型,只需要替换文件名即可 SCNScene *scene = [SCNScene sceneNamed:@"Models.scnassets/lamp/lamp.scn"]; //2.获取台灯节点(一个场景会有多个节点,此处我们只写,飞机节点则默认是场景子节点的第一个) //所有的场景有且只有一个根节点,其他所有节点都是根节点的子节点 SCNNode *shipNode = scene.rootNode.childNodes[0]; self.planeNode = shipNode; //台灯比较大,适当缩放一下并且调整位置让其在屏幕中间 shipNode.scale = SCNVector3Make(0.5, 0.5, 0.5); shipNode.position = SCNVector3Make(0, -15,-15); ; //一个台灯的3D建模不是一气呵成的,可能会有很多个子节点拼接,所以里面的子节点也要一起改,否则上面的修改会无效 for (SCNNode *node in shipNode.childNodes) { node.scale = SCNVector3Make(0.5, 0.5, 0.5); node.position = SCNVector3Make(0, -15,-15); } self.planeNode.position = SCNVector3Make(0, 0, -20); //3.绕相机旋转 //绕相机旋转的关键点在于:在相机的位置创建一个空节点,然后将台灯添加到这个空节点,最后让这个空节点自身旋转,就可以实现台灯围绕相机旋转 //1.为什么要在相机的位置创建一个空节点呢?因为你不可能让相机也旋转 //2.为什么不直接让台灯旋转呢? 这样的话只能实现台灯的自转,而不能实现公转 SCNNode *node1 = [[SCNNode alloc] init]; //空节点位置与相机节点位置一致 node1.position = self.arSCNView.scene.rootNode.position; //将空节点添加到相机的根节点 [self.arSCNView.scene.rootNode addChildNode:node1]; // !!!将台灯节点作为空节点的子节点,如果不这样,那么你将看到的是台灯自己在转,而不是围着你转 [node1 addChildNode:self.planeNode]; //旋转核心动画 CABasicAnimation *moonRotationAnimation = [CABasicAnimation animationWithKeyPath:@"rotation"]; //旋转周期 moonRotationAnimation.duration = 30; //围绕Y轴旋转360度 (不明白ARKit坐标系的可以看笔者之前的文章) moonRotationAnimation.toValue = [NSValue valueWithSCNVector4:SCNVector4Make(0, 1, 0, M_PI * 2)]; //无限旋转 重复次数为无穷大 moonRotationAnimation.repeatCount = FLT_MAX; //开始旋转 !!!:切记这里是让空节点旋转,而不是台灯节点。 理由同上 [node1 addAnimation:moonRotationAnimation forKey:@"moon rotation around earth"];}#pragma mark -搭建ARKit环境//懒加载会话追踪配置- (ARSessionConfiguration *)arSessionConfiguration{ if (_arSessionConfiguration != nil) { return _arSessionConfiguration; } //1.创建世界追踪会话配置(使用ARWorldTrackingSessionConfiguration效果更加好),需要A9芯片支持 ARWorldTrackingSessionConfiguration *configuration = [[ARWorldTrackingSessionConfiguration alloc] init]; //2.设置追踪方向(追踪平面,后面会用到) configuration.planeDetection = ARPlaneDetectionHorizontal; _arSessionConfiguration = configuration; //3.自适应灯光(相机从暗到强光快速过渡效果会平缓一些) _arSessionConfiguration.lightEstimationEnabled = YES; return _arSessionConfiguration;}//懒加载拍摄会话- (ARSession *)arSession{ if(_arSession != nil) { return _arSession; } //1.创建会话 _arSession = [[ARSession alloc] init]; _arSession.delegate = self; //2返回会话 return _arSession;}//创建AR视图- (ARSCNView *)arSCNView{ if (_arSCNView != nil) { return _arSCNView; } //1.创建AR视图 _arSCNView = [[ARSCNView alloc] initWithFrame:self.view.bounds]; //2.设置代理 捕捉到平地会在代理回调中返回 _arSCNView.delegate = self; //2.设置视图会话 _arSCNView.session = self.arSession; //3.自动刷新灯光(3D游戏用到,此处可忽略) _arSCNView.automaticallyUpdatesLighting = YES; return _arSCNView;}#pragma mark -- ARSCNViewDelegate//添加节点时候调用(当开启平地捕捉模式之后,如果捕捉到平地,ARKit会自动添加一个平地节点)- (void)rendererid <SCNSceneRenderer>)renderer didAddNodeSCNNode *)node forAnchorARAnchor *)anchor{}//刷新时调用- (void)rendererid <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]; // Dispose of any resources that can be recreated.}/*#pragma mark - Navigation// In a storyboard-based application, you will often want to do a little preparation before navigation- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller.}*/@end1.3-代码下载地址 |
|
|
|
|
|