Something weird is happening to my 'menu' scene, no other scenes in my app have this issue.
Sprites from files do not display (only for this scene) for some weird reason, I put them in a sprite sheet and added them to the texture cache to fix this.
However, the problems continue and now CCLabelBMFonts get a bad access error when added to the scene (again only for this scene). Error on line:
glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) );
I'm not sure what makes this scene special, perhaps it is how it is loaded (it is the only one loaded at the start):
- (void)loadScenes
{
EAGLContext *k_context = [[[EAGLContext alloc]
initWithAPI:kEAGLRenderingAPIOpenGLES1
sharegroup:[[[[CCDirector sharedDirector] openGLView] context] sharegroup]] autorelease];
[EAGLContext setCurrentContext:k_context];
menuScene = [[MenuScene alloc] init];
}
Any ideas?
Related
To keep it simple, what is the easiest way to make the default [ Menu ] in default HelloWorld Scene (for example) as it's own layer. Issue I'm having now is that the scene is completely black, with nothing showing up!
GameLayer node:
- (id)init
{
// Enable touch handling on scene node
self.userInteractionEnabled = YES;
self.theMap = [CCTiledMap tiledMapWithFile:#"AftermathRpg.tmx"];
self.contentSize = theMap.contentSize;
self.metaLayer = [theMap layerNamed:#"Meta"];
metaLayer.visible = NO;
CCTiledMapObjectGroup *objects = [theMap objectGroupNamed:#"mainChar"];
NSMutableDictionary *startPoint = [objects objectNamed:#"startPosition"];
int x = [[startPoint valueForKey:#"x"] intValue];
int y = [[startPoint valueForKey:#"y"] intValue];
self.mainChar = [CCSprite spriteWithImageNamed:#"mainChar.png"];
mainChar.position = ccp(x,y);
[self addChild:mainChar];
[self addChild:theMap z:-1];
[self setCenterOfScreen: mainChar.position];
return self;
}
HudLayer node
-(id)init
{
CGSize winSize = [[CCDirector sharedDirector] viewSize];
CCButton *backButton = [CCButton buttonWithTitle:#"[ Menu ]" fontName:#"Verdana-Bold" fontSize:18.0f];
backButton.position = ccp(0.85f * winSize.width, 0.95f * winSize.height);
[backButton setTarget:self selector:#selector(onBackClicked:)];
[self addChild:backButton];
return self;
}
Scene
+ (GameScene *)scene
{
return [[self alloc] init];
}
- (id)init
{
// Apple recommend assigning self with supers return value
self = [super init];
if (!self) return(nil);
CGSize winSize = [CCDirector sharedDirector].viewSize;
self.gameLayer = [GameLayer node];
[self addChild:gameLayer z:-1];
//self.contentSize = self.gameLayer.contentSize;
hudLayer = [HudLayer node];
hudLayer.position = ccp(winSize.width * 0.9, winSize.height * 0.9);
[self addChild:hudLayer z:1];
return self;
}
From the OP I take that you have two issues with one being that the HUD is not static (i.e. it is moving as your map moves which you don't want) and that it is not positioning at the top of the screen.
Looking at the position issue first, your position is set to normalized. Since the scene's content size has been made to be the size of your map, which I take is larger than your screen, then this is why it is showing up at the top right of the map and not the screen. To fix this don't do normalized positioning. If you want to be able to still express the position in the 0 to 1 range, use (remove the line that sets the position type to normalized also):
CGSize winSize = [[CCDirector sharedDirector] viewSize];
backButton.position = ccp(0.85f * winSize.width, 0.95f * winSize.height);
If your map is 10,000 x 10,000 then using the normalized positioning like you are will set the button to (8,500, 9,500) rather than the top of the screen.
Looking at the static issue next, from the looks of it you have the Hello World scene that you are adding everything to right? It also looks like you are moving the Hello World scene with a call to:
[self setCenterOfScreen: player.position];
What you want to do instead is this, you first have a scene:
HelloWorldScene* scene;
And to this scene you are adding two "main" layers with one being your gameplay layers as children of a main gameplay layer and the other being your HUD layer, which for example could look something like:
GameplayLayer* gameLayer;
HudLayer* hudLayer;
[scene addChild:gameLayer];
[scene addChild:hudLayer];
When the player moves (or camera or whatever), what should be moving is the game's layer, not the root Hello World scene. Moving the root scene will move all of its children, which includes the hud. That is not what you want.
When I worked on, for example, the Goldfish Mysteries app (https://itunes.apple.com/us/app/finn-friends-mysteries/id740040227?mt=8) I had essentially layers for:
Story (comprised of multiple sub-layers like bg, characters, etc)
Text (highlighted text that plays along with the narration audio)
HUD (comprised of multiple sub-layers)
Whenever there is movement on the story level it occurs on the story layer. When the HUD appears then the text and story layers are paused recursively (i.e. them and all of their children) along with the narration audio if any is playing but the HUD layer remains untouched. Resuming consists of resuming story, text, and any playing narration. I don't remember off the top if dropping the hud moved down the story and text layers in this specific app since I don't have my iPad in front of me, but i have done apps in the past where dropping the hud shifted all other layers. In that situation and for a simple app it would be fine to move the scene since the scene would only be shifted enough to show the TOC (in this type of app for example). What you look to be doing is moving the entire scene with the player's movement, which is not what you really wanted to do by the looks of it.
Either way you want a clear separation between layers and operations that are meant to only happen on specific layers should be directed only towards those layers.
Hope this helped.
UPDATE (Edited):
Based on your new edited OP, you have a new problem that you've introduced. For the hud you shouldn't have to set the layer's position since inside the hud layer everything is already being laid out relative to the screen anyways. So what you should have instead is:
hudLayer = [HudLayer node];
[self addChild:hudLayer z:1];
The second issue is that you are not properly writing your init methods for the game and hud layer classes. The init should look like:
- (instanceType)init
{
self = [super init];
if (self)
{
// Do your init stuff...
}
return self;
}
You are never calling:
self = [super init];
I am using cocos2d on my app. I am doing a transition to another scene using
[[CCDirector sharedDirector] replaceScene:
[CCTransitionFadeDown transitionWithDuration:0.5f scene:otherScene]];
On the init part of this other scene, a menu is being built, using CCMenu. This is a full screen menu.
My problem is this: the transition happens to a black screen and then the menu appears. In other words, the transition is being done before the menu is rendered, so, I see an ugly black screen for 0.5 seconds and then, after the transition is done, I see the menu.
To make things clear imagine the first scene has a picture of a boat and the second scene a picture of a car. What I have now is the boat transitioning to black and then the car pops. I need the boat transition to the car.
how do I do that? thanks.
NOTE: I have found this guy with the same problem, but I have tried that solution without success.
This is worth a try, if you are not already using it; it removes black flickers during scene loads. Run the following method before you switch scenes, in case it affects your issue (un-comment these lines and call the method directly):
- (void) removeStartupFlicker
{
//
// THIS CODE REMOVES THE STARTUP FLICKER
//
// Uncomment the following code if you Application only supports landscape mode
//
// CC_ENABLE_DEFAULT_GL_STATES();
// CCDirector *director = [CCDirector sharedDirector];
// CGSize size = [director winSize];
// CCSprite *sprite = [CCSprite spriteWithFile:#"Default.png"];
// sprite.position = ccp(size.width/2, size.height/2);
// sprite.rotation = -90;
// [sprite visit];
// [[director openGLView] swapBuffers];
// CC_ENABLE_DEFAULT_GL_STATES();
}
I have implemented a transition in my menu (same problem), call it 'fadeInView' by adding a black layer on top of everything in the init, with an opacity of 255, and in onEnter I run an action to fade the opacity to 0. as follows:
-(id) init {
self=[super init];
if(self){
// do your stuff
blackShroudLayer_=[CCLayerColor layerWithColor:ccc4(0, 0, 0, 255) width:K_SCREEN_WIDTH height:K_SCREEN_HEIGHT];
[self addChild:blackShroudLayer_ z:500];
}
return self;
}
-(void) onEnter{
// need to [super onEnter] first to that we are running
[super onEnter];
id sh = [CCFadeTo actionWithDuration:K_FADE_TIME opacity:0];
id seq = [CCSequence actions:sh,[CCCallFunc actionWithTarget:self selector:#selector(onUnshroudComplete)], nil];
[blackShroudLayer_ runAction:seq];
}
-(void) onUnshroudComplete{
[blackShroudLayer_ removeFromParentAndCleanup:YES];
}
the constants and blackShroudLayer_ are defined in the class .h file.
I have this problem with openGL ES 1.1 on iPhone. I have made myself c++ engine that makes all the job in opengl and a view that shows the rendered content. The problem is that sometimes it works ok and sometimes (most of the times) it shows messed up view. By messed up i mean that objects that do not move appear in different locations, rotated, stretched, o ther parts of the scene is okay or invisible, there is no user interacion nor FPS (its just one frame when it breaks up). I thought it may be because my depth buffer is shitty. But i think that the overall buffer engines may be bad. Anyways these are the parts from my code.
I have view, that initializes like this:
self = [super initWithFrame:frame];
if (self) {
CAEAGLLayer* eaglLayer = (CAEAGLLayer*) super.layer;
eaglLayer.opaque = YES;
m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if (!m_context || ![EAGLContext setCurrentContext:m_context]) {
[self release];
return nil;
}
cplusplusEngine = CreateRenderer();
[m_context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:eaglLayer];
cplusplusEngine ->Initialize(CGRectGetWidth(frame), CGRectGetHeight(frame));
//[self drawView: nil];
//m_timestamp = CACurrentMediaTime();
CADisplayLink* displayLink;
displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(drawView:)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[displayLink setFrameInterval:1/45];
[self loadUpTextures];
}
return self;
the draw View method looks like this:
GLint a = cplusplusengine->Render();
[m_context presentRenderbuffer:GL_RENDERBUFFER_OES];
now i create the buffer and present it, i also create buffers in engine like this:
int widthB, heightB;
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,GL_RENDERBUFFER_WIDTH_OES, &widthB);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,GL_RENDERBUFFER_HEIGHT_OES, &heightB); glViewport(0, 0, widthB, heightB);
// Create a depth buffer that has the same size as the color buffer.
glGenRenderbuffersOES(1, &m_depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, widthB, heightB);
// Create the framebuffer object.
GLuint framebuffer;
glGenFramebuffersOES(1, &framebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, framebuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, m_colorRenderbuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES,GL_RENDERBUFFER_OES,m_depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);
And every frame i clear color and depth buffers. Now i do get message from instruments for that draw view's line "present renderbuffer" it says "OpenGL ES performed an unnecessary logical buffer store operation. This is typically caused by not clearing buffers at the start of the rendering loop and not discarding buffers at the end of the rendering loop. If your application clears the depth buffer at the beginning of each frame, it should discard the depth buffer at the end of each frame. Please see the EXT_discard_framebuffer extension for more details." Now i am trying to work my a** off to solve this but i cannot find the solution. I may have few places in textures where this may be happening. It would be helpful to at least find out why opengl may draw messy.
P.S. I do load up textures in that view and set them in engine like this. engineTexture[index] = viewsTextureValueAt[index]; That just sets the GLuint from views texture pointer to engine texture pointer. Can i do that? It works but i don't know whether this is the case. I do get errors even if i comment out all the texture usages though.
I managed to work this out myself. It seems my buffers are all okay. My textures are also good. The error was lying in one simple "common newbie mistake". I used quite few variables to manipulate and align all my scene. It seems that when I was using those variables in objective-c without first defining the values to zero it was okay, the compiler somehow assigned 0 to them, but now, when I use c++ engine, all variables that I did not define now gets random values, this makes my application randomly crash up in different ways. For example my button alignment array was set only for last 4 buttons, first one is in 0 position so I left that number undefined so that's why that button flew off somewhere every time I launched. One time the value got 1700000+ another -0.000056+ and so.
I am doing as follows
-(void)Play {
[CCDirector setDirectorType:kCCDirectorTypeDisplayLink];
CCDirector *director = [CCDirector sharedDirector];
//[director setDeviceOrientation:kCCDeviceOrientationLandscapeLeft];
[director setAnimationInterval:1.0/60];
[director setDisplayFPS:NO];
CGRect rect = CGRectMake(0, 0, 320, 480);
EAGLView *glView = [EAGLView viewWithFrame:rect
pixelFormat:kEAGLColorFormatRGB565 // kEAGLColorFormatRGBA8
depthFormat:0 // GL_DEPTH_COMPONENT16_OES
];
[director setOpenGLView:glView];
[window addSubview:glView];
[window makeKeyAndVisible];
[director setDisplayFPS:YES];
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];
CCScene *scene = [CCScene node];
CCLayer *layer = [PongLayer node];
[scene addChild :layer];
[[CCDirector sharedDirector] runWithScene: scene];
}
After adding layer to scene, I can go and see screen of PongLayer, but now how to destroy its object when you come back on the main screen from where you start game
I did change layers, but previous layer is working on back end, like if I do put some NSLog that I can see that log yet in running conditions, so what to do?
because when I again call play method, then I don't want to refresh all my array and all data for new scene.
Cocos2d objects such as CCLayer and CCSprite are coded to be auto release objects.
You'll see that the 'node' method alloc's inits and autoreleases an object when it's created.
Methods such as 'spriteWithFile:' and 'layerWithColor:' also are coded with autoreleased.
When you add an object as a child to a Layer or any other node for that matter, it creates a pointer to the object, retaining it. When the object is removed as a child, it deallocates itself.
So, when you move from Scene to Scene, the scene that is removed, deallocates it's children (layers) and they in turn their children (sprites).
http://www.cocos2d-iphone.org/wiki/doku.php/start
The only exception is when you create a sprite like this..
CCSprite *mySprite = [CCSprite alloc] init];
You have personally allocated and initialized this sprite, you must release it.
If you have CCLog turned on, it logs out when CCSprites are deallocated.
Another small thing worth mentioning, Scene's won't deallocate themselves if you 'push' the scene, only popping and replacing scene's cause 1 to deallocate.
I have an OpenGL ES application for the iPhone I am developing, being a port of a 2d-oriented application from another platform. I have chosen to render the graphics using OpenGL ES for performance reasons. However, the main application runs on a separate thread (due to the original application design), so from within my app delegate I do this:
- (void) applicationDidFinishLaunching:(UIApplication *)application {
CGRect rect = [[UIScreen mainScreen] bounds];
glView = [[EAGLView alloc] initWithFrame:rect];
[window addSubview:glView];
// launch main application in separate thread
[NSThread detachNewThreadSelector:#selector(applicationMainThread) toTarget:self withObject:nil];
}
However, I notice that any calls within the applicationMainThread that try to render something to the screen do not render anything, until that thread terminates.
I set up the actual OpenGL ES context on the child application thread, not the UI thread. If I do this:
- (void) applicationMainThread {
CGRect rect = [[UIScreen mainScreen] bounds];
[glView createContext]; // creates the open GL ES context
//Initialize OpenGL states
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glMatrixMode(GL_PROJECTION);
glOrthof(0, rect.size.width, 0, rect.size.height, -1, 1);
glMatrixMode(GL_MODELVIEW);
Texture2D *tex = [[Texture2D alloc] initWithImage:[UIImage imageNamed:#"iphone_default.png"]];
glBindTexture(GL_TEXTURE_2D, [tex name]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glDisable(GL_BLEND);
[tex drawInRect:[glView bounds]];
glEnable(GL_BLEND);
[tex release];
[glView drawView];
}
Then the texture is updated to the screen pretty much immediately, as I would expect.
However, if after the [glView drawView] call I add this one line:
[NSThread sleepForTimeInterval:5.0]; // sleep for 5 seconds
Then the screen is only updated after the 5 second delay completes. This leads me to believe that the screen only updates when the thread itself terminates (need to do more testing to confirm). This means that when I substitute the actual application code, which does multiple screen updates, none of the updates actually happen (leaving a white screen) until the application thread exits, not exactly what I wanted!
So - is there any way I can get around this, or have I done something obviously wrong?
You have to be doing something obviously wrong, as multithreaded OpenGL rendering works just fine on iPhone. I can’t tell you what’s wrong with your code, but I can show you how we do it. It took me several iterations to get there, because the sample OpenGL code from Apple mashes everything together.
In the end I came up with three classes: Stage, Framebuffer and GLView. The Stage contains the game rendering logic and knows how to render itself to a framebuffer. The framebuffer class is a wrapper around the OpenGL framebuffer and renders to a renderbuffer or a EAGLDrawable. GLView is the drawable to render the framebuffer to, it contains all the OpenGL setup stuff. In the application entry point I create an OpenGL context, a GLView, a framebuffer that renders to this GLView and a Stage that renders using the framebuffer. The Stage update method runs in a separate thread and looks a bit like this:
- (void) mainLoop
{
[fbuffer bind];
[currentScene visit];
[[EAGLContext currentContext]
presentRenderbuffer:GL_RENDERBUFFER_OES];
[fbuffer unbind];
}
In plain English, it binds the framebuffer, walks the game object graph (= renders the scene), presents the framebuffer contents on the screen and unbinds the framebuffer. The presentRenderbuffer call is a bit misplaced, it belongs somewhere higher in the design – the Stage should just render into framebuffer and let you do whatever you want to do with the framebuffer. But I could not find the right place, so I just left the call there.
Otherwise I am pretty much content with the design: all the classes are simple, coherent and testable. There’s also a Scheduler class that creates the thread and calls Stage’s mainLoop as fast as possible:
- (void) loop
{
[EAGLContext setCurrentContext:context];
while (running)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
#synchronized (context)
{
[stage mainLoop];
}
[fpsCounter update];
[pool release];
}
}
- (void) run
{
NSAssert(!running, #"Scheduler already running.");
running = YES;
[fpsCounter reset];
context = [EAGLContext currentContext];
[NSThread detachNewThreadSelector:#selector(loop)
toTarget:self withObject:nil];
}
The game update thread is synchronized using the OpenGL context so that we can be sure that we don’t corrupt the context in the main thread. (Simple rule: All drawing has to be done in the game update loop or synchronized by the GL context.)
Hope that helps.
Seems that I missed the bleeding obvious...
glViewport(0, 0, rect.size.width, rect.size.height);
glScissor(0, 0, rect.size.width, rect.size.height);
... and the updates appear as they should. I think what happened is without the viewport and scissor set on the child thread context which used a sharegroup (was set on the parent thread context), only when the child thread exited did the view update with the proper viewport, thus finally displaying my updates. Or something like that! (I'm still an OpenGLES newbie!)