使用Sprite Kit转换物理体

3
我正在制作一个2D游戏,其中一个角色站在屏幕左侧不动,物体从右侧向他飞来。他需要能够将这些飞行的敌人拍打下来。我有一个包含“拍打”动画帧的精灵动画。在拍打过程中,他的身体会稍微移动,他的手臂从靠在地上旋转到完全伸展在头顶上,然后拍打到地面上。这就是它的样子:SumoSmash Animation GIF 为此,我有一个名为SumoWarrior的类,它是SKSpriteNode子类。SumoWarrior精灵节点有一个名为warriorArm的子精灵节点。我的想法是让主要的相扑武士精灵节点显示动画,并仅将此warriorArm精灵节点用于形状为武士手臂的物理体的目的。我需要以某种方式旋转这个手臂体以跟随精灵动画,以检测与飞行物体的碰撞。
以下是创建手臂的方法:
sumoWarrior.warriorArm = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImageNamed:@"warriorArm"]];
    sumoWarrior.warriorArm.position = CGPointMake(15, 25);
    sumoWarrior.warriorArm.anchorPoint = CGPointMake(0.16, 0.7);
    sumoWarrior.warriorArm.texture = nil;
    sumoWarrior.warriorArm.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:[sumoWarrior createArmBody]];
    sumoWarrior.warriorArm.physicsBody.mass = 9999;
    sumoWarrior.warriorArm.physicsBody.categoryBitMask = CollisionPlayer;
    sumoWarrior.warriorArm.physicsBody.collisionBitMask = CollisionEnemy;
    sumoWarrior.warriorArm.physicsBody.contactTestBitMask = CollisionEnemy | CollisionAlly;
    sumoWarrior.warriorArm.physicsBody.allowsRotation = YES;
    sumoWarrior.warriorArm.physicsBody.dynamic = YES;

有没有可能通过旋转和扩展这只手臂的方式,使其能够精确地跟随动画?是否还可以更改战士的主要物理身体(仅为身体周围的多边形,不包括手臂),以便它也能跟随动画?或者我完全错过了应该采取的方法?


澄清一下,您是在询问关于仅对手臂使用物理引擎的“如何”,而不涉及身体其他部分的内容吗? - sangony
请看我在问题中提供的GIF。您可以看到从初始位置到动画结束时,身体和手臂都会改变位置和形状。我想让它们的物理体反映这些变化 - 即让物理体跟随动画的形状。这样清楚一些了吗? - damjandd
好的。下一个问题是,如果只有在手臂向下运动时才与物体接触,这是否会影响游戏逻辑?或者手臂的运动方向(从下到上或从上到下)对游戏逻辑没有影响? - sangony
只有当手臂向下挥动时才会产生影响。如果飞行的敌人在你的手臂从地面上升到休息位置时击中它,那么你就会死亡。如果物体触碰到战士的身体,你也会死亡。 - damjandd
1个回答

3
我使用Texture Packer来处理纹理和动画。我创建了一个叫做sumoAnimations的纹理图集,然后将由Texture Packer为我创建的.h文件导入到项目中。
如果您还没有它,可以获得免费副本。
在我展示代码之前,您可能需要重新考虑使用您所拥有的动画。根据您之前的评论,动画中唯一相关的帧是第15、16和17帧。我甚至不确定第17帧是否正确,因为相扑已经放下手了。这只给你3帧,根据您提供的动画,每帧时间为0.05秒,等于0.1秒。
看看我包含的3张图片,您就会知道我的意思。您可能需要考虑获取新的动画或允许帧之间更长的时间。我每帧使用0.25秒,这样您就可以更清楚地看到它。您可以将其更改为任何您喜欢的值。
至于玩家缺失和被击中,您可以在玩家周围(当然是在手臂后面)创建一个clearColor sprite rect来检测未命中对象的接触。

enter image description here enter image description here enter image description here

#import "MyScene.h"
#import "sumoAnimation.h"

@interface MyScene()<SKPhysicsContactDelegate>
@end

@implementation MyScene
{
    SKSpriteNode *sumo;
    SKSpriteNode *arm;

    SKAction *block0;
    SKAction *block1;
    SKAction *block2;
    SKAction *block3;
    SKAction *block4;

    SKAction *slapHappy;
    SKAction *wait0;
    SKAction *wait1;
}

-(id)initWithSize:(CGSize)size
{
    if (self = [super initWithSize:size])
    {
        sumo = [SKSpriteNode spriteNodeWithTexture:SUMOANIMATION_TEX_SUMO_001];
        sumo.anchorPoint = CGPointMake(0, 0);
        sumo.position = CGPointMake(0, 0);
        [self addChild:sumo];

        arm = [SKSpriteNode spriteNodeWithColor:[SKColor clearColor] size:CGSizeMake(34, 14)];
        arm.anchorPoint = CGPointMake(0, 0);
        arm.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(34,14) center:CGPointMake(17, 7)];
        arm.physicsBody.dynamic = NO;

        slapHappy = [SKAction animateWithTextures:SUMOANIMATION_ANIM_SUMO timePerFrame:0.25];

        // start the animation
        block0 = [SKAction runBlock:^{
            [sumo runAction:slapHappy];
        }];

        // time until frame 15 is reached
        wait0 = [SKAction waitForDuration:3.50];

        // add arm at frame 15 positon
        block1 = [SKAction runBlock:^{
            arm.position = CGPointMake(205, 125);
            arm.zRotation = 1.3;
            [self addChild:arm];
        }];

        // wait until next frame
        wait1 = [SKAction waitForDuration:0.25]; // time in between frames

        // move arm and rotate to frame 16 position
        block2 = [SKAction runBlock:^{
            arm.position = CGPointMake(224, 105);
            arm.zRotation = 0.4;
        }];

        // move arm and rotate to frame 17 position
        block3 = [SKAction runBlock:^{
            arm.position = CGPointMake(215, 68);
            arm.zRotation = -0.65;
        }];

        // remove arm from view
        block4 = [SKAction runBlock:^{
            [arm removeFromParent];
        }];

    }
    return self;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [sumo runAction:[SKAction sequence:@[block0, wait0, block1, wait1, block2, wait1, block3, wait1, block4]]];
}

-(void)update:(CFTimeInterval)currentTime
{
    //
}

@end

根据其他评论更新

以下图片概述了我的建议。为相扑击打的框架添加物理体矩形。这将使您不必在精确位置为每个帧添加一个物体。它还将使击打更加有效。

enter image description here

你的物体仍然会掉落到地面上并播放碎裂动画。记住,你的相扑动画移动得非常快,玩家无法看到每一帧的精确位置。
你的想法是让手臂“推”物体,这需要更精确的动画。例如,手臂的位置每次变化一个增量。然后,你必须精确地在手上定位身体。我并不是说这是不可能的,但肯定需要很多工作和难度。

请问您能否解释一下您是如何从动画中计算出手臂的位置的呢? - damjandd
1
我使用了试错法。我将精灵设置为仅显示所需的帧,并相应地调整物理体。每帧需要尝试几次。我不擅长数学,所以我相信你可以想出一个方程来自动完成它 :) - sangony
是的,问题是,我真的很不擅长数学 :) 无论如何,谢谢 :) - damjandd
我对这种方法有问题。似乎机械臂的身体只是在一个帧与下一个帧之间闪烁,这使得飞行物体可以通过其路径而不被撞击。你有什么解决方案吗? - damjandd
那么这将是一个飞行物体,撞上一堵墙并死亡。我忘了提到该物体需要被手臂推向地面,当它撞到地面时,会被"压碎",我有一个关于物体碎裂的动画。明白我的意思吗? - damjandd
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接