I am currently making a Flappy Bird-copy. Relax, its just for me and the learningpart, im not going to publish it, so dont hate.
The bird is locked at:
self.size.width/3
The pipes are generated like this:
- (void)generatePipes {
for (NSInteger i = 0; i < 3; i++) {
pipeNode = [SKNode node];
[pipeNode setName:#"pipe"];
[pipeNode setPosition:CGPointMake(self.size.width + 100.0 + (200.0 * i), 0.0)];
[self addChild:pipeNode];
**BLABLABLA. Some code**
[pipeTop setPosition:CGPointMake(0.0, arc4random_uniform(250) + 460.0)];
[pipeBottom setPosition:CGPointMake(0.0, pipeTop.position.y - (550.0 + arc4random_uniform(10)))];
[pipeTop setPhysicsBody:[SKPhysicsBody bodyWithRectangleOfSize:pipeTop.size]];
[pipeBottom setPhysicsBody:[SKPhysicsBody bodyWithRectangleOfSize:pipeBottom.size]];
[pipeTop.physicsBody setDynamic:NO];
[pipeBottom.physicsBody setDynamic:NO];
pipeTop.physicsBody.categoryBitMask = blockBitMask;
pipeBottom.physicsBody.categoryBitMask = blockBitMask;
pipeNode.physicsBody.categoryBitMask = blockBitMask;
[pipeNode addChild:pipeTop];
//[pipeTop attachDebugRectWithSize:pipeTop.size];
//[pipeBottom attachDebugRectWithSize:pipeBottom.size];
[pipeNode addChild:pipeBottom];
}
}
THis is the only thing i have made somewhat work, and yes, i am new to game-development. FirstDistance is the distance before the first pipe arrive:
firstDistance += -moveAmount.x;
if(touchBegan > 0 && firstDistance > (self.size.width -(self.size.width/3)- 60)){
distanceSinceLastPipe += -moveAmount.x;
if (distanceSinceLastPipe >= 140.0) {
distanceSinceLastPipe = 0.0;
score += 1;
[_scoreLabel setText:[NSNumberFormatter localizedStringFromNumber:#(score)
numberStyle:NSNumberFormatterDecimalStyle]];
[self runAction:[SKAction playSoundFileNamed:#"pipe.mp3" waitForCompletion:NO]];
}
}
How do i tell the update-method that the pipes are passing the bird most efficent? Count pixels between pipes and reset it? Or is it any way to detect when they pass?
If you know the horizontal position of the bird you could use some "simple" math to calculate how long it will take the pipe to reach the position that means it has passed the bird. Some pseudoCode:
CGFloat totalDistanceForPipeToMove = pipe.position.x - endPosition.x; // endPosition == the final destination for the pipe.
CGFloat relativeSpeed = totalDistanceForPipeToMove / duration; // duration being the SKAction's duration
CGFloat distanceToBird = pipe.position.x - birdPosition.x;
CGFloat timeThePipeReachesTheBird = distanceToBird / relativeSpeed;
Then you can create an SKActionSequence, firing it at the same time as the pipe begins to move:
SKAction *wait = [SKAction waitForDuration: timeThePipeReachesTheBird];
SKAction *addToScore = [SKAction performSelector:#selector(addToScore) onTarget:self]; // performing the addToScore method
SKAction *sequence = [SKAction sequence:#[wait, addToScore]];
Another way to achieve what you are looking for is by having an invincible sprite trailing the birdSprite. Whenever this "scoreSprite" collides with a pipe you know the pipe has passed the bird...
Related
Is it possible to increment a variable's value by passing this variable to some function parameter, so that variable's value changes e.g.
int y=0;
void someFunc(int a);
somefunc(y+50);
NSLog(#"y=#f",y);
prints "y = 50"
You can do it this way
someFunc(y=y+50);
someFunc(y=+50);
but is there more elegant way accomplishing this, using pointers maybe? e.g
someFunc(*y+50);
This is the snippet of my code where I want to accomplish this
-(void)drawRect:(CGRect)rect
{
CGFloat currentX = 0;
CGFloat currentY = height / 2;
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ctx, 5);
CGContextSetStrokeColorWithColor(ctx, [UIColor blackColor].CGColor);
CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextMoveToPoint(ctx,currentX, currentY);
CGContextAddLineToPoint(ctx, currentX=currentX+50, currentY - 200);
CGContextAddLineToPoint(ctx, currentX=+50, currentY - 200);
CGContextAddLineToPoint(ctx, currentX=+50, currentY - 200);
CGContextAddLineToPoint(ctx, currentX=+50, currentY - 200);
CGContextDrawPath(ctx, kCGPathStroke);
}
I want to keep that currentX's value being updated everytime I increment it in the function parameter. Get it?
You can do this by passing a pointer to the variable. This is called an "out parameter" or "call by reference":
void someFunc( int * a, int o )
{
…
*a = *a + o;
}
int y = 0;
someFunc( &y, 50 );
// y is y+o
Either you need to change the value before calling the function, in which case you do:
x = x + something;
func(x);
or if you need to change the value after calling the function, you do like this:
func(x);
x = x + something;
Common sense.
NOTE: never write code like func(x=something) with the assignment inside the parameter list. Because in case you get a habit of doing so, you will soon be writing order-of-evaluation bugs such as func(x, x=something) // BAD, unspecified behavior.
Alternatively, if you need to change the value inside the function, you could pass its address:
void func (int* param)
{
*param = something;
}
...
func(&x);
But you can obviosuly only do this if the function is written by you. This doesn't seem to be the case here, since those functions are apparently part of some Apple library.
So i have this problem where my SKAction dosen't run properly. Sometimes it runs by duration of 0.2 but most of the times it is instant no animation of the node moving to its position.
array_dot[j].runAction(SKAction.moveToY(premakni_do, duration: 0.2))
More code where this is used. This is the function to move the Sphere Nodes to one place down if the place is free underneath them (idk if this code is good but it works kinda :P). Also 152 on Y is the max as low as they can go.
What i do is run through all the nodes that match the names then move them to an array, reverse that array and then move them as low as they can go. But as mentioned before the moving of the Nodes works, but most of the times (90%) the animation doesn't run. Also i am not using any of the physics.
func premakni_pike()
{
array_dot.removeAll()
var preveri_pozicija: CGFloat = 0
self.enumerateChildNodesWithName("//*")
{
node, stop in
if(node.name == "blue" || node.name == "yellow" || node.name == "red" || node.name == "green")
{
self.array_dot.append(node)
}
}
let velikost_array = array_dot.count
array_dot = array_dot.reverse()
var premakni_do: CGFloat = 0
for j in 0...velikost_array-1
{
if(array_dot[j].position.y != 156)
{
premakni_do = array_dot[j].position.y
for i in 0...6
{
preveri_pozicija = array_dot[j].position.y - 60
if(preveri_pozicija < 156)
{
break
}
print(array_dot[j].name,i,preveri_pozicija,array_dot[j].position.y)
let novi_node = self.nodeAtPoint(CGPoint(x: array_dot[j].position.x, y: preveri_pozicija))
if(array_dot[j].intersectsNode(novi_node))
{
array_dot[j].position.y = preveri_pozicija
premakni_do = premakni_do - 60
print("PRAZNO")
}
else
{
break
}
}
array_dot[j].runAction(SKAction.moveToY(premakni_do, duration: 0.2))
}
}
}
Are you sure runAction is not working? It may be that you are setting the nodes position and then telling it to move to that position. Try removing the lines
if(array_dot[j].intersectsNode(novi_node))
{
array_dot[j].position.y = preveri_pozicija
premakni_do = premakni_do - 60
print("PRAZNO")
}
else
{
break
}
I have a function which does some array manipulation on an NSMutableArray. Somehow after a couple of loops of the while function the values inside 2 local variables are total garbage. They are not being assigned or manipulated anywhere. Here's how:
Here's the function:
-(void) normalize_path:(NSMutableArray *)path tool:(Tool)tool
{
NSUInteger last_accepted_point_index = 0;
NSUInteger current_point_index = 1;
while(current_point_index < [path count]){
VPoint * p1, * p2;
[[path objectAtIndex:last_accepted_point_index] getValue:&p1];
[[path objectAtIndex:current_point_index] getValue:&p2];
//float distance = [self distance_between:p1 and:p2];
// if(distance < MIN_POINT_DISTANCE){
// [path removeObjectAtIndex:current_point_index];
// }else{
// float opacity = tool.max_opacity - distance * tool.opacity_sensitivity;
// opacity = opacity <= tool.min_opacity ? tool.min_opacity : opacity;
// p2->opacity = opacity;
// float thickness = tool.max_thickness - distance * tool.thickness_sensitivity;
// thickness = thickness <= tool.min_thickness ? tool.min_thickness : thickness;
// p2->thickness = thickness;
// last_accepted_point_index = current_point_index;
// //current_point_index++;
// }
}
}
And it's called only in one place like so:
//...
[self normalize_path:opath tool:pen];
//...
Every run creates different values. I am confounded! What is going on here?
I think that this may be a memory issue which you can fix by removing the & from in front of p1 and p2 in the getValue: call. It's difficult to be totally sure as you haven't said what VPoint is, but normally this code would look like this:
VPoint p1, p2;
[[path objectAtIndex:last_accepted_point_index] getValue:&p1];
[[path objectAtIndex:current_point_index] getValue:&p2];
This would then set p1 and p2 to the actual values. Your distance between function would then not take references but the actual values of p1 and p2 (if you want to pass references as you are doing now, you'd put & in front of p1 and p2 in the distance callBetween method call.
First, let me clarify what I mean by "grouped models." I'm not actually sure what the standard terminology for this is. In order to reduce the number of rendering calls, I am grouping multiple models into a single model, and rendering the whole thing with a single call to glDrawElements (using VBOs). In my code, I call this a ModelGroup. I use it for various things, but especially for large groups of geometrically simple objects (like buildings in a city, or particles).
The problem has recently surfaced where my ModelGroups are rendering very slowly. I have isolated the slowdown to the actual call to glDrawElements by putting a timer around it. For instance, my particles used to render ~10k particles (without instancing) at around 2ms or so. I can't recall the exact number, but let's just say the rendering was definitely not the bottleneck as it currently is. As of now, a single call to glDrawElements with 10k particles takes right about 256ms. This performance is only marginally better than rendering the objects each with separate calls to glDrawElements. So, there is clearly a massive burden on the GPU for some reason.
What has changed in my engine:
I recently updated XCode and changed from using EAGLView to using GLKViewController. I changed nothing else in my code between these two very different states of performance. I will say that, in order to migrate over to the use of the GLKViewController, I recreated my project entirely and added all of my source files in. Then I rewrote my game loop to be updated by the GLKViewController's update function. This was a very minor change, however.
Just to be completely clear on what my ModelGroup class does, I will post the function that compiles the added models into the display model which is rendered.
-(bool) compileDisplayModelWithNormals:(bool)updateNormals withTexcoords:(bool)updateTexcoords withColor:(bool)updateColor withElements:(bool)updateElements
{
modelCompiled = YES;
bool initd = displayModel->positions;
// set properties
if( !initd )
{
displayModel->primType = GL_UNSIGNED_SHORT;
displayModel->elementType = GL_TRIANGLES;
displayModel->positionType = GL_FLOAT;
displayModel->texcoordType = GL_FLOAT;
displayModel->colorType = GL_FLOAT;
displayModel->normalType = GL_FLOAT;
displayModel->positionSize = 3;
displayModel->normalSize = 3;
displayModel->texcoordSize = 2;
displayModel->colorSize = 4;
// initialize to zero
displayModel->numVertices = 0;
displayModel->numElements = 0;
displayModel->positionArraySize = 0;
displayModel->texcoordArraySize = 0;
displayModel->normalArraySize = 0;
displayModel->elementArraySize = 0;
displayModel->colorArraySize = 0;
// sum the sizes
for( NSObject<RenderedItem> *ri in items )
{
GLModel *model = ri.modelAsset.model.displayModel;
displayModel->numVertices += model->numVertices;
displayModel->numElements += model->numElements;
displayModel->positionArraySize += model->positionArraySize;
displayModel->texcoordArraySize += model->texcoordArraySize;
displayModel->normalArraySize += model->normalArraySize;
displayModel->elementArraySize += model->elementArraySize;
displayModel->colorArraySize += model->colorArraySize;
}
displayModel->positions = (GLfloat *)malloc( displayModel->positionArraySize );
displayModel->texcoords = (GLfloat *)malloc( displayModel->texcoordArraySize );
displayModel->normals = (GLfloat *)malloc( displayModel->normalArraySize );
displayModel->elements = (GLushort *)malloc( displayModel->elementArraySize );
displayModel->colors = (GLfloat *)malloc( displayModel->colorArraySize );
}
// update the data
int vertexOffset = 0;
int elementOffset = 0;
for( int j = 0; j < [items count]; j++ )
{
NSObject<RenderedItem> *ri = (GameItem *)[items objectAtIndex:j];
GLModel *model = ri.modelAsset.model.displayModel;
if( !ri.geometryUpdate )
{
vertexOffset += model->numVertices;
continue;
}
// reset the update flag
ri.geometryUpdate = NO;
// get GameItem transform data
rpVec3 pos = [ri getPosition];
rpMat3 rot = [ri orientation];
int NoV = model->numVertices;
int NoE = model->numElements;
for( int i = 0; i < NoV; i++ )
{
// positions
rpVec3 r = rpVec3( model->positions, model->positionSize * i );
// scale
rpVec3 s = ri.scale;
r.swizzleLocal( s );
// rotate
r = rot * r;
// translate
r.addLocal( pos );
int start = model->positionSize * (vertexOffset + i);
for( int k = 0; k < model->positionSize; k++ )
displayModel->positions[start + k] = r[k];
if( updateTexcoords )
{
// texcoords
start = model->texcoordSize * (vertexOffset + i);
if( model->texcoords )
for( int k = 0; k < model->texcoordSize; k++ )
displayModel->texcoords[start + k] = model->texcoords[model->texcoordSize * i + k];
}
if( updateNormals )
{
// normals (need to be rotated)
if( model->normals )
{
for( int k = 0; k < model->normalSize; k++ )
{
rpVec3 vn = rpVec3( model->normals, model->normalSize * i );
rpVec3 vnRot = rot * vn;
start = model->normalSize * (vertexOffset + i);
displayModel->normals[start + k] = vnRot[k];
}
}
}
if( updateColor )
{
if( model->colors )
{
start = model->colorSize * (vertexOffset + i);
displayModel->colors[start] = ri.color.r;
displayModel->colors[start + 1] = ri.color.g;
displayModel->colors[start + 2] = ri.color.b;
displayModel->colors[start + 3] = ri.color.a;
}
}
}
if( updateElements )
{
for( int i = 0; i < NoE; i++ )
{
// elements
displayModel->elements[elementOffset + i] = model->elements[i] + vertexOffset;
}
}
vertexOffset += NoV;
elementOffset += NoE;
}
return YES;
}
Just to be complete, here is how I render the particles. Inside the particle field draw function:
glBindVertexArray( modelGroup.displayModel->modelID );
glBindTexture( GL_TEXTURE_2D, textureID );
// set shader program
if( changeShader ) glUseProgram( shader.programID );
[modelViewStack push];
mtxMultiply( modelViewProjectionMatrix.m, [projectionStack top].m, [modelViewStack top].m );
glUniformMatrix4fv( shader.modelViewProjectionMatrixID, 1, GL_FALSE, modelViewProjectionMatrix.m );
[DebugTimer check:#"particle main start"];
glDrawElements( GL_TRIANGLES, modelGroup.displayModel->numElements, GL_UNSIGNED_SHORT, 0 );
[DebugTimer check:#"particle main end"];
[modelViewStack pop];
The two statements that sandwich the glDrawElements statement are the timer I used to measure time between events.
Also, I just wanted to add that I have run on both the device and the iPad simulator 6.1 with the same result. The simulator is slower at performing multiple draw calls, but both are equally slow at calling glDrawElements for a ModelGroup. As far as hardware acceleration is concerned, I have checked to make sure that this performance hit isn't coming as some side effect of a lack of acceleration. I rendered a model read in from a file which contained 1024 cubes (similar to a ModelGroup for a city) which rendered with no problem (no 20ms delay as with 1000 cubes in a ModelGroup).
I believe that I have solved the mystery, in a manner of speaking. It is, after all, still a mystery to me why this solves the problem.
I had been using my own custom enum values for these functions
glEnableVertexAttribArray
glVertexAttribPointer
instead of using the newly (as of iOS 5.0, I think) Apple-specified values that came as part of the GLKViewController class:
GLKVertexAttribPosition
GLKVertexAttribNormal
GLKVertexAttribTexcoord0
Having made this change yielded the kind of performance I expected when calling glDrawElements. My model groups will now render on the order of 0.1 ms as they should, rather than ~20 ms. As I said, I really do not understand exactly why this fixes anything, but it's a solution all the same.
I need to create repeatable sequence of actions (CCMoveBy, CCMoveTo), but they need to use random params (position, duration). I wrote 2 methods:
-(void) randomizeVec
and
-(void) calcTiltDuration
In fact, those params depends on external variable (factor). I can't do something like that:
id randomize = [CCCallFuncN actionWithTarget:self selector:#selector(randomizeVec)];
id calcTilt = [CCCallFuncN actionWithTarget:self selector:#selector(calcTiltDuration)];
CCMoveBy* tilt = [CCMoveBy actionWithDuration:mTIltDuration position:randomVec];
CCMoveTo* restore = [CCMoveTo actionWithDuration:mTIltDuration position:initialTowerNodePos];
CCDelayTime* wait = [CCDelayTime actionWithDuration:0.1];
CCSequence* seq = [CCSequence actions:wait,calcTilt,randomize, tilt, restore, nil];
[aNode runAction:[CCRepeatForever actionWithAction:seq]];
because values are copied once and randomizeVec and calcTiltDuration don't affect actions, so I wrote a method:
-(void) moveRandomVector:(CCNode*) node
{
int dx = rand_between(-1, 2) * mShakeFactor *2 ;
int dy = rand_between(-1, 2) * mShakeFactor *2;
CCMoveBy* action = [CCMoveBy actionWithDuration:0.1/mShakeFactor position:CGPointMake(dx, dy)];
CCMoveTo* action2 = [CCMoveTo actionWithDuration:0.1/mShakeFactor position:initialTowerNodePos];
CCEaseInOut* easyTilt = [CCEaseInOut actionWithAction:action rate:0.1];
CCEaseInOut* easyRestore = [CCEaseInOut actionWithAction:action2 rate:0.1];
CCSequence* seq = [CCSequence actions:easyTilt,easyRestore , nil];
[node runAction:seq];
}
which is called via CCCallFuncN:
id action = [CCCallFuncN actionWithTarget:self selector:#selector(moveRandomVector:)];
CCSequence* sq = [CCSequence actionOne:action two:[CCDelayTime actionWithDuration:0.1]];
[towerNode runAction:[CCRepeatForever actionWithAction:sq]];
The factors change (mShakeFactor) in external methods.
Is it proper approach? I'm not Cocos2d expert and I worry about situation when action that runs sequence with CCCallFuncN in it stops but, CCMove* actions and others in moveRandomVector don't. Maybe you know different approach that I could implement?
Finally I moved the code responsible for starting actions to update method. Each time update is called I check if I can run sequence of actions (Boolean variable). If I can I run it and then set the variable to NO, but after last action in sequence I call method, which checks if I can run sequence again. It is comfortable, because it allows me to change variables that affect actions (CCMoveBy, CCMoveTo) and it resembles CCRepeatForever.