quarta-feira, 1 de junho de 2016

Games in C++ can't be that hard, right? - YAML.

These last weeks I've been making some progress with the components and rendering system of the game engine. It is all so crude, but it's working and that makes me happy. I've managed to get a moveable first-person camera with a cube in the scene, reduce the amount of code needed to add a component to an entity and even got both orthographic and perspective camera projections!

Now, indeed, reinventing the wheel is totally unproductive. With the amount of code I've written here I could have doubled the amount of features in my last game prototype made in Unity. In this c++ project, I'm still at cube point.

Anyway, a friend has suggested me to try to make my rendering system in Vulkan – an idea that I grew fond of. But first, there are other things I want to do, and one of them is to avoid “rebuilds” every stupid test change such as screen resolution or, guess what, switching between OpenGL and Vulkan. As a starting point, it seems like a good idea to reduce “rebuilds” by tackling two areas:

- Components, which can be coded by scripting languages such as FalconPL or Lua
- Assets data.

Components scripting and FalconPL / Lua is too far away from me right now; I simply don't have any clue on how to set up them with my engine architecture and bind everything, and I can't tell if FalconPL is still alive as well. That leaves me figuring out only how to load assets data. It could be done with a scripting language, too, but the truth is that any kind of file reading technique will do.

In the past, in a restaurant simulator prototype, I've used JSON to store data for things such as food recipes or employees names. In that, there are many advantages like being able to store rules anywhere that allows storage of text, and they are readable enough to be written with any text editor. It'd be nice if these features were available for the engine assets information. If I write down what data I need, for example, to set up a texture and a material, I'd get something like the following:

Texture:
    - Name : HappyFace
    - File : HappyFace.png
    - WrapMode : Clamp

Material:
    - Name : UnlitHappyFace
    - Texture : HappyFace
    - Shader : TextureUnlit

It is really easy to write and to read, and it is also YAML. Sure, loading times will be way longer than if I were reading binary data, but then I can simply write a “stupid compiler” to make the binary files from the texts – if I ever come to need this performance. To read the files, I've used a small but elegant library called Yaml-Cpp. The rendering configuration file seemed a nice “hello world” practice. Here's how it turned out:

Video:
    - ScreenWidth : 800
    - ScreenHeight : 600
    - Renderer : OpenGL

The Yaml-Cpp is really easy to use. The code is clear and straight-forward, even with some (not all) error precautions. With some practice, the code should get even simpler.


void GameSettings::Load(std::string fileName)
{
    YAML::Node mainNode;

    try
    {
        mainNode = YAML::LoadFile(fileName);
    }
    catch (std::ifstream::failure e)
    {
        throw Engine::FailedToLoadException(std::string("Failed to load game configuration: file not successfully loaded."));
    }

    if (mainNode["Video"])
    {
        YAML::Node videoNode = mainNode["Video"];
        for (const auto& node : videoNode)
        {
            if (!node.first.IsScalar() || !node.second.IsScalar()) continue;

            if (node.first.as<std::string>() == "ScreenWidth")
            {
                m_ScreenWidth = node.second.as<int>();
            }

            if (node.first.as<std::string>() == "ScreenHeight")
            {
                m_ScreenHeight = node.second.as<int>();
            }

            if (node.first.as<std::string>() == "Renderer")
            {
                std::string renderer = node.second.as<std::string>();

                if (renderer == "OpenGL")
                {
                    m_GameRenderer = GameRenderer::OpenGL;
                }
                else if (renderer == "Vulkan")
                {
                    m_GameRenderer = GameRenderer::Vulkan;
                }
            }
        }
    }
}

Now I'll try to think in a way to store the assets meta data in disk. Again, I'll probably end up with something like in Unity 3d.


The code has been highlighted by hilite.me.

domingo, 8 de maio de 2016

Games in C++ can't be that hard, right? - Code refactoring already.

It's been some time since last post. I've been tying pieces of code together as I programmed a way of rendering 3d meshes with Open GL. One awful point of the “learning-as-you-go” way of thinking is that, even if each small part is simpler to learn and understand, binding everything together can become a real problem as the communication between classes wasn't properly analyzed and decided upon.

So, you get pieces of code like this:

auto entity = entityPtr.lock();
entity->AddComponent(std::make_shared<Engine::ComponentReferenceStoringFactory<Engine::Transform>>(compFactories->GetFactory<Engine::Transform>()->Instantiate(), Engine::Global->GetComponentTypeMap()->GetTypeId<Engine::Transform>()));
entity->AddComponent(std::make_shared<Engine::ComponentReferenceStoringFactory<Engine::MeshFilter>>(compFactories->GetFactory<Engine::MeshFilter>()->Instantiate(), Engine::Global->GetComponentTypeMap()->GetTypeId<Engine::MeshFilter>()));
entity->AddComponent(std::make_shared<Engine::ComponentReferenceStoringFactory<Engine::MeshRenderer>>(compFactories->GetFactory<Engine::MeshRenderer>()->Instantiate(), Engine::Global->GetComponentTypeMap()->GetTypeId<Engine::MeshRenderer>()));
entity->AddComponent(std::make_shared<Engine::ComponentReferenceStoringFactory<Engine::SimpleControlBehavior>>(compFactories->GetFactory<Engine::SimpleControlBehavior>()->Instantiate(), Engine::Global->GetComponentTypeMap()->GetTypeId<Engine::SimpleControlBehavior>()));

A lot of time has been used to tie things together, as the snippet above can show. And another good deal of time will be needed to refactor and create utility classes and functions so that the system itself does not become painful.


Lesson learned: plan (more) before you code. C++ can become quite verbose.

terça-feira, 22 de março de 2016

Games in C++ can't be that hard, right? - Handles: hey freak me out, I'm coding!

About fifteen days ago I've posted about smart pointers and ownership. After laying out the rules for a piece of the system I wanted to code – namely, a way to iterate over instances of a type, following some restrictions and demands – I ended up concluding that maybe I should try to use handles. With the little time I have to code side projects, this ended up being a heavier task than expected. Because of c++ stuff I didn't know, didn't remember, didn't understand, or any permutation of these, I had to throw way many attempts.

Soooooo… the truth is, things got too complex and I decided to fall back into KISS (Keep it Simple, Stupid). Planning a bigger scope set of rules and demands would take too much of the little time available, so I've used a set of rules for the containers themselves, with handles.

[r1] – Containers are the unique owners of the data stored there.
[r2] – Contained data must be created within the container itself.
[r3] – Handles to contained data are created only (and only when) with the storing of data.
[r4] – Handles must be encapsulated by smart pointers. They must know how to resolve their destruction, and of the data they point to.
[r5] – Iterators that "directly" point to stored data must be available. They are unsafe and should be used with caution.
[r6] – Data can be destroyed even when their handles are still alive. This will make the handles point to a specific "No Data" element.
[r7] – Containers must assume "non pointers" template data types.
[r8] – Containers must be inheritable from an abstract container.

I know some rules can be rather controversial, but I'll leave it as is for now. Actually, they'll force some restrictions on the stored data as well. The biggest problem probably comes from rule 8; there are reasons that I want a uniform representation of any possible container for the same data type, such as a list of containers to be used by factories.

A good name for the container might be StoringFactory. After researching for a while, it seems to be rather unusual to mix up a container together with a factory, as the separation in "container only" and "factory only" classes is desirable. However, for my needs, it seems to make more sense to simply merge them together, with a name to clearly show this mix of responsibilities. Am I deciding wrong? Don't know, but I'm really afraid of over-engineering this, right now...


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
template <class DataType>
class StoringFactory
{
public:
    virtual ~StoringFactory() {}

    // Deletes the data pointed by a handle. The handle won't be discarded,
    // so any holding handle to the same data will still be "valid", and
    // return noDataPointer.
    virtual void DeleteData(StoringFactoryHandle<DataType>& handle) = 0;

    // Deletes the data pointed by a handle, and the handle itself. Using the
    // same holding handle after deleting it will result in undefined behavior.
    virtual void DeleteHandle(StoringFactoryHandle<DataType>& handle) = 0;
    virtual DataType* Get(StoringFactoryHandle<DataType>& handle) const = 0;

    virtual DataType* SetNoDataPointer(std::unique_ptr<DataType> noDataPointer) = 0;

    // Returns the number of registered elements in this container. The name is not
    // size as it could be misleading.
    virtual int ElementsCount() const = 0;

    // Iterators for ranged-for loops
    virtual StoringFactoryIterator<DataType> begin() const = 0;
    virtual StoringFactoryIterator<DataType> end() const = 0;
};


The abstract StoringFactory class above is rather simple. Someone that is somehow reading this post will probably notice that there are no methods to actually insert or create data into this container. The reason for that is that I want to try using emplacement methods with variadic templates. There's a catch, though: they can't be virtual, forcing them to be in the child classes. How, then, can I insert data into the container when I have only a pointer to the base class? It turns out that it is indeed possible, by means of abstract factories or dependency injections, and some magic.

Next comes the dubious methods of DeleteData and DeleteHandle. DeleteData is rather dangerous and nonsensical, but I want the ability to remove data from memory without first needing to properly clean up anything related to its handle (such as every object that has a shared ownership of the handle). This can be useful in some very rare cases, as unloading low priority resources when in need of memory. The DeleteHandle probably shouldn't be public. Handles will call this method to destroy themselves, but I don't see any reason to not make this method protected, and the handles a friend class of the StoringContainer.

And what does the handle looks like?


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
using handle_type = unsigned int;

template <class DataType>
class StoringFactoryHandle
{
public:
    StoringFactoryHandle(handle_type handle, StoringFactory<DataType>* factory);

    inline DataType* Get() const;
    inline handle_type GetHandleValue() const; // TODO : restrict creation access



protected:
    StoringFactory<DataType>* m_Factory;
    handle_type m_Handle;
};


They're just a small container with enough information to refer back to the original data. I've restricted the handles to store handle_type (that is, unsigned int) as their "handling key". I could have used pointers, but ended up deciding on "indexes" or "keys" instead. This is a decision that'll probably be very hard to change later on... so let me pretend to be proud and sure of what I'm doing.

Finally, all that remain are the iterators types for the StoringFactory class. It is possible to make an abstract iterator type and then inherit as needed, for each storage desired, such as lists, maps, etc. This should work, by relying on Covariance, but soon many flaws of this idea would sprout everywhere. One of the problems, for example, is the need of casting between parent and child types of the iterator. Even if that is solved, templates and slicing would come right after with many other problems. The path I chose was to use the pimpl idiom, though I feel like I'm missing a bit of its point.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
template <class DataType>
class StoringFactoryIterator
{
public:
    StoringFactoryIterator(StoringFactoryIterator<DataType>& other);
    StoringFactoryIterator(std::unique_ptr<StoringFactoryIteratorImpl<DataType>> iteratorImplementation);
    ~StoringFactoryIterator() = default;

    DataType& operator *() const;
    const StoringFactoryIterator& operator ++();
    bool operator !=(const StoringFactoryIterator& other) const;



protected:
    std::unique_ptr<StoringFactoryIteratorImpl<DataType>> m_StoringFactoryIteratorImpl;
};



template <class DataType>
class StoringFactoryIteratorImpl
{
public:
    StoringFactoryIteratorImpl() {}
    virtual ~StoringFactoryIteratorImpl() {}

    virtual DataType& Dereference() const = 0;
    virtual const StoringFactoryIteratorImpl& PreIncrement() = 0;
    virtual bool IsDifferent(const StoringFactoryIteratorImpl& other) const = 0;

    virtual StoringFactoryIteratorImpl* clone() = 0;
};


The idea was to use a single iterator "interface" class to redirect operations to a real implementation, that is storage specific. Some problems with slicing still remains, such as making clones of the iterator implementation when the "interface" is copied around. Since the implementation is handled only by the "interface" class, methods such as clone are used. Casting is still a problem (i.e when the user try to copy or compare the iterator of one type of storage with one of another type of storage). I didn't decide on any special semantics for that, but later there'll be probably some assertions for debug builds.

Some methods are still missing, such as container sizes, but this is the basic for my storing with handles system for game components. I'm also implementing an ArrayStoringFactory class, that inherits from StoringFactory, but I don't think it's code has anything interesting right now.

Let's see what happens in the next 15 days. After finishing up theses classes and adding any new needed method to the base classes, I'll make some unit tests and then the Transform component. Maybe I'll have some thoughts to write in this blog again.

All code in this post has been pretty-printed by hilite.me

domingo, 6 de março de 2016

Games in C++ can't be that hard, right? - Shared Pointers and Ownership.

        C++ was my favorite language back then, at college, and even before. Fun were the days where Segmentation Fault wouldn't immediately translate to time or money wasted around, nor unhappy clients contacting the team. Right after graduating I've started using C# with Unity 3d and got into the Garbage Collected world – and I've stayed with C# from the very then.

        I've decided to start a simple C++ game on my free time. It can't be that hard, right? And the game will run a lot faster, right? Actually, both are wrong. C++ can be very hard if you aren't used to think in terms memory management, resources ownership and mapping, and even when and how their memory will be released. Even with smart pointers and RAII, everything can eventually go south. In fact, even in Garbage Collected environments with RAII and everything else, things WILL eventually go south... And about being faster, even if the compiled code do have raw speed in terms of execution time, a lot more often than not, slowness is caused by design, code architecture, algorithmic decisions and so on. The end result is that in hands of good programmers, it's possible that games in C# or Java run faster simply because they're easier to profile, debug and refactor – and refactoring is as time demanding as it is need for any project survival as it grows.

        Enough about that! I've decided to use c++ and so I've started searching around about what good practices there are when using the "new" standard smart pointers. "New" because they've already existed in Boost since like ever. Then I found this amazing stack overflow answer, by David (quoting here):

"
Personally, this is how I (more or less) do it:
  • unique_ptrs are for sole ownership
  • raw pointers mean whoever gave me the raw pointer guarantees the lifetime of that object to match or exceed my lifetime.
  • shared_ptrs are for shared ownership
  • weak_ptrs are for when a system wants to check if the object still exists before using it. This is rare in my code since I find it cleaner to have a system guarantee the lifetime of anything it passes it's subsystems (in which case I use a raw pointer)
By far I use more unique_ptrs than shared_ptrs, and more raw pointers than weak pointers.
"

        These guidelines are indeed very accurate and simple, as, in my opinion, smart pointers greatest virtue is showing intention over “simply” managing memory. There are, of course, exceptions where those guidelines can't be properly followed, but exceptions exists for everything in the coding world. Looking at smart pointers types, we can see that there's an immediate synergy between unique_ptr and raw pointers, and shared_ptr and weak_ptr. It's simple, right? Can't be that hard. Again, this turns out to be wrong. Not because of the guidelines nor the pointers, but because of the problem that came way before them: resource ownership.

        I've structure a simple Entity-Components-System for a game. I want Entities to have a list of components they hold, but I want actual components data to be stored somewhere else, in a ComponentsContainer class. The reason is that I want my systems to be able to iterate over all components of a single type easily.

        I bet that didn't make any sense, right? Well, I've imposed a few restrictions on each part of this structure:

Components:
  • [c1] Components can have methods, but "high level" methods such as "Update" or "Render" are desired to be in a System, which will iterate over all components executing their logic.
  • [c2] Components must be able to store references to other components, of the same or any other entity. They must have means of easily checking if that component still exists.
  • [c3] Components can be destroyed anytime, by any component or System.
  • [c4] Components should be automatically deleted if no one references them, if possible.
  • [c5] Components are owned by a single entity.


Entities:
  • [e1] They have a unique identifier
  • [e2] Must not have logic other than helping methods
  • [e3] They uniquely own their components


Systems:
  • [s1] Systems have the responsibility of iterating over components of a single type (the one that the system works with), executing logic, rendering, or whatever else it is supposed to do (i.e spatial partitioning for another system, such as collision testing)


        There are problems with [s1], [e3], [c2] and [c5].

        Because of [c2], it is desired that the entities own each component by means of a shared_ptr, even if they are the unique owners. With this, references to any component can be weak_ptr, and we can check whether they (the referenced components) have been deleted or not. Alternatively, using a unique_ptr would mean our references are now raw pointers. By the guidelines above, a raw pointer indicates that the resource pointed to will outlive the component, which is not guaranteed because of [c3]. With shared_ptr, actually, the solution for this is kind of straight away: entities must not ever allow another shared_ptr share the component, pushing us back to the original "unique" ownership.

        Now, [s1] says that we need means to iterate over all components of a single type. It is possible to achieve this by simply iterating over all entities, searching for components of a specific type. There are some ways of achieving this, such as storing a matrix of entities X components, etc, but I personally don't like the concept of checking if an entity has a component in order for us to update it, in a System. If I want to iterate over all components of a type, it does not mean I am also interested in checking if they exists. Checking for this in Systems might be made later, as a need for deep refactoring.

        Trying to not over-engineer, the components storing can be moved out of the entities, into a data container. A data container will also make some stuff a little bit simpler, such as factories and dependency injection, if I decide on them later. I want these characteristics:

  • [d1] They must work with ranged for loops. Come on, that's cool! Every iteration must be over a valid component (in the context of allocated and alive)
  • [d2] They do not need to worry about specific alternative accessing methods. For example, methods to allow accessing specific elements of an octree is too specific. The system will have to know the container type anyway, to do this effectively.
  • [d3] They must not force any specific type of structuring. This means that they might be used as vectors, lists, maps, trees, octrees, etc
  • [d4] They must be able to store data contiguously
  • [d5] Reordering elements should not break any references
  • [d6] Data not used by anyone should be deleted, to avoid leaks.


        The rule [d4] is a critical one. It is listed here to avoid the need of some refactoring later. [d4] together with [d1] permits intuitive (for the System) sequential access over contiguous memory data. Even if I don't want to implement this right now (and I dont), this will inevitably become an important optimization later, as it allows for cache-friendly access.

        With the data container, the components won't be stored in entities anymore, although entities still are their unique owners. Simple? No. There is a problem here. If the data is contiguous, then it's very likely that the components are stored by value in an array. We just can't delete it. However, were it in another data structure, such as tree of elements in the heap, then deleting would be a must. Fortunately, smart_ptr allows us to pass a function – better yet, a lambda - to use as deleting logic. We can pass this complexity back to the container, making it create the new component for us and returning a shared_ptr with the deleter set.

        However, it turns out that smart pointers aren't enough anymore, because of [d5]. If the container reorders the elements, how am I supposed to update the references in the entities? They need to point to something else, instead of the data itself: maybe a handle – and then now we have a smart pointer to a handle. It is kind of like de-referencing twice, to access the data we want.

        Since we have arrived at handles, now it is hard to let go of them. They can still exists even if the resourced they "handle" is not around anymore. If we can update a handle when the resource is deleted, we can make it either return nothing, or return something to expose the error. For example, a handle to a sound effect could return a default error-like sound when accessed after the original sound has been unloaded. This means allowing the data container being able to unload the components even if they are still being used by other entities. The use for this sounds dubious for components, but it is interesting if we can make the container generic.


        So, how to structure everything with handles? I have no idea. I'll be thinking about it and then make a new post in the future.

segunda-feira, 22 de fevereiro de 2016

Okay, time to try to become active, again!

        I'm back... again. Really, keeping a blog updated is a lot harder than I initially thought. And I still need to learn ways to improve this basic layout...

        I haven't been writing in english for a while, so I may have got a little rusty and so I say sorry in advance! A few years ago, I tried to go indie on mobile and failed. I'd point out two main mistakes:

- Failing to notice I'm an awful game designer.
- Throwing away many prototypes. Have you ever heard MVP? Probably some of those prototypes that haven't ever seen the real world had a chance. Who knows? I've never released them, so I'll never know – and sometimes, not knowing is worse than failing.

        Rants aside, I got a job and had to stop games development since there were so many things that I needed to learn in order to keep up with the team. Working in a professional team has really helped me to develop new ways of handling other people code and logic as well as how to behave in a project under the wings of Scrum (if you're a programmer, you probably understand how hard this could be).

        Now I've got some free time for side projects again, and went off using Unity 3d until I realized that on free time it's really hard to make a complete game – even in Unity – so I'll take this opportunity to go back to C++ that, despite being my favorite language, I've only used until graduation, and also learn C11. Don't know if I'll continue the "asm" series, but surely there'll be some posts about my frustations adventures with C++ in the near future. And yes, I'm very aware that if I wouldn’t finish a game in Unity 3d, its a lot more likely that I won't ever get even close to it in a lower level environment.

For great justice.

quinta-feira, 15 de outubro de 2015

Areas de atuação na indústria de games

Existem 3 grandes áreas de atuação na indústria de games que um jovem profissional pode (e deve) focar. Embora no futuro, principalmente se você for ter uma empresa indie, você terá que saber um pouco de cada área, é sempre importante no começo escolher uma área para se desenvolver mais e se tornar um especialista.

Geralmente no curso de ciências da computação ou sistemas de informação o foco é a programação, então dessas 3 areas os alunos desses cursos passam por somente uma. Porem qualquer um pode desenvolver o seu conhecimento e habilidades através de uma palavrinha (mais pra recurso) magica que existe hoje em dia. É ela mesmo...a internet!

Qualquer que seja a área de atuação que você decida se especializar, a internet é o caminho para você aprender e por em pratica tudo o que precisa para se tornar um expert.
Sem mais delongas, vamos as áreas:

Programação: “são os responsáveis por desenvolver toda a lógica do jogo. Existem várias especialidades que podem ser seguidas nessa área, dentre elas o desenvolvimento da física do jogo, da inteligência artificial, da renderização gráfica, do processamento sonoro, da jogabilidade, da interface do usuário, dentre inúmeras outras.
Sérgio Oliveira

O programador será aquele que cria toda a logica que dará a vida ao jogo. Existem inúmeras especializações dentro da programação, e nessa área geralmente o profissional precisa ter conhecimentos das teorias da computação (como grafos, estruturas de dados) e de matemática/física (muito, mas muito mesmo, usada no dia a dia dos jogos).
Especializacoes incluem (mas não se limitam): engine, gameplay, physics, network, AI (artificial intelligence), UI (user interface), render (computer graphics e shaders).

Game Design: “são os responsáveis por projetar o jogo como um todo, desde a formulação do conceito do jogo, até a sua jogabilidade. Para tanto, precisam estar atentos às notícias e tendências da indústria.”
Sérgio Oliveira

Enquanto o programador instrui o computador em como o jogo funciona, é o game designer que dita para o programador o que deve ser feito no jogo. Explicando melhor, o game designer cria mecânicas e sistemas (em sua cabeça) de como o jogo deve funcionar, e passa para o programador.
Imagine o jogo do Mario. Foi um game designer que decidiu que seria um jogo de plataforma, onde o Mario anda sempre para a direita, ele pode correr, andar e pular, ele pode matar inimigos pulando na cabeça deles, ele tem upgrades (inclusive um que faz ele voar), ele tem um tempo limite para passar de fase, e um limite de vidas (que pode ser aumentado coletando moedas ou cogumelos verdes). Depois disso o game designer foi e decidiu quais são e como funcionam os inimigos do Mario. Depois disso ele decidiu como serão os leveis (fases). E por ai vai...
Então tudo que é mecânica de jogabilidade, alguem teve que pensar e planejar, e esse alguém é nosso game designer.
Algumas especializações de game designer são: gameplay, level design, narrativa e roteiro, game feel, character, enemies, combat.

Arte: “são os responsáveis pelo desenvolvimento de toda e qualquer coisa que exija arte visual. Podem se encaixar na concepção da arte, na modelagem 3D dos personagens, do cenário, animação, texturização, iluminação, dentre tantas outras que envolvam imagem.
Novamente, Sergio Oliveira

O trabalho de um artista é o de dar sentimento, estética, vida ao jogo! São eles os responsáveis por elaborar o visual e som de um jogo para que o jogador se sinta imerso na experiência.
Pegando o Mario como exemplo, eles que desenham e animam cada personagem, o ambiente e cenário, a interface do usuário, e criam os sons épicos que lembraremos para o resto da vida (https://www.youtube.com/watch?v=VH8mQRXemuo).
Algumas especializações dentro dessa área são: Concept, Character, Environment, UI, trilhas sonoras (OST), efeitos especiais sonoros (sound FX), cutscene (animações ou vídeos).

Percebam que não existe jogo sem esses 3 especialistas, ou melhor, sem essas 3 áreas.

Existem outras especializações? Sim, mas vocês somente chegarão nelas após ter uma das acima...

Percebam que estou misturando propositalmente inglês com português, pois como disse no artigo anterior, é hora de aprender inglês! (What time is it? Its adventure time, but in english!)

Refs:


domingo, 4 de outubro de 2015

Quero trabalhar com games (yeyyy)! Tá, mas o que eu preciso para isso?


Se você é um aluno que esta iniciando o seu curso de computação, ou desenvolvimento de jogos, ou simplesmente um profissional que quer mudar de carreira, esse artigo vai te dar a diretriz inicial (ou pré-requisitos mesmo) para iniciar uma carreira na indústria dos games (jogos).

Eu (game developer, professor de desenvolvimento de jogos) considero essencial esses 3 pontos para inicio:
  • Entusiasmo e vontade
  • Inglês
  • Motivação de um mercado mais exigente

Vamos explicar cada um deles.

O primeiro ponto é o do entusiasmo e vontade. Essa é uma área que exige do profissional uma grande paixão pelo tema. Se você quer ser um programador java, não precisa amar java. Se você quer ser um game developer (desenvolvedor de jogos) PRECISA amá-los.

Felizmente esse é o requisito mais fácil para a maioria das pessoas, principalmente as mais jovens, que já nasceram na era digital.

Mas o requisito vai além, é preciso ter realmente muita vontade para trabalhar nessa área. Considerando o mercado indie (depois posso falar mais sobre o tema), a maioria dos desenvolvedores já desistiu de projetos por falta de motivação. Tem muitos artigos sobre o tema, uma pre-pesquisa rápida para vocês:


Essa recomendação de artigo nos leva ao ponto 2, o inglês! A língua inglesa já é essencial para quem pretende trabalhar na área de computação, tecnologia da informação, ou ate mesmo tecnologia pura. Mas se você pretende trabalhar na área de jogos, ou simplesmente fazer seu jogo como um hobby, inglês é vital. Ou seja, sem saber inglês é impossível, então se você ainda não sabe, aprenda. (hoje em dia tem varias formas...ver series com legenda em inglês e depois sem legenda, sites que ensinam, arrumar um amigo americano e praticar com ele conversação por Skype, ou simplesmente pagar uma escola ou prof. particular para aprender)

Por fim, o ultimo pré-requisito, é novamente a motivação! Mas nesse caso vista de um ângulo diferente. Para quem trabalha em empresas grandes, é de conhecimento publico o árduo trabalho de se fazer jogos, envolvendo rotinas de trabalho de 60 a 80 horas semanais por anos. O chamada Crunch, a rotina de trabalho em que se gasta cada segundo possível de sua semana trabalhando, geralmente é adotada no final de projetos, para evitar atraso. Infelizmente o Crunch já virou o padrão, e hoje é adotado desde o inicio do projeto, e é uma das grandes causas de burnout e evasão de ótimos profissionais da área (e em parte, o que motivou a revolução indie).

Muitos artigos falam sobre o assunto e seus males, mas as grandes empresas continuam utilizando o Crunch como regra.


Quem acha que o Crunch é exclusivo de empresas grandes, e quer fazer sua própria empresa para desenvolver jogos indies e assim evitar o Crunch, também esta enganado. Os desenvolvedores indies mais famosos admitem publicamente trabalhar tanto quanto trabalhavam em empresas grandes (com Crunch), com a diferença de que eles trabalham para eles mesmos (para a empresa que eles criaram).


Então é importante estar ciente de que é uma carreira que vai exigir muito de vocês. (Como ilustração vejam o filme Indie Game The Movie: http://buy.indiegamethemovie.com/)
A seguinte figura mostra o tempo de Crunch de desenvolvedores no mercado



Fonte: http://www.gamasutra.com/view/feature/188671/game_developer_qualityoflife_.php

Além disso, uma outra parte importante no quesito motivação, é a comparação com outras possíveis carreiras. Porque ao escolher uma carreira, você está deixando de escolher varias outras. Esteja ciente que outras carreiras na TI, geralmente, envolvem salários melhores com horas melhores. Se você quer ser desenvolvedor indie então, entenda que dinheiro será sim um grande problema. Vários desenvolvedores indies famosos contam suas historia de como sobreviveram comendo Nissin Miojo por anos (sim, o macarrão instantâneo, pq ele é barato!) até conseguir o seuprimeiro jogo de sucesso:


Se você não se assustou tanto para já ter desistido, então parabéns! Bem vindo ao time, jovem motivado desenvolvedor de games!