I'm making a simple iOS game where the goal is to get the angel in the middle of the screen to shoot arrows at monsters coming at him from all sides. But when I try to calculate the direction of the arrow being shot, Xcode says that the "angel" sprite doesn't exist, even though I've already used it. Here's my code:
#import "MyScene.h"
static inline CGPoint rwAdd(CGPoint a, CGPoint b)
{
return CGPointMake(a.x + b.x, a.y + b.y);
}
static inline CGPoint rwSub(CGPoint a, CGPoint b)
{
return CGPointMake(a.x - b.x, a.y - b.y);
}
static inline CGPoint rwMult(CGPoint a, float b)
{
return CGPointMake(a.x * b, a.y * b);
}
static inline float rwLength(CGPoint a)
{
return sqrtf(a.x * a.x + a.y * a.y);
}
static inline CGPoint rwNormalize(CGPoint a)
{
float length = rwLength(a);
return CGPointMake(a.x / length, a.y / length);
}
#implementation MyScene
- (id) initWithSize: (CGSize) size
{
if (self = [super initWithSize:size])
{
self.backgroundColor = [SKColor colorWithRed: 1.0 green: 1.0 blue: 1.0 alpha: 1.0];
SKSpriteNode *angel = [SKSpriteNode spriteNodeWithImageNamed: #"Angel"];
angel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
angel.xScale = 0.25;
angel.yScale = 0.25;
[self addChild: angel];
}
return self;
}
- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event
{
UITouch * touch = [touches anyObject];
CGPoint location = [touch locationInNode: self];
SKSpriteNode *arrow = [SKSpriteNode spriteNodeWithImageNamed:#"Arrow"];
projectile.position = self.angel.position;
CGPoint offset = rwSub(location, arrow.position);
[self addChild: arrow];
CGPoint direction = rwNormalize(offset);
CGPoint shootAmount = rwMult(direction, 500);
CGPoint realDest = rwAdd(shootAmount, arrow.position);
float velocity = 480.0/1.0;
float realMoveDuration = self.size.width / velocity;
SKAction *actionMove = [SKAction moveTo: realDest duration: realMoveDuration];
SKAction *actionMoveDone = [SKAction removeFromParent];
[arrow runAction: [SKAction sequence: #[actionMove, actionMoveDone]]];
}
#end
When I'm defining "arrow.position = self.angel.position", Xcode doesn't recognize "angel" as a sprite. Any help is much appreciated, thanks.
The variable is out of scope. You are setting it as a child so you must retrieve it in the other touchesEnded: method you will need to do the following:
SKSpriteNode *angel = [self childNodeWithName:#"Angel"];
Related
I am making a game which purpose it is to catch multiple objects that are falling from the top of the screen. In the bottom there is a basket to catch the objects. i managed to randomly spawn objects from the top dropping to the bottom using raywenderlich's tutorial : http://www.raywenderlich.com/42699/spritekit-tutorial-for-beginners
But what i want is that when i tap on that random object, the image of that object changes into another image , so just for imagination if the random objects are cats, after i tap them they have to become dogs, how do i have to program this?
edit this is what i got so far :
#import "MyScene.h"
static NSString* basketCategoryName = #"basket";
static NSString* monsterCategoryName= #"monster";
static const uint32_t projectileCategory = 0x1 << 0;
static const uint32_t monsterCategory = 0x1 << 1;
#interface MyScene() <SKPhysicsContactDelegate>
#property (nonatomic) SKLabelNode * scoreLabelNode;
#property int score;
#property (nonatomic) SKSpriteNode * basket;
#property (nonatomic) SKSpriteNode * monster;
#property (nonatomic) BOOL isFingerOnBasket;
#property (nonatomic) BOOL isFingerOnMonster;
#property (nonatomic) BOOL isTouching;
#property (nonatomic) NSTimeInterval lastSpawnTimeInterval;
#property (nonatomic) NSTimeInterval lastUpdateTimeInterval;
//#property (nonatomic, strong) SKSpriteNode *selectedNode;
#end
#implementation MyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
// Initialize label and create a label which holds the score
_score = 0;
_scoreLabelNode = [SKLabelNode labelNodeWithFontNamed:#"MarkerFelt-Wide"];
_scoreLabelNode.position = CGPointMake( CGRectGetMidX( self.frame ), 3 * self.frame.size.height / 4 );
_scoreLabelNode.zPosition = 100;
_scoreLabelNode.text = [NSString stringWithFormat:#"%d", _score];
[self addChild:_scoreLabelNode];
// Set the background
SKTexture* groundTexture = [SKTexture textureWithImageNamed:#"AcornFlipTestBackground1136x640.png"];
groundTexture.filteringMode = SKTextureFilteringNearest;
for( int i = 0; i < 2 + self.frame.size.width / ( groundTexture.size.width * 2 ); ++i ) {
SKSpriteNode* sprite = [SKSpriteNode spriteNodeWithTexture:groundTexture];
[sprite setScale:1.0];
sprite.size = CGSizeMake(self.frame.size.width,self.frame.size.height);
sprite.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
[self addChild:sprite];
}
// Make grafity for sprite
self.physicsWorld.gravity = CGVectorMake(0.0f, 0.0f);
self.physicsWorld.contactDelegate = self;
// Make catching object sprite
self.basket = [SKSpriteNode spriteNodeWithImageNamed:#"bedTest.png"];
self.basket.position = CGPointMake(CGRectGetMidX(self.frame), _basket.frame.size.height * 0.5f);
self.basket.name = basketCategoryName;
[self addChild:self.basket];
// For default this is set to no until user touches the basket and the game begins.
self.isTouching = NO;
}
return self;
}
-(void)addAcorn{
if(_isTouching == YES) {
self.monster= [SKSpriteNode spriteNodeWithImageNamed:#"AcornFinal.png"];
// Determine where to spawn the monster along the X axis
int minX = self.monster.size.width;
int maxX = self.frame.size.width - self.monster.size.width;
int rangeX = maxX - minX;
int actualX = (arc4random() % rangeX)+minX;
// Random position along the X axis as calculated above
// This describe from which way the acorns move
// - means moving from top to the right and + means moving from the top to the left
self.monster.position = CGPointMake(actualX ,self.frame.size.height+ self.monster.size.height);
self.monster.name = monsterCategoryName;
[self addChild:self.monster];
CGSize contactSize = CGSizeMake(self.monster.size.width - 5.0, self.monster.size.height - 10.0);
self.monster.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:contactSize]; // 1
self.monster.physicsBody.dynamic = YES; // 2
self.monster.physicsBody.categoryBitMask = monsterCategory; // 3
self.monster.physicsBody.contactTestBitMask = projectileCategory; // 4
self.monster.physicsBody.collisionBitMask = 0; // 5
// Determine speed of the monster
int minDuration = 8.0;
int maxDuration = 10.0;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
// Create the actions
SKAction * actionMove = [SKAction moveTo:CGPointMake(actualX,-self.monster.size.height) duration:actualDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
[self.monster runAction:[SKAction sequence:#[actionMove, actionMoveDone]]];
}
}
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 0.5) {
self.lastSpawnTimeInterval = 0;
[self addAcorn];
}
}
- (void)update:(NSTimeInterval)currentTime {
// Handle time delta.
// If we drop below 60fps, we still want everything to move the same distance.
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
self.lastUpdateTimeInterval = currentTime;
if (timeSinceLast > 1) { // more than a second since last update
timeSinceLast = 1.0 / 60.0;
self.lastUpdateTimeInterval = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
self.isTouching = YES;
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode* body = [self nodeAtPoint:location];
if ([body.name isEqualToString:basketCategoryName])
{
NSLog(#"Began touch on basket");
self.isFingerOnBasket = YES;
}
else if ([body.name isEqualToString:monsterCategoryName])
{
NSLog(#"Began touch on MONSTER");
self.isFingerOnMonster = YES;
}
}
-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
if (self.isFingerOnMonster) {
// 2 Get touch location
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
CGPoint previousLocation = [touch previousLocationInNode:self];
// 3 Get node for paddle
SKSpriteNode* monster = (SKSpriteNode*)[self childNodeWithName: monsterCategoryName];
int oldPosition = monster.position.x + (location.x - previousLocation.x);
self.monster = [SKSpriteNode spriteNodeWithImageNamed:#"AcornFinal.png"];
monster.position = CGPointMake(oldPosition, monster.position.y);
NSLog(#"reached the touch though");
}
// 1 Check whether user tapped paddle
if (self.isFingerOnBasket) {
// 2 Get touch location
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
CGPoint previousLocation = [touch previousLocationInNode:self];
// 3 Get node for paddle
SKSpriteNode* basket = (SKSpriteNode*)[self childNodeWithName: basketCategoryName];
// 4 Calculate new position along x for paddle
int basketX = basket.position.x + (location.x - previousLocation.x);
// 5 Limit x so that the paddle will not leave the screen to left or right
basketX = MAX(basketX, basket.size.width/2);
basketX = MIN(basketX, self.size.width - basket.size.width/2);
// 6 Update position of paddle
basket.position = CGPointMake(basketX, basket.position.y);
CGSize contactSize = CGSizeMake(basket.size.width - 8.0, basket.size.height - 8.0);
basket.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:contactSize];
basket.physicsBody.dynamic = YES;
basket.physicsBody.categoryBitMask = projectileCategory;
basket.physicsBody.contactTestBitMask = monsterCategory;
basket.physicsBody.collisionBitMask = 0;
basket.physicsBody.usesPreciseCollisionDetection = YES;
}
}
- (void)projectile:(SKSpriteNode *)basket didCollideWithMonster:(SKSpriteNode *)monster {
NSLog(#"Hit");
[monster removeFromParent];
}
- (void)didBeginContact:(SKPhysicsContact *)contact
{
// 1
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
// 2
if ((firstBody.categoryBitMask & projectileCategory) != 0 &&
(secondBody.categoryBitMask & monsterCategory) != 0)
{
[self projectile:(SKSpriteNode *) firstBody.node didCollideWithMonster:(SKSpriteNode *) secondBody.node];
NSLog(#"test");
_score++;
_scoreLabelNode.text = [NSString stringWithFormat:#"%d", _score];
}
}
// Removing this void will result in being able to drag the basket accross the screen without touching the basket itself.
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
self.isFingerOnBasket = NO;
self.isFingerOnMonster = NO;
}
#end
Once a touch is detected on a sprite (I assume you already got that working out) you can either create the sprite again with spriteNodeWithImageNamed. Make sure you save the previous node's position and set it again on the new sprite so it will match the position of the old sprite.
CGPoint oldPosition = touchedSprite.position;
touchedSprite = [SKSpritNode spriteWithImageNamed:#"imgge.png"];
touchedSprite.position = oldPosition;
// If you have any other sprite properties you will have to save them as well
You can also set the texture with setTexture method which will not require you to change anything else (e.g. position) :
[touchedSprite setTexture:[SKTexture textureWithImageNamed:#"image.png"]];
EDIT :
Answering your question in the comments you implement this in the touchesEnded method of the parent node of the sprites :
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self];
for (SKSpriteNode *sprite in fallingSprites) { // Assuming fallingSprite is an array containing the cat sprites you want to detect touches for
if (CGRectContainsPoint(sprite.frame, touchLocation)) {
[sprite setTexture:[SKTexture textureWithImageNamed:#"dog.png"]];
}
}
}
Another approach (Haven't tried it yet) is to subclass SKSpriteNode and implement the same method but without the touch detection in rect since if this method is called the sprite has been touched .
Each part of the skeleton model is a spritekit node. Find the node that you wish to change and update its texture property like this:
spriteKitNode.texture = // Updated SKTexture
In your TouchesEnded event:
Add a small skspritenode positioned at the touch location with a small physics body that last for a short duration. ("touch spritenode)
Set up contact between all possible transformed objects and the "touch spritenode"
Create a subclass of SKSpritenode for the falling objects that has a variable to stores their type.
In the method called from contact between the touch spritenode and the falling objects:
Remove the touchspritenode first
Create if statements to check which falling object was touched.
Update the image of the contacted falling object spritenode according to its type.
If you are following Raywenderlich you have access to the actual syntax
I'm making a simple iOS game where the goal is to have an angel in the middle of the screen shoot arrows at monsters coming at him from all sides. When the user taps the screen, I want an arrow to travel from the angel in the direction of the tap. But when I tap the screen, it travels from the corner of the screen, not the middle. Here's my code:
#import "MyScene.h"
static inline CGPoint rwAdd(CGPoint a, CGPoint b)
{
return CGPointMake(a.x + b.x, a.y + b.y);
}
static inline CGPoint rwSub(CGPoint a, CGPoint b)
{
return CGPointMake(a.x - b.x, a.y - b.y);
}
static inline CGPoint rwMult(CGPoint a, float b)
{
return CGPointMake(a.x * b, a.y * b);
}
static inline float rwLength(CGPoint a)
{
return sqrtf(a.x * a.x + a.y * a.y);
}
static inline CGPoint rwNormalize(CGPoint a)
{
float length = rwLength(a);
return CGPointMake(a.x / length, a.y / length);
}
#implementation MyScene
- (id) initWithSize: (CGSize) size
{
if (self = [super initWithSize:size])
{
self.backgroundColor = [SKColor colorWithRed: 1.0 green: 1.0 blue: 1.0 alpha: 1.0];
SKSpriteNode *angel = [SKSpriteNode spriteNodeWithImageNamed: #"Angel"];
angel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
angel.xScale = 0.25;
angel.yScale = 0.25;
[self addChild: angel];
}
return self;
}
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
{
UITouch * touch = [touches anyObject];
CGPoint location = [touch locationInNode: self];
SKSpriteNode *arrow = [SKSpriteNode spriteNodeWithImageNamed: #"Arrow"];
arrow.xScale = 0.25;
arrow.yScale = 0.25;
SKNode *angel = [self childNodeWithName: #"Angel"];
arrow.position = angel.position;
CGPoint offset = rwSub(location, arrow.position);
[self addChild: arrow];
CGPoint direction = rwNormalize(offset);
CGPoint shootAmount = rwMult(direction, 500);
CGPoint realDest = rwAdd(shootAmount, arrow.position);
float velocity = 480.0/1.0;
float realMoveDuration = self.size.width / velocity;
SKAction *actionMove = [SKAction moveTo: realDest duration: realMoveDuration];
SKAction *actionMoveDone = [SKAction removeFromParent];
[arrow runAction: [SKAction sequence: #[actionMove, actionMoveDone]]];
}
#end
Suggestions? Thanks.
Make the following modifications:
#implementation MyScene
{
SKSpriteNode *angel; // <- add this
}
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
{
UITouch * touch = [touches anyObject];
CGPoint location = [touch locationInNode: self];
SKSpriteNode *arrow = [SKSpriteNode spriteNodeWithImageNamed: #"Arrow"];
arrow.xScale = 0.25;
arrow.yScale = 0.25;
//SKNode *angel = [self childNodeWithName: #"Angel"]; // <- delete this
arrow.position = angel.position;
CGPoint offset = rwSub(location, arrow.position);
[self addChild: arrow];
CGPoint direction = rwNormalize(offset);
CGPoint shootAmount = rwMult(direction, 500);
CGPoint realDest = rwAdd(shootAmount, arrow.position);
float velocity = 480.0/1.0;
float realMoveDuration = self.size.width / velocity;
SKAction *actionMove = [SKAction moveTo: realDest duration: realMoveDuration];
SKAction *actionMoveDone = [SKAction removeFromParent];
[arrow runAction: [SKAction sequence: #[actionMove, actionMoveDone]]];
}
I've been trying to work on a simple Sprite Kit game that involves dodging red balls. I'm using the built-in gravity mechanism, but I'm having trouble preventing the player from falling through the ground. I've looked up a solution (set ground.physicsBody.dynamic = NO), but the player still falls through. What exactly do I need to do?
Edit: The green and brown texture is the ground. Right now the player is set to not being dynamic, so it is 'flying'
Here is my code in the MyScene.m file:
//
// MyScene.m
// DodgeMan
//
// Created by Cormac Chester on 3/8/14.
// Copyright (c) 2014 Testman Industries. All rights reserved.
//
#import "MyScene.h"
#import "EndGameScene.h"
static const uint32_t redBallCategory = 0x1 << 0;
static const uint32_t playerCategory = 0x1 << 1;
#implementation MyScene
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
/* Setup your scene here */
//Sets player location
playerLocX = 50;
playerLocY = 100;
//Sets player score
score = 0;
//Set Background
self.backgroundColor = [SKColor colorWithRed:0.53 green:0.81 blue:0.92 alpha:1.0];
//Set Ground
SKSpriteNode *ground = [SKSpriteNode spriteNodeWithImageNamed:#"ground"];
ground.position = CGPointMake(CGRectGetMidX(self.frame), 34);
ground.xScale = 0.5;
ground.yScale = 0.5;
ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ground.size];
ground.physicsBody.dynamic = NO;
//Player
self.playerSprite = [SKSpriteNode spriteNodeWithImageNamed:#"character"];
self.playerSprite.position = CGPointMake(playerLocX, playerLocY);
//Set Player Physics
self.playerSprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.playerSprite.size];
self.playerSprite.physicsBody.dynamic = YES;
self.playerSprite.physicsBody.categoryBitMask = playerCategory;
self.playerSprite.physicsBody.contactTestBitMask = redBallCategory;
self.playerSprite.physicsBody.collisionBitMask = 0;
self.playerSprite.physicsBody.usesPreciseCollisionDetection = YES;
//Score Label
self.scoreLabel = [SKLabelNode labelNodeWithFontNamed:#"Arial-BoldMT"];
self.scoreLabel.text = #"0";
self.scoreLabel.fontSize = 40;
self.scoreLabel.fontColor = [SKColor blackColor];
self.scoreLabel.position = CGPointMake(50, 260);
//Pause Button
self.pauseButton = [SKSpriteNode spriteNodeWithImageNamed:#"pauseButton"];
self.pauseButton.position = CGPointMake(self.frame.size.width / 2, self.frame.size.height - 40);
self.pauseButton.name = #"pauseButton";
//Add nodes
[self addChild:ground];
[self addChild:self.playerSprite];
[self addChild:self.scoreLabel];
//[self addChild:self.pauseButton];
//Sets gravity
self.physicsWorld.gravity = CGVectorMake(0,-2);
self.physicsWorld.contactDelegate = self;
}
return self;
}
-(void)addBall
{
SKSpriteNode *redBall = [SKSpriteNode spriteNodeWithImageNamed:#"locationIndicator"];
int minY = redBall.size.height / 2;
int maxY = self.frame.size.height - redBall.size.height / 2;
int rangeY = maxY - minY;
int actualY = (arc4random() % rangeY) + minY;
NSLog(#"Actual Y: %i", actualY);
//Initiates red ball offscreen
if (actualY >= 75)
{
//Prevents balls from spawning in the ground
redBall.position = CGPointMake(self.frame.size.width + redBall.size.width/2, actualY);
[self addChild:redBall];
}
redBall.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:redBall.size.width/2];
redBall.physicsBody.dynamic = YES;
redBall.physicsBody.categoryBitMask = redBallCategory;
redBall.physicsBody.contactTestBitMask = playerCategory;
redBall.physicsBody.collisionBitMask = 0;
redBall.physicsBody.affectedByGravity = NO;
redBall.physicsBody.usesPreciseCollisionDetection = YES;
//Determine speed of red ball
int minDuration = 3.0;
int maxDuration = 5.0;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
// Create the actions
SKAction *actionMove = [SKAction moveTo:CGPointMake(-redBall.size.width/2, actualY) duration:actualDuration];
SKAction *actionMoveDone = [SKAction removeFromParent];
SKAction *ballCross = [SKAction runBlock:^{
score++;
self.scoreString = [NSString stringWithFormat:#"%i", score];
self.scoreLabel.text = self.scoreString;
NSLog(#"Score was incremented. Score is now %d", score);
}];
[redBall runAction:[SKAction sequence:#[actionMove, ballCross, actionMoveDone]]];
}
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast
{
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 0.5) {
self.lastSpawnTimeInterval = 0;
[self addBall];
}
}
-(void)update:(CFTimeInterval)currentTime
{
/* Called before each frame is rendered */
// Handle time delta.
//Prevents bad stuff happening
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
self.lastUpdateTimeInterval = currentTime;
if (timeSinceLast > 1) { // more than a second since last update
timeSinceLast = 1.0 / 120.0;
self.lastUpdateTimeInterval = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}
NSDate *startTime;
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
/* Called when a touch begins */
[super touchesBegan:touches withEvent:event];
//Starts Timer
startTime = [NSDate date];
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
//Pauses Scene
if ([node.name isEqualToString:#"pauseButton"])
{
NSLog(#"Pause button pressed");
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
/* Called when a touch ends */
[super touchesEnded:touches withEvent:event];
NSTimeInterval elapsedTime = [startTime timeIntervalSinceNow];
NSString *elapsedTimeString = [NSString stringWithFormat:#"Elapsed time: %f", elapsedTime];
NSLog(#"%#", elapsedTimeString);
for (UITouch *touch in touches)
{
//Gets location of touch
CGPoint location = [touch locationInNode:self];
NSLog(#"Touch Location X: %f \n Touch Location Y: %f", location.x, location.y);
//Prevents destination from being in the ground
if (location.y < 88)
{
location.y = 87.5;
}
//Moves and animates player
//int velocity = elapsedTime * -3000;
int velocity = 800.0/1.0;
NSLog(#"Velocity: %i", velocity);
float realMoveDuration = self.size.width / velocity;
SKAction *actionMove = [SKAction moveTo:location duration:realMoveDuration];
[self.playerSprite runAction:[SKAction sequence:#[actionMove]]];
}
NSLog(#"Touch ended");
}
//Collision between ball and player
- (void)redBall:(SKSpriteNode *)redBall didCollideWithPlayer:(SKSpriteNode *)playerSprite
{
NSLog(#"Player died");
[redBall removeFromParent];
[playerSprite removeFromParent];
SKTransition *reveal = [SKTransition crossFadeWithDuration:0.5];
SKScene *endGameScene = [[EndGameScene alloc] initWithSize:self.size gameEnded:YES];
[self.view presentScene:endGameScene transition: reveal];
}
- (void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
//Red ball collides with the player
if ((firstBody.categoryBitMask & redBallCategory) != 0 && (secondBody.categoryBitMask & playerCategory) != 0)
{
[self redBall:(SKSpriteNode *) firstBody.node didCollideWithPlayer:(SKSpriteNode *) secondBody.node];
}
}
#end
you definitely can't set its dynamic to no brother. you need gravity effect that player. (he is not effected by physic world so he is flying right now. we need him to fall down to ground aren't we? :)
So here is the simple solution. idea is that you create a "invisible rectangle block" on the ground surface that has physic body. and you need to set its dynamic to no in order to prevent it falling down
so this block is a node obviously, and its size : as high as the ground , and as wide as the screen. and you need to adjust the position a little bit to put its upper bound right on the ground surface.
good luck
i actually drew a picture but i can't post it here because of my reputation :(
Your problem is with physicsBody's categoryBitMask and collisionTestBitMask.
Your bitwise declarations :
static const uint32_t redBallCategory = 0x1 << 0;
static const uint32_t playerCategory = 0x1 << 1;
This has actually set the following bit patterns (i've shortened to 8 bits for the example) :
redBallCategory - 00000001 and
playerCategory - 00000010
However in the following code, you tell the player to only collide with collision bit mask - 00000000;
self.playerSprite.physicsBody.collisionBitMask = 0;
So your first problem is here. The player will not collide with any category that you have defined.
Your second problem is you have not given the ground a categoryBitMask, or collisionBitMask. By default this means all bits are set, IE the ground's collisionBitMask is equal to 11111111;
There will be no collision between these two physics bodies.
Try this - i have simply added a third physics category, and edited your code slightly to set the ground categoryBitMask / collisionBitMask, and your player collisionBitMask.
static const uint32_t redBallCategory = 0x1 << 0;
static const uint32_t playerCategory = 0x1 << 1;
static const uint32_t groundCategory = 0x1 << 2;
#implementation MyScene
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
/* Setup your scene here */
//Sets player location
playerLocX = 50;
playerLocY = 100;
//Sets player score
score = 0;
//Set Background
self.backgroundColor = [SKColor colorWithRed:0.53 green:0.81 blue:0.92 alpha:1.0];
//Set Ground
SKSpriteNode *ground = [SKSpriteNode spriteNodeWithImageNamed:#"ground"];
ground.position = CGPointMake(CGRectGetMidX(self.frame), 34);
ground.xScale = 0.5;
ground.yScale = 0.5;
ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ground.size];
ground.physicsBody.categoryBitMask=groundCategory;
ground.physicsBody.collisionBitMask=playerCategory|redBallCategory;
ground.physicsBody.dynamic = NO;
//Player
self.playerSprite = [SKSpriteNode spriteNodeWithImageNamed:#"character"];
self.playerSprite.position = CGPointMake(playerLocX, playerLocY);
//Set Player Physics
self.playerSprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.playerSprite.size];
self.playerSprite.physicsBody.dynamic = YES;
self.playerSprite.physicsBody.categoryBitMask = playerCategory;
self.playerSprite.physicsBody.contactTestBitMask = redBallCategory;
self.playerSprite.physicsBody.collisionBitMask = groundCategory|redBallCategory;
self.playerSprite.physicsBody.usesPreciseCollisionDetection = YES;
//Score Label
self.scoreLabel = [SKLabelNode labelNodeWithFontNamed:#"Arial-BoldMT"];
self.scoreLabel.text = #"0";
self.scoreLabel.fontSize = 40;
self.scoreLabel.fontColor = [SKColor blackColor];
self.scoreLabel.position = CGPointMake(50, 260);
//Pause Button
self.pauseButton = [SKSpriteNode spriteNodeWithImageNamed:#"pauseButton"];
self.pauseButton.position = CGPointMake(self.frame.size.width / 2, self.frame.size.height - 40);
self.pauseButton.name = #"pauseButton";
//Add nodes
[self addChild:ground];
[self addChild:self.playerSprite];
[self addChild:self.scoreLabel];
//[self addChild:self.pauseButton];
//Sets gravity
self.physicsWorld.gravity = CGVectorMake(0,-2);
self.physicsWorld.contactDelegate = self;
}
return self;
}
Just:
self.playerSprite.physicsBody.dynamic = NO;
should work.
Your problem occurs due to scaling. For some reason in sprite kit scaling an image doesn't change it's size when used in following code. Judging by your pictures, your physics body rectangle for your ground is actually twice as big as you think and already engulfing the player, which is why there would by no collision detection. This is from recent experience with a very similar style game.
Do you have an edge loop physcis body around the scene? Collision flags and category flags set correctly so the player collides with the ground?
i had same problem and i soved it simply...
On the update method i've putted an if statement:
if(player.position.y<your_closest_value_near_ground){
player.position.y == your_Closest_value_near_ground
}
The comparition differs by the anchor point you have.. hope it helps someone
I have a small problem. In this tutorial How To Make a Tile-Based Game with Cocos2D 2.X used cocos2d V2.0, I wanna make this in cocos2d V3.0. So, it doesn't work! Thanks! (I don't speak english)
I think problem in this line - self.position = viewPoint;
#property (strong) CCTiledMap *tileMap;
#property (strong) CCTiledMapLayer *background;
#property (strong) CCSprite *player;
- (id)init
{
// Apple recommend assigning self with supers return value
self = [super init];
if (!self) return(nil);
// Enable touch handling on scene node
self.userInteractionEnabled = YES;
self.tileMap = [CCTiledMap tiledMapWithFile:#"TileMap.tmx"];
self.background = [_tileMap layerNamed:#"Background"];
[self addChild:_tileMap z:-1];
CCTiledMapObjectGroup *objectGroup = [_tileMap objectGroupNamed:#"Objects"];
NSAssert(objectGroup != nil, #"tile map has no objects object layer");
NSDictionary *spawnPoint = [objectGroup objectNamed:#"SpawnPoint"];
int x = [spawnPoint[#"x"] integerValue];
int y = [spawnPoint[#"y"] integerValue];
_player = [CCSprite spriteWithImageNamed:#"Player.png"];
_player.position = ccp(x,y);
[self addChild:_player];
[self setViewPointCenter:_player.position];
// done
return self;
}
- (void)setViewPointCenter:(CGPoint) position {
CGSize winSize = [CCDirector sharedDirector].viewSize;
int x = MAX(position.x, winSize.width/2);
int y = MAX(position.y, winSize.height/2);
x = MIN(x, (_tileMap.mapSize.width * _tileMap.tileSize.width) - winSize.width / 2);
y = MIN(y, (_tileMap.mapSize.height * _tileMap.tileSize.height) - winSize.height/2);
CGPoint actualPosition = ccp(x, y);
CGPoint centerOfView = ccp(winSize.width/2, winSize.height/2);
CGPoint viewPoint = ccpSub(centerOfView, actualPosition);
self.position = viewPoint;
}
-(void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInView:touch.view];
touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
CGPoint playerPos = _player.position;
CGPoint diff = ccpSub(touchLocation, playerPos);
if ( abs(diff.x) > abs(diff.y) ) {
if (diff.x > 0) {
playerPos.x += _tileMap.tileSize.width;
} else {
playerPos.x -= _tileMap.tileSize.width;
}
} else {
if (diff.y > 0) {
playerPos.y += _tileMap.tileSize.height;
} else {
playerPos.y -= _tileMap.tileSize.height;
}
}
CCLOG(#"playerPos %#",CGPointCreateDictionaryRepresentation(playerPos));
// safety check on the bounds of the map
if (playerPos.x <= (_tileMap.mapSize.width * _tileMap.tileSize.width) &&
playerPos.y <= (_tileMap.mapSize.height * _tileMap.tileSize.height) &&
playerPos.y >= 0 &&
playerPos.x >= 0 )
{
[self setPlayerPosition:playerPos];
}
[self setViewPointCenter:_player.position];
NSLog(#"%#", NSStringFromCGPoint(touchLocation));
}
-(void)setPlayerPosition:(CGPoint)position {
_player.position = position;
}
The problem is that the area of the user interaction is by default bound to the contentSize of the scene (screen size).
When calling your setViewPointCenter method, you are moving the scene position out of this area where the touch event is not handled.
You have to extend this area of the contentSize of the tile map like that :
[self setContentSize:[_tileMap contentSize]];
self.userInteractionEnabled = YES;
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
This code spawns a monster, but no enemy.
I expect an enemy to be spawned, why doesn't it?
#import "MyScene.h"
#import "GameOverScene.h"
static const uint32_t projectileCategory = 0x1 << 0;
static const uint32_t monsterCategory = 0x1 << 1;
static const uint32_t enemyCategory = 0x1 << 1;
// 1
#interface MyScene () <SKPhysicsContactDelegate>
#property (nonatomic) SKSpriteNode * player;
#property (nonatomic) NSTimeInterval lastSpawnTimeInterval;
#property (nonatomic) NSTimeInterval lastUpdateTimeInterval;
#property (nonatomic) int monstersDestroyed;
#property (nonatomic) int enemysDestroyed;
#end
static inline CGPoint rwAdd(CGPoint a, CGPoint b) {
return CGPointMake(a.x + b.x, a.y + b.y);
}
static inline CGPoint rwSub(CGPoint a, CGPoint b) {
return CGPointMake(a.x - b.x, a.y - b.y);
}
static inline CGPoint rwMult(CGPoint a, float b) {
return CGPointMake(a.x * b, a.y * b);
}
static inline float rwLength(CGPoint a) {
return sqrtf(a.x * a.x + a.y * a.y);
}
// Makes a vector have a length of 1
static inline CGPoint rwNormalize(CGPoint a) {
float length = rwLength(a);
return CGPointMake(a.x / length, a.y / length);
}
#implementation MyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
// 2
NSLog(#"Size: %#", NSStringFromCGSize(size));
// 3
self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
// 4
self.player = [SKSpriteNode spriteNodeWithImageNamed:#"player"];
self.player.position = CGPointMake(self.player.size.width/2, self.frame.size.height/2);
[self addChild:self.player];
self.physicsWorld.gravity = CGVectorMake(0,0);
self.physicsWorld.contactDelegate = self;
}
return self;
}
-(void)addMonster {
// Create sprite
SKSpriteNode * monster = [SKSpriteNode spriteNodeWithImageNamed:#"monster"];
monster.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:monster.size]; // 1
monster.physicsBody.dynamic = YES; // 2
monster.physicsBody.categoryBitMask = monsterCategory; // 3
monster.physicsBody.contactTestBitMask = projectileCategory; // 4
monster.physicsBody.collisionBitMask = 0; // 5
// Determine where to spawn the monster along the Y axis
int minY = monster.size.height / 2;
int maxY = self.frame.size.height - monster.size.height / 2;
int rangeY = maxY - minY;
int actualY = (arc4random() % rangeY) + minY;
// Create the monster slightly off-screen along the right edge,
// and along a random position along the Y axis as calculated above
monster.position = CGPointMake(self.frame.size.width + monster.size.width/2, actualY);
[self addChild:monster];
// Determine speed of the monster
int minDuration = 2.0;
int maxDuration = 4.0;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
// Create the actions
SKAction * actionMove = [SKAction moveTo:CGPointMake(-monster.size.width/2, actualY) duration:actualDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
SKAction * loseAction = [SKAction runBlock:^{
SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
SKScene * gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:NO];
[self.view presentScene:gameOverScene transition: reveal];
}];
[monster runAction:[SKAction sequence:#[actionMove, loseAction, actionMoveDone]]];
}
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 1) {
self.lastSpawnTimeInterval = 0;
[self addMonster];
}
}
- (void)update:(NSTimeInterval)currentTime {
// Handle time delta.
// If we drop below 60fps, we still want everything to move the same distance.
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
self.lastUpdateTimeInterval = currentTime;
if (timeSinceLast > 1) { // more than a second since last update
timeSinceLast = 1.0 / 60.0;
self.lastUpdateTimeInterval = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self runAction:[SKAction playSoundFileNamed:#"pew-pew-lei.caf" waitForCompletion:NO]];
// 1 - Choose one of the touches to work with
UITouch * touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
// 2 - Set up initial location of projectile
SKSpriteNode * projectile = [SKSpriteNode spriteNodeWithImageNamed:#"projectile"];
projectile.position = self.player.position;
projectile.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:projectile.size.width/2];
projectile.physicsBody.dynamic = YES;
projectile.physicsBody.categoryBitMask = projectileCategory;
projectile.physicsBody.contactTestBitMask = monsterCategory;
projectile.physicsBody.contactTestBitMask = enemyCategory;
projectile.physicsBody.collisionBitMask = 0;
projectile.physicsBody.usesPreciseCollisionDetection = YES;
// 3- Determine offset of location to projectile
CGPoint offset = rwSub(location, projectile.position);
// 4 - Bail out if you are shooting down or backwards
if (offset.x <= 0) return;
// 5 - OK to add now - we've double checked position
[self addChild:projectile];
// 6 - Get the direction of where to shoot
CGPoint direction = rwNormalize(offset);
// 7 - Make it shoot far enough to be guaranteed off screen
CGPoint shootAmount = rwMult(direction, 1000);
// 8 - Add the shoot amount to the current position
CGPoint realDest = rwAdd(shootAmount, projectile.position);
// 9 - Create the actions
float velocity = 480.0/1.0;
float realMoveDuration = self.size.width / velocity;
SKAction * actionMove = [SKAction moveTo:realDest duration:realMoveDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
[projectile runAction:[SKAction sequence:#[actionMove, actionMoveDone]]];
}
- (void)projectile:(SKSpriteNode *)projectile didCollideWithMonster:(SKSpriteNode *)monster {
NSLog(#"Hit");
[projectile removeFromParent];
[monster removeFromParent];
self.monstersDestroyed++;
if (self.monstersDestroyed > 80) {
SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
SKScene * gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:YES];
[self.view presentScene:gameOverScene transition: reveal];
}
}
- (void)didBeginContact:(SKPhysicsContact *)contact
{
// 1
SKPhysicsBody *firstBody, *secondBody, *thirdBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
thirdBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
thirdBody = contact.bodyA;
}
// 2
if ((firstBody.categoryBitMask & projectileCategory) != 0 &&
(secondBody.categoryBitMask & enemyCategory) != 0 &&
(thirdBody.categoryBitMask & monsterCategory) != 0)
{
[self projectile:(SKSpriteNode *) firstBody.node didCollideWithMonster:(SKSpriteNode *) secondBody.node];
[self projectile:(SKSpriteNode *) firstBody.node didCollideWithEnemy: (SKSpriteNode *) secondBody.node];
}
}
- (void)addEnemy {
// Create sprite
SKSpriteNode * enemy = [SKSpriteNode spriteNodeWithImageNamed:#"enemy"];
enemy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:enemy.size]; // 1
enemy.physicsBody.dynamic = YES; // 2
enemy.physicsBody.categoryBitMask = enemyCategory; // 3
enemy.physicsBody.contactTestBitMask = projectileCategory; // 4
enemy.physicsBody.collisionBitMask = 0; // 5
// Determine where to spawn the monster along the Y axis
int minY = enemy.size.height / 2;
int maxY = self.frame.size.height - enemy.size.height / 2;
int rangeY = maxY - minY;
int actualY = (arc4random() % rangeY) + minY;
// Create the monster slightly off-screen along the right edge,
// and along a random position along the Y axis as calculated above
enemy.position = CGPointMake(self.frame.size.width + enemy.size.width/2, actualY);
[self addChild:enemy];
// Determine speed of the monster
int minDuration = 1.0;
int maxDuration = 6.0;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
// Create the actions
SKAction * actionMove = [SKAction moveTo:CGPointMake(-enemy.size.width/2, actualY) duration:actualDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
SKAction * loseAction = [SKAction runBlock:^{
SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
SKScene * gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:NO];
[self.view presentScene:gameOverScene transition: reveal];
}];
[enemy runAction:[SKAction sequence:#[actionMove, loseAction, actionMoveDone]]];
}
- (void)projectile:(SKSpriteNode *)projectile didCollideWithEnemy:(SKSpriteNode *)enemy {
NSLog(#"Hit");
[projectile removeFromParent];
[enemy removeFromParent];
self.enemysDestroyed++;
if (self.enemysDestroyed > 80) {
SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
SKScene * gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:YES];
[self.view presentScene:gameOverScene transition: reveal];
}
}
#end
I am not sure if you realize this, but you never actually call addEnemy. Look through the code. You will find a call to addMonster but never addEnemy. Implementing the method is one thing- without calling the method, whatever is inside will never run.