I'm New to SpriteKit and build a simple CLickerGame in SpriteKit. Now i have the game finish but the thing which should spawn to click on it, spawns from time to time outside the screen. Where can i select the arc4random area where the thing should spawn?
#implementation MyScene
-(void)CreateTestObject
{
SKSpriteNode *TestObject = [SKSpriteNode spriteNodeWithImageNamed:#"TestObject"];
int maxX = CGRectGetMaxX(self.frame);
float ranX = (arc4random()%maxX) + 1;
int maxY = CGRectGetMaxY(self.frame);
float ranY = (arc4random()%maxY) + 1;
TestObject.position = CGPointMake(ranX, ranY);
TestObject = CGSizeMake(75, 75);
TestObject = #"Object";
[self addChild:TestObject];
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
bg = [SKSpriteNode spriteNodeWithImageNamed:#"bg.png"];
bg.position = CGPointMake(160, 284);
bg.size = CGSizeMake(self.frame.size.width, self.frame.size.height);
[self addChild:bg];
[self CreateTestObject];
}
return self;
}
-(void)selectNodeForTouch:(CGPoint)touchLocation
{
SKSpriteNode *touchedNode = (SKSpriteNode *)[self nodeAtPoint:touchLocation];
if([[touchedNode name] isEqualToString:#"TestObject"])
{
SKSpriteNode *TestObject = (SKSpriteNode *)[self childNodeWithName:#"TestObject"];
TestObject.name = #"DisabledTestObject";
SKAction *grow = [SKAction scaleTo:1.2 duration:0.1];
SKAction*shrink = [SKAction scaleTo:0 duration:0.07];
SKAction *removeNode = [SKAction removeFromParent];
SKAction *seq = [SKAction sequence:#[grow, shrink, removeNode]];
[TestObject runAction:seq];
[self CreateTestObject];
[self runAction:[SKAction playSoundFileNamed:#"Sound.aif" waitForCompletion:NO]];
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
//CGPoint location = [touch locationInNode:self];
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
[self selectNodeForTouch:positionInScene];
}
}
#end
Modify the max values for your (x,y) coordinates of any object being added to your view.
For example, on an iPhone 5 display in landscape mode and the spawned object being of size width=100, height=100, the max x value should be no more than 518. Width of screen being 568 less half the width of your object (50).
The same logic applies for the y value. The max y value should be 270 given the same object dimensions.
The above assumes that you have not changed your object's anchor point from (0.5,0.5) to another value.
Related
I have two touches in method "touchesBegan" they both starts at the same time, but i want to second touch to wait while first touch end. How can i achieve that? Here are what I have:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location =[touch locationInView:self.view];
SKNode *touchedNode = [self nodeAtPoint:location];
if(touchedNode && [touchedNode.name isEqual:#"tapButton"]){
[self runAction:[SKAction playSoundFileNamed:#"buttons.wav" waitForCompletion:NO]];
[self createBackLn];
[self createBottom];
[self ballCreate];
[tapButton removeFromParent];
}
if (groupSprite.children.count < 3) {
CGPoint touchlocation = [touch locationInNode:self];
CGPoint location = CGPointMake(touchlocation.x, IS_IPAD()?108: 75);
sprite = [SKSpriteNode spriteNodeWithImageNamed:#"board"];
if(IS_IPAD()){
sprite.xScale = 0.5;
sprite.yScale = 0.7;
}else{
sprite.xScale = 0.3;
sprite.yScale = 0.5;
}
sprite.position = location;
sprite.zPosition = 2;
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.frame.size];
sprite.physicsBody.dynamic = NO;
sprite.physicsBody.categoryBitMask = boardCategory;
sprite.physicsBody.contactTestBitMask = ballCategory | secballCategory;
SKAction *delay = [SKAction waitForDuration:IS_IPAD()?0.6:0.4];
SKAction *remove = [SKAction removeFromParent];
SKAction *actionSequence = [SKAction sequence:#[delay,remove]];
[sprite runAction:actionSequence];
[groupSprite addChild:sprite];
}
}
}
I have two nodes and a boolean. Simple enough. When node A contacts Node B and the boolean is 0, nothing happens. However if the boolean is 1, Node A is removed through the didBeganContact method.
Extremely simple, however I have an annoying problem on when I want Node A removed.
Node B is a rectangle and node A is a square going in the middle of the rectangle, the boolean is called and turned into 1 when I tap and hold the Node B using the touchesBegan method. Now before Node A contacts Node B, I tap and hold Node B and when Node A contacts, its removed, but when Node A is already in the middle, and I tap Node B, nothing happens and I don't know why.
Rectangle Method
-(void)rectangle
{
SKSpriteNode *rectangle = [[SKSpriteNode alloc] init];
rectangle = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(75, 150)];
rectangle.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
rectangle.name = #"rect";
rectangle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:rectangle.size];
rectangle.physicsBody.categoryBitMask = rectangleCategory;
rectangle.physicsBody.contactTestBitMask = fallingSquareCategory;
rectangle.physicsBody.collisionBitMask = 0;
[self addChild:rectangle];
}
touchesBeganMethod
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"rect"])
{
radBool = 1;
}
}
touchesEnded
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"rect"])
{
radBool = 0;
}
}
square Method
-(void)square
{
SKAction *move = [SKAction moveToY:CGRectGetMidY(self.frame) duration:1.75];
SKSpriteNode *fallingSquare = [[SKSpriteNode alloc] init];
fallingSquare = [SKSpriteNode spriteNodeWithColor:[UIColor yellowColor] size:CGSizeMake(75, 75)];
fallingSquare.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame));
fallingSquare.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:fallingSquare.size];
fallingSquare.physicsBody.categoryBitMask = fallingSquareCategory;
fallingSquare.physicsBody.contactTestBitMask = rectangleCategory
fallingSquare.physicsBody.collisionBitMask = 0;
[self addChild:fallingSquare];
[fallingSquare runAction:move];
}
didBeginContact
static inline SKSpriteNode *nodeFromBody(SKPhysicsBody *body1, SKPhysicsBody *body2, uint32_t category) {
SKSpriteNode *node = nil;
if (body1.categoryBitMask & category) {
node = (SKSpriteNode *)body1.node;
}
else if (body2.categoryBitMask & category) {
node = (SKSpriteNode *)body2.node;
}
return node;
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
SKSpriteNode *R1 = nil;
SKSpriteNode *fallingS = nil;
firstBody = contact.bodyA;
secondBody = contact.bodyB;
R1 = nodeFromBody(firstBody, secondBody, rectangleCategory);
fallingS = nodeFromBody(firstBody, secondBody, fallingSquareCategory);
if (R1 && fallingS && radBool == 1)
{
[fallingS removeFromParent];
}
}
I believe your issue is the "begin" part of didBeginContact. It only gets called the first time they contact and not every loop. Because the bool was not set to YES when they first contacted it will never be evaluated again.
I believe I ran into this issue once before and the solution was to create a new physical body when you touch it. This "should" trigger didBeginContact the next go around. You might also be able to change a property on the physical body, but if I recall correctly I didn't get that to work and had to init a new physical body.
For example try updating your touchesBegan with this
touchesBeganMethod
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"rect"])
{
radBool = 1;
node.physicsBody = nil;
node.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:rectangle.size];
node.physicsBody.categoryBitMask = rectangleCategory;
node.physicsBody.contactTestBitMask = fallingSquareCategory;
node.physicsBody.collisionBitMask = 0;
}
}
Hope that works for you.
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];
}
}
I'm trying to change some SKActions of an existing Sprite Kit tutorial project, but I'm running into issues when it comes to movement. The Tutorial and GitHub project is here:
https://www.codefellows.org/blogs/simple-sprite-kit-game-tutorial-part1
https://github.com/megharastogi/GameTutorial
As you can see in the code below, each tap only moves the node once. How do I change it so that a long tap will move continuous move the node? I tried a few things like repeatActionForever, but that didn't work very well.
-(void)addShip
{
//initalizing spaceship node
ship = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
[ship setScale:0.5];
ship.zRotation = - M_PI / 2;
//Adding SpriteKit physicsBody for collision detection
ship.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ship.size];
ship.physicsBody.categoryBitMask = shipCategory;
ship.physicsBody.dynamic = YES;
ship.physicsBody.contactTestBitMask = obstacleCategory;
ship.physicsBody.collisionBitMask = 0;
ship.physicsBody.usesPreciseCollisionDetection = YES;
ship.name = #"ship";
ship.position = CGPointMake(120,160);
actionMoveUp = [SKAction moveByX:0 y:30 duration:.2];
actionMoveDown = [SKAction moveByX:0 y:-30 duration:.2];
[self addChild:ship];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self.scene];
if(touchLocation.y >ship.position.y){
if(ship.position.y < 270){
[ship runAction:actionMoveUp];
}
}else{
if(ship.position.y > 50){
[ship runAction:actionMoveDown];
}
}
}
- (void)didMoveToView:(SKView *)view
{
UILongPressGestureRecognizer *tapper = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(tappedScreen:)];
tapper.minimumPressDuration = 0.1;
[view addGestureRecognizer:tapper];
}
- (void)tappedScreen:(UITapGestureRecognizer *)recognizer
{
float touchY = [self convertPointFromView:[recognizer locationInView:self.view]].y;
SKSpriteNode *ship = [self childNodeWithName:#"ship"];
if (recognizer.state == UIGestureRecognizerStateBegan) {
if(touchY >ship.position.y){
[ship runAction:[SKAction repeatActionForever:actionMoveUp] withKey:#"longTap"];
}else{
[ship runAction:[SKAction repeatActionForever:actionMoveDown] withKey:#"longTap"];
}
}
if (recognizer.state == UIGestureRecognizerStateEnded) {
[ship removeActionForKey:#"longTap"];
}
}
Add these two methods in your code.
I'm trying to write a basic cause and effect game. So far I've managed to display an image of a rocket on the screen (appers randomly on the x axles) and when touched launches. The program works fine but I want the rocket to reappear (loop 10 times), can anyone give me some guidance on how I can achieve this.
I have attached the code from my scene.m below...
#import "MyScene.h"
#implementation MyScene
#import AVFoundation;
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.backgroundColor = [SKColor whiteColor];
SKSpriteNode *rocket1 = [SKSpriteNode spriteNodeWithImageNamed:#"rocket.png"];
CGRect rocket1frame = CGRectMake(100, 100, 100, 100);
[rocket1 setSize:rocket1frame.size];
rocket1.position = CGPointMake(CGRectGetMidX(self.frame), 100);
[self addChild:rocket1];
_ship = rocket1;
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint currentLocation = [[touches anyObject] locationInNode:self];
CGPoint previousLocation = [[touches anyObject] previousLocationInNode:self];
CGRect shipRect = _ship.frame;
if (CGRectContainsPoint(shipRect, previousLocation))
{
CGPoint lvPosition = CGPointMake(_ship.position.x - (previousLocation.x - currentLocation.x), _ship.position.y);
_ship.position = lvPosition;
SKAction *sound = [SKAction playSoundFileNamed:#"slideup.mp3" waitForCompletion:NO];
SKAction *moveNode = [SKAction moveByX:lvPosition.x y:3000.0 duration:3.0];
[_ship runAction: sound];
[_ship runAction: moveNode];
}
}
#end;
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint currentLocation = [[touches anyObject] locationInNode:self];
CGPoint previousLocation = [[touches anyObject] previousLocationInNode:self];
CGRect shipRect = _ship.frame;
if (CGRectContainsPoint(shipRect, previousLocation))
{
[self launch:10 p1:currentLocation p2:previousLocation rect1:shipRect];
}
}
-(void)launch:(int)count p1:(CGPoint) currentLocation p2:(CGPoint) previousLocation rect1:(CGRect) shipRect
{
CGPoint lvPosition = CGPointMake(_ship.position.x - (previousLocation.x - currentLocation.x), _ship.position.y);
_ship.position = lvPosition;
SKAction *sound = [SKAction playSoundFileNamed:#"slideup.mp3" waitForCompletion:NO];
SKAction *moveNode = [SKAction moveByX:lvPosition.x y:3000.0 duration:3.0];
[_ship runAction: sound];
[_ship runAction: moveNode completion:^{
if (count)
[self launch:count-- p1:currentLocation p2:previousLocation rect1:shipRect];
}];
}