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?
Related
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.
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;
}
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
Hey guys, i ready many many questions about that point, but i really didn't get the right one yet.
So.. this is the problem..
This is my first project with cocos2d, so sorry for that.
I have one scene called Gameplay inside that i have one Layer with the name Grid and inside of this grid, have many many Block (Layer too).
I need check when you touch one Block, i do this before with Interface Builder, so when i call touchesBegin i have the exact touch in one view. But in cocos2D, i understand you have to check the position of the objects, and not hit test then right?!
So my touchesBegin is like this:
- (void)ccTouchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView: [touch view]];
//location = [[Director sharedDirector] convertCoordinate: location];
for(int i = 0; i < [arrAllBlocks count]; i++)
{
for (int j = 0; j < [[arrAllBlocks objectAtIndex:i] count]; j++)
{
Block *tempBlock = [[arrAllBlocks objectAtIndex:i] objectAtIndex:j];
CGRect mySurface = (CGRectMake(tempBlock.position.x, tempBlock.position.y, tempBlock.contentSize.width,tempBlock.contentSize.height));
if(CGRectContainsPoint(mySurface, location))
{
NSLog(#"Hit Positions %fl:%fl",location.x,location.y);
NSLog(#"Object Positions %fl:%fl",tempBlock.position.x,tempBlock.position.y);
NSLog(#"Object Color:%# hited", tempBlock.strName);
}
}
}
}
The first Problem is: This looks upsidedown! When i click in one of this blocks at the first line! I get the block at last line!! I Really dont get that! And the hit not seens perfect to me! And when i convertCoordinate this going even worst!
Someone can help me?! And sorry for bad english =/
There are two things to remember...
Cocos2D coordinate system's origin is bottom left corner.
By default, the anchor points of display elements are their centers.
So this is how I solved this problem:
Set the anchor points of the elements to be bottom left as well.
[block setAnchorPoint:ccp(0, 0)];
Use convertCoordinate in ccTouchesBegan.
-(BOOL)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint pt = [touch locationInView:[touch view]];
CGPoint ptConv = [[Director sharedDirector] convertCoordinate:pt];
}
Calculate the rectangle of the element and compare...
CGSize size = [block contentSize];
CGPoint point = [block position];
rect = CGRectMake(point.x, point.y, size.width, size.height);
if (CGRectContainsPoint(rect, ptConv))
{
// touched
}
It looks like the only step you are missing is to change the anchor point.
If you prefer not to change the anchor point, you need to change the way rectangle is calculated; adding and subtracting the half of width and height.
Hope it helps.
Remember that the coordinate system in Cocos2D is upside down from that of Cocoa. (Really, Cocoa is upside down to me, but it's just a convention).
I forget this all the time myself!
I am facing one problem. I have done some coding to rotate cpSegmentShapeNew but its not working . Have a look on the following code,
//**creating shape
testBody = cpBodyNew(INFINITY, INFINITY);
cpShape* testShape = cpSegmentShapeNew(testBody, cpv(230, 82), cpv(193, 46), 0.0f);
testShape->e = 0.0;
testShape->u = 0.0;
testShape->data = flipper;
testShape->collision_type = 2;
cpSpaceAddStaticShape(space, testShape);
//Body moving when user touch
-(BOOL) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//event that starts when a finger touchs the screen
UITouch *touch = [touches anyObject];
CGPoint tmpLoc = [touch locationInView: [touch view]];
CGPoint location = [[Director sharedDirector] convertCoordinate:tmpLoc];
ball.position = location;
ballBody->p = location;
[flipper runAction:[RotateTo actionWithDuration:0.1f angle:60]];
cpBodySetAngle(testBody, 60);
cpvrotate(testBody->rot, cpv(100000,0));
return kEventHandled;
}
Please anyone tell me that where i am wrong.
Thanks.
Greetings,
The problem is that you are rotating both objects (sprite + body) through code.
What you need is rotate one, and let the other object know it's happened so it can do it too.
For example, if you move the body, then the method that updates the sprites should look like that:
void updateShapes(void* ptr, void* unused)
{
cpShape* shape = (cpShape*)ptr;
Sprite* sprite = shape->data;
if(sprite)
{
cpBody* body = shape->body;
[sprite setPosition:cpv(body->p.x, body->p.y)];
[sprite setRotation: (float) CC_RADIANS_TO_DEGREES( -body->a )];
}
}
The last line of code updates the rotation. That's the line you are missing.
I hope that helps you or someone else in the future.
Good luck cocos2d mate !
Yohann T.