Make a sprite only jump once - ios

Working with Xcode, Spritebuilder and Cocos2d, I am trying to let the players sprite only jump 1 single time. So when the player taps the screen, the sprite jumps, and only when it has landed, the player should again be able to jump again. I haven't found any clear explanations so far, so hence my question.
Here's my code
Edit so that the code only lets the sprite jump once:
static const CGFloat scrollSpeed = 0.4;
BOOL isInAir;
#implementation GameScene
{
CCSprite *player1;
CCPhysicsNode *physicsNode1;
CCNode *ground1;
CCNode *ground2;
}
- (void)didLoadFromCCB
{
self.userInteractionEnabled = TRUE;
grounds = #[ground1, ground2]
}
- (void)update:(CCTime)delta
{
// moves the player to the right
player1.position = ccp(player1.position.x + delta * scrollSpeed, player1.position.y);
// move the camera with the player
physicsNode1.position = ccp(physicsNode1.position.x - (scrollSpeed *delta), physicsNode1.position.y);
// loop the ground
for (CCNode *ground in grounds)
{
// get the world position of the ground
CGPoint groundWorldPosition = [physicsNode1 convertToWorldSpace:ground.position];
// get the screen position of the ground
CGPoint groundScreenPosition = [self convertToNodeSpace:groundWorldPosition];
// if the left corner is one complete width off the screen, move it to the right
if (groundScreenPosition.x <= (-1 * ground.contentSize.width))
{
// puts the ground piece that is about to leave the screen behind the last one
ground.position = ccp(ground.position.x + 600, ground.position.y);
//NSLog(#"player1 pos: %#", NSStringFromCGPoint(ground.position));
}
}
// clamp velocity
float yVelocity = clampf(player1.physicsBody.velocity.y, -1 * MAXFLOAT, 200.f);
player1.physicsBody.velocity = ccp(0, yVelocity);
}
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
if (isInAir == NO)
{
[player1.physicsBody applyImpulse:ccp(0, 150)];
isInAir = YES;
}
}
- (BOOL) ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair player1:(CCNode *)player1 ground: (CCNode *)ground
{
NSLog(#"player is touching the ground");
isInAir = NO;
return YES;
}
#end

You can use a BOOL. Here is my code for a jump on touch:
BOOL isInAir;//Make this global.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
SKSpriteNode *node = [self nodesAtPoint:[touch locationInNode:self]];
if (isInAir == NO)
{
CGFloat impulseX = 0.0f;
CGFloat impulseY = 600.0f;
[node.physicsBody applyImpulse:CGVectorMake(impulseX, impulseY) atPoint:node.position];
isInAir = YES;
//other stuff
}
}
Then when you contact ground, set isInAir to NO.

Related

Allow and Disallow Move Touch on some Angles in Circular Slider

I am using EVARoundSlider, which has Slider Arc with start Point and End point. Which makes user to rotate on slider clockwise and anticlockwise.
On touchesMoved method I am able to get First Angle, Last Angle and Current Angle position in Slider View. I want user to stop at End point handler, when it reaches at 360 degree and when start Point handler reaches at 0 degree.
// Working Code on circular Slider View
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
CGFloat fAngle = [self handleAngleByIndex:0];
CGFloat lAngle = [self handleAngleByIndex:1];
int a = AngleFromNorth(_centerPoint, location, SLIDER_FLIPPED);
NSLog(#"First_Angle :%.02f",fAngle);
NSLog(#"last_Angle :%.02f",lAngle);
NSLog(#"Current_Angle :%d",a);
[super touchesMoved:touches withEvent:event];
_dragging = NO;
if (!self.isDragging)
{
if ([self.delegate respondsToSelector:#selector(sliderDraggin:)])
{
[self.delegate sliderDraggin:self];
}
//return;
}
if ([self.delegate respondsToSelector:#selector(sliderDraggin:)])
{
[self.delegate sliderDraggin:self];
}
NSUInteger nearestHandleIndex = [self indexOfNearesPointForTouch:touch];
_nearesHandle = (nearestHandleIndex != NSNotFound) ? _handles[#(nearestHandleIndex)] : nil;
CGFloat d = [_math distanceBetweenPoint:_centerPoint andPoint:location];
CGFloat inOff = _radius - _sliderWidth - _offset.inside;
CGFloat outOff = _radius + _offset.outside;
if (d < inOff || d > outOff)
{
if (self.isNeedToRevoke)
{
_dragging = YES;
return;
}
}
[self.delegate slider:self touchValue:YES atIndex:nearestHandleIndex];
dispatch_async(dispatch_get_main_queue(), ^{
int a = AngleFromNorth(_centerPoint, location, SLIDER_FLIPPED);
[self moveView:_nearesHandle toAngle:a];
[self drawArc];
});
[self informDelegateAboutHandles];
}
If i try to put some condition for StartAngle and End Angle. At a particular points. it is get hanged and nothing slides further.

Speed of touch varies with number of objects on Scene

I am creating game in which user can hit the object falling from the top of the screen with the racket. user can continuously move the racket but if it is at minimal speed or is at rest it should not hit the object, but if it above the minimal speed user should hit them. I have achieved that but the issue is when user start touching the racket which continously move with the user touch, the speed varition is their it does not start with the same speed and while touch is moving at that time also some times speed is very less even though the movement is fast. Here is my piece of code
-(void)didMoveToView:(SKView *)view {
self.physicsWorld.contactDelegate = (id)self;
racketNode = [SKSpriteNode spriteNodeWithImageNamed:#"racket"];
racketNode.size = CGSizeMake(50,50);
racketNode.position = CGPointMake(self.frame.origin.x + self.frame.size.width - 50,50);
racketNode.name = #"racket";
[self addChild:racketNode];
}
-(void) didBeginContact:(SKPhysicsContact *)contact {
SKSpriteNode *nodeA = (SKSpriteNode *)contact.bodyA.node ;
SKSpriteNode *nodeB = (SKSpriteNode *) contact.bodyB.node;
if (([nodeA.name isEqualToString:#"racket"] && [nodeB.name isEqualToString:#"fallingObject"])) {
if (racketNode.speed > kMinSpeed)
[nodeB removeFromParent];
else {
nodeB.physicsBody.contactTestBitMask = 0;
[self performSelector:#selector(providingCollsion:) withObject:nodeB afterDelay:0.1];
}
}
}
-(void) providingCollsion:(SKSpriteNode *) node {
node.physicsBody.contactTestBitMask = racketHit;
}
-(void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
start = location;
startTime = touch.timestamp;
racketNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:racketNode.frame.size];
racketNode.physicsBody.categoryBitMask = racket;
racketNode.physicsBody.contactTestBitMask = HitIt;
racketNode.physicsBody.dynamic = NO;
racketNode.physicsBody.affectedByGravity = NO;
[racketNode runAction:[SKAction moveTo:location duration:0.01]];
}
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
racketNode.physicsBody = nil;
racketNode.speed = 0;
}
-(void) touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
CGFloat dx = location.x - start.x;
CGFloat dy = location.y - start.y;
CGFloat magnitude = sqrt(dx*dx+dy*dy);
// Determine time difference from start of the gesture
CGFloat dt = touch.timestamp - startTime;
// Determine gesture speed in points/sec
CGFloat speed = magnitude/dt;
racketNode.speed = speed;
[handNode runAction:[SKAction moveTo:[touch locationInNode:self] duration:0.01]];
}
Please tell me which part my code is wrong so as to make same object collide with high speed only not on slow speed and also no collision on stable state.
Instead of doing it manually, use UIPanGestureRecognizer to handle your swipes. With it, there is a velocity property that you can use to check if the speed is greater than a given value.
Here is a great tutorial to do it:
https://www.raywenderlich.com/76020/using-uigesturerecognizer-with-swift-tutorial

How do I make a sprite node point to a different sprite?

I want to change sprites when the user swipes left and right. Everything I found on stack overflow talks about changing textures. However I want to change the sprite completely.
here is my code that tries to do that
#implementation GameScene
SKSpriteNode* player;
SKSpriteNode* playerL;
SKSpriteNode* playerR;
- (void) addPlayer
{
//player right
playerR = [[SKSpriteNode alloc] initWithImageNamed: #"pummelManTestR.png"];
playerR.scale = 0.2f;
playerR.name = playerCategoryName;
playerR.position = CGPointMake(CGRectGetMidX(self.frame)*1.3f, playerR.frame.size.height *1.4f);
//player left
playerL = [[SKSpriteNode alloc] initWithImageNamed: #"pummelManTestL.png"];
playerL.scale = 0.2f;
playerL.name = playerCategoryName;
playerL.position = CGPointMake(CGRectGetMidX(self.frame)*1.3f, playerL.frame.size.height *1.4f);
//set the starting player position
player = playerL;
[self addChild:player];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint endPosition = [touch locationInNode:self];
if (touchStartPoint.x < endPosition.x && player != playerR) {
// Right swipe
//change the image for the player to the other side
player = playerR;
NSLog(#"right");
} else if (touchStartPoint.x > endPosition.x && player != playerL){
// Left swipe
//change the image for the player to the other side
player = playerL;
NSLog(#"left");
}
}
the NSLogs print the correct information but the sprite on the screen doesn't change.
Any help will be greatly appreciated.
player.texture = "your image"
player.name = "your name"
....
redefine the element, with this solution i'm test! all right!

Sprite Kit: create centred sprites in grid

I want to create a new sprite in a grid if the field is empty. I also want this new sprite to be centred in the empty field. Let's say the field is:
1 1 1
1 1 0
1 1 1
Whenever I click on one of the fields that is filled with a 1, the sprite disappears (this is what I want and this works fine).
Whenever I click on the 0, a new sprite is added with the following code:
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
SKSpriteNode *touchedNode = (SKSpriteNode *)[self nodeAtPoint:positionInScene];
if(touchedNode.position.x == 0) { // x is 0 when I click an empty field, aka background
[self addThing:positionInScene];
}
And:
-(void)addThing:(CGPoint)newLocation{
SKSpriteNode *thing = [SKSpriteNode spriteNodeWithImageNamed:#"thing"];
thing.location = newLocation;
[self addChild:thing];
thing.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:thing.size.width/2];
thing.physicsBody.dynamic = NO;
thing.physicsBody.categoryBitMask = somethingCategory;
thing.physicsBody.contactTestBitMask = somethingElseCategory;
thing.physicsBody.collisionBitMask = 0;
}
However, this sprite is centred in the exact coordinates I click, rather than the centre point of the empty field. How can I make this happen?
The following creates an N x M grid with a sprite at each row and column. When the user touches one of the sprites, it rotates the sprite and then removes it from the scene. If a sprite is not found at the touch location, a sprite is added there.
#define kBoardMinX 25
#define kBoardMinY 25
#define kNumCols 3
#define kNumRows 3
#define kSpacing 40
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
[self addPiecesToScene];
}
return self;
}
// Convert row and col to the appropriate point (in scene coordinates) in the grid
- (CGPoint) pointFromRow:(NSInteger)row andCol:(NSInteger)col
{
return CGPointMake(kBoardMinX+col*kSpacing, kBoardMinY+(kNumRows-row-1)*kSpacing);
}
// If a sprite is found at (row, col) rotate it, else add a sprite there
- (void) rotatePieceAtRow:(NSInteger)row andCol:(NSInteger)col
{
SKSpriteNode *node = (SKSpriteNode *)[self nodeAtPoint:[self pointFromRow:row andCol:col]];
if (node && (SKNode *)node != self) {
SKAction *rotate = [SKAction rotateByAngle:M_PI*2 duration:2];
SKAction *remove = [SKAction removeFromParent];
SKAction *rotateThenRemove = [SKAction sequence:#[rotate,remove]];
[node runAction:rotateThenRemove];
}
else {
[self addPieceAtRow:row andCol:col];
}
}
- (void) togglePieceAtTouchLocation:(CGPoint)location
{
// Convert touch location to row and column
NSInteger col = (NSInteger)roundf((location.x - kBoardMinX) / kSpacing);
NSInteger row = kNumRows - (NSInteger)roundf((location.y - kBoardMinY) / kSpacing) - 1;
// Check if the touch was within the grid
if (col >= 0 && col < kNumCols && row >= 0 && row < kNumRows) {
[self rotatePieceAtRow:row andCol:col];
}
}
- (void) addPieceAtRow:(NSInteger)row andCol:(NSInteger)col
{
CGSize size = CGSizeMake(32, 32);
SKColor *color = [SKColor whiteColor];
SKSpriteNode *node = [SKSpriteNode spriteNodeWithColor:color size:size];
node.position = [self pointFromRow:row andCol:col];
[self addChild:node];
}
- (void) addPiecesToScene
{
for (int row=0;row<kNumRows;row++) {
for (int col=0;col<kNumCols;col++) {
[self addPieceAtRow:row andCol:col];
}
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
[self togglePieceAtTouchLocation:location];
}
}

Having trouble adding multiple physics objects in Xcode cocos2d

I'm new to cocos2d and objective-c. I know I'm missing something here, but I just can't find the solution. Hope someone can help....
My goal is to be able to click any of the sprites that I've placed on the screen. To start, I've put 2 sprites on the screen. (Each sprite is in the shape of a star).
The problem is, only the second sprite placed on the screen is clickable. My guess is that when I call addNewStar, it replaces _star with the latest star sprite, and takes the previous _star out of the physics node. I want all the stars I add to be in the physics node and be clickable. No clue how to do this.
Here is my code...hopefully someone can point out my mistake(s)!
#implementation MainScene {
CCSprite *_star;
CCPhysicsNode *_physicsNode;
CCNode *_ground;
CCLabelTTF *_scoreLabel;
BOOL _gameOver;
CCButton *_restartButton;
}
- (void)didLoadFromCCB {
self.userInteractionEnabled = TRUE;
[self addNewStar];
[self addNewStar];
// set collision txpe
_ground.physicsBody.collisionType = #"level";
// set this class as delegate
_physicsNode.collisionDelegate = self;
}
-(void)addNewStar {
//This successfully loads my star onto the screen
_star = (Star *)[CCBReader load:#"Star"];
_star.physicsBody.collisionGroup = #"starGroup";
_star.physicsBody.collisionType = #"star";
_star.position = ccp(200,300);
[_physicsNode addChild:_star];
}
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair star:(CCNode *)star level:(CCNode *)level {
[self gameOver];
return TRUE;
}
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
float ranNum1 = (arc4random_uniform(10000));
float ranNum2 = (arc4random_uniform(10000));
float sideForce = ranNum1 - ranNum2;
if (!_gameOver) {
CGPoint touchLocation = [touch locationInNode:_physicsNode];
if(CGRectContainsPoint([_star boundingBox], touchLocation))
{
[_star.physicsBody applyImpulse:ccp(sideForce, 1000.f)];
[_star.physicsBody applyAngularImpulse:2500.f];
}
}
}
- (void)restart {
CCScene *scene = [CCBReader loadAsScene:#"MainScene"];
[[CCDirector sharedDirector] replaceScene:scene];
}
- (void)gameOver {
if (!_gameOver) {
_gameOver = TRUE;
_restartButton.visible = TRUE;
_star.rotation = 90.f;
_star.physicsBody.allowsRotation = FALSE;
[_star stopAllActions];
CCActionMoveBy *moveBy = [CCActionMoveBy actionWithDuration:0.2f position:ccp(-2, 2)];
CCActionInterval *reverseMovement = [moveBy reverse];
CCActionSequence *shakeSequence = [CCActionSequence actionWithArray:#[moveBy, reverseMovement]];
CCActionEaseBounce *bounce = [CCActionEaseBounce actionWithAction:shakeSequence];
[self runAction:bounce];
}
}
#end
You only have one pointer to one star. So when you test if the star was touched in your touches began method then of course only the second one would do anything since your sole pointer is only pointing at the most recently created star.
To answer your question, the simple approach you could take is to add each star to an NSArray. Then in your touches began method you can loop over the array and see which star was touched.
Example:
// Recommend using properties over i-vars
#property (nonatomic, strong) NSMutableArray* starList;
// In init or onEnter...
self.starList = [NSMutableArray array];
// Each time you create a new star...
Star* star = ...;
[self addChild:star];
[self.starList addObject:star];
// In your touches began...
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
...
for (Star* star in self.starList)
{
// If star is touched...
// ...add your impulses, etc
}
}
This is touchBegan from my project. Maybe you can change code little bit for your need. What you can see here? After getting location of touch code searching b2Body which was touched. In your example you need to search your _physicsNode for _child that was touched.
for(UITouch *touch in allTouches)
{
CGPoint location = [touch locationInView:touch.view];
location = [[CCDirector sharedDirector] convertToGL:location];
b2Vec2 worldLoc = b2Vec2(ptm(location.x), ptm(location.y));
// SEARCH YOUR CHILD FROM NODE HERE
for (b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetType() == b2_dynamicBody)
if (b->IsBullet() == false)
{
for (b2Fixture *f = b->GetFixtureList(); f; f = f->GetNext())
{
// Hit!
if (f->TestPoint(worldLoc))
{
//do stuff
}
break;
}
}
}
}

Resources