Passing iOS sprites from one class to another - ios

I have a Spritekit project in xcode on my main screen. However I would really like to manage it from another class so as to not cluster my main. So I added a new class to manage and handle it. Sadly I can't seem to get it to show in my main (or I might be doing it wrong). My myscene.m consists of pretty much the same code including the spaceship and this is where I moved the code too ( I didn't bother adding the methods like touchesBegan)
// joysitck.m
// controlpad
//
#import "joysitck.h"
#implementation joysitck
{
BOOL controlButtonOn;
}
float controlx=200;
float controly=200;
float touchX = 0.0;
float touchY=0.0;
- (instancetype)init
{
if (self = [super init]) {
self.name = #"controlPadNode";
SKSpriteNode *controlPadNode = [SKSpriteNode spriteNodeWithImageNamed:#"game-controller-frontb"];
controlPadNode.position = CGPointMake(controlx,controly);
[controlPadNode setScale:0.5];
controlPadNode.name = #"controlPadNode";
controlPadNode.zPosition = 1.0;
[self addChild:controlPadNode];
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
//if control pad touched
if ([node.name isEqualToString:#"controlPadNode"]) {
touchX = location.x;
touchY = location.y;
controlButtonOn= true;
}
}
//when touch moves
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
//if control pad touched
if ([node.name isEqualToString:#"controlPadNode"]) {
touchX = location.x;
touchY = location.y;
controlButtonOn= true;
}
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
//if control pad touched
if ([node.name isEqualToString:#"controlPadNode"]) {
controlButtonOn= false;
}
}
//update method
-(void)update:(NSTimeInterval)currentTime {
if (controlButtonOn) {
//the control pad
SKNode *controlNode = [self childNodeWithName:#"controlPadNode"];
SKNode *node = [self childNodeWithName:#"spaceShipNode"];
float angle = atan2f (touchY - controly, touchX - controlx) ;
SKAction *moveShip=[SKAction moveByX:6*cosf(angle) y:0 duration:0.005];
[node runAction: moveShip];
}
}
#end

You haven't added joystick node to the scene.
Add this code to MyScene's initWithSize method (before [self addship]):
joysitck *joysitckNode = [[joysitck alloc] init];
[self addChild:joysitckNode];

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];
}

Drag SKSpriteNode based on touch location

I am trying to drag a sprite in the y axis but make the sprite "stick" to the users finger depending on where the touches began on the node.
The sprite is currently dragging but it seems to be snapping the anchorpoint of the sprite to the touch location within the node.
Im assuming it has something to do with getting the location within the node by doing [touch locationInNode:selectedNode]; but I am unsure where to go from there.
Here is my current code.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
CGPoint newPosition = CGPointMake(node.position.x, location.y);
if ([node.name isEqualToString:self.selectedNode] ) {
if (newPosition.y > 230) {
newPosition.y = 230;
}
node.position = newPosition;
}
}
}
You need to offset the newPosition based on the current touch position on the node.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:self.selectedNode] )
{
CGPoint previousLocation = [touch previousLocationInNode:self];
float diff = location.y - previousLocation.y;
CGPoint newPosition = CGPointMake(node.position.x, node.position.y + diff);
if (newPosition.y > 230) {
newPosition.y = 230;
}
node.position = newPosition;
}
}
}
There are a couple of ways of doing this. The sample code below tracks the user's touch location and moves the sprite towards that position during the update method. You can modify the code to only move on the y axis or x axis.
#implementation MyScene
{
SKSpriteNode *object1;
CGPoint destinationLocation;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
[self createObject];
destinationLocation = CGPointMake(300, 150);
}
return self;
}
-(void)createObject
{
object1 = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(50, 50)];
object1.position = CGPointMake(300, 150);
[self addChild:object1];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
destinationLocation = [touch locationInNode:self.scene];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
destinationLocation = [touch locationInNode:self.scene];
}
-(void)update:(CFTimeInterval)currentTime
{
float x = fabs(object1.position.x - destinationLocation.x);
float y = fabs(object1.position.y - destinationLocation.y);
float divider = 0;
if(x > y)
{
divider = x;
} else
{
divider = y;
}
float xMove = (x/divider)*8; // change number to increase or decrease speed
float yMove = (y/divider)*8; // change number to increase or decrease speed
if(object1.position.x > destinationLocation.x)
object1.position = CGPointMake(object1.position.x-xMove, object1.position.y);
if(object1.position.x < destinationLocation.x)
object1.position = CGPointMake(object1.position.x+xMove, object1.position.y);
if(object1.position.y > destinationLocation.y)
object1.position = CGPointMake(object1.position.x, object1.position.y-yMove);
if(object1.position.y < destinationLocation.y)
object1.position = CGPointMake(object1.position.x, object1.position.y+yMove);
}
#end

Change SpriteNodes coordinates with User touch

I'm currently programming a small game with SpriteKit in Xcode for the iOS platform. I wrote a code which adds some SpriteNodes on 2 different "lines", so half of the nodes are moving with the y-coordinate 100 and the others with y = 200, and they're all moving from the left side to the right which an infinity loop. Now I want that the user can touch on one SpriteNode, then moves his finger to another SpriteNode, but it has to be located on another line, then removes his finger and the SpriteNode with the TOUCHBEGAN should change its y-coordinate with the TOUCHEND Node. How can I accomplish this?
-(void)add
{
SKSpriteNode *sprite2 = [SKSpriteNode spriteNodeWithImageNamed:#"test1.png"];
sprite2.position = CGPointMake(-40, self.frame.size.height / 2);
sprite2.size = CGSizeMake(100, 32);
SKSpriteNode *sprite3 = [SKSpriteNode spriteNodeWithImageNamed:#"test.png"];
sprite3.position = CGPointMake(-40, (self.frame.size.height / 2) + 90);
sprite3.size = CGSizeMake(32, 100);
[self addChild:sprite1];
[self addChild:sprite2];
SKAction *actionMove1 = [SKAction moveTo:CGPointMake(400, (self.frame.size.height / 2) - 90) duration:12];
SKAction *actionMove2 = [SKAction moveTo:CGPointMake(200, (self.frame.size.height / 2)) duration:12];
SKAction *actionMoveDone = [SKAction removeFromParent];
[sprite1 runAction:[SKAction sequence:#[actionMove1, actionMoveDone]]];
[sprite2 runAction:[SKAction sequence:#[actionMove2, actionMoveDone]]];
}
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast
{
self.lastSpawnTime += timeSinceLast;
if (self.lastSpawnTime > 2)
{
self.lastSpawnTime = 0;
[self add];
}
}
- (void)update:(CFTimeInterval)currentTime
{
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTime;
self.lastUpdateTime = currentTime;
if (timeSinceLast > 2)
{
timeSinceLast = 1.0 /60.0;
self.lastUpdateTime = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}
You have to subclass SkSpriteNode:
to detect TouchEvents you have to set "userInteractionEnabled" to "YES"
to react on TouchEvents implement the touch method which fits for you needs:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{}
In this method you can change the coordinates of your sprite
add these function like that in your sksence
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent )event {
/ Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if([node.name isEqualToString:#"plane"])
{
NSLog(#"_______touch ended");
//x and y position of object at Scene
NSLog(#"%f",node.position.x);
NSLog(#"%f",node.position.y);
NSLog(#"%#",node);
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if([node.name isEqualToString:#"plane"])
{
NSLog(#"_______touch mobing");
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if([node.name isEqualToString:#"plane"])
{
NSLog(#"______touch begin");
//x and y position of object at Scene
NSLog(#"%f",node.position.x);
NSLog(#"%f",node.position.y);
NSLog(#"%#",node);
}
}

Detect touch on UIView

I have a node that acts like an UIButton and a node that performs an action when the screen gets touched.
The problem is that the lion jump action does not get fired.
In addition: both names of the nodes are correct.
My code for the UIEvent:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode: self];
SKNode *node = [self nodeAtPoint:location];
UITouch *lionTouch = [touches anyObject];
CGPoint locationLion = [lionTouch locationInView:[self view]];
SKNode *lionNode = [self nodeAtPoint:locationLion];
if ([lionNode.name isEqualToString:#"veggieLion"]) {
if (!lionIsJumping) {
[self lionJump];
lionIsJumping = YES;
}
}
if ([node.name isEqualToString:#"repalyButton"]) {
// Button action
}
}
Apple documentation for SKNode says the method nodeAtPoint "Returns the deepest descendant that intersects a point."
I've had this trouble in the past. where the method would return the SKSpriteNode that I had used for the background, even if I were definitely tapping the target sprite/node!
Since you are checking for two nodes, I would approach it in the following way:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode: self];
SKNode * lionNode = [self childNodeWithName:#"veggieLion"];
SKNode * replayButton = [self childNodeWithName:#"repalyButton"];
if ([self.lionNode containsPoint:location]) {
if (!lionIsJumping) {
[self lionJump];
lionIsJumping = YES;
}
return;
}
if ([self.replayButton containsPoint:location]) {
// Button action
return;
}
/* test for other targets */
}

How to detect subclass sprite touch

I subclass a sprite called newSprite.h / newSprite.m, and I add a sprite in it
CCSprite *nsprite = [CCSprite spriteWithFile:#"mouse.png"];
[self addChild: nsprite];
and in gamelayer.m, I add the following code
newSprite *newp = [newSprite node];
newp.position = ccp(actualX, actualY);
[self addChild:newp];
[_NSMutableArrayName addObject:newp];
when I use following code to detect which sprite i touched
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [self convertTouchToNodeSpace: touch];
for (CCSprite *target in _NSMutableArrayName) {
if (CGRectContainsPoint(target.boundingBox, location)) {
CCLOG(#"yes i am touched");
}
}
}
but it doesn't work, the sprite can not be detected, so where is the wrong? please help me, thanks
Try using this:
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [self convertTouchToNodeSpace:touch];
for (CCSprite *target in _NSMutableArrayName) {
CGSize size = node.contentSize;
CGRect r = CGRectMake(0.f, 0.f,
size.width, size.height);
if (CGRectContainsPoint(r, local)) {
CCLOG(#"yes i am touched");
}
}
}
You are trying to detect touches on sub sprite and giving the bounds of parent sprite.
First, take nsprite as class variable in NewSprite to keep its reference when you call it from GameLayer. Then try changing this method like:
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [self convertTouchToNodeSpace: touch];
for (CCSprite *target in _NSMutableArrayName) {
CCSize size = target.nSprite.contentSize;
CCRect rect = CCRectMake(target.position.x - size.width/2, target.position.y - size.height/2, width, height);
if (CGRectContainsPoint(rect, location)) {
CCLOG(#"yes i am touched");
}
}
}

Resources