Cocos2d-x Game Framework

repository·v4·Indexed 12 days ago

https://github.com/cocos2d/cocos2d-x

A multi-platform C++ framework for building 2D games, interactive books, and graphical applications. It supports iOS, Android, macOS, Windows, and Linux, featuring scene management, physics integration (Box2d and Chipmunk), and rendering via OpenGL ES 2.0, OpenGL 2.1, and Metal.

Tokens
23.2K
Snippets
70
Records
96
Agent score
98%

What's inside Cocos2d-x

  1. Core features of the cocos2d-x framework

    v4

    cocos2d-x provides a comprehensive suite of tools for 2D game development, including:

    • Scene Management: Workflow and transitions between scenes.
    • Graphics & Animation: Sprites, Sprite Sheets, Particle systems, and Skeleton Animations (Spine/Armature).
    • Actions: Transformation (Move, Rotate, Scale, etc.), Composable (Sequence, Spawn, Repeat), and Ease actions.
    • Physics: Integrated support for Box2d and Chipmunk.
    • Input: Touch, Mouse, Keyboard, and Accelerometer support.
    • Rendering: OpenGL ES 2.0 (mobile), OpenGL 2.1 (desktop), and Metal (macOS/iOS).
  2. Apply const to variables, parameters, and methods

    v4

    Use const to enable compile-time type checking and improve code readability.

    Guidelines:

    • Arguments: If a function does not modify an argument passed by reference or pointer, mark that argument as const.
    • Methods: Declare methods as const whenever they do not modify class member variables, do not call non-const methods, and do not return non-const pointers/references to data members. Accessors should almost always be const.
    • Data Members: Consider making data members const if they do not need to be modified after construction.

    Placement: While const int* foo is common, int const *foo is also acceptable. The project encourages putting const first (e.g., const int), but consistency with existing code is the priority.

    class Foo {
    public:
        // Accessor should be const
        int getValue() const; 
    
        // Argument passed by reference should be const if not modified
        void processData(const std::string& data);
    
    private:
        const int _id; // Data member that doesn't change after construction
    };
  3. Avoid using C++ exceptions

    v4

    Cocos2d-x does not use C++ exceptions. This prohibition includes exception-related features introduced in C++11, such as noexcept, std::exception_ptr, and std::nested_exception.

    Note: There is an exception to this rule for Windows-specific code.

    Instead of exceptions, use error codes or assertions to handle failures.

  4. Manage Ownership with Smart Pointers

    v4

    When dynamic allocation is necessary, use ownership logic to manage memory safely.

    Best Practices:

    • Single Ownership: Prefer having a single, fixed owner for dynamically allocated objects.
    • Transfer Ownership: Use std::unique_ptr to make ownership transfer explicit. If other code only needs access, pass a pointer or reference without transferring ownership.
    • Shared Ownership: Avoid shared ownership unless there is a significant performance benefit (e.g., avoiding expensive copies of immutable objects). If used, prefer std::shared_ptr<const T>.
    • Avoid Legacy Types:
      • Never use linked_ptr or std::auto_ptr.
      • Use std::unique_ptr instead of scoped_ptr (unless maintaining compatibility with older C++ versions).

    Example of Explicit Ownership Transfer:

    std::unique_ptr<Foo> FooFactory();
    void FooConsumer(std::unique_ptr<Foo> ptr);
  5. Rules for Multiple Inheritance and Interfaces

    v4

    Multiple inheritance is restricted to specific use cases to avoid complexity:

    • Multiple Inheritance: Only allowed if at most one base class has an implementation. All other base classes must be pure interface classes.
    • Interface Classes: A class is considered a pure interface if it meets these requirements:
      • It has only public pure virtual (= 0) methods and static methods.
      • It has no non-static data members.
      • It has no constructors, or if it does, they are protected and take no arguments.
      • It must declare a virtual destructor (which should not be pure).
    • Naming Convention: Pure interface classes should end with the Interface suffix (e.g., MyClassInterface). This signals to other developers that they should not add implemented methods or non-static data members.
  6. Order function parameters by input then output

    v4

    When defining functions, follow this parameter ordering rule:

    1. Inputs: values or const references.
    2. Outputs/IO: non-const pointers.

    Always place all input-only parameters before any output parameters. If you need to add new parameters, do not simply append them to the end if they are inputs; place them before the output parameters to maintain this order.

  7. Use nonmember or static member functions instead of global functions

    v4

    To avoid polluting the global namespace, prefer using nonmember functions within a namespace or static member functions. Use completely global functions only when absolutely necessary.

    • Nonmember functions: Should reside in a namespace and should not depend on external variables.
    • Static member functions: Useful when a function is logically tied to a class but does not require a class instance.
    • Scope limiting: If a nonmember function is only needed within a specific .cpp file, use an unnamed namespace or static linkage (e.g., static int Foo() { ... }) to limit its scope to that translation unit.
  8. Guidelines for inline functions

    v4

    Inline functions allow the compiler to expand the function at the call site rather than using a standard function call mechanism. While they can improve performance for small functions, overuse can increase code size and decrease performance due to instruction cache pressure.

    Decision Rules:

    • Size Limit: Only define functions inline if they are small (typically 10 lines or less).
    • Accessors/Mutators: It is acceptable and common to inline short accessors and mutators.
    • Complexity: Avoid inlining functions containing loops or switch statements unless the loop/switch is rarely executed.
    • Recursion/Virtual: Recursive functions and virtual functions are typically not inlined by the compiler. Use inline for virtual functions primarily for convenience or to document behavior within the class definition.
  9. Implement Access Control for Data Members

    v4

    Follow these rules for managing class data visibility:

    • Private by default: Make data members private.
    • Accessors/Mutators: Provide access through accessor functions (e.g., getFoo()) and mutator functions (e.g., setFoo()).
    • Naming Convention: Typically, a private variable is named with a leading underscore (e.g., _foo).
    • Inlining: Definitions of accessors should usually be inlined in the header file.
    • Exception: static const data members (typically named in ALL_CAPS) do not need to be private.
  10. Handle reference arguments in function parameters

    v4

    When passing parameters by reference in C++, follow these conventions to ensure clarity and safety:

    1. Input Arguments: Use const T& for input parameters. This communicates that the function will not modify the object and that a null value is not expected.
    2. Output Arguments: Use pointers (e.g., T* out) for output arguments. This follows the convention that input arguments are values or const references, while output arguments are pointers.
    3. When to use const T* instead of const T&: Use a const pointer for input only if:
      • You need to allow a nullptr to be passed.
      • The function needs to save a pointer or reference to the input for later use.
      • You are passing const char* for strings.

    Avoid non-const reference parameters unless required by specific conventions like swap().

    void foo(const string &in, string *out);
  11. Use namespaces correctly to prevent collisions

    v4

    Namespaces should be used to subdivide the global scope and prevent name collisions.

    Unnamed Namespaces

    • Allowed in: .cpp files only.
    • Purpose: To avoid runtime naming conflicts for local symbols.
    • Restriction: Do not use unnamed namespaces in .h files, as this can violate the C++ One Definition Rule (ODR).

    Named Namespaces

    • Usage: Wrap the entire source file after includes and forward declarations.
    • Indentation: Do not indent code inside a namespace.
    • Cocos2d-x Convention: Use NS_CC_BEGIN and NS_CC_END macros for the cocos2d namespace.
    • Prohibitions:
      • Do not use using namespace ... (using-directives) in headers or to pollute namespaces.
      • Do not declare anything in namespace std.
      • Do not use inline namespaces.

    Using Declarations and Aliases

    • Using-declarations: using ::foo::bar; is allowed in .cpp files, or inside functions/methods/classes in .h files.
    • Namespace Aliases: namespace fbz = ::foo::bar::baz; is allowed in .cpp files and inside functions/methods. Avoid defining aliases in public headers to keep APIs small.
    // In a .h file using cocos2d namespace
    NS_CC_BEGIN
    
    class MyClass {
    public:
        void foo();
    };
    
    NS_CC_END
    
    // In a .cpp file using unnamed namespace
    namespace {
    
    enum { UNUSED, EOF, ERROR };
    bool atEof() { return _pos == EOF; }
    }
    
    // In a .cpp file using named namespace
    namespace mynamespace {
    
    void MyClass::foo() {
        // implementation
    }
    
    } // namespace mynamespace
  12. Rules for static and global variables

    v4

    To avoid indeterminate construction/destruction order bugs, follow these strict rules for static and global variables:

    1. No class-type globals/statics: Static or global variables of class type are forbidden. They cause bugs because the order of construction and destruction is not guaranteed.
    2. Use POD only: Objects with static storage duration (globals, static members, etc.) must be Plain Old Data (POD) (e.g., int, char, float, pointers, or arrays/structs of POD).
    3. No containers: This rule forbids std::vector or std::string as globals/statics. Use C-style arrays (char[]) instead.
    4. No function-initialized POD: Do not initialize static POD variables with the result of a function unless that function is guaranteed not to depend on other globals (e.g., getenv()).
    5. Handling class-type needs: If you absolutely need a global class-type object, initialize a raw pointer from main() or pthread_once(). Do not use smart pointers, as their destructors will suffer from the same indeterminate destruction order issues.