Sprite Kit Creating Selection Menu - sprite-kit

i have just finished implementing core functionality in my sprite kit tower defence game, and now am trying to refine the game. Firstly, i was thinking of doing a selection menu to select which tower to place. This is done in games such as Kingdom Rush where you can select between 4 different towers. However, i am kinda lost as to how to do this.
http://i.ytimg.com/vi/ihc4G76jA_Q/0.jpg // image of tower selection
any suggestions would be greatly appreciated!
thanks

As promised, heres a little code to get you going, I don't have my mac available to me at the moment so there may be a couple of typos but the concept is there:
Start out by defining which areas on your map are able to hold towers, this can be done in a few ways but as it's SpriteKit I'll use a sprite called towerPlot to represent an area that can take a tower.
Define a method to add a plot to a location with something like:
-(void)addTowerPlotAt:(CGPoint)location{
SKSpriteNode* plot = [SKSpriteNode spriteNodeWithImageNamed: "#plotImage";
plot.location = location;
plot.name = #"towerPlot";
plot.zPosition = 1; //if needed
[self addChild: plot];
}
And your viewDidLoad method add something like:
[self addTowerPlotAt:CGPointMake(someX, someY)];
[self addTowerPlotAt:CGPointMake(otherX, otherY)];
In order to add your plots in the correct positions.
Next up is to check whether a touch was located on a plot;
-(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 the node is a plot, bring up the menu
if ([node.name isEqualToString:#"towerPlot"]) {
//add the menu frame
SKSpriteNode* towerMenu = [SKSpriteNode spriteNodeWithImageNamed: "#TowerMenu";
towerMenu.position = node.position;
SKSpriteNode* towerType1= [SKSpriteNode spriteNodeWithImageNamed: "#Tower1";
towerType1.position = CGPointMake(someX, someY); /* remember this is relative to the menu, the next tower type has an example */
towerType1.name = #"towerType1";
[towerMenu addChild:towerType1];
SKSpriteNode* towerType2= [SKSpriteNode spriteNodeWithImageNamed: "#Tower2";
towerType2.position = CGPointMake((towerType2.size.width/2)-(towerMenu.size.width/2), 0); /* would add it to to the middle left of the menu */
towerType1.name = #"towerType2";
[towerMenu addChild:towerType2];
//add and position as many tower types as you need
[self addChild: towerMenu];
}
if ([node.name isEqualToString:#"towerType1"]) {
//code to build the tower here
}
}
There are other little bits you would need such as removing the menu after selecting a tower and ensuring that a tap returns a nodes name if they are layered oddly, but that's the gist of it I think.

Related

SpriteKit SKFieldNode: Why can't I move the field during runtime?

I am trying to move a SKFieldNode (radialGravity) during runtime:
let movingField = SKFieldNode.radialGravityField(
movingField.position = location //location is a touch position
addChild(movingField)
It crashes as soon as I command the field node to change positions. I have no problem placing the field node in a static position at didMoveToView.
I'm guessing SKFieldNodes simply have to stay in place, but maybe theres a solution.
The main idea is to attach the radial fields to sprites (which have physics bodies attached) so that they have attraction/repulsion interactions. I realize this can be done in a more manual approach, but I was trying to take advantage of the physics available in SpriteKit.
Thanks!
I can easily move field nodes by making them move to follow touch events. Works fine.
Inside of GameScene.m
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
self.backgroundColor = SKColorWithRGB(0, 0, 0);
// This uses SKTUtils to load my simple SKEmitter
SKEmitterNode *nd = [SKEmitterNode skt_emitterNamed:#"bokeh"];
nd.particleZPosition += 100;
nd.position = CGPointMake(self.size.width/2,self.size.height/2);
nd.fieldBitMask = 1;
field = [SKFieldNode radialGravityField];
field.position = CGPointMake(self.size.width/2,self.size.height/2);;
field.minimumRadius = 50;
field.categoryBitMask = 1;
[self addChild:field];
[self addChild:nd];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint t = [touch locationInNode:self];
field.position = t;
}
In my app I have a very similar approach like you. I can set new positions of field nodes just fine. Here is an excerpt:
// _targetPos is the touch location in UIView coordinates
CGPoint pos = [k.world convertToScenePoint:_targetPos]; // convert to scene coordinates
self.gravityField.position = pos;
self.dragField.position = pos;
I can only assume the crash you have mentioned occurs somewhere else in your code. Which line of code does Xcode show when your app crashes?

Sprites with same name

Hey I am trying to move some sprites at the same time, they are all the same type of sprite.So I just used a for loop to add the sprite in the game but when I try to move all of the sprites only one moves.
I am trying to simulate running , when the user taps on the Stone the stone will move down and another stone will appear at the top there will be 4 stones so when one goes down another will come on the top and they rest of the stones will move down. I am haveing trouble moveing the stone that comes on the top of the screen that stone doesn't move like the other stones .
Here is some of the Code:
self.Stone = [Stone spriteNodeWithColor:[SKColor blackColor] size:CGSizeMake(80, 142)];
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
self.node =[self nodeAtPoint:location];
if ([self.node.name isEqualToString:#"stone"]) {
SKAction *hey = [SKAction moveToY:self.Stone.position.y - 142 duration:1];
[self runAction:hey];
}
They are basically going to be 4 stones on the screen and each time you tap the stones move down.
The name of the sprite has no real bearing on identification in regards to movement. You can either make one sprite the parent and attach all other sprites as children. Doing this will make all children move when you move the parent or you can dump all the sprites into an array and move each one separately in a loop.
Updated to include parent/child example
Below is an example of parent/child. When you tap the screen, the parent sprite is move +10 on x and y. The child will move along with the parent.
Keep in mind that the child position is in relation to the parent and not the view.
#import "MyScene.h"
#implementation MyScene
{
SKSpriteNode *mainSprite;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
self.physicsWorld.contactDelegate = self;
mainSprite = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(50, 50)];
mainSprite.position = CGPointMake(100, 100);
[self addChild:mainSprite];
SKSpriteNode *child1 = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size:CGSizeMake(20, 20)];
child1.position = CGPointMake(40, 40);
[mainSprite addChild:child1];
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
mainSprite.position = CGPointMake(mainSprite.position.x+10, mainSprite.position.y+10);
}
-(void)update:(CFTimeInterval)currentTime
{
//
}
#end

Detect Touch on image Texture in OpenGl

I am trying to develop a game using openGL where i have used GLTextureLoader class to load images and these sprites are moving from left to right with some calculated velocity , i need to detect touch on these images.
Since your purpose is very simple, all you have to do is draw whatever object you have twice, the visible one and another one with just color on an invisible buffer. then you check for the location where the user pressed in the invisible buffer, see what color it is and there you have your object.
http://www.lighthouse3d.com/opengl/picking/index.php3?color1
That is the basic theory.
OpenGL is a rendering API. It only draws stuff.
Techniques like lighthouse3d do work, but glReadPixels is slow.
You should check this on the CPU; that is, for each drawn sprite, test if the touch position is inside.
I found out how to do it ,as per my requirement , As i said i am not the expert in openGL but managed to do it some way around.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[touches allObjects] objectAtIndex:0];
CGPoint touchLocation = [touch locationInView:self.view];
touchLocation = touchLocation = CGPointMake(touchLocation.x, 320 - touchLocation.y);
//self.player.moveVelocity = GLKVector2Make(50, 50);
//NSLog(#"Location in view %#", NSStringFromCGPoint(touchLocation));
//NSLog(#"bounding box is %#",NSStringFromCGRect([self.player boundingBox]));
GameSprite *temp;
for (GameSprite *tempSprite in self.children) {
if (CGRectContainsPoint([tempSprite boundingBox], touchLocation)) {
NSLog(#"touched the player");
temp =tempSprite;
}
}
[self.children removeObject:temp];
}
- (CGRect)boundingBox {
CGRect rect = CGRectMake(self.position.x, self.position.y, self.contentSize.width, self.contentSize.height);
return rect;
}

How do I calculate the position of a view animated from a 3-D carousel?

I have developed a psuedo 3D carousel for the iPhone that looks pretty much like the image below:
I basically create an array of UIViews and then register for the touch events:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// if we're not already tracking a touch then...
if(!trackingTouch)
{
// ... track any of the new touches, we don't care which ...
trackingTouch = [touches anyObject];
}
}
While the movement is still being registered I do the following:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// if our touch moved then...
if([touches containsObject:trackingTouch])
{
// use the movement of the touch to decide
// how much to rotate the carousel
CGPoint locationNow = [trackingTouch locationInView:self];
CGPoint locationThen = [trackingTouch previousLocationInView:self];
lastAngle = currentAngle;
currentAngle += (locationNow.x - locationThen.x) * 180.0f / self.bounds.size.width;
// and update the view positions
[self setCarouselAngle:currentAngle];
}
}
I then call when the touches end:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// if our touch ended then...
if([touches containsObject:trackingTouch])
{
// make sure we're no longer tracking it
trackingTouch = nil;
// and kick off the inertial animation
animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:#selector(animateAngle) userInfo:nil repeats:YES];
}
}
The animageAngle method basically then calls another method with effectively divides 360 degrees by the number of views in the array and use the cosine and sine functions to generate the x and y coordinates.
The above all works good however, I am having difficulty comprehending the maths required to complete the following:
Click on a view out of the main focus i.e. not at the front e.g the purple view in the image
Rotate the purple view into focus i.e. to the front
Load a new view controller once the rotation has complete
I understand that I have to calculate the angle between the view in its current position and the planned destination to make the rotation but I can not for the life of me figure it out.
Apologies for the prior confusion - I have been looking at this problem for sooooo long that I am starting to lose the plot.
Thanks
Finally....I managed to crack it.
I was acting incredibly stupid and was not thinking logically. I basically re-ran a NSTimer calling a method that run the following calculation on the currentAngle property (in psuedo code):
CGFloat currentLocation;
CGFloat nextLocation;
CGFloat destinationLocation;
currentLocation = viewImInterestedIn.frame.origin.x;
nextLocation = currentLocation - 1; //or + 1 if the selected view is to the left of centre
destinationLocation = 115.0f //set this to a default value
currentAngle += (currentLocation - nextLocation ) * 180.0f / self.bounds.size.width;
// and update the view positions
[self setCarouselAngle:currentAngle];
Et voila!
Remarkably simple - I have to learn to just take a step back sometimes and come back to a problem with a clear head.
Thanks all

Smoothly drag a Sprite in cocos2d - iPhone

I have implemented a drag on a sprite object as follows..
-(BOOL)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch * touch = [touches anyObject];
CGPoint location = [[Director sharedDirector] convertCoordinate: [touch locationInView:touch.view]];
[diskSprite setPosition:ccp(location.x , location.y )];
return kEventHandled;
}
but this dragging is not smooth.....
when i drag fast with my thumb the object left from the path.
Thanks
Probably a little bit late but I was searching for a similar thing.
I found this great Tutorial which explained everything:
http://www.raywenderlich.com/2343/how-to-drag-and-drop-sprites-with-cocos2d
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
CGPoint oldTouchLocation = [touch previousLocationInView:touch.view];
oldTouchLocation = [[CCDirector sharedDirector] convertToGL:oldTouchLocation];
oldTouchLocation = [self convertToNodeSpace:oldTouchLocation];
CGPoint translation = ccpSub(touchLocation, oldTouchLocation);
CGPoint newPos = ccpAdd(mySpriteToMove.position, translation);
mySpriteToMove.position = newPos;
}
I had this same issue with my game. Dragging operations appeared jerky. I believe the reason is that touch events aren't generated fast enough to give a smooth appearance.
To solve the problem I smoothed the motion out by running an action on the sprite toward the desired location, instead of setting the position immediately.
I'm not exactly sure what you mean by "the object left from the path". I suppose what you mean is that if you drag your finger over the screen in an arc or circle, that the sprite will "jump" from point to point, instead of follow your finger precisely. Is this correct?
If you want your sprite to follow an exact path, you will have to create a path and then set the sprite to follow it. What you do now is simply set the sprite's position to the touch position, but a "dragged" touch will not create an event for every pixel it touches.
It is fairly easy to create a path for touches received, and code samples can be found here and there. However, if the sprite's speed (in pixels per frame) is too high, you will always see it "jump", even if you use a smooth path.
Example:
You can animate a sprite over a circular path. If you animate this to complete the path in 1 second, you will likely see smooth animation. But if it runs at a high speed, like a full circle in 4 frames, you will just see your sprite at 4 places, not in a smooth circle.
If you wish to 'correct' that, you will need to look into blending, or determine what the maximum speed is for acceptable motion, and slow your sprite down when it's too fast.
I hope that answers your question. If it's not clear, feel free to edit your question, or add a comment to my answer.
look here, what I suggest you to try in such case:
-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
if (_binCaptured) {
CGPoint location = [self convertTouchToNodeSpace:touch];
[_sprite stopAllActions];
id move = [CCEaseIn actionWithAction:[CCMoveTo actionWithDuration:0.1 position:ccp(location.x, _sprite.position.y)]];
[_sprite runAction:move];
}
}
And it really work smoothly.
I enjoyed this easy way.