I am trying to build up my first game with cocos2d. I am trying to insert bullets. when I get this error. The problem is it only occurs when a player fires not for enemy sprites. When this error occurs no only the position of player swaps with another and back also the bullet is destroyed after hitting two targets.
OpenGL error 0x0503 in -[EAGLView swapBuffers]
my weapon class has the following bullet implementation
if([self.bulletsArray count] <= ([self.numberOfBulletsPerFire intValue]*[self.numberOfBulletsOnScreen intValue]))
{
for (int i =0; i< [self.numberOfBulletsPerFire intValue]; i++) {
BulletClass *bullet = [[Bullet alloc]initWithPosition:position Direction:direction strength:self.weaponLevel spriteArray:spriteArray enemyArray:enemyArray base:base];
[self.bulletsArray addObject:bullet];
[self addChild:bullet];
[bullet release];bullet = nil;
}
}
in BulletClass i have the init method as :
(id)initWithPosition:(CGPoint)position
Direction:(KDirectionInput)direction
strength:(NSNumber *)strength
spriteArray:(NSMutableArray *)sprites
enemyArray:(NSMutableArray *)enemyArray
base:(CCSprite *)base{
if ((self = [super init])) {
self.base = base;
self.strength = strength;
self.movementDirection = direction;
self.spriteArray = sprites;
self.enemyArray = enemyArray;
self.velocity = 200/1;
self.bullet.position = position;
[self addChild:self.bullet z:2];
}
return self;
}
can anyone help me out here..
There are a few issues here that may be contributing to your problems.
First, while not a bug but more of a performance thing, you should not place counts and other object methods like intValue in your conditions, as this slows down the program. Define a local variable just before your loops that equals this number, and then use that variable in the loop so the software doesn't have count the same array or extract the same value over and over again every time it loops.
More importantly, I do not see anywhere that you are using a sprite frame cache (sprite batch), and if you are using sprite arrays that are very large where your array counts are high and manipulating and drawing these sprites many times as is typically requested by bullet operations, you could be adding a lot of burden to OpenGL unnecessarily, perhaps creating the buffer error you saw.
You should use a cocos sprite batch for your sprite arrays and give this another try; at the very least it will vastly improve the program's performance for bullet operations.
Related
Im using SDL to write a simulation that displays quite a big tilemap(around 240*240 tiles). Since im quite new to the SDL library I cant really tell if the pretty slow performance while rendering more than 50,000 tiles is actually normal. Every tile is visible at all times, being around 4*4px big. Currently its iterating every frame through a 2d array and rendering every single tile, which gives me about 40fps, too slow to actually put any game logic behind the system.
I tried to find some alternative systems, like only updating updated tiles but people always commented on how this is a bad practice and that the renderer is supposed to be cleaned every frame and so on.
Here a picture of the map
So I basically wanted to ask if there is any more performant system than rendering every single tile every frame.
Edit: So heres the simple rendering method im using
void World::DirtyBiomeDraw(Graphics *graphics) {
if(_biomeTexture == NULL) {
_biomeTexture = graphics->loadImage("assets/biome_sprites.png");
printf("Biome texture loaded.\n");
}
for(int i = 0; i < globals::WORLD_WIDTH; i++) {
for(int l = 0; l < globals::WORLD_HEIGHT; l++) {
SDL_Rect srect;
srect.h = globals::SPRITE_SIZE;
srect.w = globals::SPRITE_SIZE;
if(sites[l][i].biome > 0) {
srect.y = 0;
srect.x = (globals::SPRITE_SIZE * sites[l][i].biome) - globals::SPRITE_SIZE;
}
else {
srect.y = globals::SPRITE_SIZE;
srect.x = globals::SPRITE_SIZE * fabs(sites[l][i].biome);
}
SDL_Rect drect = {i * globals::SPRITE_SIZE * globals::SPRITE_SCALE, l * globals::SPRITE_SIZE * globals::SPRITE_SCALE,
globals::SPRITE_SIZE * globals::SPRITE_SCALE, globals::SPRITE_SIZE * globals::SPRITE_SCALE};
graphics->blitOnRenderer(_biomeTexture, &srect, &drect);
}
}
}
So in this context every tile is called "site", this is because they're also storing information like moisture, temperature and so on.
Every site got a biome assigned during the generation process, every biome is basically an ID, every land biome has an ID higher than 0 and every water id is 0 or lower.
This allows me to put every biome sprite ordered by ID into the "biome_sprites.png" image. All the land sprites are basically in the first row, while all the water tiles are in the second row. This way I dont have to manually assign a sprite to a biome and the method can do it itself by multiplying the tile size(basically the width) with the biome.
Heres the biome ID table from my SDD/GDD and the actual spritesheet.
The blitOnRenderer method from the graphics class basically just runs a SDL_RenderCopy blitting the texture onto the renderer.
void Graphics::blitOnRenderer(SDL_Texture *texture, SDL_Rect
*sourceRectangle, SDL_Rect *destinationRectangle) {
SDL_RenderCopy(this->_renderer, texture, sourceRectangle, destinationRectangle);
}
In the game loop every frame a RenderClear and RenderPresent gets called.
I really hope I explained it understandably, ask anything you want, im the one asking you guys for help so the least I can do is be cooperative :D
Poke the SDL2 devs for a multi-item version of SDL_RenderCopy() (similar to the existing SDL_RenderDrawLines()/SDL_RenderDrawPoints()/SDL_RenderDrawRects() functions) and/or batched SDL_Renderer backends.
Right now you're trying slam at least 240*240 = 57000 draw-calls down the GPU's throat; you can usually only count on 1000-4000 draw-calls in any given 16 milliseconds.
Alternatively switch to OpenGL & do the batching yourself.
I made a small project in C++ using SFML. I added sprites (a character). I have organized the sprites like Assets/Player/Idle/Idle1.jpg then Idle2.jpg and so on.
I initialize the sprites like this:
texture = new Texture[116];
sprite = new Sprite[116];
texture[0].loadFromFile("Assests/Iori/Ready1.png");
sprite[0].setTexture(texture[0]);
texture[1].loadFromFile("Assests/Iori/Ready2.png");
sprite[1].setTexture(texture[1]);
texture[2].loadFromFile("Assests/Iori/Ready3.png");
sprite[2].setTexture(texture[2]);
texture[3].loadFromFile("Assests/Iori/Ready4.png");
sprite[3].setTexture(texture[3]);
texture[4].loadFromFile("Assests/Iori/Ready5.png");
sprite[4].setTexture(texture[4]);
texture[5].loadFromFile("Assests/Iori/Ready6.png");
sprite[5].setTexture(texture[5]);
texture[6].loadFromFile("Assests/Iori/Ready7.png");
And so on. Now this method works fine and loads my sprites completely. But I don't think this is the efficient or right method. I have to load up 100+ sprites and that would just be a mess for a programmer.
I was wondering can you initialize these sprites via a loop (for/while) that would reduce the code a lot.
Use basic for loop and construct name of the texture dynamicaly.
You might need to use explicit cast to std::string or char* depending on the loadFromFile method.
static const int count = 116;
texture = new Texture[count];
sprite = new Sprite[count];
for (int i = 0; i < count; ++i)
{
texture[i].loadFromFile("Assests/Iori/Ready" + std::to_string(i + 1) + ".png");
sprite[i].setTexture(texture[i]);
}
ok so i've been messing around with chipmunk a bit, and i can get two sprites to bounce off of each other, but when i try to use the following method, it never fires,
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair tower:(CCNode *)nodeA BG:
(CCNode *)nodeB
{
NSLog(#"HELLO");
return YES;
}
Heres where I create the physics node:
_physics = [CCPhysicsNode node];
_physics.debugDraw = YES;
[self addChild:_physics z:1];
_physics.collisionDelegate = self;
I use this code to create the first sprite:
background = [CCSprite spriteWithImageNamed:gameLevelImage];
[background setPosition:ccp(winSize.width/2,winSize.height/2)];
background.physicsBody.collisionType = #"BG";
background.physicsBody = [CCPhysicsBody bodyWithCircleOfRadius:50 andCenter:self.position];
and this for the other :
tower = [[TowerType alloc] initWithTheGame:self location:ccp(winSize.width/2, winSize.height/2)];
[towers addObject:tower];
[self MenuItemsVisible];
tower.physicsBody = [CCPhysicsBody bodyWithCircleOfRadius:50 andCenter:tower.position];
tower.physicsBody.collisionType = #"tower";
I also have the protocol in the h file.
if anyone knows whats happening help would be greatly appreciated. (:
First of all, are both bodies under the same CCPhysicsNode?
Second, ccPhysicsCollisionBegin is just called when the collision BEGIN, so as both of your bodies are one over the other and they aparenttly will move together due to gravity the collision will never begin, because they started colliding. The cycle for collision evaluation is:
ccPhysicsCollisionBegin: called once both bodies start colliding
ccPhysicsCollisionPreSolve: called every frame update, before physics calculations
ccPhysicsCollisionPostSolve : called every frame, after physics calculations
ccPhysicsCollisionSeparates: called once they separate
Make sure your sprites are allocated properly before you try to set the collisionType. That was the issue for me in my similar case.
I'm building an app that has gallery of portraits (like a museum) and my character(s) walk past them from left-to-right-to-left. My portrait (images) are all 2048x2048. I haven't reduced their size yet because I want to make sure they can be used for the iPad version. I know their large size is an issue because it crashes when I try to load all of them at once (and it takes a long time to launch even with only 10 images).
That being said, my real issue is trying to create an efficient method for adding/removing them (sprites) as needed. I came up with something that works but it is clearly not the best way to do this.
I am hoping someone can suggest a more efficient approach.
Here is my code. You can assume another method takes care of loading the images into a mutable array called framedSprites (except I can only load 10 at a time because of the size/crashing). The following method (checkPosition) is called every time the screen position changes (via a TouchMoved swipe). As I see it, I will have to create similar statements for each image/portrait in the array (very inefficient and time consuming)...
-(void)checkPosition {
CGSize winSize = [CCDirector sharedDirector].winSize;
for (CCSprite *sprite in framedSprites) {
if (sprite.tag == 2) {
if ((sprite.position.x > 2000.0f)&&(sprite.position.x < 2010.0f)) {
CCSprite *portrait = (CCSprite *)[_backgroundNode getChildByTag:0];
if (portrait.tag == 0) {
NSLog(#"Removing a Portrait Left 2 Places From This One");
[_backgroundNode removeChildByTag:0 cleanup:YES];
}
}
if ((sprite.position.x > 1980.0f)&&(sprite.position.x < 1990.0f)) {
CCSprite *portrait = (CCSprite *)[_backgroundNode getChildByTag:0];
if (portrait == nil) {
CCSprite * framedSprite = (CCSprite *)[framedSprites objectAtIndex:0];
NSLog(#"Adding a Portrait Left, 2 Places From This One");
framedSprite.position = ccp(600,winSize.height/2); //figuring these positions is also not efficient and time consuming
[_backgroundNode addChild:framedSprite z:0 parallaxRatio:ccp(0.4f,0.5f) positionOffset:framedSprite.position];
}
}
}
}
}
You should only need to have 3 images loaded at any one time to allow scrolling in either direction (assuming you're not also allowing movement in the y direction). The one on display, one either side as a buffer and then lazy load and replace as needed.
If you don't need to see the entire image at once you could also consider cutting it into tiles using a free tool like Tilen (http://itunes.apple.com/gb/app/tilen/id409199185?mt=12) and then loading the tiles as required.
I am having an issue in Kobold2d-Cocos2d regarding the layering of CCSprite objects.
All of my code works fine, except that a certain tile (a CCSprite), held in an array, is supposed to always be on top. The code has an array of references to CCSprites (Tile) and goes along all the tiles swapping them with each other. So element 0 and 1 switch (in the array and on screen using a moveTo), then the new element 1 and 2 switch, then the new element 2 and 3 switch etc...
The idea is the Tile at the start of the chain leapfrogs to the front of the line!
I always want that first tile to stay on top, but it isn't. It only does sometimes (when it's above the other Tile it's switching places with on the screen, implying it was added later)
Here's the CCScene code in question:
//reference first CCSprite in array -works
Tile *firstTile = [tileArray objectAtIndex:(int)[[[selTracker oldSelectionArray] objectAtIndex:0]element]];
//How much time should be spend leapfrogging each two tiles broken up over 1 second.
float intervalTime = .75/(float)[[selTracker oldSelectionArray] count];
//Will use this to count each tile set we leapfrog
float currentPass = 0;
//TODO: destroy this wrapper class that holds number, use NSNumber. Fine for now.
SimpCoord *lastCord;
//Make moving sprite higher than all others. DOESN'T WORK?
[self reorderChild:firstTile z:100];
//Loop through all the tiles that were selected
for (SimpCoord *coord in [selTracker oldSelectionArray])
{
if (lastCord)
{
//Queue each tile to leapfrog with our moving tile
if ([self respondsToSelector:#selector(switchTilesFuncWrapper:)])
{
NSArray *argArray = [NSArray arrayWithObjects:
[NSNumber numberWithInt:[lastCord element]],
[NSNumber numberWithInt:[coord element]],
[NSNumber numberWithFloat:(intervalTime)], nil];
[self performSelector:#selector(switchTilesFuncWrapper:) withObject:argArray afterDelay:((currentPass) * intervalTime)];
}
currentPass++;
}
lastCord = coord;
}
`
That calls the following code by the way that actually swaps the two Tiles (I excluded the function wrapper middleman I needed for multiple arguments):
(In case it makes it easier to understand, rather than use a 2d array to hold all my tiles I just only draw 10 per line, hence the tileCoordsByElement method - but that shouldn't be important)
//
// Switch two tiles on the screen and in the holder array
// onlySwitchArray pass in true to only swap tiles in organization array
// but provide no animation.
- (void) switchTiles:(NSNumber*)elePosVal secPos:(NSNumber*)elePos2Val timeToSwitch:(NSNumber*)time
{
float switchTime = [time floatValue];
int elePos = [elePosVal intValue];
int elePos2 = [elePos2Val intValue];
Tile *tmpTile = [tileArray objectAtIndex:elePos];
Tile *tmpTile2 = [tileArray objectAtIndex:elePos2];
//Move actual tiles on screen
//Move Tile is elePos (1) to elePos2
int curCol = 0;
int curRow = 0;
[self tileCoordsByElement:elePos2 x:&curRow y:&curCol];
CCSequence *tmpTile1_Seq = [CCSequence actions:
[CCMoveTo actionWithDuration:switchTime position:ccp((curRow-1)*tmpTile.width + tmpTile.width/2,
(curCol-1)*tmpTile.height + tmpTile.height/2)], nil];
[tmpTile runAction:[CCRepeat actionWithAction:tmpTile1_Seq times:1]];
//Move Tile that is elePos2 to elePos(1)
[self tileCoordsByElement:elePos x:&curRow y:&curCol];
CCSequence *tmpTile1_Seq2 = [CCSequence actions:
[CCMoveTo actionWithDuration:switchTime position:ccp((curRow-1)*tmpTile.width + tmpTile.width/2,
(curCol-1)*tmpTile.height + tmpTile.height/2)], nil];
[tmpTile2 runAction:[CCRepeat actionWithAction:tmpTile1_Seq2 times:1]];
//Swap tiles in array second
[tileArray replaceObjectAtIndex:elePos withObject:tmpTile2];
[tileArray replaceObjectAtIndex:elePos2 withObject:tmpTile];
}
Alright.
I'm aware some things I'm doing aren't exactly efficient and if it's not relevant I don't really want to focus to heavily on them. This is just a learning exercise for me - I just honestly don't understand why the origional Tile won't stay on top.
I've tried every resource or example I possibly can find for answers (sample included code, tutorials online, terrible book I bought, random somewhat related stackoverflow articles) and I'm getting nothing :\
In case it matters this is how I origionally added my sprites to the scene:
//Set up A CCSpriteBatchNode to load in pList/pngs of images
[[CCSpriteFrameCache sharedSpriteFrameCache]
addSpriteFramesWithFile:#"zAtl_BasicImages_64.plist"];
nodeBasicImages = [CCSpriteBatchNode batchNodeWithFile:#"zAtl_BasicImages_64.png" capacity:100];
//Initialize array used to track tiles in scene
tileArray = [[NSMutableArray alloc] init];
for (int i = 0; i<100; i++)
{
Tile *tmpTile = [[Tile alloc] init];
[tileArray addObject:tmpTile];
[self reorderChild:tmpTile z:-1];
}
Note: I checked and the zOrder on both Tiles is correct on the line before the MoveTo animation begins.
I added my sprites not to my layer, but to an image node because I was using a Sprite Atlas. So I was trying to access sprites that weren't even there. - I'm a new user so going to have to wait 8 hours until I can close. Peace. Why it didn't throw an error I'll never know.