Some questions on why this works, odd implementation? Also, black screen bug

Hello! I have been facing an issue:

Scene* HelloWorld::createScene()
{
// create the scene with physics enabled
Scene* scene = Scene::createWithPhysics();

// set gravity
scene->getPhysicsWorld()->setGravity(Vec2(0, -900));

// optional: set debug draw
scene->getPhysicsWorld()->setDebugDrawMask(0xffff);

auto layer = HelloWorld::create();
scene->addChild(layer);

return scene;
}

When

auto layer = HelloWorld::create();
scene->addChild(layer);

is commented out, then I receive only a black screen with fps information on it. Why does including those two lines of code make it work properly?
And furthermore, if I add a child to my Scene that is just another scene (Since HelloWorld inherits from Scene), aren’t I just adding a scene as a child to another scene?
And when adding objects to the returned scene, are they added to the PhysicsWorldScene or the child Scene?

It is all very confusing. Here is a copy and paste of full file for reference:

#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
USING_NS_CC;

//version 1, works when addchild!!!!!!!!
Scene* HelloWorld::createScene()
{
	// create the scene with physics enabled
	Scene* scene = Scene::createWithPhysics();

	// set gravity
	scene->getPhysicsWorld()->setGravity(Vec2(0, -900));

	// optional: set debug draw
	scene->getPhysicsWorld()->setDebugDrawMask(0xffff);

	auto layer = HelloWorld::create();
	scene->addChild(layer);

	return scene;
}

//version 2 !!!!!!!! This implementation actually makes sense to me. Only one scene instead of two, easy to read code, and it works flawlessly.
/*Scene* HelloWorld::createScene()
{
	HelloWorld* scene = new HelloWorld();
	if (scene && scene->initWithPhysics() && scene->init())
	{
		scene->autorelease();
		// custom code
		scene->getPhysicsWorld()->setGravity({ 0.0f,-900.0f });
		scene->getPhysicsWorld()->setSubsteps(1);

		scene->getPhysicsWorld()->setDebugDrawMask(0xffff);

		// return the scene
		return scene;
	}
	CC_SAFE_DELETE(scene);
	return nullptr;
}*/

void onKeyPressed(EventKeyboard::KeyCode keyCode, Event* event)
{
	log("Key with keycode %d pressed", keyCode);
}
void onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event)
{
	log("Key with keycode %d released", keyCode);
}


// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Scene::init() )
    {
        return false;
    }
    
    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

	

    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    //    you may modify it.

    // add a "close" icon to exit the progress. it's an autorelease object
	
    auto closeItem = MenuItemImage::create(
                                           "CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
    closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
                                origin.y + closeItem->getContentSize().height/2));

    // create menu, it's an autorelease object
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 4);

   //auto block = Sprite::create("14.png");
   
	Sprite* block[10];
	for (int i = 0; i < 10; i++) {
		block[i] = Sprite::create("14.png");
		auto physicsbody = PhysicsBody::createBox(Size(130.0f, 140.00f),
			PhysicsMaterial(0.1f, .9f, 0.0f));
		physicsbody->setDynamic(false);
		block[i]->setPhysicsBody(physicsbody);
	}
	int offset = 0;
	for (int i = 0; i < 10; i++) {
		block[i]->setPosition(Vec2(offset,100));
		offset+=128;
	}
	for (int i = 0; i < 10; i++) {
		addChild(block[i]);
	}
	auto bg = Sprite::create("BG.png");
	auto rkt = Rect(0, 0, 1024, 768);
	bg->setTextureRect(rkt);
	bg->setScale(2);
	addChild(bg, -1);

	auto physicsbody1 = PhysicsBody::createBox(Size(40.0f, 51.0f),
		PhysicsMaterial(0.1f, 1.0f, 0.0f));
	auto mush = Sprite::create("Mushroom_1.png");
	mush->setPosition(Vec2(450,400));
	physicsbody1->setDynamic(true);
	mush->setPhysicsBody(physicsbody1);
	auto moveBy = MoveTo::create(10, Vec2(700, 600));

	addChild(mush);

    return true;
}


void HelloWorld::menuCloseCallback(Ref* pSender)
{
    //Close the cocos2d-x game scene and quit the application
    Director::getInstance()->end();

    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
    
    /*To navigate back to native iOS screen(if present) without quitting the application  ,do not use Director::getInstance()->end() and exit(0) as given above,instead trigger a custom event created in RootViewController.mm as below*/
    
    //EventCustom customEndEvent("game_scene_close_event");
    //_eventDispatcher->dispatchEvent(&customEndEvent);
    
    
}

Thank you very much for your help guys.
Michael

1 Like

It is confusing when you start - especially as the default project has this mixing of scene and layer into a single class.

What you have is HelloWorld, which is a Layer, which has a method that returns a Scene - entirely illogical in my book, but that’s how their example programs work.

The FPS display is handled outside of the scene, hence it still gets displayed.

No. HelloWorld inherits from Layer, not Scene - sure, it’s called helloWorldScene - but that’s just silly naming!

Essentially a Scene contains a bunch of Nodes (layers, usually) which in turn contain other Nodes (sprites, for example)

When the cocos engine runs the scene, it iterates over all of the scene’s children, and draws them.

So if you don’t create a layer and add it to the scene, then there’s nothing to draw

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"
#include <string.h>

class HelloWorld : public cocos2d::Scene
{
public:
    static Scene* createScene();
	std::map<std::string, bool> keys;
    virtual bool init();
    
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    
    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);

	void update(float dt) override;
};

#endif // __HELLOWORLD_SCENE_H__

I think it used to inherit from Layer…l but mine seems to inherit from scene. A part of the 3.0 update I think.

So, assuming I’m adding a scene to a scene, how in the world does the addChild function know which scene to add a node to? It’s all quite confusing :slight_smile:

Thank you for your explanations so far.
Edit: Does adding the child in a different z order impact which scene the child is added to?

Edit: Another question - why not just scrap scene all together and use a node as a base? What’s so special about the scene class?

interesting! I just created a new project with version 3.13 and HelloWorld inherits from Layer.

So i have no idea what has changed between 3.13 and whatever version you are using!!

No the z order won’t impact anything other than the order the children are drawn in.

Scene is a ‘virtual’ placeholder I think, the idea being that you can push and pop scenes from a stack to change between them - so it’s just the non graphical parent of all the layers (I think Layer is deprecated, too, actually, as it is really just a Node)

Because I am curious, I am downloading the latest release of cocos2d-x so I will take a look at Helloworld and see if I can make sense of what has changed!

Strange - they have changed the basic project to have helloworldscene as a scene - which is good for the naming but still not as sensible as I feel it could be!

A Scene

Scene is a subclass of Node that is used only as an abstract concept.

Scene and Node are almost identical with the difference that Scene has its
anchor point (by default) at the center of the screen.

For the moment Scene has no other logic than that, but in future releases it might have
additional logic.

It is a good practice to use a Scene as the parent of all your nodes.

Scene will create a default camera for you.

Whereas a layer

Layer is a subclass of Node that implements the TouchEventsDelegate protocol.

All features from Node are valid, plus the following new features:

  • It can receive iPhone Touches
  • It can receive Accelerometer input

Personally I refactor somewhat, have a GameController class which is instantiated from AppDelegate and constructs my scenes and adds layers to them - I think the HelloWorldScene was confusing before (because it was called Scene but was a Layer) and now is confusing because it adds sprites etc. directly to itself - which is fine (it’s just a node after all) but inconsistent with how one would normally write a cocos program!