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)
Related
guys!
I am following this tutorial How to make a platform game like Super Mario Brothers to create a simple platform game. In the tutorial, the character sprite's size is a about a size of a single tile, and collision detection is calculated for 8 surrounding tiles. I modified the sprite's size to be equal about 4 tiles (2x2) and calculate collisions with 12 surrounding tiles. It works fine for the bottom and right sprite's edges, but the left and upper edges a little bit overlap with an obstacle before collision occurs, that is obviously wrong.
I feel that I have some mistakes in there, but as I am quite new in Spritekit I cannot spot them. I would really appreciate if someone could help me with it. Thanks in advance.
Here is the update function for the character:
- (void)update:(NSTimeInterval)delta
{
CGPoint gravity = CGPointMake(0.0, -450.0);
CGPoint gravityStep = CGPointMultiplyScalar(gravity, delta);
CGPoint forwardMove = CGPointMake(800.0, 0.0);
CGPoint forwardMoveStep = CGPointMultiplyScalar(forwardMove, delta);
CGPoint backwardMove = CGPointMake(-800.0, 0.0);
CGPoint backwardMoveStep = CGPointMultiplyScalar(backwardMove, delta);
if (self.forwardMarch)
{
self.velocity = CGPointAdd(self.velocity, forwardMoveStep);
}
if (self.backwardMarch)
{
self.velocity = CGPointAdd(self.velocity, backwardMoveStep);
}
self.velocity = CGPointAdd(self.velocity, gravityStep);
self.velocity = CGPointMake(self.velocity.x * 0.9, self.velocity.y);
// setup minimum and maximum limits for the motion speed
CGPoint minMovement = CGPointMake(0.0, -450);
CGPoint maxMovement = CGPointMake(0.0, 0.0);
// forward motion
if (self.velocity.x >= 0)
{
minMovement = CGPointMake(0.0, -450);
maxMovement = CGPointMake(120.0, 250.0);
}
// backward motion
if (self.velocity.x < 0)
{
minMovement = CGPointMake(-120.0, -450);
maxMovement = CGPointMake(0.0, 250.0);
}
self.velocity = CGPointMake(Clamp(self.velocity.x, minMovement.x, maxMovement.x), Clamp(self.velocity.y, minMovement.y, maxMovement.y));
CGPoint velocityStep = CGPointMultiplyScalar(self.velocity, delta);
self.newPosition = CGPointAdd(self.position, velocityStep);
}
This function find the bounding box of the sprite
- (CGRect)collisionBoundingBox
{
CGPoint diff = CGPointSubtract(self.newPosition, self.position);
return CGRectOffset(self.frame, diff.x, diff.y);
}
And the function where I handle collisions
- (void)handleObstacleCollisionsForPlayer:(Player *)player forLayer:(TMXLayer *)layer
{
NSInteger indices[12] = {13, 14, 1, 2, 4, 8, 7, 11, 0, 3, 12, 15};
player.onGround = NO;
for (NSUInteger i = 0; i < 12; i++)
{
NSInteger tileIndex = indices[i];
CGRect playerRect = [player collisionBoundingBox];
CGPoint playerCoord = [layer coordForPoint:player.newPosition];
NSInteger tileColumn = tileIndex % 4;
NSInteger tileRow = tileIndex / 4;
CGPoint tileCoord = CGPointMake(playerCoord.x + (tileColumn - 1), playerCoord.y + (tileRow - 1));
NSInteger gid = [self tileGIDAtTileCoord:tileCoord forLayer:layer];
if (gid)
{
CGRect tileRect = [self tileRectFromTileCoords:tileCoord];
if (CGRectIntersectsRect(playerRect, tileRect))
{
CGRect intersection = CGRectIntersection(playerRect, tileRect);
if (tileIndex == 13 || tileIndex == 14)
{
//tile is below
player.newPosition = CGPointMake(player.newPosition.x, player.newPosition.y + intersection.size.height);
player.velocity = CGPointMake(player.velocity.x, 0.0);
player.onGround = YES;
}
else if (tileIndex == 1 || tileIndex == 2)
{
//tile is directly above
player.newPosition = CGPointMake(player.newPosition.x, player.newPosition.y - intersection.size.height);
}
else if (tileIndex == 4 || tileIndex == 8)
{
//tile is left
player.newPosition = CGPointMake(player.newPosition.x + intersection.size.width, player.newPosition.y);
}
else if (tileIndex == 7 || tileIndex == 11)
{
//tile is right
player.newPosition = CGPointMake(player.newPosition.x - intersection.size.width, player.newPosition.y);
}
}
}
}
player.position = player.newPosition;
}
inside your GameViewController.m enable debug physics mode
that will draw a thick outline around every box and give u exact information about collision tiles may i think either your hero or tile sizes are differ than the tutorial you following
skView.showsPhysics= YES;
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.
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 am new to Cocos2d and hence not aware of most of the classes and also the functions. In my application, I want to implement a module like touching and dragging the ball in a direction of a barrel will pass the ball and will put the ball in the barrel. I am able to pass the ball in the direction but then it just would go out of the screen following the coordinates(x.y).
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// Choose one of the touches to work with
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
// Determine offset of location to projectile
int offX = location.x - ball.position.x;
int offY = location.y - ball.position.y;
// Bail out if we are shooting down or backwards
if (offX <= 0)
return;
// Determine where we wish to shoot the projectile to
int realX = winSize.width + (ball.contentSize.height/2);
float ratio = (float) offY / (float) offX;
int realY = (realX * ratio) + ball.position.y;
CGPoint realDest = ccp(realX, realY);
if(realX>=320)
realX = 320;
if(realY>=480)
realY = 480;
CGPoint realDest = ccp(realX, realY);
int good = goodBarrel.position.x;
int bad = badBarrel.position.x;
int destY = realDest.x;
if(destY<=good)
destY = good;
if(destY>=bad)
destY = bad;
realDest.x = destY+10;
// Determine the length of how far we're shooting
int offRealX = realX - ball.position.x;
int offRealY = realY - ball.position.y;
float length = sqrtf((offRealX*offRealX)+(offRealY*offRealY));
float velocity = 480/1; // 480pixels/1sec
float realMoveDuration = length/velocity;
// Move projectile to actual endpoint
[ball runAction:[CCSequence actions:
[CCMoveTo actionWithDuration:realMoveDuration position:realDest],
[CCCallFuncN actionWithTarget:self selector:#selector(spriteMoveFinished:)],
nil]];
}
I need to do something like it will get the idea of barrels position and finding the top of the barrel and will put the ball in the center of the barrel and also while moving the ball,the size of the ball gets reduced to give effect of throwing the ball to a distant place. GoodBarrel and BadBarrel are the two barrels I have put in the opposite of ball to put the ball in the same. Is there any function or method I can work on?
Immediately after the [ball runAction: .... ]; line, you can run another action on the same object, simultaneously. Add a
[ball runAction:[CCScaleTo actionWithDuration:realMoveDuration scale:0.4f];
both actions will complete at the same time, providing the 'illusion' of distance you are seeking to create.
You can use collision detection, or in other words check to see if the CGRect of ball and barrel intersect
- (void)collisionDetection:(ccTime)dt{
CGRect ballRect = CGRectMake(
ball.position.x,
ball.position.y,
10,
10);
CGRect barrelRect = CGRectMake(
barrel.position.x,
barrel.position.y,
50,
10);
if (CGRectIntersectsRect(ballRect, barrelRect))
{
// Run Animation of Ball Dropping in the Barrel
[self removeChild:ball cleanup: YES];
}
}
hope this is what you need.
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....