Error when creating a ball object out of a skspritenode - ios

I created a ball object to later show on screen when I tap it. However when I try to do that, it gives me an error. Unfortunately i can't make something out of it.
ballClass.h:
#import SpriteKit;
#import "ballClass.h"
#implementation ballClass
+ (SKSpriteNode*)ballNode {
SKSpriteNode* node = [SKSpriteNode spriteNodeWithImageNamed:#"Ball"];
return node;
}
#end
ballClass.m:
#import <Foundation/Foundation.h>
#interface ballClass : NSObject
+ (SKSpriteNode*)ballNode;
#end
My touchesBegan method in my game scene.m:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKSpriteNode *ball = [ballClass ballNode];
ball.position = location;
[self addChild:ball];
}
}
Any help would be greatly appreciated. Thanks in advance.

It should be:
ballClass.h
#import SpriteKit;
#import <Foundation/Foundation.h>
#interface ballClass : NSObject
+ (SKSpriteNode*)ballNode;
#end
ballClass.m
#import "ballClass.h"
#implementation ballClass
+ (SKSpriteNode*)ballNode {
SKSpriteNode* node = [SKSpriteNode spriteNodeWithImageNamed:#"Ball"];
return node;
}
#end
In the YourScene.m:
//at the beginning of the file
#import ballClass.h
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKSpriteNode *ball = [ballClass ballNode];
ball.position = location;
[self addChild:ball];
}
}

Related

SKNode Subclass Not Moving Children

I have a sprite that can't exist in my game without a pairing SKFieldNode so my solution was to create a subclass of SKSpriteNode and create a property for the SKFieldNode but it didn't work because the SKSpriteNode was acting weird (I don't remember exactly what happened). So my next approach was to change the subclass to SKNode and then I would make the SKSpriteNode and the SKFieldNode a property of this new SKNode. But then it turns out that touchesMoved will only move one of the properties (whichever is on top) which turns out to always be the SKSpriteNode.
What's the best approach to this problem, and how can I fix it so that I can have an SKFieldNode for every SKSpriteNode while still making sure that actions and methods still work properly.
Current code of SKNode subclass:
#interface Whirlpool : SKNode
- (instancetype)initWithPosition:(CGPoint)pos region:(float)region strength:(float)strength falloff:(float)falloff;
#property (nonatomic, strong) SKFieldNode *gravityField;
#end
#import "Whirlpool.h"
#import "Categories.h"
#implementation Whirlpool
- (instancetype)initWithPosition:(CGPoint)pos region:(float)region strength:(float)strength falloff:(float)falloff {
if (self = [super init]) {
// whirlpool sprite
SKSpriteNode *whirlpoolSprite = [[SKSpriteNode alloc] initWithImageNamed:#"whirlpool"];
whirlpoolSprite.size = CGSizeMake(100, 100);
whirlpoolSprite.position = pos;
//removing physicsBody and associated attributes for now so that the boat does not collide with the whirlpool
//whirlpoolSprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:whirlpoolSprite.size.width / 2];
//whirlpoolSprite.physicsBody.dynamic = NO;
whirlpoolSprite.zPosition = 1;
whirlpoolSprite.name = #"whirlpool";
[whirlpoolSprite runAction:[SKAction repeatActionForever:[self sharedRotateAction]]];
// whirlpool gravity field
_gravityField = [SKFieldNode radialGravityField];
_gravityField.position = pos;
_gravityField.strength = strength;
_gravityField.falloff = falloff;
_gravityField.region = [[SKRegion alloc] initWithRadius:region];
_gravityField.physicsBody.categoryBitMask = gravityFieldCategory;
_gravityField.zPosition = 1;
[self addChild:whirlpoolSprite];
[self addChild:_gravityField];
}
return self;
}
- (SKAction *)sharedRotateAction {
static SKAction *rotateWhirlpool;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
rotateWhirlpool = [SKAction rotateByAngle:-M_PI * 2 duration:4.0];
});
return rotateWhirlpool;
}
#end
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// we don't want the player to be able to move the whirlpools after the button is pressed
if (_isRunning) {
return;
}
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
// if the finger touches the boat or the whirlpool, update its location
if ([node.name isEqualToString:#"boat"]) {
node.position = CGPointMake(location.x, node.position.y);
} else if ([node.name isEqualToString:#"whirlpool"]) {
node.position = location;
}
}
}
I believe your issues comes down to that fact that "whirlpool" is a child of your SKNode subclass. So when you are identifying that you are indeed touching a "whirlpool" you are moving it within its parent (the SKNode subclass) and the SKFieldNode and parent stay put. This little adjustment to your original code should work...if I understand the problem correctly.
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// we don't want the player to be able to move the whirlpools after the button is pressed
if (_isRunning) {
return;
}
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
// if the finger touches the boat or the whirlpool, update its location
if ([node.name isEqualToString:#"boat"]) {
node.position = CGPointMake(location.x, node.position.y);
} else if ([node.name isEqualToString:#"whirlpool"]) {
//move the SKNode subclass the whirlpool is a child of
node.parent.position = location;
}
}
}
Hopefully that helps.
Yikes, the problem here is the way you are grabbing nodes, you may be grabbing the wrong nodes due to all the children. Instead take this approach:
We already know that you are subclassing your sprites, so in your subclasses, add the following code:
//Whirlpool
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self.parent];//Unless you are retaining the scene in the child, then use that
self.position = location;
}
}
Then:
//Boat
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self.parent];//Unless you are retaining the scene in the child, then use that
self.position.x = location.x;
}
}
Then for your Scene, do this:
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// we don't want the player to be able to move the whirlpools after the button is pressed
if (_isRunning) {
return;
}
//At this point it should call the children on touch events
[super.touchesMoved: touches withEvent:event];
}

How to make several sprite nodes each with a special path to follow

I am trying to make a game like flight control where there are x amount of cars and each one has its own path to follow. My idea was to make a car class of subtype SKNode and have a CGMutablePathRef instance variable for each object. I can make the CGMutablePathRef with touchsBegins, touchsMoved, and touchesEnded and figured I could then have each object follow its specific path with the SKAction followPath. Is this a good approach? If not how would you do it? This is what I have so far for the Vehicle class:
Vehicle.h
#interface Vehicle: SKNode
#property (nonatomic, assign) CGMutablePathRef pathToFollow;
#property (nonatomic) CGFloat speed;
#property (nonatomic) int direction;
- (instancetype)initWith:(CGMutablePathRef)pathPassed;
#end
Vehicle.m
#implementation Vehicle : SKNode
#synthesize pathToFollow;
#synthesize speed;
#synthesize direction;
- (id)init
{
self = [super init];
if (self) {
SKSpriteNode *vehicle = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
[self addChild:vehicle];
[self setDirection:4];
[vehicle setName:#"car"];
[self setScale:.5];
}
return self;
}
- (id)initWith:(CGMutablePathRef)pathPassed {
self = [super init];
if (self) {
SKSpriteNode *vehicle = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
[self addChild:vehicle];
[self setPathToFollow:pathPassed];
[self setDirection:4];
[vehicle setName:#"car"];
[self setScale:.1];
}
return self;
}
#end
EDIT: Adding GameScene in hopes that it will help. I am having a problem with my implementation now where the Vehicle object is following the path but only after it jumps up to the coordinates of the first point + the nodes position on the scene (ex: position = (200, 200), first point = (123, 456) it will go to (323, 656) then follow the path). It draws the line where I want it but then it follows from way up on this coordinate. Thanks again for the help!
GameScene.m
-(void)didMoveToView:(SKView *)view {
_car = [[Vehicle new] init]; // init the Vehicle object then set position
[_car setPosition:CGPointMake(self.scene.size.width/2, self.scene.size.height/2)];
[self addChild_car]; // add the child to the scene
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// get the node that was touched
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
_nodeTouchedFirst = [self nodeAtPoint:positionInScene];
if ([_nodeTouchedFirst.name isEqualToString:#"car"]) {
if (lineNode != NULL) { // remove the previous path
[lineNode removeFromParent];
[CGPathRelease(pathToDraw);
}
//Start a new path and display it on the screen
pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
lineNode = [SKShapeNode];
lineNode.path = pathToDraw;
lineNode.strokeColor = [SKColor redColor];
[self addChild:lineNode];
}
} // touchesBegan
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
if ([_nodeTouchedFirst.name isEqualToString:#"car"]) {
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
CGPathAddLineToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
lineNode.path = pathToDraw;
}
} // touchesMoved
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([_nodeTouchedFirst.name isEqualToString:#"car"]) {
SKAction *followPath = [SKAction followPath:pathToDraw asOffset:YES orientToPath:NO duration:2];
[_nodeTouchedFirst runAction:followPath];
lineNode.strokeColor = [SKColor grayColor];
}
}
Some observations that may or may not help:
You're setting the position of _car and adding it to the scene before you give it a path to follow.
You're referencing both _nodeTouchedFirst and nodeTouchedFirst, "car" and "car1".
You're following a path with asOffset:YES, orientToPath:NO, duration:2 (rather than asOffset:NO, orientToPath:YES, speed:2).
direction doesn't correspond to the SKNode zRotation property.

Where Did This Method Come From in SpriteKit

I have been following a tutorial on SpriteKit, and for the life of me, I don't know where this one method comes from. In a StartGameLayer, I have this code in its entirety:
#import "StartGameLayer.h"
#interface StartGameLayer()
#property (nonatomic, retain) SKSpriteNode* playButton;
#end
#implementation StartGameLayer
- (id)initWithSize:(CGSize)size
{
if(self = [super initWithSize:size])
{
SKSpriteNode* startGameText = [SKSpriteNode spriteNodeWithImageNamed:#"FlappyBirdText"];
startGameText.position = CGPointMake(size.width * 0.5f, size.height * 0.8f);
[self addChild:startGameText];
SKSpriteNode* playButton = [SKSpriteNode spriteNodeWithImageNamed:#"PlayButton"];
playButton.position = CGPointMake(size.width * 0.5f, size.height * 0.30f);
[self addChild:playButton];
[self setPlayButton:playButton];
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
if ([_playButton containsPoint:location])
{
if([self.delegate respondsToSelector:#selector(startGameLayer:tapRecognizedOnButton:)])
{
[self.delegate startGameLayer:self tapRecognizedOnButton:StartGameLayerPlayButton];
}
}
}
#end
Where does the setPlayButton:(SKSpriteNode* ) method get set? In the help file it says it is declared in this file, but this is the only instance I see of it, and it is just driving me the tiniest bit nuts how this method is created.

Having trouble getting a touch location in sprite-kit screne

I am making a game for a final project and I need to know where the user touches so I can move the player around.
here is the .h file
#import <SpriteKit/SpriteKit.h>
#interface BYFGameScene : SKScene
#end
the header of the .m
#import "BYFGameScene.h"
#interface BYFGameScene()
#property BOOL contentCreated;
#property (nonatomic)SKSpriteNode *player;
#end
#implementation BYFGameScene
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint *location = [[touches anyObject] location];
NSLog(#"the location is %#",location);
}
I think that touches began would be the appropriate method to get the location and in previous apps this is how I would get the location but it keeps throwing a exception and I am not sure why.
I have also tried
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGFloat location = [[touches anyObject] locationInView:self];
NSLog(#"the location is %f",location);
}
but it gives an error saying that UIView and BYFGameScene(the current scene) are not compatible types
also how would I manipulate the players position?
\here is what I thought would work but it does not.
-(void)moveUp
{
if(self.player.position==CGpoint 700)
{
}
else
{
self.player.position = self.player.position +100;
}
}
700 would be the max position
Any help would be appreciated
thanks
I think this is what you're looking for:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
//touch point, see locationInNode
CGPoint location = [touch locationInNode:self];
// SKNode *node = [self nodeAtPoint:location];

IOS touch tracking code

I'm making an app for iPhone in Xcode, and It requires a box to follow my finger only on the X axis. I couldn't find any solution online to this, and my coding knowledge isn't that great.
I've been trying to use touchesBegan and touchesMoved.
Could someone please write me up some code please?
First you need the UIGestureRecognizerDelegate on your ViewController.h file:
#interface ViewController : UIViewController <UIGestureRecognizerDelegate>
#end
Then you declare an UIImageView on your ViewController.m, like so, with a BOOL to track if a touch event is occurring inside your UIImageView:
#interface ViewController () {
UIImageView *ballImage;
BOOL touchStarted;
}
Then you initialize the UIImageView on viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
UIImage *image = [UIImage imageNamed:#"ball.png"];
ballImage = [[UIImageView alloc]initWithImage:image];
[ballImage setFrame:CGRectMake(self.view.center.x, self.view.center.y, ballImage.frame.size.width, ballImage.frame.size.height)];
[self.view addSubview:ballImage];
}
After that you can start doing your modifications to what's best for you using these methods:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touch_point = [touch locationInView:ballImage];
if ([ballImage pointInside:touch_point withEvent:event])
{
touchStarted = YES;
} else {
touchStarted = NO;
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([touches count]==1 && touchStarted) {
UITouch *touch = [touches anyObject];
CGPoint p0 = [touch previousLocationInView:ballImage];
CGPoint p1 = [touch locationInView:ballImage];
CGPoint center = ballImage.center;
center.x += p1.x - p0.x;
// if you need to move only on the x axis
// comment the following line:
center.y += p1.y - p0.y;
ballImage.center = center;
NSLog(#"moving UIImageView...");
}
}

Resources