Zooming out just a bit cause everything to disappear

Hi

I am trying to implement zoom out of perspective camera project using the mouse wheel.
Created a new default Cocos Hello World project and added a method in HelloWorldScene called
HelloWorld::initZoomListener
{
auto listener = EventListenerMouse::create();

    listener->onMouseScroll = ([](EventMouse* event)
        {
            auto cam = Camera::getDefaultCamera();
            auto z = cam->getPositionZ();

            if (event->getScrollY() > 0)
                cam->setPositionZ(z + 10);
            else if (event->getScrollY() < 0)
                cam->setPositionZ(z - 10);
        });

    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
}

Thats it.
I’m calling initZoomListener in the scene init method.

Now the zoom works, however if you zoom out just a little bit, everything suddendly disappearing.

Anyone knows why ?

Can you post your whole HelloWorld class? I’d like to drop it in a clean project and test.

Hi

Its just the default HelloWorld class, here it is:

 Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
 
 http://www.cocos2d-x.org
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:
 
 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ****************************************************************************/

#include "HelloWorldScene.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{
    return HelloWorld::create();
}

// Print useful error message instead of segfaulting when files are not there.
static void problemLoading(const char* filename)
{
    printf("Error while loading: %s\n", filename);
    printf("Depending on how you compiled you might have to add 'Resources/' in front of filenames in HelloWorldScene.cpp\n");
}

// 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));

    if (closeItem == nullptr ||
        closeItem->getContentSize().width <= 0 ||
        closeItem->getContentSize().height <= 0)
    {
        problemLoading("'CloseNormal.png' and 'CloseSelected.png'");
    }
    else
    {
        float x = origin.x + visibleSize.width - closeItem->getContentSize().width/2;
        float y = origin.y + closeItem->getContentSize().height/2;
        closeItem->setPosition(Vec2(x,y));
    }

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

    /////////////////////////////
    // 3. add your codes below...

    // add a label shows "Hello World"
    // create and initialize a label

    auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);
    if (label == nullptr)
    {
        problemLoading("'fonts/Marker Felt.ttf'");
    }
    else
    {
        // position the label on the center of the screen
        label->setPosition(Vec2(origin.x + visibleSize.width/2,
                                origin.y + visibleSize.height - label->getContentSize().height));

        // add the label as a child to this layer
        this->addChild(label, 1);
    }

    // add "HelloWorld" splash screen"
    auto sprite = Sprite::create("HelloWorld.png");
    if (sprite == nullptr)
    {
        problemLoading("'HelloWorld.png'");
    }
    else
    {
        // position the sprite on the center of the screen
        sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

        // add the sprite as a child to this layer
        this->addChild(sprite, 0);
    }

    initZoomListener();

	
    return true;
}


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

    /*To navigate back to native iOS screen(if present) without quitting the application  ,do not use Director::getInstance()->end() as given above,instead trigger a custom event created in RootViewController.mm as below*/

    //EventCustom customEndEvent("game_scene_close_event");
    //_eventDispatcher->dispatchEvent(&customEndEvent);


}

void HelloWorld::initZoomListener()
{
    auto listener = EventListenerMouse::create();

    listener->onMouseScroll = ([](EventMouse* event)
        {
            auto cam = Camera::getDefaultCamera();

            auto z = cam->getPositionZ();

            log("new z %f", (z + 10));

            if (event->getScrollY() > 0)
            {
                cam->setPositionZ(z + 10);
            }
            else if (event->getScrollY() < 0)
            {
                cam->setPositionZ(z - 10);
            }



        });

    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
}

Anyone ?
I thought this is a pretty basic question…

Check this. How many times its printing? Try to zoom-out 0.1

I’m pretty sure that this is likely due to going beyond the near and far planes (of the camera frustum). I think by default it is 0 and 1000? So with a change of 10 every single frame, essentially (at least while the mouse wheel is still scrolling), it’s going to zoom in/out quickly and reach the two culling planes (Z = 0 and Z = 1000), at which point everything disappears.

You probably just need to limit (e.g. clamp) the zoom (camera Z position) to these values.

Look for something like NearPlane or FarPlane as variable, field, or function.

NOTE: some games choose to manipulate the world itself, for example by scaling the root Node larger/smaller to zoom in/out, instead of using Cocos2d-x’s Camera system.

1 Like

Actually, sorry, I bet the numbers are somewhat different than I’m thinking due to your using a projection [3D] camera, and not the orthographic camera I’m used to using (which 1000 is probably at least close, might be -1000 to 1000 for non-projection orthographic).

I believe my overall point is correct, check on the Near/Far plane and maybe just not what Z-position is when crossing those planes and then either prevent scrolling beyond either, or learn more about the camera system if you have more complex requirements.

For a projection camera the actual near and far plane are probably different and likely based on a y-axis value (height? half-height? not sure).

Oh, and you can probably ignore my comment about other games since that info would be for 2D orthographic games.

1 Like

As mentioned by @stevetranby its definitely a camera clip. Camera will always clip your scene whenever camera distance is beyond far plane or past the near plan, you can tweak these plans but you will reach clip region easily specially in near plan while you zooming in.

Good idea is to set scale on your scene. You can add float _scale member to class and initialize it to 1.0f. Also modify your code initZoomListener logic as follows:

            if (event->getScrollY() > 0)
            {
                _scale *= 1.1f;
            }
            else if (event->getScrollY() < 0)
            {
                _scale *= .9f;
            }
            this->setScale(_scale);

You can tweak constant scale factor as per your need (I took .1f). Just make sure scale factor is always been multiplied, otherwise you would not see uniform scale as you keep scrolling to zoom in or zoom out.

1 Like