Using Lua scripting from cocos2d-x

Add Lua to a new cocos2d-x c++ project.

NO preamble – let’s get into it.

cocos new usingLuaFromCPP -l cpp

Creates the new project.

Download Lua from http://www.lua.org (I’m using 5.3)

Download LuaBridge from https://github.com/vinniefalco/LuaBridge

Open the cocos2d-x project in Xcode

Copy the two downloaded folders to the Classes folder of your project.
Then right-click on the project and use the Add Files To… option to add the Lua and LuaBridge folders – making sure you use the options button to add the folders to both projects (mobile and desktop) if you want to use Lua in both) – and create Groups not folder references.
(I had loads of problems adding the folders elsewhere because first headers could’t be found, then I got linker errors. One day I will understand how Xcode works and get this right – but for now just add the folders as described!)

Build and Run just to be sure!

For this example I want to have a layer that will be used to represent my game world.
I want a script that will determine what’s in that world.
To keep things simple, the script will just determine which of two sprites to create, at what location. The actual creation of the sprites will be handled in the C++ code.
The ‘expanded’ idea will be that I can use scripts to replace what would often be done using pList files; so now I can add calculations and loops as well as simple data.

So let’s create a new class, WorldLayer

WorldLayer.hpp

#ifndef WorldLayer_hpp
#define WorldLayer_hpp

class WorldLayer : public cocos2d::Layer
{
public:
    CREATE_FUNC(WorldLayer);

    virtual bool init();

    void addASprite(int spriteType, float x, float y);
};

#endif /* WorldLayer_hpp */ 

WorldLayer.cpp

#include "WorldLayer.hpp"

USING_NS_CC;

bool WorldLayer::init()
{
    if ( !Layer::init() )
    {
        return false;
    }
    return true;
}

void WorldLayer::addASprite(int spriteType, float x, float y)
{
    Sprite* sprite;
    if (spriteType == 0)
        sprite = Sprite::create("CloseNormal.png");
    else
        sprite = Sprite::create("CloseNormal.png");

    sprite->setPosition(Vec2(x,y));

    this->addChild(sprite);
}

The method that is important is the ‘addASprite’ method which we will be calling from a script later on.

For now, let’s test it by changing HellowWorldScene.cpp’s init method as below…

bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }

    auto layer = WorldLayer::create();
    this->addChild(layer);

    layer->addASprite(1,100,100);

    return true;
}

Run this and you should see the single sprite created at (100,100)

A single sprite controlled by the c++ code - just for testing
A single sprite controlled by the c++ code – just for testing

So – time to add some scripting stuff!.

To avoid confusion, just remove the layer->addASprite(1,100,100); line so we will know any sprites displayed will be displayed from the script.

Go back to HelloWorldScene.cpp and add

#include "LuaBridge.h"
#include "lua.hpp"
using namespace luabridge;

Build again to make sure the header files can be found.

Add a new file to the project and call it “TestScript.lua” (I put mine in the Classes folder for simplicity)
Add this code to this file

function createSprites(yOffset, ySize)
    for x = 0, 600, 6 do
        y = math.sin(x)
        worldLayer:addASprite(1, x, y * ySize + yOffset)
    end --for
end --createSprites()

The script will just display 100 sprites in a Sine wave pattern.

The Y offset parameter lifts the sprites above the bottom of the screen and the Size parameter determines the height of the waves

So – now we have a script we need to tell Lua (via LuaBridge) about our WorldLayer class and its functions.

Change Helloworld::init() as follows

bool HelloWorld::init()
{
    if ( !Layer::init() )
    {
        return false;
    }

    auto worldLayer = WorldLayer::create();
    this->addChild(worldLayer);
//------
    // Prepare Lua - this is pretty standard startup for using Lua.
    // IN a real game you might want to have the LuaState as a class field or a global of some sort, so that it is accesible throughout your App
    lua_State* LuaState = luaL_newstate();
    luaL_openlibs(LuaState);

    // Load the script file into Lua
    auto file = FileUtils::getInstance( ) -> fullPathForFilename( "TestScript.lua" );
    luaL_loadfile(LuaState, file.c_str());

    // Tell Lua about our WorldLayer class and its addASprite method.
    // The strings "WorldLayer" and "addASprite" are what the class and method will be called within Lua - they don't have to match the actual names
    luabridge::getGlobalNamespace(LuaState)
        .beginClass<WorldLayer>("WorldLayer")               // Remember this is the Class name, not an instance name
        .addFunction("addASprite", &WorldLayer::addASprite)
        .endClass();

    luabridge::push(LuaState, worldLayer);                  // Push the instance pointer onto the LuaBridge stack
    lua_setglobal(LuaState, "worldLayer");                  // Tell Lua what to call this object

    /*
     Thanks to http://www.troubleshooters.com/codecorn/lua/lua_c_calls_lua.htmThanks
     PRIMING RUN. FORGET THIS AND YOU'RE TOAST
     */
    lua_pcall(LuaState, 0, 0, 0);

    //Call the Lua function    createSprites(yOffset, ySize)

    lua_getglobal(LuaState, "createSprites");   // the function.
    lua_pushnumber(LuaState, 200);              // parameter 1 (yOffset)
    lua_pushnumber(LuaState, 20);               // parameter 2 (ySize)

    /* call the function with 2 arguments, return no results */
    lua_call(LuaState, 2, 0);


    lua_close(LuaState);
//------
    return true;
}

Now run – and you should see the following:

SIne wave sprites - controller by Lua
SIne wave sprites – controller by Lua

Easy, eh?

Adding ChaiScript to a Cocos2d-x Project

The Problem

Most games have different words and levels – or their equivalent.

To define each level it is common to use a plist file, or similar XML equivalent, or even a simple text file, or maybe an SQLLite database. This works, and works well, but is limited in its flexibility.

As a simple example, let’s take a game of drafts (checkers). the board is 8×8 squares, and the starting position of the pieces is fixed – 3 rows for each colour, with all the pieces on only one colour squares – leaving two empty rows in the middle of the board.

I’ll create a (really *really*) simple example project capable simply of displaying one of two different sprites (our ‘black’ or ‘white’ piece) in an 8×8 grid.

When that’s up and running, I’ll add the Chaiscript engine to the project, and write a script to set up the starting level.

 

Creating the Project

To create a new project, from the Terminal:

cocos new CocosChai -p com.PooperPig.CocosChai -l cpp -d CocosChai

You can call the project whatever you like, of course, and replace the “com.PooperPig” with your own ‘company’ name.

Once created, open the project in your favourite IDE. I’m using a Mac so XCode is my IDE of choice. Build and run to make sure everything works up front.

Initial Set Up

Our game is going to use cut-down versions of a couple of classes I usually use:

Entity this is a class representing any ‘thing’ in my game. In this case each ‘piece’ in the game will be an Entity.

Entity.hpp
#ifndef Entity_hpp
#define Entity_hpp


USING_NS_CC;

class Entity : public cocos2d::Ref
{
public:
    CREATE_FUNC(Entity);
    bool init();
    bool initWithValues(Vec2 location,std::string name);
    // For simplicity I make the sprite property public - but it's not good form!
    cocos2d::Sprite* sprite;

private:
    Vec2 location;
    std::string name;
    
};

#endif /* Entity_hpp */
Entity.cpp
#include "Entity.hpp"

bool Entity::init()
{
    return true;
}
bool Entity::initWithValues(Vec2 location,std::string name)
{
    if (!this->init()) return false;
    
    this->location = location;
    this->name = name;
    
    this->sprite = cocos2d::Sprite::create(name);
    this->sprite->setPosition(location);
    
    return true;
}

EntityFactory this is a class that knows how to create various entities. In this game it’s pretty simple, as we only really have one Entity type (or two if you count Black and White)

EntityFactory.hpp
#ifndef EntityFactory_hpp
#define EntityFactory_hpp

#include "EntityFactory.hpp"
#include "Entity.hpp"
#include "cocos2d.h"

USING_NS_CC;
class EntityFactory
{
public:
    cocos2d::Vector<Entity*> entities;
    void getEntitiesForLevel(int levelNumber);
    Entity* getEntityOfType(std::string classname, Vec2 location, std::string name);
};
#endif /* EntityFactory_hpp */
EntityFactory.cpp
#include "EntityFactory.hpp"
void EntityFactory::getEntitiesForLevel(int levelNumber)
{
    getEntityOfType("Test", Vec2(100,100), "CloseNormal.png");
}

// Add a new entity of the required type to the collection of entities
Entity* EntityFactory::getEntityOfType(std::string classname, Vec2 location, std::string name)
{
    auto entity = Entity::create();
    entity->initWithValues(location, name);
    entities.pushBack(entity);
    CCLOG("%s created at (%f,%f)", name.c_str(), location.x, location.y);
    return entity;
}

Now we need to add some code to actually use our factory to create some entities.
Edit HelloWorldScane.cpp
Add

#include "EntityFactory.hpp"

to the top of the source.

Replace the entire init method as follows:

bool HelloWorld::init()
{
    if ( !Layer::init() )
    {
        return false;
    }
    
    auto factory = new EntityFactory();
    auto entity = factory->getEntityOfType("Test1", Vec2(100,100), "CloseNormal.png");
    this->addChild(entity->sprite);
    
    return true;
}

In this code we create a new factory, use it to create a new entity (using the CloseNormal.png that is already included in the project, then add the sprite as a child to this layer – allowing it to be displayed.

If you run the program now, you should see that single image displayed on screen.

Running on the Mac
The initial program running on the Mac

Getting ChaiScript

We’re about to add ChaiScript to our project – so first we need to download it.

Head to Chaiscript.com and download the source, and put it somewhere sensible! (I have a Projects folder in my Documents folder – so I added a Chaiscript-5.7.1 folder.

I want to do some maths in this example app – for which we also need to download the Chaiscript Extras source from https://github.com/ChaiScript/ChaiScript_extras

I added this to my Projects folder too – although we only actually need a couple of files out of this download.

The files we need are in the folder include/chaiscript/extras – so find this folder and copy it to the include/chaiscript/extras folder in the Chaiscript-5.7.1 folder. (You don’t actually have to do this – but it makes more sense to me to keep all my Chaiscript header files in the same place).

Adding ChaiScript to the Project

One of the great things about ChaiScript is that it is “Header-Only”. That is, all you need is to include the header(s) in your source and then use it!

So we need to tell our IDE where to find the headers!

In Xcode, highlight the Project in the Project Navigator, then select Build Settings. Find the Header Search Paths (or User header Search Paths) and add the path to the chaiscript include folder, and specify Recursive – so the compiler will search all the sub-folders rather than you having to specify the exact location of the header files. (This is optional, of course – you can specify the whole path of each header if you prefer).
Do not forget to do this for the correct Target! – I seem to always forget, and so it all works when I run for (say) IOS, but not for Mac!

Adding the header search path in XCode
Adding the header search path

Creating the Engine

I don’t know if Engine is the right word – but it makes sense to me. To use ChaiScript we need to perform two essential tasks: create the Engine, and then tell the engine about any classes, functions, fields etc. in our C++ program that we want our scripts to be aware of.

Add a new C++ file, ChaiscriptCreator.cpp, to your project:

ChaiscriptCreator.hpp
#ifndef CHAISCRIPTCREATOR
#define CHAISCRIPTCREATOR

namespace chaiscript
{
    class ChaiScript;
    class Module;
}

namespace CocosChai
{
    std::unique_ptr<chaiscript::ChaiScript> create_chaiscript();
}

#endif
ChaiscriptCreator.cpp
#include "ChaiscriptCreator.hpp"
#include "ChaiscriptBindings.hpp"
#include "chaiscript/chaiscript.hpp"
#include "chaiscript/chaiscript_stdlib.hpp"

namespace CocosChai
{
    std::unique_ptr<chaiscript::ChaiScript> create_chaiscript()
    {
        auto chai = std::unique_ptr<chaiscript::ChaiScript>(new chaiscript::ChaiScript(chaiscript::Std_Lib::library()));
        chai->add(createChaiscriptBindings());
        auto scriptFileName = cocos2d::FileUtils::getInstance()->fullPathForFilename("Setup.chai");
        chai->eval_file(scriptFileName);
        
        return chai;
    }    
}

You may notice that ChaiscriptCreator references ChaiscriptBindings.hpp – so we can’t build until we’ve written that code. Create the new C++ files, called ChaiscriptBinginfs.hpp and .cpp

ChaiscriptBindings.hpp
#ifndef CHAISCRIPTBINDINGS
#define CHAISCRIPTBINDINGS

namespace chaiscript
{
    class Module;
}

namespace CocosChai
{
    std::shared_ptr<chaiscript::Module> createChaiscriptBindings();
}

#endif
ChaiscriptBindings.cpp
#include "ChaiscriptBindings.hpp"
#include "chaiscript/chaiscript.hpp"

#include "chaiscript/extras/math.hpp"

#include "Entity.hpp"
#include "EntityFactory.hpp"

#define ADD_FUN(Class, Name) module->add(chaiscript::fun(&Class::Name), #Name )

namespace CocosChai
{
    std::shared_ptr<chaiscript::Module> createChaiscriptBindings()
    {
        auto module = std::make_shared<chaiscript::Module>();
        // add the math library so we can use math stuff in our script
        auto mathlib = chaiscript::extras::math::bootstrap();
        module->add(mathlib);
        
        // Add the Vec2 type so we can use them and return them in scripts
        module->add(chaiscript::user_type<cocos2d::Vec2>(), "Vec2");
        module->add(chaiscript::constructor<cocos2d::Vec2(float, float)>(), "Vec2");            // Define the Vec2 Constructor
        
        module->add(chaiscript::fun(&cocos2d::Vec2::x),"x");                                    // Define the Vec2 'properties'
        module->add(chaiscript::fun(&cocos2d::Vec2::y),"y");
        
        
        // Tell chaiscript about our classes
        module->add(chaiscript::user_type<Entity>(), "Entity"); // this isn't strictly necessary but makes error messages nicer
        module->add(chaiscript::user_type<EntityFactory>(), "EntityFactory");
        
        // Give chaiscript a reference to the methods we want it to access
        ADD_FUN(EntityFactory, getEntityOfType);
        
        ADD_FUN(Entity, initWithValues);
        
        return module;
    }
    
}

Ok – so this should build (it won’t do anything much, yet) but let’s walk through the code.

In ChaiscriptCreator.cpp there’s only one method – create_chaiscript() – that returns a unique pointer to a Chaiscript Engine.
It does this as follows:
10. Create a unique pointer to the Engine
11. Adds the result of the createChaiscriptBindings() method (which we’ll talk about shortly)
12. Gets the full pathname of a script file for us to load. (we’ll write some script soon)
13. Loads the script file into the Engine.
15. Returns the Engine

Incidentally, you don’t have to create a script file if you don’t want to – you can use strings in C++ and then have ChaiScript execute the strings as script – depending on your use-cases this might be a good option for you.

Binding C++ to ChaiScript

Let’s take a look at the single method in ChaiscriptBindings.cpp
This is the file you will make most changes to in your own project. Here is where we tell the ChaiScript engine about, well, about anything we need it to know about!

9. Is a preprocessor macro to shorten what we need to write to bind methods of classes to ChaiScript
15. We create a ChaiScript Module
17. We create a ChaiScript Math ‘object’
18. We add the Math ‘object’ to the Module
21 – 25 we tell Chaiscript about the cocos2d::Vec2 structure, so we can use it in our script.
29. We tell ChaiScript about our Entity class
30. We tell ChaiScript about our EntityFactory class
33. We tell ChaiScript about the getEntityOfType method in our EntityFactory class
35. We tell ChaiScript about the initWithValues method in our Entity class

That’s it – nothing too complex!
You can see that it would be quite simple to tell ChaiScript about more classes and methods or types, if that’s what you need.

Next, we’ll actually write some script!

Writing Script

ChaiScript syntax is similar to JavaScript – and I’m not going to try to teach you about its syntax – just enough for us to get some script executing.

First, let’s create a source file to write our script in.

In XCode, add a new file called “Setup.chai” (it can be called anything at all – but in the code we just wrote, that’s what we called it, so that’s what I’m using!).

As XCode doesn’t know what a file with suffix ‘chai’ is, there will be no syntax highlighting. Fix this by using the Menu Option EditorSyntax ColoringJavaScript

Here’s the function

Setup.chai
def HelloWorld(x)
{
    return "Hello " + x
}

Running the Script

Now we have a function called “HelloWord”, let’s run it.
Change the HelloWorld::init function so it looks like this:

HelloWorld::init()
bool HelloWorld::init()
{

    if ( !Layer::init() )
    {
        return false;
    }
    
    // Get the Chaiscript Engine
    auto chaiscript = CocosChai::create_chaiscript();
    // Get a reference to the function in the script that takes a string parameter, returns a string, and is called "HelloWorld"
    auto fn = chaiscript->eval<std::function<std::string (std::string)>>("HelloWorld");
    
    // Call the function
    std::string s = fn("PooperPig");

    // Log the resutl
    CCLOG("%s", s.c_str());

    return true;
}

Line 10 gets the ChaiScript Engine object, using the code in our ChaiscriptCreator.cpp.
Line 12 defines a function pointer that will give our c++ code access to the ChaiScript function “HelloWorld”.
Line 15 calls the function, putting the result in the variable ‘s’.
Finally, line 18 logs the output to XCode’s output window.

Run the program; look at the console and, with luck, you’ll see “Hello PooperPig” there – generated by your script!

More Scripting

Ok – so we did a “Hello World” script, but now let’s get a little more realistic (but not too complex!)

We already have our EntityFactory ready to create Entity’s for us, so I”m going to write a script that takes a level number as a parameter, and creates a number of entities in a different pattern depending on the level number.
There is already a C++ function called “getEntitiesForLevel” in EntityFactory. We’ll change this so that it calls the script function “SetUpLevel()”.

First, here’s the script.

Setup.chai
def setUpLevel(level)
{
    var black = "CloseNormal.png"
    var white = "CloseSelected.png"
    
    var x=0;
    var y=0;
    
    var w = 40
    var h = 40

    switch (level)
    {
        case(1)
        {
            for( x=0; x < 8; x += 2)
            {
                for( y = 0; y<3; ++y)
                {
                    Factory.getEntityOfType("Test", Vec2(20+x*w,20+y*h), black)
                }
            }
            for( x=0; x < 8; x += 2)
            {
                for( var y = 8; y>5; --y)
                {
                    Factory.getEntityOfType("Test", Vec2(20+x*w,20+y*h), white)
                }
            }

            break;
        }
        case(2)
        {
            x = 0;
            y = 0;
            Factory.getEntityOfType("Test", Vec2(20+x*w,20+y*h), white)
            x = 4
            y = 2;
            Factory.getEntityOfType("Test", Vec2(20+x*w,20+y*h), white)
            x = 3
            y = 6;
            Factory.getEntityOfType("Test", Vec2(20+x*w,20+y*h), black)
            
            break;
        }
        default
        {
        }
    }
}

It’s pretty simple stuff. Depending on the level number passed as a parameter, we create a different pattern of pieces.

As the EntityFactory will be calling functions from a script, it needs to have a ChaiScript Engine instance to use. We’ll pass an instance to it, so we need to make a change to getEntitiesForLevel to add a parameter to this method.

EntityFactory.hpp
#ifndef EntityFactory_hpp
#define EntityFactory_hpp

#include "EntityFactory.hpp"
#include "Entity.hpp"
#include "cocos2d.h"
#include "chaiscript/chaiscript.hpp"

USING_NS_CC;
class EntityFactory
{
private:
    std::function<void (int)> setUpLevel_script = NULL;
public:
    cocos2d::Vector<Entity*> entities;
    void getEntitiesForLevel(int levelNumber, chaiscript::ChaiScript &chai);

    Entity* getEntityOfType(std::string classname, Vec2 location, std::string name);
};
#endif /* EntityFactory_hpp */

We’ve added chaiscript.hpp to our #includes.
We’ve declared a std::function that takes a single int parameter and returns void, and we’ve called it “setUpLevel_script”
We’ve changed the signature of getEntitiesFor Level to include a reference to our ChaiScript engine.

EntityFactory.cpp – changes
void EntityFactory::getEntitiesForLevel(int levelNumber, chaiscript::ChaiScript &chai)
{
    if (setUpLevel_script == NULL)
    {
        setUpLevel_script = chai.eval<std::function<void (int)>>("setUpLevel");
    }
    setUpLevel_script(levelNumber);   
}

We’ve changed getEntitiesForLevel() to add a parameter to allow us to pass it a ChaiScript engine object, and the method now creates a std::function that references the ChaiScript function (setUpLevel) we just wrote – and then executes that function.

Finally, we need to make a few changes to HelloWorldScene.cpp.
Add a #include “Entity.h”, then change the init function as below

HelloWorldScene.cpp – init function
bool HelloWorld::init()
{

    if ( !Layer::init() )
    {
        return false;
    }

    // Get a reference to the Chaiscript Engine
    auto chaiscript = CocosChai::create_chaiscript();

    // Instantiate our factory
    auto factory  = new EntityFactory();

    // tell ChaisCript about our factory
    chaiscript->addGlobal(chaiscript::var(factory), "Factory");
    
    // Ask the factory to get entities for this level - pass a reference to the ChaiScript engine so that the factory can use it
    factory->getEntitiesForLevel(2, *chaiscript);
    
    // Now the factory has created all of the Entities, add their sprites as children so we can actually see them
    for (auto entity:factory->entities)
    {
        this->addChild(entity->sprite);
    }
    return true;
}

The comments are hopefully self-explanatory.
So now you can run the App. Change the first parameter in factory->getEntitiesForLevel(2, *chaiscript); to show level 1 or level 2.

Sure, it’s not pretty – but I think it shows the potential !

Level 2
Level 2

Level 1
Level 1

Scripting in cocos2d-x

The Problem:

I’m using plist files to define my terrain, and that plist can get quite large. Although it is essentially just a list of points, there’s additional data (such as the image used for the ‘sub-terrain’ and the sprite to use for the surface) too.

In some levels it would be nice to be able to:
a) Have a somewhat random terrain.
b) Calculate the terrain within given parameters
c) Have repetitive sections of terrain

Initially I thought of writing the various options in C++, then specifying various parameters in my plist file. But this has the disadvantage that, if I want to do a different type of terrain, it’s hard for me to do so without building a new version of the App.

But what if I could define the terrain and the algorithm in a text file – that could be downloaded without re-releasing the App?

This sounded like a good idea to me, so I started investigating scripting for cocos2d-x.

The most obvious solution would seem to be to use Lua – which comes with Cocos2d-x (or so it seems) but after spending some time, I couldn’t find any good info on adding Lua scripting to an existing C++ project.

Then I came across ChaiScript at http://chaiscript.com/ and https://github.com/ChaiScript/ChaiScript

So I spent quite a long time investigating, playing, swearing and asking questions of the very helpful and very very clever developer.

And now I think I have the basics…

In my next post I will document how to create a cocos2d-x project that includes ChaiScript scripts to define settings for the App.

Sometime I hate Computers

so a nice chap on the cocos2d-x site has written a nce utility function to take a string like

myFileName[0-9].png

and return 10 filenames from 0 to 9.

Handy for places where I have animations, for example.

So I implemented it, but it didn’t handle [9-0] (i.e. down from 9 to 0)

So I fixed that & tried to do a pull request

had a few problems – but got there. Then turned out my code was crap.

Made the change.

Pushed again.

During this process, XCode seemed to get its knickers in a knot.

Long and short – every single project in my Projects folder was deleted!

All of them!

Holy hell!

Symbolic Linking for Cocos

I’ve read loads of times about being able to have one version of cocos2d-x installed for all my projects – but never really followed up – but cocos is so big, having several versions really is a pain, so I decided to do it.
And it is easy!

I have an existing project at

/Users/PooperPig/Documents/Projects/MyGame

Inside this project I have the cocos2d folder, containing all the cocos2d stuff, and all the other folders like Classes, Resources and all the proj.xxx folders.

First things first, I grab the latest and greatest version of cocos2d and put it in my Projects folder, in a folder called cocos2d-x-3.7.

In my case, this is what I did to get the latest (you can just download the zip if you want – I just did this bit in case I want to make changes and submit them back to cocos2d-x)

$ git clone git://github.com/cocos2d/cocos2d-x.git
$ cd cocos2d-x
$ git checkout v3
$ ./download-deps.py
$ git submodule update --init

Now I go to my game project folder, and delete the cocos2d folder! Ok – so I really just copied it somewhere just in case – but you can delete it safely – after all you can always download it again!

So now I need to create a symbolic link from my project folder, pointing to the cocos2d-x-3.7 folder.

ln -s /Users/PooperPig/Documents/Projects/cocos2d-x-3.7 cocos2d

That’s really it! I can open up my project in XCode and run it just like normal!

The obvious question now is “What do I do when I want to upgrade the version of Cocos2d-x?”

Easy peasy.

Once you have created your new cocos2d-x-3.n directory, change to the project directory and use

ln -nfs /Users/PooperPig/Documents/Projects/cocos2d-x-3.8 cocos2d

this command simply replaces the current symbolic link with a new one.

Now you can test your project – and if it doesn’t work, you can use the ln -nfs again to point it back to old version!

A Git by any other name

Ages ago I used Git for PooperPig – but had some problems & went back to SVN (which I had used previously, and so was familiar with).

Well, while faffing about recently, I wanted to go back to a previous version – and found it relatively complex in SVN with the support of XCode (i.e. there’s no support for going back at all!)

For this (and a couple of other) reasons, I have decided to try Git again.

So, I’m giving BitBucket a go.

It’s free (which is a good price) and they have all sorts of other tools (for cash) that I might decide to use…

So far it’s proving relatively easy – although I am really puzzled by the fact that can’t find any .svn files in my project – I wanted to remove them before pushing my project to git!

Right now, I”m watching the file upload to Bitbucket (the BitBucket site is really helpful, lots of tutorials and stuff to help you start – so I’m just following their instructions)

What will be interesting is how XCode handles it once it’s there!

Wish me luck.

simplicity is Always the best idea

After struggling with new Scenes, and trying to figure out how to cleanly handle having a pause screen, I decided to step back and look at it a different way.

What do I really want to achieve?

Show a new layer, with some options on it (like continue, restart, choose a level)

so why am I messing around with scenes? well, the reason was because I thought thats how it should be done. But why can’t I just shove a pause layer on top of the game?

That’s what I just did – and it works much better, with much less code.

the only thing it doesn’t do is transitions – which if I want them, I am sure I could fake up anyway!

So I introduced a pauseGame() function…

void GameController::pauseGame()
{
	stopAllFunctions();
	Globals::holdTouchFunctions = true;
	Globals::gamePaused = true;
	this->unscheduleUpdate();
}

stopAllFunctions is just a method that stops any of the user-functions (jumping, pooping etc.) that have started already; this is important if the user presses Fart and pause – or we will hear farting int eh pause screen!

holdTouchFunctions is just a flag to see if touches are going to be processed in the game. As I use areas of the screen for control, I need to disable this when paused to avoid the pig trying to jump if that area of the screen is touched.

gamePaused is a flag that I check in the main update() method – if true, I do nothing, so nothing gets updated! Simples!

I have a corresponding unPauseGame() method too – just sets the flags to false and calls scheduleUpdate.

I had a real problem when paused that, despite resetting all physical objects, my box2d object seemed to magically retain some residual velocity.
I gave up after a while and resorted to setting it back to zero in the update method – it’s only used in debug mode when I’m editing – so no real overhead.

A New Beginning

Of course I wrote PooperPig by starting with the fun bit – the game engine itself.

 

So when you start the game it just started playing.

Then I added a menu option, so I can choose levels, and that sort of stuff.

But I just kinda shoved it in there – and it was a bit of a hack.

Then I added a spawning sound to my entities. So now when I run the game I see the menu, but hear my spawning sound – because in the background it starts then pauses the game.

So now I’m changing tack, ripping it out and replacing it with a more sensible process.

Attempt 1: Coded and hacked my way through it without thinking too much- it all worked, but there were lots of small changes all through the code. It all worked more or less, but the terrain sprites suddenly had a black background.

Couldn’t figure it out quickly, so undid most of the changes and started again – with a more methodical approach (you know, like thinking first!)

We’ll see what happens!

 

… Well, what happened was it went horribly wrong!

Can;t figure out how to do the simplest thing:

Start the game, show a ‘pause’ screen.

Select a level in the pause screen

play that level

Press a pause button

display the pause screen

either select a level or un-pause

I’m tired and I have a cold. That’s my excuse!

Maybe tomorrow!

Spawn of Animation

Up until now I have had animations for the following:

  1.     Default ,
  2.     Dying
  3.     Dead 
  4.     Flying 
  5.     Spitting 
  6.     Eating 
  7.     Farting 
  8.     Pooping

and I decided to create another for Spawning.

I don’t use cocos2d-x own Animation classes – I use  my own, because, honestly, I didn’t think the cocos2d-x animation classes gave me enough control when I first looked at them; subsequently I think maybe they do, but I have my own class which is nicely flexible!

Adding a new type is a simple matter of adding an entry to a pList, together with a string representation – so I added spawning with string “Spawning”

Now I can call Animation::startAnimation(enum::Spawning) and it looks for an animation XXXXSpawning in the collection of animations for that particular Visible Object (another of my classes).

Encountered a couple of issues.

Firstly, if you die while farting(!) I needed to stop the farting animation; I’d just used showAnimation(enum::Default);

And in my reset function I call StopFarting();

So, at the start of a level, it showed the default animation instead of the spawning animation. easily fixed.

The other problem was with jumping. I have a flying animation shown when Poopy jumps. so when he lands, detected using a sensor, I changed the animation to default. Of course, this also stuffed up the spawning animation, as during spawning, Poopy would land, and the spawning animation would stop.

Again, not a difficult fix.

Now all I have to do is actually design a spawning animation! (for testing I used the dying animation, shown backward.)

These changes also gave me a new method, continuePreviousAnimation()

so now, if I am in the middle of one animation, and go off to do something else, instead of reverting back to the default animation, I can continue with the one I was showing before.

 

There’s a hole in the sky!

Well, I thought it was about time I started blogging again – if only to make me do more to PooperPig!

I just added the ability to change the surface (floor or roof) collision mask – this will let me set up places where certain entities can go through the floor or roof.

Why would I want to do that?

My idea here is that I can put some baddies above the roof. they will be activated when PooperPig comes close to them, and start following him (if they’re that sort of baddy) but can’t get to him.

Then, at some point, there’s a hole in the roof through which they can fall.

Depending on the roof height and textures, they may be invisible to the player, so the first they will know is when a bunch of baddies drop out of the sky 🙂

First try was sort of OK. It all works, but in testing it I changed the texture of the ‘sub-terrain’ above the roof – and what actually changed was the texture below the floor!

Time to put by debugging hat on!

That was easy!

Because roof and floor are so similar, but my C++ knowledge is so limited, I have a bunch of code for handling floor and roof that is similar but not quite identical.

Of course, I should have a single method so that changes to the calculation needn’t be done in two places – but it’s quite neet the way it works & I don’t need to change it at the moment. Perhaps one day!

What had happened was that in my ‘drawRoofSubterrain()’ method I had a call to the method ‘drawSubterrainFloorPortion();

Oops!

Now that’s fixed, it looks fine (i.e. the terrain above the roof changes where the ‘hole’ should appear. Now to add a baddy!

Baddy added. He falls through the hole, fine!

Unfortunately this has revealed a bug in my baddy code; the baddy accelerates toward Poopy – but if he misses (which he does if he’s stuck above the roof) he doesn’t turn around and come back for another try!

Aha! turns out that friction was set to 0 on the roof and on the baddy so applying the opposite force never changed his direction and wold at best have bought him to a stop!

Fixed that, and (having added friction) the baddy didn’t move at all!

Of course! the force being applied for chasing Poopy was too small.

Adjusted that and now all is well. Baddy chases Poopy, while stuck above the roof, until it comes across the hole, when it drops down.