Reputation: 1318
I'm all new to SpriteKit.
I'm trying to solve this problem: I have two balls in my scene and when I drag one ball an "hit" the other ball, this ball should roll away using the correct physics.
In my test code I only can "move" the second ball, he is not using the "force" of the hit...
Here is my scene code:
#import "HittingScene.h"
@interface HittingScene()
@property (nonatomic, strong) SKShapeNode *targetBall;
@property (nonatomic, strong) SKShapeNode *mainBall;
@property (nonatomic, weak) SKShapeNode *draggedNode;
@end
@implementation HittingScene
- (id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size]) {
[self addChild:[self createMainBall]];
[self addChild:[self createTargetBall]];
[self setPhysicsBody:[SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame]];
}
return self;
}
- (SKShapeNode *)createTargetBall
{
self.targetBall = [SKShapeNode node];
CGPathRef path = CGPathCreateWithEllipseInRect((CGRect){{-20, -20}, {40, 40}}, NULL);
[self.targetBall setPath:path];
CGPathRelease(path);
[self.targetBall setPosition:CGPointMake(200, 200)];
[self.targetBall setName:@"targetBall"];
[self.targetBall setPhysicsBody:[SKPhysicsBody bodyWithCircleOfRadius:20.0]];
self.targetBall.physicsBody.dynamic = YES;
self.targetBall.physicsBody.affectedByGravity = NO;
self.targetBall.physicsBody.restitution = 0.7;
return self.targetBall;
}
- (SKShapeNode *)createMainBall
{
self.mainBall = [SKShapeNode node];
CGPathRef path = CGPathCreateWithEllipseInRect((CGRect){{-20, -20}, {40, 40}}, NULL);
[self.mainBall setPath:path];
CGPathRelease(path);
[self.mainBall setPosition:CGPointMake(100, 100)];
[self.mainBall setName:@"mainBall"];
[self.mainBall setPhysicsBody:[SKPhysicsBody bodyWithCircleOfRadius:20.0]];
self.mainBall.physicsBody.dynamic = YES;
self.mainBall.physicsBody.affectedByGravity = NO;
self.mainBall.physicsBody.restitution = 0.7;
return self.mainBall;
}
- (void)touchesBegan:(NSSet*) touches withEvent:(UIEvent*) event
{
self.draggedNode = (SKShapeNode *)[self nodeAtPoint:[[touches anyObject] locationInNode:self]];
}
- (void)touchesMoved:(NSSet*) touches withEvent:(UIEvent*) event
{
self.draggedNode.position = [[touches anyObject] locationInNode:self];
}
- (void)touchesEnded:(NSSet*) touches withEvent:(UIEvent*) event
{
self.draggedNode = nil;
}
@end
Anyone with an idea how to solve this?
Thanks, Urkman
Upvotes: 0
Views: 385
Reputation: 1318
And here is a possible solution:
#import "HittingScene.h"
@interface HittingScene() <SKPhysicsContactDelegate>
@property (nonatomic, strong) SKShapeNode *targetBall;
@property (nonatomic, strong) SKShapeNode *mainBall;
@property (nonatomic) CGPoint lastTouch;
@property (nonatomic) bool isDragging;
@end
@implementation HittingScene
static const uint32_t mainBallCategory = 0x1 << 0;
static const uint32_t targetBallCategory = 0x1 << 1;
- (id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size]) {
self.physicsWorld.gravity = CGVectorMake(0, 0);
[self addChild:[self createMainBall]];
[self addChild:[self createTargetBall]];
[self setPhysicsBody:[SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame]];
self.physicsWorld.contactDelegate = self;
[self.targetBall.physicsBody applyForce:CGVectorMake(30, 30)];
}
return self;
}
- (SKShapeNode *)createTargetBall
{
self.targetBall = [SKShapeNode node];
CGPathRef path = CGPathCreateWithEllipseInRect((CGRect){{-20, -20}, {40, 40}}, NULL);
[self.targetBall setPath:path];
CGPathRelease(path);
[self.targetBall setPosition:CGPointMake(200, 200)];
[self.targetBall setName:@"targetBall"];
[self.targetBall setPhysicsBody:[SKPhysicsBody bodyWithCircleOfRadius:20.0]];
self.targetBall.physicsBody.dynamic = YES;
self.targetBall.physicsBody.affectedByGravity = YES;
self.targetBall.physicsBody.restitution = 0.7;
self.targetBall.physicsBody.categoryBitMask = targetBallCategory;
self.targetBall.physicsBody.contactTestBitMask = mainBallCategory;
return self.targetBall;
}
- (SKShapeNode *)createMainBall
{
self.mainBall = [SKShapeNode node];
CGPathRef path = CGPathCreateWithEllipseInRect((CGRect){{-20, -20}, {40, 40}}, NULL);
[self.mainBall setPath:path];
CGPathRelease(path);
[self.mainBall setPosition:CGPointMake(100, 100)];
[self.mainBall setName:@"mainBall"];
[self.mainBall setPhysicsBody:[SKPhysicsBody bodyWithCircleOfRadius:20.0]];
self.mainBall.physicsBody.dynamic = NO;
self.mainBall.physicsBody.affectedByGravity = NO;
self.mainBall.physicsBody.restitution = 0.7;
self.mainBall.physicsBody.categoryBitMask = mainBallCategory;
return self.mainBall;
}
- (void)touchesBegan:(NSSet*) touches withEvent:(UIEvent*) event
{
if ((SKShapeNode *)[self nodeAtPoint:[[touches anyObject] locationInNode:self]] == self.mainBall) {
self.isDragging = YES;
}
}
- (void) didBeginContact:(SKPhysicsContact *)contact
{
if (self.isDragging) {
float angle = atan2f (self.lastTouch.y - self.mainBall.position.y, self.lastTouch.x - self.mainBall.position.x) ;
float distance = hypotf(self.lastTouch.x - self.mainBall.position.x, self.lastTouch.y - self.mainBall.position.y);
// calculate vector
CGFloat thrust = distance;
CGVector thrustVector = CGVectorMake(thrust*cosf(angle), thrust*sinf(angle));
// apply force or impluse to target node
// force is not working
// [self.targetBall.physicsBody applyForce:thrustVector];
// impulse is working
[self.targetBall.physicsBody applyImpulse:thrustVector];
}
}
- (void)touchesMoved:(NSSet*) touches withEvent:(UIEvent*) event
{
self.lastTouch = [[touches anyObject] locationInNode:self];
if (self.isDragging) {
[self.mainBall runAction:[SKAction moveTo:[[touches anyObject] locationInNode:self] duration:0.01]];
}
}
- (void)touchesEnded:(NSSet*) touches withEvent:(UIEvent*) event
{
self.isDragging = NO;
}
@end
Upvotes: 0
Reputation: 803
I think the problem is that you're just changing the position of the ball on touch. Doing so just places the node at a new point and doesn't actually involve any force. You will need to set up a collision system where a certain amount of force is applied to the target ball when the main ball collides with it.
applyForce:atPoint: in SKPhysicsBody looks like a good place to start.
Upvotes: 1