Optimizing Graphics(AutoBatch)

Optimizing Graphics(AutoBatch)
0

#41

That works. For the app I’m working on I handle it in that way too, and they are saved out at a smaller scale, so they get scaled to 100% at runtime.


#42


Just found this also according to this since 2.3


#43

What does it mean that they are saved to a smaller scale and get scaled at %100 at runtime, this doesn’t happen using the normal file not cut in half ?


#44

Well, what we noticed is that the background still looks great if we scale it down to 70% of original size, and save it out as such.

Once you load the background image (or image slices), you just scale them up to 100% size.

For example, say your background is 2200x1000:

2200x1000 * 0.70 = 1540x700 sized PNG.

When you load it, you scale it to 100% as follows:

1/0.7 * 1540x700 = 2200x1000 on screen

just use backgroundSprite->setScale(1 / 0.7f);

This way your texture can stay below the 2048 texture limit.


#45

Ok so you guys just scaled it down then, it would still be fine if I just used 2 1100x960 pngs correct ? Or do you recommend the scaling option instead ?


#46

Either works, and you can even use both slicing and scaling together.


#47

When you use a sprite sheet do you have to also unload it ? Like calling the removeUnusedSprite or if memory isn’t a issue will we be fine just following the docs and loading the sprite sheet ?


#48

Not necessarily, since Cocos2d-x handles that aspect, and will remove unused textures if you start running out of memory, if I remember correctly. How I personally handle it is that I have a separate resource manager class that tracks the loading (or preloading) of sprite sheets and other resources, and also handles unloading when required, since I wanted more control over the total memory usage at any given time.


#49

Ah ok i see does anyone know if it’ll be worth the extra work for my game if I only have 15 OpenGL calls, for my level 1 and like 50 for my level 3, will creating a sprite sheet reduce the OpenGL calls to 1 since they’ll all be on the sprite sheet? I’m trying to optimize as much as possible because at random times in my game it will lag for a few seconds


#50

It doesn’t hurt to create sprite sheets, but you shouldn’t automatically assume the lag is caused by the draw calls, because it can be many different things that may cause that issue.

Do some kind of performance profiling on your running app. Even Visual Studio 2017 has a great profiler built in to show you the execution time of all different parts of your application, and you can drill down to get to specific functions that take the most time. Using such tools will help you quickly track down the source of your game lag.


#51

That’s true I wonder if xcode has something similar, since I don’t have windows or visual studio, im starting to think it might be from updating 24 sprites y position in my update loop does any one think this is to much ? the lag usually happens at random moments or especially when I get notifications from other other apps on my iphone, this is my first game i ever made so im not sure I did things the best way possible I feel like there might be a more efficient way does any one see of anyways I could improve this update loop to make it do less word and still accomplish the same thing ?

void LevelOne::update(float dt)
{
    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    
    auto ballPosition = ball->getPosition();
    
    if(start == 1) {
    
    if(moveLeft == true){
        
        ball->setPositionX(ball->getPositionX() - 9);
        
        if(ballPosition.x <= origin.x + 15){
            ball->setPositionX(origin.x + 15);
        }

    }else if(moveRight == true)
    {
        ball->setPositionX(ball->getPositionX() + 9);
        
        if(ballPosition.x >= visibleSize.width - 15){
            ball->setPositionX(visibleSize.width - 15);
        }
        
    }
        
    
    auto position = red1left->getPosition();
    auto position2 = green2left->getPosition();
    auto position3 = yellow3left->getPosition();
    auto position4 = red4left->getPosition();
    auto position5 = green5left->getPosition();
    auto position6 = yellow6left->getPosition();
    auto position7 = red7left->getPosition();
    auto position8 = green8left->getPosition();
    auto position9 = yellow9left->getPosition();
    auto position10 = red10left->getPosition();
    auto position11 = green11left->getPosition();
    auto position12 = yellow12left->getPosition();
        
    position.y += 4.8;
    position2.y += 4.8;
    position3.y += 4.8;
    position4.y += 4.8;
    position5.y += 4.8;
    position6.y += 4.8;
    position7.y += 4.8;
    position8.y += 4.8;
    position9.y += 4.8;
    position10.y += 4.8;
    position11.y += 4.8;
    position12.y += 4.8;
    
    
    if(position.y > visibleSize.height + 60) {
        
        int random = arc4random_uniform(45);
        
        position.y = position12.y - 216;
        position.x = random + 201;
        red1right->setPositionX(position.x + 708);
        scoreOne = false;
        
    }
        
        if(ballPosition.y < position.y && scoreOne == false){
            SimpleAudioEngine::getInstance()->playEffect("skore.wav");
            score++;
            __String *tempScore = __String::createWithFormat("%i", score );
            scoreLabel->setString(tempScore->getCString());
            ball->setTexture("greenball.png");
            scoreOne = true;
        }
    
    
    if(position2.y > visibleSize.height + 60){
        position2.y = position.y - 216;
        scoreTwo = false;
    }
        
        if(ballPosition.y < position2.y && scoreTwo == false){
            SimpleAudioEngine::getInstance()->playEffect("skore.wav");
            score++;
            __String *tempScore = __String::createWithFormat("%i", score );
            scoreLabel->setString(tempScore->getCString());
            ball->setTexture("yellowball.png");
            scoreTwo = true;
        }
    
    if(position3.y > visibleSize.height + 60){
        
        int random = arc4random_uniform(45);
        
        position3.y = position2.y - 216;
        position3.x = random + 201;
        yellow3right->setPositionX(position3.x + 708);
        scoreThree = false;
    }
        
        if(ballPosition.y < position3.y && scoreThree == false){
            SimpleAudioEngine::getInstance()->playEffect("skore.wav");
            score++;
            __String *tempScore = __String::createWithFormat("%i", score );
            scoreLabel->setString(tempScore->getCString());
            ball->setTexture("redball.png");
            scoreThree = true;
        }
    
    if(position4.y > visibleSize.height + 60){
        position4.y = position3.y - 216;
        scoreFour = false;
    }
    
        if(ballPosition.y < position4.y && scoreFour == false){
            SimpleAudioEngine::getInstance()->playEffect("skore.wav");
            score++;
            __String *tempScore = __String::createWithFormat("%i", score );
            scoreLabel->setString(tempScore->getCString());
            ball->setTexture("greenball.png");
            scoreFour = true;
        }
        
    if(position5.y > visibleSize.height + 60){
        
        int random = arc4random_uniform(45);
        
        position5.y = position4.y - 216;
        position5.x = random + 201;
        green5right->setPositionX(position5.x + 708);
        
        scoreFive = false;
    }
        
        if(ballPosition.y < position5.y && scoreFive == false){
            SimpleAudioEngine::getInstance()->playEffect("skore.wav");
            score++;
            __String *tempScore = __String::createWithFormat("%i", score );
            scoreLabel->setString(tempScore->getCString());
            ball->setTexture("yellowball.png");
            scoreFive = true;
        }
    
    if(position6.y > visibleSize.height + 60){
        position6.y = position5.y - 216;
        scoreSix = false;
    }
        
        if(ballPosition.y < position6.y && scoreSix == false){
            SimpleAudioEngine::getInstance()->playEffect("skore.wav");
            score++;
            __String *tempScore = __String::createWithFormat("%i", score );
            scoreLabel->setString(tempScore->getCString());
            ball->setTexture("redball.png");
            scoreSix = true;
        }
    
    if(position7.y > visibleSize.height + 60){
        
        int random = arc4random_uniform(45);
        
        position7.y = position6.y - 216;
        position7.x = random + 201;
        red7right->setPositionX(position7.x + 708);
        
        scoreSeven = false;
    }
        
        if(ballPosition.y < position7.y && scoreSeven == false){
            SimpleAudioEngine::getInstance()->playEffect("skore.wav");
            score++;
            __String *tempScore = __String::createWithFormat("%i", score );
            scoreLabel->setString(tempScore->getCString());
            ball->setTexture("greenball.png");
            scoreSeven = true;
        }
    
    if(position8.y > visibleSize.height + 60){
        position8.y = position7.y - 216;
        
        scoreEight = false;
    }
        
        if(ballPosition.y < position8.y && scoreEight == false){
            SimpleAudioEngine::getInstance()->playEffect("skore.wav");
            score++;
            __String *tempScore = __String::createWithFormat("%i", score );
            scoreLabel->setString(tempScore->getCString());
            ball->setTexture("yellowball.png");
            scoreEight = true;
        }
        
    if(position9.y > visibleSize.height + 60){
        
        int random = arc4random_uniform(45);
        
        position9.y = position8.y - 216;
        position9.x = random + 201;
        yellow9right->setPositionX(position9.x + 708);

        scoreNine = false;
    }
        
        if(ballPosition.y < position9.y && scoreNine == false){
            SimpleAudioEngine::getInstance()->playEffect("skore.wav");
            score++;
            __String *tempScore = __String::createWithFormat("%i", score );
            scoreLabel->setString(tempScore->getCString());
            ball->setTexture("redball.png");
            scoreNine = true;
        }
    
    if(position10.y > visibleSize.height + 60){
        position10.y = position9.y - 216;
        scoreTen = false;
    }
        
        if(ballPosition.y < position10.y && scoreTen == false){
            SimpleAudioEngine::getInstance()->playEffect("skore.wav");
            score++;
            __String *tempScore = __String::createWithFormat("%i", score );
            scoreLabel->setString(tempScore->getCString());
            ball->setTexture("greenball.png");
            scoreTen = true;
        }
    
    if(position11.y > visibleSize.height + 60){
        
        int random = arc4random_uniform(45);
        
        position11.y = position10.y - 216;
        position11.x = random + 201;
        green11right->setPositionX(position11.x + 708);
        scoreEleven = false;
    }
        
        if(ballPosition.y < position11.y && scoreEleven == false){
            SimpleAudioEngine::getInstance()->playEffect("skore.wav");
            score++;
            __String *tempScore = __String::createWithFormat("%i", score );
            scoreLabel->setString(tempScore->getCString());
            ball->setTexture("yellowball.png");
            scoreEleven = true;
        }
    
    if(position12.y > visibleSize.height + 60){
        position12.y = position11.y - 216;
        scoreTwelve = false;
    }
        
        if(ballPosition.y < position12.y && scoreTwelve == false){
            SimpleAudioEngine::getInstance()->playEffect("skore.wav");
            score++;
            __String *tempScore = __String::createWithFormat("%i", score );
            scoreLabel->setString(tempScore->getCString());
            ball->setTexture("redball.png");
            scoreTwelve = true;
        }
    
    
    red1left->setPosition(position);
    red1right->setPositionY(position.y);
    
    green2left->setPosition(position2);
    green2right->setPositionY(position2.y);

    yellow3left->setPosition(position3);
    yellow3right->setPositionY(position3.y);
    
    red4left->setPosition(position4);
    red4right->setPositionY(position4.y);
    
    green5left->setPosition(position5);
    green5right->setPositionY(position5.y);

    yellow6left->setPosition(position6);
    yellow6right->setPositionY(position6.y);
    
    red7left->setPosition(position7);
    red7right->setPositionY(position7.y);
    
    green8left->setPosition(position8);
    green8right->setPositionY(position8.y);
    
    yellow9left->setPosition(position9);
    yellow9right->setPositionY(position9.y);
    
    red10left->setPosition(position10);
    red10right->setPositionY(position10.y);
    
    green11left->setPosition(position11);
    green11right->setPositionY(position11.y);
    
    yellow12left->setPosition(position12);
    yellow12right->setPositionY(position12.y);
        
    }

#52

When I first start coding long long looong ago, I did exactly what you did, put everything in one function. Eventually, with experience, you realise that’s not the best or most efficient way to implement anything.

Cocos2d-x has an event based system that you seem to be partially using, wherever you set the moveLeft, start etc., which you then check for every single loop in your update method (can be thought of as “polling”).

Why don’t you take advantage of the event based system, so that whenever you receive input, act on it then and there, and only do as much as you need to. What I mean by that is avoid doing too much in the event handlers as well, otherwise you’ll end up facing the same issues.

You have a lot of duplication in your code, which you could split off into separate functions, just to make it more manageable. For instance, you’re using __String::createWithFormat("%i", score), then calling getCString() on it to pass to setString(), which accepts std::string. Why covert from __String (internally std::string) to c-string then when there is absolutely no need to do so, and incurring the cost of associated code paths.

Try this:

    auto scoreString = cocos2d::StringUtils::format("%i", score);
    scoreLabel->setString(scoreString);

That eliminates the creation of the __String object, and removes the need to covert between types so many times.

Even better, move it into it’s own method:

void LevelOne::UpdateScore(int score)
{
    auto scoreString = cocos2d::StringUtils::format("%i", score);
    scoreLabel->setString(scoreString);
}

There’s plenty you can change, but that’s up to you to figure out, and it can only come from learning and experience. Best to just look at source code from other projects, and look up information about code refactoring (many books out there). If you can get your hands on a copy of a book titled “Code Complete 2”, then that may help you too.


#53

Ok thanks a lot for the advice !! I will have to learn more about this on my own to see what I can improve, the reason I didn’t split the string code into its own method was I thought maybe it would be faster for the code to have it right there instead of calling a method for it but maybe that’s not the case I’m not sure ? Also is it bad if my game is using %100 cpu ?


#54

That has a name… “premature optimization”… At most you would have saved a few CPU cycles, which is absolutely not worth it, and in addition to that, you actually made your code significantly harder to maintain.



etc etc…plenty more info about it online… I’m sure you’ll understand when you read about it.

Yes! There should be no reason at all for your game to be using 100% CPU. I’m not saying that no game can ever use 100% CPU, and I’m just focusing on your specific game at the moment, and there is no way it would need all that CPU power.

Since you work on OS X, here’s info on how to use the profiler in XCode (you know you could have just done a search for “OS X C++ Profiler” and found this info yourself):


#55

Ok thanks I need to figure out why my game says it using %100 cpu on Xcode , I feel like it has to do with all the code in the update method , because that’s where most of my code is, in going to need to find more ways to improve the code in my update method. I knew it didn’t look right when it said %100 cpu usage because I’m only moving some images from the bottom of the screen to the top and repeating that. Going to try to do some profiling on Xcode later. By the way thanks for all the useful links I really appreciate you going out of your way to provide me that information.


#56

I’m not sure if this will help, but have you tried using actions? Like MoveTo, MoveBy etc? That would remove the need of manually adjusting the Y position in the update method.


#57

I did try when I first started developing the game but I couldn’t figure out how I would make it work on different devices (since heights vary) I wonder if it somehow could be possible to move from previous sprite.y position - X amount to the top of the screen and then replace it back on the bottom of the screen and keep repeating that, does anyone know would this go in a method or in the init method ? I think I might give it another shot maybe it’ll lower the cpu usage


#58

If you’re using FIXED_WIDTH for some devices, where the height varies, then try this:

Say the object you want to move is named obj:

auto visibleSize = Director::getInstance()->getVisibleSize();
auto moveTo = MoveTo::create(duration, Point(obj->getPositionX(), visibleSize.height));
obj->runAction(moveTo);

Now, if you want your duration to be proportional to the initial location of the object, then:

auto duration = totalDuration * (obj->getPositionY() / visibleSize.height);

Which means it takes totalDuration to do the entire height if the screen, but say your object starts half way up the screen, but you don’t want it to take totalDuration to get from half way to top, so you get the percentage of how far you’re up the screen, and multiply it by the totalDuration.

I’m not sure what mechanisms exist in Cocos2d-x to detect when objects reach a specific point (the physics engine has that functionality, but I assume you’re not using it), so what you can do is check the location of the object in the update() method. If it has gone past the top of the screen, you can just call:

obj->setPositionY(0 - obj->getContentSize().height);
auto moveTo = MoveTo::create(duration, Point(obj->getPositionX(), visibleSize.height);

to get back down just below the bottom of the screen, so it appears from the bottom smoothly.

You can even use a sequence, so:

obj->setPositionY(0 - obj->getContentSize().height);
auto moveTo = MoveTo::create(duration, Point(obj->getPositionX(), visibleSize.height);

etc. etc.

I’m just guessing what you would need, but all I can suggest is that you go through the cocos2d-x cpp-test demo app and see all the different actions available to you, and who knows, you may find what you’re looking for in there.


#59

Thanks for all the examples, I will be experimenting with this today and profiling and hopefully I’m successful with it. I think I will be after looking through some of the cocos2dx-x docs and some books I own, thanks again @R101


#60

I was wrong on the CPU usage it is actaully around 30% but when the app goes to the background then comes back to the foreground it jumps to %100 but then slowly goes back down