模拟水/使精灵“漂浮"在 spritekit 中的水上 [英] Simulate water/ make sprite "float" on water in spritekit

查看:18
本文介绍了模拟水/使精灵“漂浮"在 spritekit 中的水上的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为我的游戏添加水.除了不同的背景颜色,没有太多的.但是,我希望 player-sprite 漂浮在它上面(或在它的中间).如果玩家只是从下面走进水中,我希望他漂浮到顶部.如果他跌倒了,我希望他慢慢改变方向并重新漂浮起来.

I'm trying to add water to my game. Except for a different background color, there isn't much to it. However, I'd like the player-sprite to float on top of it (or halfway in it). If the player just walks into the water from below, I'd like him to float to the top. If he falls down, I'd like him to slowly change direction and float back up.

当他在水中时,我尝试将重力设为负值,但这会给我带来一些不想要的效果.例如,当他(玩家)浮出水面时,正常重力会将他向下推,水会将他向上推,等等.最终,玩家将在水中弹跳",被从一端推到另一端.当他浮出水面时,我希望他平静地留在水面上.我怎样才能做到这一点?

I tried making the gravity negative when he's in the water, but this gives me some slightly unwanted effects. For example as he (the player) surfaces, the normal gravity will push him back down, the water will push him up, and so on. Ultimately the player will be "bouncing" in the water, being pushed from one end to another. I'd like him to calmly remain on top of the water when he surfaces. How can I achieve this?

这是我的 update-loop 中的代码:

Here's the code I have in my 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.

Basically this changes my gravity to 2 when the player is in the water, and otherwise changes it to -4 unless it's already -4.

谢谢!

推荐答案

我相信您在模拟水方面有三种可能的选择.

There are three possible options I believe you have with regards to simulating water.

1) 如评论中所述,您可以尝试使用 SKFieldNode (iOS 8+).但是从个人经验来看,现场节点并没有真正为我做这件事,因为除非您对其进行大量自定义,否则您无法对模拟进行太多控制,在这种情况下,您不妨从头开始进行自己的计算并减少复杂性.

1) As mentioned in the comments you could try to use SKFieldNode (iOS 8+). But from personal experience the field node didn't really do it for me because you don't get much control over your simulation with it unless you heavily customize it, in which case you might as well just do your own calculations from scratch and reduce complexity.

2) 您可以在水中调整精灵的线性和旋转阻尼.事实上,甚至苹果在他们的文档中的引用中也提到了这一点.然而,这不会给你浮力.

2) You could adjust the linear and rotational damping of your sprite when inside the water. In fact, even apple mentions this in the quote from their documentation. However this won't give you buoyancy.

linearDamping 和 angularDamping 属性用于计算身体在世界中移动时产生的摩擦.例如,这个可用于模拟空气或水的摩擦.

The linearDamping and angularDamping properties are used to calculate friction on the body as it moves through the world. For example, this might be used to simulate air or water friction.

3) 自己执行计算.在更新方法中,检查身体何时进入水",当它进入时,您可以计算粘度和/或浮力并相应地调整节点的速度.我认为这是最好的选择,但也更困难.

3) Perform the calculations yourself. In the update method, check when the body enters you "water" and when it does you can calculate viscosity and/or buoyancy and adjust the velocity of your node accordingly. This in my opinion is the best option but also the more difficult.

编辑:我刚刚用 Swift 写了一个选项 3 的快速示例.我想这就是你要找的.我在顶部添加了因子常数,因此您可以对其进行调整以获得您想要的结果.该动作是动态应用的,因此它不会干扰您当前的速度(即您可以在水中控制您的角色).下面是场景的代码和一个 gif.请记住,增量时间假定为每秒 60 帧 (1/60),并且没有速度钳制.这些是您可能需要也可能不需要的功能,具体取决于您的游戏.

Edit: I just wrote a quick example of option 3 in Swift. I think it's what you are looking for. I added factor constants on the top so you can adjust it to get exactly what you want. The motion is applied dynamically so it won't interfere with you current velocity (i.e. you can control your character while in the water). Below is the code for the scene and a gif as well. Keep in mind that the delta time is assumed to be 60 frames a second (1/60) and there is no velocity clamping. These are features you may or may not want depending on your game.

斯威夫特

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

这篇关于模拟水/使精灵“漂浮"在 spritekit 中的水上的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆