I have a viewController with cocos scene which I push in my navigation controller. In this view controller I have this methods:
-(void) viewDidLoad
{
[super viewDidLoad];
[_cc3FrameView addSubview: [self createGLView]];
CC3Backgrounder.sharedBackgrounder.shouldRunTasksOnRequestingThread = YES;
}
- (void) viewWillAppear:(BOOL)animated
{
if (!sceneInitialized) {
sceneInitialized = YES;
[CCDirector.sharedDirector runWithScene: [[self makePanoramaScene] asCCScene]];
} else {
[CCDirector.sharedDirector resume];
}
[CCDirector.sharedDirector startAnimation];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[self runningPanoramaScene] sceneWillShow];
});
}
-(AxPanoramaScene *)runningPanoramaScene
{
CCScene *scene = [CCDirector.sharedDirector runningScene];
AxPanoramaLayer *panoramaLayer = [scene.children lastObject];
AxPanoramaScene *panoramaScene = (AxPanoramaScene *)panoramaLayer.cc3Scene;
return panoramaScene;
}
-(void)viewWillDisappear:(BOOL)animated
{
[CCDirector.sharedDirector pause];
}
While I push this controller - everything is working, but when I pop this controller and push it again - I got the bright pink screen and continuos messages in log:
2014-12-12 19:30:06.447 UniversalMapExample[2262:258353] cocos2d: animation started with frame interval: 60.00
2014-12-12 19:30:06.452 UniversalMapExample[2262:258353] cocos2d: surface size: 768x973
OpenGL error GL_INVALID_OPERATION detected at -[CCES2Renderer resizeFromLayer:] 161
2014-12-12 19:30:06.452 UniversalMapExample[2262:258353] Failed to make complete framebuffer object 0x8219
2014-12-12 19:30:06.453 UniversalMapExample[2262:258353] cocos2d: surface size: 768x973
OpenGL error GL_INVALID_OPERATION detected at -[CCES2Renderer resizeFromLayer:] 161
2014-12-12 19:30:06.453 UniversalMapExample[2262:258353] Failed to make complete framebuffer object 0x8219
OpenGL error GL_INVALID_OPERATION detected at -[CCRenderer(NoARCPrivate) setRenderState:] 232
OpenGL error GL_INVALID_OPERATION detected at -[CCRenderer(NoARCPrivate) setRenderState:] 232
OpenGL error GL_INVALID_OPERATION detected at -[CCRenderer(NoARCPrivate) setRenderState:] 232
OpenGL error GL_INVALID_OPERATION detected at -[CCRenderer(NoARCPrivate) setRenderState:] 232
[***GL ERROR***] GL_INVALID_VALUE: Numeric argument is out of range from glUseProgram(15).
[***GL ERROR***] GL_INVALID_OPERATION: Operation not allowed in current state from glUniform3fv(7, 4, (0.329, 0.944, 0.000)) setting u_cc3LightSpotDirectionModel.
[***GL ERROR***] GL_INVALID_OPERATION: Operation not allowed in current state from glUniformMatrix4fv(12, 1, GL_FALSE,
[0.021050, -0.007339, -0.000000, 0.210503
0.000000, -0.000000, 0.017596, -0.977556
0.005937, 0.017031, 0.000000, -0.660065
0.005926, 0.016997, 0.000000, 1.339256]) setting u_cc3MatrixModelViewProj.
How to correctly push the controller with cocos scene several times? I changed the example from cocos sources to this code. What am I doing wrong here? Please, note - that my controller is not CCDirector - it just contains a view with Cocos scene - the realization is like CC3DemoMultiScene. Thanks!
Remember that the CCDirector is a UIViewController, but it is also a singleton, which give it some unique nuances.
For example, you seem to be invoking the createGLView method every time you want to replace your controller. If it follows the CC3DemoMultiScene design, this will try to recreate another CCGLView before the old one has been released from the CCDirector singleton.
It's generally best to treat the CCDirector and the CCGLView that you create for it as a self-contained reusable unit. As you pop the containing controller, leave everything as is, and simply add and remove the CCGLView from the view hierarchy each time.
...Bill
This is a copy/paste from my blog where I covered a similar issue. The only difference is that I wanted full UIKit integration. I did have the problem where the 2nd time through. Perhaps it will help you.
http://www.notthepainter.com/full-cocos2d-uikit-integration/
I was working on a cocos2d based tapping game and I wasn’t able to run my game twice. It was clear that I wasn’t shutting the 1st game down correctly or building the 2nd game correctly, or both!
There are a lot of tutorials on the web out there teaching you how to add UIKit buttons to your Cocos2D app, or how to launch Cocos2D from your UIKit based app. But I needed to do both. I wanted a UIViewController under my game and UIKit widgets on top of my game. I spent a lot of time reading and this is what I came up with.
First, building the Xcode project was a nightmare. I eventually used the cocos2d/box2d template and then ripped out the files I didn’t needed, and added all my original files back in. The AppDelegate.m file looks just like a non-cocos2d app should look. This goes against the grain of many of the tutorials which advise you to build your cocos2d environment in the AppDelegate. I struggled with that, didn’t have luck for most of a Friday and then on Monday I put in a Cocos2DSingleton and it pretty much ran first time.
Here is my GameViewController’s viewDidLoad method:
- (void)viewDidLoad
{
[super viewDidLoad];
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:NO];
TTCocos2DSingleton *shared = [TTCocos2DSingleton sharedCocos2D];
CCGLView *glView = [shared currentGLView];
[self.view insertSubview:glView atIndex:1];
}
There are a view things to note. GameViewController has game UIButtons, score UILabels, and other game type of UI widgets. This lets me do a lot of the game controls in Interface Builder, not laying them out by hand. Notice I hide the status bar since the game is full-screen.
I get my cocos2d instance via the singleton, get its glView and insert this into the GameViewController’s view at index 1. This puts it below all the game controls. I’ll show you the sharedCocos2D method later, lets look at viewWillAppear.
- (void) viewWillAppear:(BOOL)animated
{
if(![[CCDirector sharedDirector] runningScene]){
CCScene *scene = [MyGameLayer scene];
myGame = [MyGameLayer node];
myGame.delegate = self;
[scene addChild: myGame];
[[CCDirector sharedDirector] runWithScene:scene];
} else {
// we have a scene already, replace the original to get a new game
[[CCDirector sharedDirector] startAnimation];
CCScene *scene = [MyGameLayer scene];
myGame = [MyGameLayer node];
myGame.delegate = self;
[scene addChild: myGame];
[[CCDirector sharedDirector] replaceScene:scene];
}
}
Notice how we treat the first run differently from the second run. For the second, and subsequent runs, we replace the scene with a new one. This avoids all “restarting” problems. Also notice that I set a delegate. I use a delegate protocol to communicate between my game layer and my UIViewController.
My singleton pattern comes from the Duck Rowing blog which I must admit is a pretty awesome name for a blog. I’m not going to show all the singleton code here, this blog is about cocos2d, but here is how I build my cocos2d environment.
+ (TTCocos2DSingleton *) sharedCocos2D;
{
static dispatch_once_t onceQueue;
dispatch_once(&onceQueue, ^{
if (sharedInstance) {
return;
}
sharedInstance = [[TTCocos2DSingleton alloc]init];
// Create an CCGLView with a RGB565 color buffer, and a depth buffer of 0-bits
sharedInstance->glView = [CCGLView viewWithFrame:[[UIScreen mainScreen] bounds]
pixelFormat:kEAGLColorFormatRGB565 //kEAGLColorFormatRGBA8
depthFormat:0 //GL_DEPTH_COMPONENT24_OES
preserveBackbuffer:NO
sharegroup:nil
multiSampling:NO
numberOfSamples:0];
[sharedInstance->glView setMultipleTouchEnabled:YES];
[sharedInstance setupDirector];
});
return sharedInstance;
}
The singleton sets up the CCGLView, enables multi-touch and then sets up the director. (I put that in another method since I thought, erroneously, that I’d need to call it elsewhere. Turns out I didn’t need to.)
- (void)setupDirector
{
CCDirectorIOS *director = (CCDirectorIOS*) [CCDirector sharedDirector];
[director setView:glView];
[director enableRetinaDisplay:YES];
director.wantsFullScreenLayout = YES;
[director setDisplayStats:NO];
[director setAnimationInterval:1.0/60];
}
And in setupDirector we set the usual suspects needed for a cocos2d app. Now the game can be played multiple times, I have a full UIViewController/UINavController underneath it, and I have UIKit widgets on top of my game. Nirvana.
Related
I have cocos2d setup as a sub-project in my iOS app, running the latest release (3.5) and targeting iOS versions 9.0+. The problem I'm having is that the cocos2d screen is black when I download and install the app from TestFairy. I have debugged it both on the simulator and on real device and the cocos2d scene loads properly.
It's a simple finger painting app and it's all based on UIKit. Cocos2d is added to a ViewController in the standard way. The relevant code is:
CCDirector *director = [CCDirector sharedDirector];
// If the director's OpenGL view hasn't been set up yet, then we should create it now. If you would like to prevent this "lazy loading", you should initialize the director and set its view elsewhere in your code.
if ([director isViewLoaded] == NO) {
director.view = [self createDirectorGLView];
[self didInitializeDirector];
}
else {
director.view.frame = [self getCanvasFrame];
[director.view setNeedsLayout];
[director.view layoutIfNeeded];
}
director.delegate = self;
// Add the director as a child view controller.
[self addChildViewController:director];
// Add the director's OpenGL view, and send it to the back of the view hierarchy so we can place UIKit elements on top of it.
[self.targetView addSubview:director.view];
[self.targetView sendSubviewToBack:director.view];
// Ensure we fulfill all of our view controller containment requirements.
[director didMoveToParentViewController:self];
The createDirectorGLView is as follows:
- (CCGLView *)createDirectorGLView {
_glView = [CCGLView viewWithFrame:[self getCanvasFrame]
pixelFormat:kEAGLColorFormatRGB565
depthFormat:0
preserveBackbuffer:NO
sharegroup:nil
multiSampling:NO
numberOfSamples:0];
return _glView;
}
I also have a scene which contains the CCNode that handles the drawing. It is created like this:
#implementation IntroScene
- (id)init:(CGRect)frame {
self = [super init];
if (self) {
_canvas = [[LineDrawer alloc] init];
_canvas.contentSize = CGSizeMake(frame.size.width, frame.size.height);
_canvas.position = CGPointZero;
_canvas.anchorPoint = CGPointZero;
[self addChild:_canvas];
}
return self;
}
#end
I can't get access to the console logs for some reason while the app is running from TestFairy so I can't say what's going on there. When I debug from Xcode however, everything looks normal and the cocos2d canvas is loading/working just fine. I have a feeling it might be something to do with the way the project is archiving but I'm not sure. Any ideas?
Edit
So I managed to reproduce it in Debug mode on a device and I saw a few errors which I think are related to TestFairy's screen recording using OpenGL and cocos2d.
2016-06-06 12:49:56.362 GraffiTab[5654:2941813] *** Assertion failure in -[CCRenderStateGL initWithBlendMode:shader:shaderUniforms:copyUniforms:], /Users/georgichristov/Documents/Workspace/Examples/GraffiTab/GraffiTab-iOS/GraffiTab/ExternalLibs/Cocos2d/cocos2d/CCRendererBasicTypes.m:356
2016-06-06 12:49:56.362 GraffiTab[5654:2941813] [visit] error CCRenderState: Blending mode is nil
2016-06-06 12:49:56.705 GraffiTab[5654:2941813] TestFairy: Initializing SDK version 1.7.6 (20160531-772d18b7)
2016-06-06 12:49:58.057 GraffiTab[5654:2941813] TestFairy: Session started successfully
2016-06-06 12:49:58.929 GraffiTab[5654:2941813] Please remove uses of SCRCException!
I have an OpenGL ios application which I have built on top of the framework used in the tutorials on 71squared.com
All the views, states, controls are state managed within the application, and there is only one UIViewController.
The basic game loop structure is as follows;
GameAppDelegate - this holds the UIView glView, and launches it in applicationDidFinishLaunching as follows;
[glView performSelectorOnMainThread:#selector(mainGameLoop) withObject:nil waitUntilDone:NO];
GLView contains the mainGameLoop method, and is derived from UIView
The main game loop does the following, as well as creating then releasing an auto release pool and managing a frame counter;
while(true)
{
[gameController updateScene];
[self drawView];
}
gameController is a UIViewController derived class.
drawView does the following;
[EAGLContext setCurrentContext:context];
glBindFrameBufferOES(GL_FRAMEBUFFER_OES, viewFrameBuffer);
[gameController renderScene];
glBindRenderBufferOES(GL_RENDERBUFFER_OES, viewRenderBuffer);
[context presentRenderBuffer:GL_RENDERBUFFER_OES];
OKAY... this is all fine.
However, I am trying to display a game centre leaderboard, which just doesnt do anything. I have a feeling I may need to manage the state and do something a little different in the render loop when the leaderboard is presented. The following code is my UIViewController gameController;
GKGameCenterViewController* gameCenterViewController = [[GKGameCenterViewControler alloc] init];
gameCenterController.viewState = GKGameCenterViewControllerStateLeaderboards;
[self presentViewController:gameCenterController animated:NO completion:nil];
The code runs, but doesn't display anything, the game loop just continues. Why not?
EDIT:
Tried triggering the main game loop via both NSTimer and CADisplayLink with no success, presentViewController still does not appear.
Ok, your problem is that main thread is always busy with while(true).
I want suggest you, next time, to use GLKit. It's much simple and template in Xcode has everything what you need for simple application.
In current situation you need create loop for drawing and it has 2 different ways:
1. create CADisplayLink - which is better, here you can grab code
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(mainGameLoop)];
[displayLink setFrameInterval:2];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
2. simple create NSTimer with interval, which is will call mainGameLoop each iteration, but it's not so perfect solution
Here you can find some interesting, Jeff Lamarche has a lot of good tutorials and explanations
https://github.com/jlamarche/iOS-OpenGLES-Stuff
http://iphonedevelopment.blogspot.ie/
I am trying to make my pause screen in my game. I am using the framework Cocos2d V3 RC4 in IOS and XCODE and SpriteBuilder. I read a lot of post and i think that i have two aproachs posible:
1º Push a total scene foward the main scene. (THIS WORK FINE TO ME)
In the MAIN SCENE i call this to pause the game
CCScene *pausa = [CCBReader loadAsScene:#"Pausa"];
[[CCDirector sharedDirector] pushScene:pausa];
and then in the Pause class i call this to pop the pause scene and take back the Main scene:
[[CCDirector sharedDirector] popScene];
2º Take a CNode in front of the MAIN SCENE, making it with transparency, opaque, and disableing the main scene touch, animations, actions, etc… (THIS DOESN’t WORK FOR ME AND I WHANT THIS !!!)
I doit in this way:
In the main Scene:
CCScene *pausa = [CCBReader loadAsScene:#"Pausa"];
[self addChild:pausa];
AND I TRY with ALL THIS METHODS:
// [self unscheduleAllSelectors];
// [self stopAllActions];
// [self setPaused:TRUE];
// [self setUserInteractionEnabled:FALSE];
The node is added but Have not Touch exclusively… The Node that is behind I can touch it…
I try olso with :
[[CCDirector sharedDirector] pushScene:pausa];
(in the main scene) with result obviosly bad, and i try with
[self setExclusiveTouch:TRUE];
in the pause didLoadFromCCB method but also I cant make it have a Exclusive touch. i Can STILLPRESS buttons and sprites from the back Node…
What I am doing Wrong, And how is the correct code/aproach tu use to handle a pause node like I want for method 2??
Resuming... I only want a Modal Window... (like in zk framework, in java, the Window (CNode in Cocos2d) come in front and the background keep disabled and in grey)
Thanks for read and hope someone can help
Here is my implementation from a game that I am doing
- (void) pauseGame
{
CCLOG(#"Pause game");
_contentNode.paused = YES;
_contentNode.userInteractionEnabled = NO;
_gamePausedNode = (GamePausedNode *)[self loadCCBWithNameAndPositionInCenter:#"PausedNode"];
[self addChild:_gamePausedNode];
}
gamePausedGame is a CCNode, but it could be CCSprite as well. It is not actually a CCScene, nor is it loaded by one because a modal view like this is not really a scene.
You usually want to group the CCNode objects together in one CCNode like my _contentNode so you can pause them with one click.
Update : I have edited the code to the bare minimals
CCPhysicsNode *_physics;
_physics.paused = true;// It will pause your game but not actions.
_physics.paused = false;// It will resume your spinning and falling of sprites while button is pressable like during pause game.
I'm trying to learn Cocos2D from Learning Cocos2D, A hands-on guide by Rod Strougo & Ray Wenderlich, but it uses Cocos 1, not 2 which is out now. I imagine the book is still relevant later on, but in the first chapter I'm running into an issue telling the director to run a scene, because it seems like that whole process is now different in Cocos2D 2.
I'd rather not have to buy a different book, so I was hoping that changing the way to run a scene is fairly simple.
This is what the book says to do:
find the -applicationDidFinishLaunching method and comment out:
[[CCDirector sharedDirector]runWithScene: [HelloWorld scene]];
and add:
[[CCDirector sharedDirector]runWithScene:[GameScene node]];
I can't find anything that looks like that in AppDelegate, instead it looks like this has something to do with the new way:
[director_ pushScene: [IntroLayer scene]];
My attempts to adapt what the tutorial says to the new way has so far failed, but maybe it is an easy fix.
Incase it is the GameScene which is outdated:
GameScene.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "BackgroundLayer.h"
#import "GameplayLayer.h"
#import "CCScene.h"
#interface GameScene : CCScene
{
}
#end
GameScene.m
#implementation GameScene
-(id)init
{
self = [super init];
if (self != nil)
{
BackgroundLayer *backgroundLayer = [BackgroundLayer node];
[self addChild:backgroundLayer z:0];
GameplayLayer *gameplayLayer = [GameplayLayer node];
[self addChild:gameplayLayer z:5];
}
return self;
}
#end
The issue you are having is with the class method +scene. That is used when creating the scene within a layer instead of initializing a scene and having that instance create its own child layers. You will understand the differences later in the book when you get more into the scene -> layer relationship.
Comment out [director_ pushScene: [IntroLayer scene]]; in -applicationDidFinishLaunching and replace it with the following:
[director_ pushScene:[GameScene node]];
That should work just fine for your needs. It will create an instance of GameScene (subclass of CCScene) with your two CCLayer subclass instances as children, those being the backgroundLayer and gameplayLayer you instantiate in the GameScene -init method.
If you are curious as to why calling [GameScene scene] was not working for you, that is because you never declared such a method in your interface. It is a little confusing, but basically you would instead create a Game Layer subclass of CCLayer and in your .h file, declare this class method:
+ (CCScene *)scene;
In your implementation .m file, you would define that method as such:
// Class initializer method
+ (CCScene *)scene {
CCScene *scene = [CCScene node]; // Create a container scene instance
GameLayer *gameLayer = [GameLayer node]; // Create an instance of the current layer class
[scene addChild:gameLayer]; // Add new layer to container scene
return scene; // Return ready-made scene and layer in one
}
Then when you call [GameLayer scene] that +scene method creates the scene for that layer and adds it as a child. It is quicker, but can be more troublesome with multiple layers in my experience.
Have you tried this:
[[CCDirector sharedDirector] pushScene:[IntroLayer scene]];
With Cocos2d v2.0.0, a lot of changes were made and a lot of people are experiencing some scaling problems and other conflicts ...
This is even more true if they read and test out Ray Wenderlich's great book : Learning Cocos2d : The viking guy is to big, the background is not centered, the suffix system for images is not working, ...
So, how to proceed ?
Basically, everything is already told in the sample project when creating a new cocos2d v2 project.
But, some of us need to get things pointed out.
Image Suffix System for iPhone (Non-retina & Retina) and iPad (Non-retina & Retina)
Check out lines 68-71 from AppDelegate.m
Here is what you need to remember :
You just need to call
CCSprite *mySprite = [CCSprite spriteWithFile:#"mySprite.png"];
No need for some if(iPad) then else if(iPhone) blabla
Make sure you called your images like as lines 68-71 from AppDelegate.m (Default : mySprite.png (iPhone) | mySprite-hd.png (iPhone Retina) | mySprite-ipad.png (iPad) | mySprite-ipadhd.png (iPad Retina)
Calling a Scene
Check out line 76 from AppDelegate.m
Just call the scene function from your class (using pushScene)
[director_ pushScene: [MyScene scene]];
Adding layers to your scene class
Supposing your scene class is called with the +(CCSene*)scene function (which is a good practice for cocos2d v2)
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
BackgroundLayer *backgroundLayer = [BackgroundLayer node];
[scene addChild:backgroundLayer z:0];
GameplayLayer *gameplayLayer = [GameplayLayer node];
[scene addChild:gameplayLayer z:5];
return scene;
}
Replacing the init function from your classes (CCScene, CCLayer, ...)
This is where scaling problems from v2 of cocos2d come from (but I do not know why).
Instead of calling -(id)init, call
-(void) onEnter{ //Do some sprite displaying with [self addChild:mySprite]; }
You can still call the -(id)init function for other things you need to load before displaying sprites.
Using SneakyInput (Joystick opensource library)
You can find how to use sneakyinput with cocos2d v2 here : http://cl.ly/1J2D2z0f3o0r3h041o3o
Multi Touch Enabling
Add this line to your layer (in the +(CCScene*) scene function or else where)
layer.isTouchEnabled = YES;
Then add this to the same .m
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSArray *touchArray=[touches allObjects];
if ([touchArray count] == 2)
{
NSLog(#"2");
}
else if([touchArray count]==1)
{
NSLog(#"1");
}
}
Finally, go to the AppDelegate.m and add this :
[[CCDirector sharedDirector].view setMultipleTouchEnabled:YES];
I would much appreciate if cocos2d v2 developers could help out and post stuff about how to use cocos2d v2 compared to v1.
cocos2d v2.0 migration guide:
http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:migrate_to_v2.0