I'm making a drawing app, and I'm having the users draw with CCRenderTexture. It basically keeps rendering a picture of a black circle to simulate drawing. When I move my finger slowly, it works really well since the circles come together to form a line. However, when I move my finger quickly, it ends up just being a bunch of circles that aren't connected (http://postimage.org/image/wvj3w632n/). My question is how I get the render texture to render the image faster or have it fill in the blanks for me.
Also, I'm not completely sold on this method, but it's what I've found while looking around. Feel free to suggest whatever you think would be better. I was originally using ccdrawline but it really killed my performance. Thanks!
The gaps between start point and the end points need to be sorted out. I am pasting code that might help you to resolve the situation you showed in the link.
in .h file
CCRenderTexture *target;
CCSprite* brush;
in the init method of .m file
target = [[CCRenderTexture renderTextureWithWidth:size.width height:size.height] retain];
[target setPosition:ccp(size.width/2, size.height/2)];
[self addChild:target z:1];
brush = [[CCSprite spriteWithFile:#"brush_i3.png"] retain];
add the touches method I am showing the touchesMoved code.
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint start = [touch locationInView: [touch view]];
start = [[CCDirector sharedDirector] convertToGL: start];
CGPoint end = [touch previousLocationInView:[touch view]];
end = [[CCDirector sharedDirector] convertToGL:end];
printf("\n x= %f \t y= %f",start.x,start.y);
float distance = ccpDistance(start, end);
if (distance > 1)
{
int d = (int)distance;
for (int i = 0; i < d; i++)
{
float difx = end.x - start.x;
float dify = end.y - start.y;
float delta = (float)i / distance;
[brush setPosition:ccp(start.x + (difx * delta), start.y + (dify * delta))];
[target begin];
[brush setColor:ccc3(0, 255, 0)];
brush.opacity = 5;
[brush visit];
[target end];
}
}
}
Hopefully it would work for you.
Its not that CCRenderTexture draws too slow its that the event only fires so often. You do need to fill in the gaps between the touch points you receive.
There is a great tutorial here about it which you may have already seen, http://www.learn-cocos2d.com/2011/12/how-to-use-ccrendertexture-motion-blur-screenshots-drawing-sketches/#sketching
Related
Okay, I have had a few questions floating around stackoverflow now, and most of them are related to this one question. I've been trying to get it myself, and I have a couple of times but the result is never quite what I expected. I am trying to develop a chain that my sprite can throw.
So far what I have done is thrown the lead of the hook from the centre of the sprite outward. Once it travels 10 pixels away from the sprites position it generates another link in the chain. The chain is then rotated to match the lead chains rotation and attached to it using a joint pin. It actually works fairly well. The only problem is it only works when I set the physics world speed to 0.01. If I return it to normal physics it throws the lead link in the chain but basically skips over everything else. Before this I tried containing the lead link in a physics body and calling a didEndContact to attach the other links but that didn't work nearly as well.
Does anyone have any ideas on how I can accomplish this? I just need the chain to extend from the sprites position, to a maximum length, and then afterwards retract. I had no idea it was going to be this difficult. Thank you in advance for all of your help, if you would like me to post my code I would be glad to, but considering I don't think it will work I haven't added it yet. Once again thank you very much in advance I have been racking my brain over this for weeks and it seems like I'm getting nowhere, although I have learnt many invaluable concepts which I am deeply appreciative to the stackoverflow community for.
This is a simple example of how to draw a line that is updated continuously...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
startingPoint = positionInScene;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
// Remove temporary line if it exist
[lineNode removeFromParent];
CGMutablePathRef pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, startingPoint.x, startingPoint.y);
CGPathAddLineToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
lineNode = [SKShapeNode node];
lineNode.path = pathToDraw;
CGPathRelease(pathToDraw);
lineNode.strokeColor = [SKColor whiteColor];
lineNode.lineWidth = 1;
[self addChild:lineNode];
}
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
// Remove temporary line
[lineNode removeFromParent];
CGMutablePathRef pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, startingPoint.x, startingPoint.y);
CGPathAddLineToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
SKShapeNode *finalLineNode = [SKShapeNode node];
finalLineNode.path = pathToDraw;
CGPathRelease(pathToDraw);
finalLineNode.strokeColor = [SKColor redColor];
finalLineNode.lineWidth = 1;
[self addChild:finalLineNode];
}
EDIT: This method detects when a line, defined by points start and end, intersects with one or more physics bodies.
- (void) rotateNodesAlongRayStart:(CGPoint)start end:(CGPoint)end
{
[self.physicsWorld enumerateBodiesAlongRayStart:start end:end
usingBlock:^(SKPhysicsBody *body, CGPoint point,
CGVector normal, BOOL *stop)
{
SKNode *node = body.node;
[node runAction:[SKAction rotateByAngle:M_PI*2 duration:3]];
}];
}
I am trying to make a game in cocos2d wherein I have sprites dropping from the top of the screen. Now, once the sprite is tapped, it should move back up. This works for me just fine but, there are some instances wherein the sprite would go back down after it goes off the screen. Also, my sprites don't disappear once they reach a certain y-axis position on the screen. Here's part of my code
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint loc = [[CCDirector sharedDirector]convertToGL:[touch locationInView:[touch view]]];
CGRect rect = CGRectMake(sprite.position.x - (sprite.contentSize.width/2), sprite.position.y - (sprite.contentSize.height/2), sprite.contentSize.width, sprite.contentSize.height);
if(CGRectContainsPoint(rect, loc))
{
[sprite runAction:[CCCallFuncN actionWithTarget:self selector:#selector(spriteCaught)]];
}}
-(void)spriteCaught
{
//currentPos is an integer to get the current position of the sprite
id moveUp = [CCMoveTo actionWithDuration:1 position:ccp(currentPos, 500)];
[sprite runAction:[CCSequence actions:moveUp, nil]];
if(sprite.position.y >= 480)
{
[self removeChild:sprite cleanup:YES];
}}
Also, I'm not sure if my syntax is correct but my conditional statement (The one that checks the position in the y-axis of the sprite) doesn't work either. How do I go about fixing this? Any help and suggestion is greatly appreciated
Instead of manual rect, use sprite's bounding box.
if(CGRectContainsPoint([sprite boundingBox], loc))
Also update spriteCaught function.
-(void)spriteCaught
{
CGSize s = [[CCDirector sharedDirector] winSize];
float dest_y = s.height+sprite.contentSize.height*0.5f; //assumed ur sprite's anchor y = 0.5
//currentPos is an integer to get the current position of the sprite
id moveUp = [CCMoveTo actionWithDuration:1 position:ccp(sprite.position, dest_y)];
id calBlokc = [CCCallBlockN actionWithBlock:^(CCNode *node)
{
//here node = sprite tht runs this action
[node removeFromParentAndCleanup:YES];
}];
id sequence = [CCSequence actions:moveUp, calBlokc, nil];
[sprite runAction:sequence];
}
How can I have the same sprite in multiple locations dynamically? I have already seen the other question, but, you can only do that with three sprites. I want to have a dynamic number of sprites. My objective is that I am trying to make, instead of shooting only one bullet, I want it to shoot three or more. I have all of the math done, but, I need to draw the three sprites in a for-loop. Here is what I have so far.
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch * touch = [touches anyObject];
CGPoint pointOne = [touch locationInView:[touch view]];
CGSize size = [[CCDirector sharedDirector] winSize];
CGPoint position = turret.position;
CGFloat degrees = angleBetweenLinesInDegrees(position, pointOne);
turret.rotation = degrees;
pointOne.y = size.height-pointOne.y;
CCSprite *projectile = [CCSprite spriteWithFile:#"projectile.png"];
projectile.position = turret.position;
// Determine offset of location to projectile
int angle = angleBetweenLinesInDegrees(position, pointOne);
int startAngle = angle-15;
int shots = 3;
NSMutableArray *projectiles = [[NSMutableArray alloc] initWithCapacity:shots];
// Ok to add now - we've double checked position
for(int i = 0;i<shots;i++) {
[self addChild:projectile z:1];
int angleToShoot = angle;
int x = size.width;
int y = x*tan(angle);
CGPoint realDest = ccp(x,y);
projectile.tag = 2;
if (paused==0 ) {
[_projectiles addObject:projectile];
// Move projectile to actual endpoint
[projectile runAction:
[CCSequence actions:
[CCMoveTo actionWithDuration:1 position:realDest],
[CCCallBlockN actionWithBlock:^(CCNode *node) {
[_projectiles removeObject:node];
[node removeFromParentAndCleanup:YES];
}],
nil]];
}
}
}
This gives me the error: 'NSInternalInconsistencyException', reason: 'child already added. It can't be added again'
you need to create 3 different sprite and add all 3 of them as a child.
usually for doing stuff like this is better to use a CCBatchNode (take a look to the cocos doc).
With a batchnode you get all the childs be drawn in 1 draw call with the only constrain that all the childs of the batchnode needs to have the texture on the same spriteSheet (or in your case if they have the same "filename")
for just 3 projectiles you wont have performance problems but its the correct way to design it, if you will need to have dozens of projectiles on screen without using a batchnode the game wont run smooth.
to recap:
create a ccbatchnode,
add the batchnode as a child of self (i suppose its ur layer or main node)
create 3 sprites and add them as a child of the batchnode
I'm designing an app that involves linear graphs. What I have right now is a line with three dots: one in the center for moving the whole line, and the other two, lower and higher on the line, for rotating it. The problem that I have is figuring out how to rotate the other two dots when the user touches on either of them.
I haven't actually done much with the rotation dots yet, but here's my code so far:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint point = [touch locationInView:graph];
if (CGRectContainsPoint(moveDot.frame, point)) {
moveDotIsTouched = YES;
}
if (CGRectContainsPoint(rotateDot1.frame, point)) {
rotateDot1IsTouched = YES;
}
if (CGRectContainsPoint(rotateDot2.frame, point)) {
rotateDot2IsTouched = YES;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint point = [touch locationInView:graph];
if (moveDotIsTouched) {
if ((point.y > 0) && (point.y < 704))
{
if (point.y < 64) {rotateDot1.hidden = YES;}
else {rotateDot1.hidden = NO;}
if (point.y > 640) {rotateDot2.hidden = YES;}
else {rotateDot2.hidden = NO;}
moveDot.center = CGPointMake(moveDot.center.x, point.y);
rotateDot1.center = CGPointMake(moveDot.center.x+64, moveDot.center.y-64);
rotateDot2.center = CGPointMake(moveDot.center.x-64, moveDot.center.y+64);
graph.base = -(point.y)/32 + 11;
if (graph.base < 0) {[functionField setText:[NSString stringWithFormat:#"y = %.2fx %.2f", graph.slope, graph.base]];}
else if (graph.base > 0) {[functionField setText:[NSString stringWithFormat:#"y = %.2fx + %.2f", graph.slope, graph.base]];}
else {[functionField setText:[NSString stringWithFormat:#"y = %.2fx", graph.slope]];}
[yintField setText:[NSString stringWithFormat:#"%.2f", graph.base]];
[self changeTable];
}
}
[graph setNeedsDisplay];
}
The numbers involving the positioning of the dots are measured in pixels. The only way I can really think of rotating the outside dots is by calculating a radius and making sure the dots are within the radius, but I'm wondering if there isn't something simpler than that. Does anyone know a simple way to move a UIImageView in a circle?
KTOneFingerRotationGestureRecognizer is a custom UIGestureRecognizer for doing one finger rotations in iOS apps. It tracks finger movement around a central point. I don't know if this is exactly what you want, but it may point you in the right direction.
I noticed that by default the sample app GLPaint comes with a recorded data in binary and it loads up on start, it is what is used to display the "Shake me" text.
I'm creating a similar painting app and I was just wondering what is the best way to actually record these strokes and load them up next time.
Currently I tried saving the location of every vertex using Core Data, then upon start it reads and renders all the points again, however I found this too slow.
Is there a better/more efficient method of doing this? Can the entire viewBuffer be saved as binary and then loaded back up?
If you look at the Recording.data you will notice that each line is its own array. To capture the ink and play it back you need to have an array of arrays. For purposes of this demo - declare a mutable array - writRay
#synthesize writRay;
//init in code
writRay = [[NSMutableArray alloc]init];
Capture the ink
// Handles the continuation of a touch.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGRect bounds = [self bounds];
UITouch* touch = [[event touchesForView:self] anyObject];
// Convert touch point from UIView referential to OpenGL one (upside-down flip)
if (firstTouch) {
firstTouch = NO;
previousLocation = [touch previousLocationInView:self];
previousLocation.y = bounds.size.height - previousLocation.y;
/******************* create a new array for this stroke's points **************/
[writRay addObject:[[NSMutableArray alloc]init]];
/***** add 1st point *********/
[[writRay objectAtIndex:[writRay count] -1]addObject:[NSValue valueWithCGPoint:previousLocation]];
} else {
location = [touch locationInView:self];
location.y = bounds.size.height - location.y;
previousLocation = [touch previousLocationInView:self];
previousLocation.y = bounds.size.height - previousLocation.y;
/********* add additional points *********/
[[writRay objectAtIndex:[writRay count] -1]addObject:[NSValue valueWithCGPoint:previousLocation]];
}
// Render the stroke
[self renderLineFromPoint:previousLocation toPoint:location];
}
Playback the ink.
- (void)playRay{
if(writRay != NULL){
for(int l = 0; l < [writRay count]; l++){
//replays my writRay -1 because of location point
for(int p = 0; p < [[writRay objectAtIndex:l]count] -1; p ++){
[self renderLineFromPoint:[[[writRay objectAtIndex:l]objectAtIndex:p]CGPointValue] toPoint:[[[writRay objectAtIndex:l]objectAtIndex:p + 1]CGPointValue]];
}
}
}
}
For best effect shake the screen to clear and call playRay from changeBrushColor in the AppController.