📜 ⬆️ ⬇️

Cocos2D-X and so easy on all devices

For several years I have been doing custom iOS games. In conditions where there is no time to sharpen, but you need to cut, go to Google and ask. Googleclocker That fate brought me to Cocos2D for iPhone and warm tube www.raywenderlich.com

I liked Objective-C, like cocos2D itself. Soft as plasticine. After decent years of writing on C ++, everything was somehow simplified. Alas, only iOS. Certainly, there were any Apportable , but for some reason I did not want to look in that direction. In addition, we felt tired of the same platform and wanted our own project, with it being played on every microwave. Unity seems to be good, but it is closed, and for me it is very important to know how it works from the inside: to evaluate potential bottlenecks, to optimize something (I often had to practice), and even just fix bugs. Plus, I wanted to start doing something right now. And since I was very familiar with the iPhone’s coconut model, it was decided to look at cocos2D-X . The one that is in C ++.

Same. Just in C ++. The same release / retain (in the latest 3.x version, some moments have already changed), the same model from the nodes. Chased the test bench (always, always see examples of the engine operation) - everything works well. I remembered, however, about one thing - about Android with its many screen resolutions.

When I made games for iPhones, everything was simple. If support for the retina was needed, then it was enough to set only one flag:
')
[director enableRetinaDisplay: YES] 

and add a set of HD graphics. Everything was a little different for iPads: some recalculation of coordinates was required. With the advent of the fifth iPhones, things got a little more complicated, but a little. However, when you are Android and you have almost an unlimited number of any screens, you need to turn on the brain a little. That's what we did.

We have added configs. All the magic of your project starts to happen in the AppDelegate class generated by the Coco script. In one of its methods, the scene is started and activated:

 bool AppDelegate::applicationDidFinishLaunching() { Director *director = Director::getInstance(); EGLView *eglView = EGLView::getInstance(); director->setOpenGLView(eglView); ... Scene *scene = GameScene::scene(); director->runWithScene(scene); return true; } 

In the scene, you load your sprites / other nodes, somehow position them, twist if necessary, etc. The mechanism for creating a sprite, for example, is closely related to the FileUtils class. Whenever you create something like:

 Sprite *object = Sprite::create("object.png") 

the internal methods (it’s good to have the code at hand) of the Sprite class ask FileUtils to run through the list of installed search paths and see if there is such a file there. Search paths? Yes, it's simple. You can create sprites like this:

 Sprite *enemy = Sprite::create("enemies/enemy0.png") 

or you can tell coconut that we have some directory or even a list in which to look for files. This reduces the scribbling, respectively, reduces the number of errors and gives flexibility. I wanted to group files in a different way - please, in the code you will not have to change all "enemies /" to, for example, "objects /". Really convenient and easy:

 FileUtils::getInstance()->addSearchPath("fonts"); FileUtils::getInstance()->addSearchPath("objects"); FileUtils::getInstance()->addSearchPath("backgrounds"); ... Sprite *back = Sprite::create("back0.png"); LabelBMFont *label = LabelBMFont::create("an incredible label", "font0.fnt"); 

Something I, apparently, spoke about any resolutions of screens. In cocos2D-X is no longer all

 [director enableRetinaDisplay: YES] 

instead, a couple of new things appeared. Want to know the physical size of the screen? Do it in this way:

 Size frameSize = Director::getInstance()->getOpenGLView()->getFrameSize(); 

I also said something about the config. This is a simple json file (rapidson parsim ) in which we have written resource path sets (oh, this accusative case). Each set looks like this:

 { "width": 960, "height": 640, "designWidth": 480, "designHeight": 320, "paths": [ "Res/960x640/fonts/", "Res/960x640/ui/", "Res/960x640/maps/" ... ] } 

Here, width and height represent the physical dimensions for which this set of resources might be suitable. Already catch, what am I getting at? When the application starts in AppDelegate :: applicationDidFinishLaunching, I load the config, run through all the path sets and check the physical size obtained from Director :: getInstance () -> getOpenGLView () -> getFrameSize () , with those width and height and load the required sizes Pictures. This, however, is not enough.

For a deeper understanding go here:
www.cocos2d-x.org/wiki/Multi_resolution_support

For practical use, it is enough to know the following. There is Director :: getInstance () -> getOpenGLView () -> setDesignResolutionSize () , which sets the relative sizes. Your iPhone, for example, has a size of 960x640 and the center of the screen can be represented by the coordinate {480, 320}. However, you can transfer some {96, 64} to setDesignResolutionSize , and then the center can be set using {48, 32}. Relative coordinates are very important, and those two parameters designWidth and designHeight from the config and expose them. So, we run around the configuration, check the sizes, load the necessary resources and set the correct relative coordinates. We are almost at the finish line.

Imagine that you run the game on a device with a huge screen resolution, but there is no set of resources to direct pixel perfec t. It's okay - we'll just tell the coconut to stretch the picture. For this, there is a Director :: getInstance () -> setContentScaleFactor () that accepts some float. Just share (in case the game is portrait) the width of the config on designWidth and you will be happy on all platforms.

There is one more parameter, the last one is ResolutionPolicy . To be honest, there are two useful things there: ResolutionPolicy :: FIXED_WIDTH and ResolutionPolicy :: FIXED_HEIGHT . If you want to play in portrait orientation - set ResolutionPolicy :: FIXED_WIDTH . At the same time, the picture will stretch in width, for example, as in my game solve Me :



With this approach, additional vertical space may appear. In the example above, I simply stretched the background so that the entire screen was bent, and the interface elements were positioned relative to its edges. However, this does not always work. So, in my other landscape-oriented reTales game (using ResolutionPolicy :: FIXED_HEIGHT ), I simply drew two sprites around the edges of the screen:



We run around the configuration, read the sets of paths, compare with the physical dimensions, load the necessary resources, apply the correct policy and stretch. But I went a little further, screwing up the localization. At the level of the engine. The peculiarity of FileUtils :: getInstance () -> addSearchPath () is that resources will be searched strictly along the paths that were set and in the same order. I did not find it in “fonts /”, it goes to “ui /”, I did not find it there - it goes to “maps /” and then searches for it in the root in case of failure Perfectly. After all, we can get the current language using Application :: getInstance () -> getCurrentLanguage () and at the time of adding the path, first add the path specific to the particular language, and then the path itself. Better to see once. There will be something like this:

 FileUtils::getInstance()->addSearchPath("fonts/en"); FileUtils::getInstance()->addSearchPath("fonts"); FileUtils::getInstance()->addSearchPath("objects/en"); FileUtils::getInstance()->addSearchPath("objects"); FileUtils::getInstance()->addSearchPath("backgrounds/en"); FileUtils::getInstance()->addSearchPath("backgrounds"); 

As a result, when trying to create a sprite, the coconut will first look at the localized folder, then into the shared folder. Need to load fonts depending on the default language? Just create a folder. Do you need the specific sprites (the flag on the soldier patch) to be loaded according to the player's language? Create a folder and drop the file there. The text itself simply lies in the same json-files as the config. Instead

 Label::create("This is label", "font.fnt") 

just use

 Label::create(Localized::getString("mainMenuCaptionLabel"), "font.fnt") 

Localized class and more you can download from the links below. I do not mind :)

Thank you for being with me, reader. Do not forget to feed your cat - he really needs you.

An example of a working config - pastebin.com/5idCpjYh
An example of a file with strings - pastebin.com/LfBxs6dA
Localized.h - pastebin.com/LwNaKrFK
Localized.cpp - pastebin.com/GHVmPvCc
Load Paths - pastebin.com/CX8Xma25
How to run the whole thing in AppDelegate - pastebin.com/dMVtY2Rb

Source: https://habr.com/ru/post/217387/


All Articles