在spritekit中模拟水/使精灵“漂浮”在水上

我正在尝试为我的游戏添加水。 除了不同的背景颜色,没有太多的东西。 但是,我希望player-sprite能够浮在顶部(或者半路)。 如果玩家从下面走进水里,我想让他漂浮在上面。 如果他跌倒了,我想让他慢慢改变方向,然后向上浮起来。

当他在水中时,我试图使重力消极,但是这给我一些有害的影响。 例如,当他(玩家)表面时,正常的重力会把他推倒,水会把他推开,等等。 最终,玩家将在水中“跳跃”,从一端推向另一端。 当他表面时,我希望他冷静地留在水面上。 我怎样才能做到这一点?

这是我在update-loop的代码:

 SKNode *backgroundNodeAtPoint = [_bgLayer nodeAtPoint:_ball.position]; if ([backgroundNodeAtPoint.name isEqualToString:@"WATER"]) { self.physicsWorld.gravity = CGVectorMake(self.physicsWorld.gravity.dx, 2); } else { if (self.physicsWorld.gravity.dy != -4) { self.physicsWorld.gravity = CGVectorMake(self.physicsWorld.gravity.dx, -4); } } 

基本上,当玩家在水中时,这会将我的重力改变为2 ,否则将其改变为-4除非它已经是-4

谢谢!

我相信你有三种可能的select来模拟水。

1)正如评论中提到的,你可以尝试使用SKFieldNode(iOS 8+)。 但是从个人的经验来看,这个领域的节点并没有真正为我做,因为除非你高度定制它,否则你不能很好地控制你的模拟,在这种情况下,你可能会从头开始做自己的计算,复杂。

2)你可以在水里调整你的精灵的线性和旋转阻尼。 事实上,即使苹果在他们的文档引用中也提到了这一点。 但是,这不会给你浮力。

linearDamping和angularDamping属性用于计算身体在世界中的摩擦。 例如,这可能被用来模拟空气或水的摩擦。

3)自己进行计算。 在更新方法中,检查人体什么时候进入“水”,什么时候进入,你可以计算粘度和/或浮力,并相应地调整节点的速度。 这在我看来是最好的select,但也更困难。

编辑 :我刚刚在Swift中写了一个选项3的快速示例。 我想这是你在找什么。 我在顶部添加了因子常量,所以你可以调整它以得到你想要的。 动作是dynamic应用的,所以它不会干扰你当前的速度(即你可以在水中控制你的angular色)。 下面是这个场景的代码和一个gif。 请记住,增量时间假定为每秒60帧(1/60),并且没有速度钳位。 这些是你可能或不想要的function,取决于你的游戏。

在这里输入图像说明

迅速

 class GameScene: SKScene { //MARK: Factors let VISCOSITY: CGFloat = 6 //Increase to make the water "thicker/stickier," creating more friction. let BUOYANCY: CGFloat = 0.4 //Slightly increase to make the object "float up faster," more buoyant. let OFFSET: CGFloat = 70 //Increase to make the object float to the surface higher. //MARK: - var object: SKSpriteNode! var water: SKSpriteNode! override func didMoveToView(view: SKView) { object = SKSpriteNode(color: UIColor.whiteColor(), size: CGSize(width: 25, height: 50)) object.physicsBody = SKPhysicsBody(rectangleOfSize: object.size) object.position = CGPoint(x: self.size.width/2.0, y: self.size.height-50) self.addChild(object) water = SKSpriteNode(color: UIColor.cyanColor(), size: CGSize(width: self.size.width, height: 300)) water.position = CGPoint(x: self.size.width/2.0, y: water.size.height/2.0) water.alpha = 0.5 self.addChild(water) self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame) } override func update(currentTime: CFTimeInterval) { if water.frame.contains(CGPoint(x:object.position.x, y:object.position.y-object.size.height/2.0)) { let rate: CGFloat = 0.01; //Controls rate of applied motion. You shouldn't really need to touch this. let disp = (((water.position.y+OFFSET)+water.size.height/2.0)-((object.position.y)-object.size.height/2.0)) * BUOYANCY let targetPos = CGPoint(x: object.position.x, y: object.position.y+disp) let targetVel = CGPoint(x: (targetPos.x-object.position.x)/(1.0/60.0), y: (targetPos.y-object.position.y)/(1.0/60.0)) let relVel: CGVector = CGVector(dx:targetVel.x-object.physicsBody.velocity.dx*VISCOSITY, dy:targetVel.y-object.physicsBody.velocity.dy*VISCOSITY); object.physicsBody.velocity=CGVector(dx:object.physicsBody.velocity.dx+relVel.dx*rate, dy:object.physicsBody.velocity.dy+relVel.dy*rate); } } override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {object.position = (touches.anyObject() as UITouch).locationInNode(self);object.physicsBody.velocity = CGVectorMake(0, 0)} } 

Objective-C的

 #import "GameScene.h" #define VISCOSITY 6.0 //Increase to make the water "thicker/stickier," creating more friction. #define BUOYANCY 0.4 //Slightly increase to make the object "float up faster," more buoyant. #define OFFSET 70.0 //Increase to make the object float to the surface higher. @interface GameScene () @property (nonatomic, strong) SKSpriteNode* object; @property (nonatomic, strong) SKSpriteNode* water; @end @implementation GameScene -(void)didMoveToView:(SKView *)view { _object = [[SKSpriteNode alloc] initWithColor:[UIColor whiteColor] size:CGSizeMake(25, 50)]; self.object.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.object.size]; self.object.position = CGPointMake(self.size.width/2.0, self.size.height-50); [self addChild:self.object]; _water = [[SKSpriteNode alloc] initWithColor:[UIColor cyanColor] size:CGSizeMake(self.size.width, 300)]; self.water.position = CGPointMake(self.size.width/2.0, self.water.size.height/2.0); self.water.alpha = 0.5; [self addChild:self.water]; self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame]; } -(void)update:(NSTimeInterval)currentTime { if (CGRectContainsPoint(self.water.frame, CGPointMake(self.object.position.x,self.object.position.y-self.object.size.height/2.0))) { const CGFloat rate = 0.01; //Controls rate of applied motion. You shouldn't really need to touch this. const CGFloat disp = (((self.water.position.y+OFFSET)+self.water.size.height/2.0)-((self.object.position.y)-self.object.size.height/2.0)) * BUOYANCY; const CGPoint targetPos = CGPointMake(self.object.position.x, self.object.position.y+disp); const CGPoint targetVel = CGPointMake((targetPos.x-self.object.position.x)/(1.0/60.0), (targetPos.y-self.object.position.y)/(1.0/60.0)); const CGVector relVel = CGVectorMake(targetVel.x-self.object.physicsBody.velocity.dx*VISCOSITY, targetVel.y-self.object.physicsBody.velocity.dy*VISCOSITY); self.object.physicsBody.velocity=CGVectorMake(self.object.physicsBody.velocity.dx+relVel.dx*rate, self.object.physicsBody.velocity.dy+relVel.dy*rate); } } -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { self.object.position = [(UITouch*)[touches anyObject] locationInNode:self]; self.object.physicsBody.velocity = CGVectorMake(0, 0); } @end 

当你的玩家与水接触时,把他推上去,直到他不再与水接触。 此时,修改玩家的接触位掩码,以便与水碰撞,从而“让他在水上行走”。

或者,您可以通过战略性放置的不可见节点触发水上接触位掩模修改,而不是等待水接触发生。

为了将玩家的联系位掩码恢复到正常状态,使用玩家将作出的另一个预定的联系(例如地面或不可见节点)作为触发。