无法阻止精灵移出屏幕
Can't prevent the sprite from moving out of the screen
我想让我的精灵在屏幕范围内移动。所以我通过 bodyWithEdgeLoopFromRect
创建了一个边缘循环,并且还设置了碰撞位掩码以使它们相互碰撞。
static const uint32_t kRocketCategory = 0x1 << 0;
static const uint32_t kEdgeCategory = 0x1 << 6;
我使用平移识别器来移动精灵,这是设置所有事物属性的当前代码。
- (void)didMoveToView:(SKView *)view
{
// Pan gesture
self.panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanFrom:)];
[self.view addGestureRecognizer:self.panRecognizer];
// Edge
self.backgroundColor = [SKColor blackColor];
self.physicsWorld.gravity = CGVectorMake(0.0f, 0.0f);
self.physicsWorld.contactDelegate = self;
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsBody.categoryBitMask = kEdgeCategory;
self.physicsBody.contactTestBitMask = kRocketCategory;
self.physicsBody.collisionBitMask = kRocketCategory;
self.physicsBody.usesPreciseCollisionDetection = YES;
self.physicsBody.restitution = 0;
// Rocket
SKTexture *texture = [SKTexture textureWithImageNamed:@"Rocketship-v2-1"];
texture.filteringMode = SKTextureFilteringNearest;
self.rocketSprite = [SKSpriteNode spriteNodeWithTexture:texture];
self.rocketSprite.position = CGPointMake(CGRectGetMidX(self.scene.frame), CGRectGetMaxY(self.scene.frame)*0.3); // 30% Y-axis
self.rocketSprite.name = @"rocketNode";
self.rocketSprite.xScale = 2;
self.rocketSprite.yScale = 2;
self.rocketSprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.rocketSprite.size];
self.rocketSprite.physicsBody.categoryBitMask = kRocketCategory;
self.rocketSprite.physicsBody.contactTestBitMask = kEdgeCategory;
self.rocketSprite.physicsBody.collisionBitMask = kEdgeCategory;
self.rocketSprite.physicsBody.usesPreciseCollisionDetection = YES;
self.rocketSprite.physicsBody.allowsRotation = NO;
self.rocketSprite.physicsBody.restitution = 0;
[self addChild:self.rocketSprite];
}
平移识别码:
- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan) {
self.selectedRocket = self.rocketSprite;
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [recognizer translationInView:recognizer.view];
translation = CGPointMake(translation.x, -translation.y);
[self panForTranslation:translation];
[recognizer setTranslation:CGPointZero inView:recognizer.view];
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
}
}
- (void)panForTranslation:(CGPoint)translation
{
CGPoint position = self.selectedRocket.position;
if ([self.selectedRocket.name isEqualToString:@"rocketNode"]) {
self.selectedRocket.position = CGPointMake(position.x + translation.x, position.y);
}
}
现在的问题是当我缓慢移动精灵(火箭)时,边缘会阻止精灵走出屏幕。但是,当我快速移动精灵时,精灵会冲出范围。请看下面的动画。我已经阅读了类似问题的一些解决方案,但我仍然不知道我的代码有什么问题。对于这里的情况,边循环和碰撞位掩码是否不够?
我在日常生活中不使用 sprite kit,但是你可以通过在你的 sprite 更新中添加这样的东西来解决它 method
(伪代码)
CGFloat x = clamp(self.position.x, minXValue, maxXValue);
CGFloat y = clamp(self.position.y, minYValue, maxYValue);
self.position = CGPointMake(x, y);
使用 SpriteKit 动作而不是直接修改精灵的位置。
发生的情况是识别器发送更新的速度不够快。精灵的边界在任何一帧中都不会与边缘相交。
在一次更新中,识别器说触摸在边缘循环的边界内。但在下一次更新中,触摸已经超越了边缘循环。这不会留下精灵边界与边缘相交的帧,因此不会检测到碰撞。
您的尝试与我在网上搜索时发现的类似,并且阅读了您尝试过的内容,看起来您几乎涵盖了大多数更常见的问题(例如,不使用 SKAction) .
我目前不使用接触位掩码来处理场景一侧的碰撞,但我确实使用了一个主动检查位置的循环。
这是我用来重新定位对象的方法:(重新定位所有东西,你可以指定它只移动火箭。
-(void)repositionObjects
{
for (CXSpriteNode *i in self.sceneObjects) // CXSpriteNode is a certain subclass
{
CGPoint position = i.position;
if (position.x > self.background.size.width || position.x < 0)
{
CGPoint newPosition;
if (position.x > self.background.size.width)
{
newPosition = CGPointMake(self.background.size.width-1, position.y);
} else {
newPosition = CGPointMake(1, position.y);
}
[i runAction:[SKAction moveTo:newPosition duration:0.0f]];
}
if (position.y > self.background.size.height || position.y < 0)
{
CGPoint newPosition;
if (position.y > self.background.size.height)
{
newPosition = CGPointMake(self.background.size.height-1, 1);
} else {
newPosition = CGPointMake(position.x, 1);
}
[i runAction:[SKAction moveTo:newPosition duration:0.0f]];
}
}
}
这是从 SKScene 循环调用的:
-(void)update:(NSTimeInterval)currentTime
{
[self repositionObjects];
}
现在,它显然没有你想要的那么优雅,我觉得它可能仍然会引起闪烁,但我还是会试一试。
此外,可能值得尝试 disable/cancel 手势暂时超出范围以停止可能导致闪烁的重复滑动。
希望这对您有所帮助。
我想让我的精灵在屏幕范围内移动。所以我通过 bodyWithEdgeLoopFromRect
创建了一个边缘循环,并且还设置了碰撞位掩码以使它们相互碰撞。
static const uint32_t kRocketCategory = 0x1 << 0;
static const uint32_t kEdgeCategory = 0x1 << 6;
我使用平移识别器来移动精灵,这是设置所有事物属性的当前代码。
- (void)didMoveToView:(SKView *)view
{
// Pan gesture
self.panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanFrom:)];
[self.view addGestureRecognizer:self.panRecognizer];
// Edge
self.backgroundColor = [SKColor blackColor];
self.physicsWorld.gravity = CGVectorMake(0.0f, 0.0f);
self.physicsWorld.contactDelegate = self;
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsBody.categoryBitMask = kEdgeCategory;
self.physicsBody.contactTestBitMask = kRocketCategory;
self.physicsBody.collisionBitMask = kRocketCategory;
self.physicsBody.usesPreciseCollisionDetection = YES;
self.physicsBody.restitution = 0;
// Rocket
SKTexture *texture = [SKTexture textureWithImageNamed:@"Rocketship-v2-1"];
texture.filteringMode = SKTextureFilteringNearest;
self.rocketSprite = [SKSpriteNode spriteNodeWithTexture:texture];
self.rocketSprite.position = CGPointMake(CGRectGetMidX(self.scene.frame), CGRectGetMaxY(self.scene.frame)*0.3); // 30% Y-axis
self.rocketSprite.name = @"rocketNode";
self.rocketSprite.xScale = 2;
self.rocketSprite.yScale = 2;
self.rocketSprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.rocketSprite.size];
self.rocketSprite.physicsBody.categoryBitMask = kRocketCategory;
self.rocketSprite.physicsBody.contactTestBitMask = kEdgeCategory;
self.rocketSprite.physicsBody.collisionBitMask = kEdgeCategory;
self.rocketSprite.physicsBody.usesPreciseCollisionDetection = YES;
self.rocketSprite.physicsBody.allowsRotation = NO;
self.rocketSprite.physicsBody.restitution = 0;
[self addChild:self.rocketSprite];
}
平移识别码:
- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan) {
self.selectedRocket = self.rocketSprite;
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [recognizer translationInView:recognizer.view];
translation = CGPointMake(translation.x, -translation.y);
[self panForTranslation:translation];
[recognizer setTranslation:CGPointZero inView:recognizer.view];
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
}
}
- (void)panForTranslation:(CGPoint)translation
{
CGPoint position = self.selectedRocket.position;
if ([self.selectedRocket.name isEqualToString:@"rocketNode"]) {
self.selectedRocket.position = CGPointMake(position.x + translation.x, position.y);
}
}
现在的问题是当我缓慢移动精灵(火箭)时,边缘会阻止精灵走出屏幕。但是,当我快速移动精灵时,精灵会冲出范围。请看下面的动画。我已经阅读了类似问题的一些解决方案,但我仍然不知道我的代码有什么问题。对于这里的情况,边循环和碰撞位掩码是否不够?
我在日常生活中不使用 sprite kit,但是你可以通过在你的 sprite 更新中添加这样的东西来解决它 method
(伪代码)
CGFloat x = clamp(self.position.x, minXValue, maxXValue);
CGFloat y = clamp(self.position.y, minYValue, maxYValue);
self.position = CGPointMake(x, y);
使用 SpriteKit 动作而不是直接修改精灵的位置。
发生的情况是识别器发送更新的速度不够快。精灵的边界在任何一帧中都不会与边缘相交。
在一次更新中,识别器说触摸在边缘循环的边界内。但在下一次更新中,触摸已经超越了边缘循环。这不会留下精灵边界与边缘相交的帧,因此不会检测到碰撞。
您的尝试与我在网上搜索时发现的类似,并且阅读了您尝试过的内容,看起来您几乎涵盖了大多数更常见的问题(例如,不使用 SKAction) .
我目前不使用接触位掩码来处理场景一侧的碰撞,但我确实使用了一个主动检查位置的循环。
这是我用来重新定位对象的方法:(重新定位所有东西,你可以指定它只移动火箭。
-(void)repositionObjects
{
for (CXSpriteNode *i in self.sceneObjects) // CXSpriteNode is a certain subclass
{
CGPoint position = i.position;
if (position.x > self.background.size.width || position.x < 0)
{
CGPoint newPosition;
if (position.x > self.background.size.width)
{
newPosition = CGPointMake(self.background.size.width-1, position.y);
} else {
newPosition = CGPointMake(1, position.y);
}
[i runAction:[SKAction moveTo:newPosition duration:0.0f]];
}
if (position.y > self.background.size.height || position.y < 0)
{
CGPoint newPosition;
if (position.y > self.background.size.height)
{
newPosition = CGPointMake(self.background.size.height-1, 1);
} else {
newPosition = CGPointMake(position.x, 1);
}
[i runAction:[SKAction moveTo:newPosition duration:0.0f]];
}
}
}
这是从 SKScene 循环调用的:
-(void)update:(NSTimeInterval)currentTime
{
[self repositionObjects];
}
现在,它显然没有你想要的那么优雅,我觉得它可能仍然会引起闪烁,但我还是会试一试。
此外,可能值得尝试 disable/cancel 手势暂时超出范围以停止可能导致闪烁的重复滑动。
希望这对您有所帮助。