Here is my code for the player and the ball that interact with each other. What I want to do is to apply force to the ball like if my player is shooting it. I want the ball to move away from my player with force. how can I apply Impulse or force to this. I have tried many times but I am a newbie with Sprite Kit.
- (void) Player {
_Player = [SKSpriteNode spriteNodeWithImageNamed:#"player1"];
_Player.xScale = 0.09;
_Player.yScale = 0.09;
_Player.position = CGPointMake(self.size.width/4, self.size.height/2);
_Player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_Player.size];
_Player.physicsBody.dynamic = NO;
[self addChild:_Player];
}
- (void) TheMethodForBall {
SKSpriteNode *sprites = [SKSpriteNode spriteNodeWithImageNamed:#"ball"];
sprites.xScale = 0.19;
sprites.yScale = 0.19;
sprites.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprites.size];
sprites.physicsBody.dynamic = YES;
sprites.physicsBody.affectedByGravity = NO;
sprites.physicsBody.allowsRotation = YES;
sprites.physicsBody.restitution = YES;
sprites.physicsBody.angularVelocity = 4;
sprites.physicsBody.usesPreciseCollisionDetection = YES;
[self addChild:sprites];
}
I think you want to apply an impulse like a kick ?
You need the following, maybe when you touch the screen / or a button
[_myBall.physicsBody applyImpulse:CGVectorMake(somePowerX, somePowerY)];
Here is another post that will help get you started
Also, this is a good tutorial for beginners.
Related
I want objects of the same category to not bounce off or be moved when they collide with one another. I've tried playing around with collisionBitMask, contactTestBitMask, mass, density, etc. Does anyone know how to do this?
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.frame.size];
self.physicsBody.affectedByGravity = NO;
self.physicsBody.allowsRotation = NO;
self.physicsBody.categoryBitMask = CollisionTypeRock;
self.physicsBody.collisionBitMask = CollisionTypeRock;
self.physicsBody.contactTestBitMask = CollisionTypeRock;
Thanks.
I am developing a game for iOS 7, and it uses Spritekit.
The main skspritenode is a helicopter, and when this helicopter makes contact with a shrink bonus pickup, the helicopter shrinks to make navigation easier for the user. Here is the code for shrinking the helicopter that works as expected (self refers to the helicopter skspritenode):
SKAction *scaleHeli = [SKAction scaleBy:.70 duration:1];
SKAction *setPhysicsBody = [SKAction runBlock:^{
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(self.size.width, self.size.height)];
self.physicsBody.mass = 0.080944441;
self.physicsBody.restitution = 1.0f;
self.physicsBody.allowsRotation = FALSE;
self.physicsBody.categoryBitMask = APAColliderTypeShip;
self.physicsBody.collisionBitMask = APAColliderTypeCavePiece;
self.physicsBody.contactTestBitMask = APAColliderTypeCavePiece | APAColliderTypeBonusPickup;
}];
SKAction *seq = [SKAction sequence:#[scaleHeli, setPhysicsBody]];
[self runAction:seq];
_shrinkEnabled = true;
I have a timer setup for the shrink bonus that lasts for a predetermined amount of time:
-(void)startShrinkTimer
{
//Creating the timer for the shield
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countDownShrinkDuration) userInfo:Nil repeats:true];
_timerSeconds = 15;
}
-(void)countDownShrinkDuration
{
if(_timerSeconds > 0)
{
--_timerSeconds;
}
else if(_timerSeconds == 0)
{
[_sceneRef disableShrinkBonus];
[_timer invalidate];
}
if(_timerSeconds < 5)
{
[_sceneRef shrinkWearingOut];
}
}
If the timer gets to be less than 5 seconds then the shrinkWearingOut function is called, which adds a label to a node that fades in and out (self refers to the helicopter skspritenode):
SKLabelNode *downLbl = [[SKLabelNode alloc] init];
downLbl.text = #"↓";
downLbl.fontColor = [UIColor whiteColor];
downLbl.fontSize = 9.0f;
downLbl.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft;
downLbl.position = CGPointMake(0,-30);
downLbl.zPosition = 5;
SKAction *fadeOut = [SKAction fadeOutWithDuration: .2];
SKAction *fadeIn = [SKAction fadeInWithDuration: .2];
SKAction *pulse = [SKAction sequence:#[fadeOut,fadeIn]];
SKAction *pulseForever = [SKAction repeatActionForever:pulse];
SKSpriteNode *wearingOut = [[SKSpriteNode alloc] init];
wearingOut.name = #"wearingOut";
[wearingOut addChild:downLbl];
[wearingOut runAction:pulseForever];
[self addChild:wearingOut];
Once the timer reaches zero, the disableShrink function is called, which attempts to remove the pulsating node and scale the helicopter to its original size (self refers to the helicopter skspritenode):
[[self childNodeWithName:#"wearingOut"] removeFromParent];
SKAction *scaleHeli = [SKAction scaleBy:1.3 duration:1];
SKAction *setPhysicsBody = [SKAction runBlock:^{
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(self.size.width, self.size.height)];
self.physicsBody.mass = 0.080944441;
self.physicsBody.restitution = 0.0f;
self.physicsBody.allowsRotation = FALSE;
self.physicsBody.categoryBitMask = APAColliderTypeShip;
self.physicsBody.collisionBitMask = APAColliderTypeCavePiece;
self.physicsBody.contactTestBitMask = APAColliderTypeCavePiece | APAColliderTypeBonusPickup;
_shrinkEnabled = false;
}];
SKAction *seq = [SKAction sequence:#[scaleHeli, setPhysicsBody]];
[self runAction:seq];
Problem # 1: This line of code:
[[self childNodeWithName:#"wearingOut"] removeFromParent];
does not actually remove the pulsating node from the helicopter node, I have set a breakpoint here and verified that the node is not nil, yet it still seems to not remove the node. Very frustrating as I have used the exact same line of code with a different bonus pickup with a different name and it works just fine. The only difference is the other bonus pickup is using an skshapenode, either way they both inherit from sknode, so they should both function the same.
Problem # 2:The pulsating node is added successfully, but only pulses a couple of times, which I find strange because I use the exact set of skactions on a different bonus and that bonus pulses until it is removed from the helicopter node.
Observations:
You should not use NSTimer. Instead use the update: method of SKScene to perform timed actions and if needed forward it to child nodes that need it. Or use SKAction runBlock sequenced with waitForDuration. NSTimer may not run synchronous with Sprite Kit updates / render loop, and they won't stop firing if a node or the scene is paused.
Check that you don't have multiple child nodes with the name "wearingOut". Sounds like you may have added more than one. The use of NSTimer may actually cause strange behavior because Sprite Kit may delay adding/removing nodes from the scene and so timing of add/remove of nodes may be an issue. This is a wild guess though.
The pulsing issue may also indicate multiple sprites, perhaps they pulsate asynchronously so they appear as not pulsating. Alternatively try the fadeAlphaTo: actions.
In general try using the documented initializers. There may be issues if you do just [[SKLabelNode alloc] init]. I ran into some issues with [SKSpriteNode node], not drawing for instance even after setting a texture. Instead do [SKLabelNode labelNodeWithFontNamed:#"Arial"] or any of the other designated initializers. Refer to the Sprite Kit Framework reference.
I implemented UIdynamics for objects (views) bouncing around the screen. On the simulator this works flawlessly, but but when testing on an actual iphone, the boundaries don't work. I will post code if necessary but this seems to me like im missing something conceptually. Any tips or ideas?
additional details: the boundaries surround the screen and i have tested on both sizes of simulator and it works fine.
"bad" is the name of the view-- (also "screenWidth" and "screenHeight" have been defined as instance variables)
///left wall
badCollision = [[UICollisionBehavior alloc] initWithItems:#[bad]];
badCollision.collisionDelegate = self;
CGPoint pt1 = CGPointMake(0, 0);
CGPoint pt2 = CGPointMake(0, screenHeight);
[badCollision addBoundaryWithIdentifier:#"leftWall" fromPoint:pt1 toPoint:pt2];
[animator addBehavior:badCollision];
//right wall
badCollision = [[UICollisionBehavior alloc] initWithItems:#[bad]];
badCollision.collisionDelegate = self;
pt1 = CGPointMake(screenWidth, 0);
pt2 = CGPointMake(screenWidth, screenHeight);
[badCollision addBoundaryWithIdentifier:#"rightWall" fromPoint:pt1 toPoint:pt2];
[animator addBehavior:badCollision];
//top wall
badCollision = [[UICollisionBehavior alloc] initWithItems:#[bad]];
badCollision.collisionDelegate = self;
pt1 = CGPointMake(0, 0);
pt2 = CGPointMake(screenWidth, 0);
[badCollision addBoundaryWithIdentifier:#"topWall" fromPoint:pt1 toPoint:pt2];
[animator addBehavior:badCollision];
//bottom wall
badCollision = [[UICollisionBehavior alloc] initWithItems:#[bad]];
badCollision.collisionDelegate = self;
pt1 = CGPointMake(0, screenHeight);
pt2 = CGPointMake(screenWidth, screenHeight);
[badCollision addBoundaryWithIdentifier:#"bottomWall" fromPoint:pt1 toPoint:pt2];
[animator addBehavior:badCollision];
And here is what happens when "bad" hits one of the walls.
NSLog(#"Wall Hit");
UIPushBehavior *badForce = [[UIPushBehavior alloc] initWithItems:#[item] mode:UIPushBehaviorModeInstantaneous];
UIView *itemView = (UIView*)item;
itemView.backgroundColor = [UIColor redColor];
[UIView animateWithDuration:0.3 animations:^{
itemView.backgroundColor = [UIColor blueColor];
}];
int xneg = (int)drand48();
if (xneg < .51)
xneg = 1;
else
xneg = -1;
int yneg = (int)drand48();
if (yneg < .51)
yneg = 1;
else
yneg = -1;
double xSpeed = xneg*(drand48()+1)/20+.02;
double ySpeed = yneg*(drand48()+1)/20+.02;
badForce.pushDirection = CGVectorMake(xSpeed,ySpeed);
badForce.active = YES;
Not even the print statement will show in the log
You seem to be behaving like you wanted to update an existing behavior (your badForce.active = YES), but you're creating a new push behavior every time. Do one or the other. If you're going to create a new push behavior every time, you have to add it to your animator every time. If you're going to create it once, then add it to your animator once, and then just update and activate it each time.
You probably want to create one push behavior, add it once, and update it again and again:
- (void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id<UIDynamicItem>)item withBoundaryIdentifier:(id<NSCopying>)identifier atPoint:(CGPoint)p
{
NSString *wallIdentifier = (id)identifier;
NSLog(#"Wall Hit: %#", wallIdentifier);
if (!self.push) {
self.push = [[UIPushBehavior alloc] initWithItems:#[item] mode:UIPushBehaviorModeInstantaneous];
self.push.active = NO;
[self.animator addBehavior:self.push];
}
UIView *itemView = (UIView*)item;
itemView.backgroundColor = [UIColor redColor];
[UIView animateWithDuration:0.3 animations:^{
itemView.backgroundColor = [UIColor blueColor];
}];
double weight = 0.5;
double xSpeed = weight * (drand48() * 2.0 - 1.0); // rand number between -weight and weight
double ySpeed = weight * (drand48() * 2.0 - 1.0); // rand number between -weight and weight
if ([wallIdentifier isEqualToString:#"bottomWall"])
ySpeed = -fabs(ySpeed);
else if ([wallIdentifier isEqualToString:#"topWall"])
ySpeed = fabs(ySpeed);
else if ([wallIdentifier isEqualToString:#"leftWall"])
xSpeed = fabs(xSpeed);
else if ([wallIdentifier isEqualToString:#"rightWall"])
xSpeed = -fabs(xSpeed);
self.push.pushDirection = CGVectorMake(xSpeed,ySpeed);
self.push.active = YES;
}
Note, I hope you forgive me for tweaking your pushDirection algorithm, but I inferred from your creating of the four walls (rather than the much easier process of defining the boundaries of the reference view to be the collision boundary) that you wanted the push vector to vary based upon which wall you happened to collide with. The above pushes down if you hit the top wall (and vice versa) and pushes right if you hit the left wall (and vice versa). But use whatever push vector algorithm you want.
The main observation is that you either want to add one push behavior and update it again and again, or you want to just add new instantaneous push behaviors each time. But the above works on both simulator and device (and I'm honestly not clear why it worked on one and not the other for you; I don't see how your original code could have worked on either).
I've created a simple cocos2d iPhone game that has a CCParticleSystemQuad emitter that emits particles across the scene that are shaped like leaves, to give the illusion of wind.
Right now, the wind (gravity) is blowing across the scene from right to left. I am currently stuck trying to figure out how to update the emitter.gravity to switch from (-500, 80) to (500, 80), hopefully without removing the particles that have already been drawn.
In this example, I'd like the switch to occur on a touch event that happens anywhere on the screen.
What should my touch event look like?
How do I detect a touch that happens any where on the screen?
I've also never implemented a scheduled update loop. Is this the direction I should be thinking? And I suppose a more basic question is, am I going about this the right way?
Here's the code I've got so far:
My init:
-(id) init
{
if( (self=[super init])) {
CCSprite * sky = [CCSprite spriteWithFile:#"sky.png"];
[self addChild:sky z:0 tag:1];
windDirection = -200;
[self leaveEmitters];
}
return self;
}
My leaveEmiiters function
-(void) leaveEmitters{
NSLog(#"The wind is :%i", windDirection);
CCParticleSystemQuad * emitter;
emitter = [[CCParticleSystemQuad alloc] initWithTotalParticles:100];
emitter.texture = [[CCTextureCache sharedTextureCache] addImage: #"particlesLeaves.png"];
emitter.emitterMode = kCCParticleModeGravity;
emitter.duration = -1;
emitter.gravity = ccp(windDirection, -80);
emitter.angle = 0;
emitter.angleVar = 360;
emitter.speed = 10;
emitter.speedVar = 100;
emitter.radialAccelVar = 0;
emitter.tangentialAccel = 0;
emitter.tangentialAccelVar = 0;
emitter.life = 10;
emitter.lifeVar = 0;
emitter.startSpin = 0;
emitter.startSpinVar = 360;
emitter.endSpin = 0;
emitter.endSpinVar = 720;
ccColor4F startColorVar = {255, 100, 0, 0};
ccColor4F startColor = {0, 240,0, 255};
emitter.startColor = startColor;
emitter.startColorVar = startColorVar;
emitter.endSize = emitter.startSize;
emitter.startSize = 60.0f;
emitter.emissionRate = 3;
emitter.blendAdditive = NO;
emitter.position = ccp(500,250);
[self addChild: emitter z:10];
emitter.autoRemoveOnFinish = YES;
CCParticleSystemQuad * emitter2;
emitter2 = [[CCParticleSystemQuad alloc] initWithTotalParticles:100];
emitter2.texture = [[CCTextureCache sharedTextureCache] addImage: #"particlesLeaves.png"];
emitter2.emitterMode = kCCParticleModeGravity;
emitter2.duration = -1;
emitter2.gravity = ccp(windDirection, 0);
emitter2.angle = 0;
emitter2.angleVar = 360;
emitter2.speed = 10;
emitter2.speedVar = 100;
emitter2.radialAccelVar = 0;
emitter2.tangentialAccel = 0;
emitter2.tangentialAccelVar = 0;
emitter2.life = 10;
emitter2.lifeVar = 0;
emitter2.startSpin = 0;
emitter2.startSpinVar = 360;
emitter2.endSpin = 0;
emitter2.endSpinVar = 720;
emitter2.startColor = startColor;
emitter2.endSize = emitter.startSize;
emitter2.startSize = 60.0f;
emitter2.emissionRate = 3;
emitter2.blendAdditive = NO;
emitter2.position = ccp(-500,250);
[self addChild: emitter2 z:10];
emitter2.autoRemoveOnFinish = YES;
}
And finally, my ccTouchesBegan function, which isn't working at all. Why?
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
int x = location.x;
int y= location.y;
CCSprite * sky = (CCSprite *) [self getChildByTag:1];
sky.anchorPoint = ccp(0, 0);
CGRect skyHitBox = CGRectMake(sky.position.x, sky.position.y, 500, 500);
if (CGRectContainsPoint(skyHitBox, location)) {
NSLog(#"touch accepted: x: %i y:%i", x, y);
}
}
Any help, feedback, or suggested learning direction would be GREATLY appreciated. Thanks so much!
EDIT: I answered my own questions like 20 seconds after I posted this...
If someone wants to post their own answers, I'll leave this up for 7 more hours.
you just can keep a CCParticleSystemQuad * emitter as private instance member so you can modify emitter.gravity inside the ccTouchesBegan
i didnt get exactly what kind of wind effect you'r trying to achieve. If you want to simply switch from one 500,80 to -500,80 its pretty easy
emitter.gravity = ccp(-emitter.gravity.x, emitter.gravity.y);
If you want to change the wind direction based on witch side of the skybox you hit, with hitting the center equal no wind, and more your touch is far from the center the stronger the wind will get.
I didnt try this but should be a good starting point.
You first need to get get the touched point in skybox coordinates
CGPoint skyTouchedPoint = ccp( x - sky.position.x, y - sky.position.y)
then you can define the center point of you sky box as
CGPoint skyCenterPoint = ccp(sky.size.width/2, sky.size.height/2);
and get a vector pointing to the right side, if you touch the right half side of the skybox and pointing left if you touch the left half side of the skybox
CGPoint windDirection = ccpSub(skyTouchedPoint, skyCenterPoint);
this vector module is higher the more far from the center you touch happened, you should now increase this vector module by a factor that suites for you with
windDirection = ccpMult(windDirection, 10.f); //try different values here
now you can use only the x component of this vector if you want the wind to use always the same y (as you posted the value of 80)
i have used cocos2d for game development.but when i use a sprite in CCLayer(inherited) to move through ccTime.but it moves very slowly.i have set in appgelegate CCDirector setAnimationInterval for (1.0/60) The code is as following:
-(id) init
{
if ((self = [super init]))
{
danceSprite = [CCSprite spriteWithFile:#"images.png"];
[self addChild:danceSprite];
// position the sprite in the center of the screen
CGSize s = [[CCDirector sharedDirector] winSize];
danceSprite.position = ccp(s.width/2,s.height/2); //CCSprite
bg_pos.x = danceSprite.position.x;
bg_pos.y = danceSprite.position.y;
[self scheduleUpdate];
}
return self;
}
-(void) update:(ccTime)delta
{
CGPoint pos = danceSprite.position;
bgX = pos.x;
//bgX -= 30.0;
int newX;
newX = bgX + 1.0 * GAME_SPEED_ADJUSTMENT;
pos.x = bgX;
}
GAME_SPEED_ADJUSTMENT
variable/constant
to multiply with delta to get a faster movement. My start setting is always a
minimum of 10
, in your case it's 1, so I would expect it to move rather slowly.
So it looks like
newX = oldX + delta *
GAME_SPEED_ADJUSTMENT
Try this first - one might also use this to increase the difficulty of the game and globally move things faster.
If this doesn't work, just come back....