做了一个简单的 SceneKit 场景,即加载动画,并控制动画人物行走,保持摄像机跟随。。。。
最终效果: 
git 传送门 : https://github.com/eassy/TimoRun
创建一个手柄控制板
这是控制板最终的样式,中间的按钮只会在触碰的时候显示出来,而且随着手指的移动,中间的按钮在大圈范围内跟随移动。
自定义一个 UIView ,利用 touchBegin 等一系列的事件检测方法,将触碰点捕捉到,同时判断触碰点是否在大圈范围内,在的话将按钮中心位置挪到触碰点处,不在的话,将按钮中心位置挪到跟触碰点在一个方向的大圈圆周这个点处。有难度的可能是计算按钮中心点最终所处的位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| - (CGFloat)lengthFromA:(CGPoint)aPoint toB:(CGPoint)bPoint { return sqrt((aPoint.x - bPoint.x)*(aPoint.x-bPoint.x) + (aPoint.y-bPoint.y)*(aPoint.y-bPoint.y)); }
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { self.pad.hidden = NO; CGPoint point = [[touches anyObject] locationInView:self]; [self movePadCenterToPoint:point animation:NO complete:^{ }]; }
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { CGPoint point = [[touches anyObject] locationInView:self]; [self movePadCenterToPoint:point animation:NO complete:^{ }]; }
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self movePadCenterToPoint:CGPointMake(_bigRadius, _bigRadius) animation:YES complete:^{ self.pad.hidden = YES; }]; }
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self movePadCenterToPoint:CGPointMake(_bigRadius, _bigRadius) animation:YES complete:^{ self.pad.hidden = YES; }]; }
- (void)movePadCenterToPoint:(CGPoint)point animation:(BOOL)animation complete:(void(^)(void))completeBlock { CGFloat length = [self lengthFromA:point toB:CGPointMake(_bigRadius, _bigRadius)]; CGPoint finalPoint; if (length < _bigRadius - _smallRadius) { finalPoint = point; } else{ //计算应该的 center CGFloat a = (point.x - _bigRadius)/(point.y - _bigRadius); CGFloat l = _bigRadius - _smallRadius; CGFloat offsetY = sqrt(l * l/(a * a + 1)); if (point.y >= _bigRadius) { finalPoint.y = offsetY + _bigRadius; } else{ finalPoint.y = _bigRadius - offsetY; } finalPoint.x = a * (finalPoint.y - _bigRadius) + _bigRadius; } if (!isnan(finalPoint.x) && !isnan(finalPoint.y)) { self.pad.center = finalPoint; } if (completeBlock) { completeBlock(); } if ([self.delegate respondsToSelector:@selector(padControl:toPoint:)]) { [self.delegate padControl:self toPoint:CGPointMake(finalPoint.x - _bigRadius, finalPoint.y - _bigRadius)]; } }
|
计算公式不难,大圆中心点位置为 (_bigRadius,_bigRadius),设大圆中心点为(x1,y1),触碰点位置为(x2,y2),最终点为(x,y),则可列公式:
1 2 3 4 5 6 7
| //bigRadius:大圆半径 smallRadius:小圆半径 (x-x1)/(y-y1) = (x2-x1)/(y2-y1) (x-x1)² + (y-y1)² = (_bigRadius - smallRadius)² 令 l = _bigRadius - _smallRadius; a = (point.x - _bigRadius)/(point.y - _bigRadius); 由此可得 offsetY = |y-y1| = sqrt(l * l/(a * a + 1)); 再根据触碰点相对于中心点的方向信息,得出 finalY,最终得出 finalX
|
加载文件中的动画和人物
因为资源文件是带动画的,所以取出来时要先 remove 掉,先保存,后 remove。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| - (void)makeTimo { SCNScene *timoScene = [SCNScene sceneNamed:@"art.scnassets/TiMo.DAE"]; self.timoNode = [SCNNode node]; for (SCNNode *childNode in timoScene.rootNode.childNodes) { [self.timoNode addChildNode:childNode]; if (childNode.animationKeys.count) { self.walkAnimation = [childNode animationForKey:childNode.animationKeys[0]]; self.walkAnimation.speed = 1.5; [childNode removeAllAnimations]; } } self.timoNode.position = SCNVector3Make(0, 0.15, 0); self.timoNode.scale = SCNVector3Make(0.2, 0.2, 0.2); SCNPhysicsBody *timoBody = [SCNPhysicsBody kinematicBody]; timoBody.mass = 10; self.timoNode.physicsBody = timoBody; [self.mainScene.rootNode addChildNode:self.timoNode]; }
|
物理身体的类型我设置成了 kinematicBody ,因为 设置成动态身体的话根本站不住。。。软趴趴倒在地上哈哈哈。可以看到我将动画保存了起来,然后 remove 掉了。
创建 Camera 和灯光
有两个 Camera ,一个是位置一直跟随人物移动的 camera,一个是一直面向人物,但是位置不变的,通过双击屏幕切换这两个视角。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| - (void)setupCamera { // create and add a camera to the scene SCNNode *cameraNode = [SCNNode node]; cameraNode.camera = [SCNCamera camera]; [self.mainScene.rootNode addChildNode:cameraNode]; // place the camera cameraNode.position = SCNVector3Make(0, 50, 60); cameraNode.rotation = SCNVector4Make(1, 0, 0, -M_PI / 6.f); cameraNode.camera.zFar = 2000; self.cameraNode = cameraNode; ((SCNView *)self.view).pointOfView = self.cameraNode; SCNNode *thirdCameraNode = [SCNNode node]; thirdCameraNode.camera = [SCNCamera camera]; [self.mainScene.rootNode addChildNode:thirdCameraNode]; thirdCameraNode.position = SCNVector3Make(0, 50, 120); thirdCameraNode.rotation = SCNVector4Make(1, 0, 0, -M_PI / 6.f); thirdCameraNode.camera.zFar = 2000; SCNLookAtConstraint *constraint = [SCNLookAtConstraint lookAtConstraintWithTarget:self.timoNode]; thirdCameraNode.constraints = @[constraint]; self.thirdCameraNode = thirdCameraNode; }
|
初始的视角是跟随的摄像机。
我添加了一个环境光,八个聚焦灯(后面我超想用聚焦灯创建一个ktv那种球状灯的),灯的方向都是照向地面。
创建环境
创建地板
创建地板的时候并没有指定大小,好像默认无限大了?
1 2 3 4 5 6 7 8 9 10 11 12 13
| - (void)makeFloor { SCNNode*floor = [SCNNode node]; floor.geometry = [SCNFloor floor]; floor.geometry.firstMaterial.diffuse.contents = @"art.scnassets/moss_diffuse.png"; floor.geometry.firstMaterial.diffuse.contentsTransform = SCNMatrix4MakeScale(2, 2, 1); //scale the wood texture floor.geometry.firstMaterial.locksAmbientWithDiffuse = YES; SCNPhysicsBody *staticBody = [SCNPhysicsBody staticBody]; floor.physicsBody = staticBody; [self.mainScene.rootNode addChildNode:floor]; }
|
创建四面墙
创建两面墙,然后使用 clone ,rotate,position 等将墙放在合适的位置。每面墙都是静态躯体。
创建一个足球
创建一个球,设置动态物理身体,然后可以被踢着走了~