Isaac Mechanism
A Petri net (also known as a place/transition net or P/T net) is one of several mathematical modeling languages for the description of distributed systems. A Petri net is a directed bipartite graph, in which the nodes represent transitions (i.e. events that may occur, represented by bars) and places (i.e. conditions, represented by circles). The directed arcs describe which places are pre- and/or post-conditions for which transitions (signified by arrows). Some sources[1] state that Petri nets were invented in August 1939 by Carl Adam Petri—at the age of 13—for the purpose of describing chemical processes.
Like industry standards such as UML activity diagrams, Business Process Model and Notation and EPCs, Petri nets offer a graphical notation for stepwise processes that include choice, iteration, and concurrent execution. Unlike these standards, Petri nets have an exact mathematical definition of their execution semantics, with a well-developed mathematical theory for process analysis.
A Petri net consists of places, transitions, and arcs. Arcs run from a place to a transition or vice versa, never between places or between transitions. The places from which an arc runs to a transition are called the input places of the transition; the places to which arcs run from a transition are called the output places of the transition.
So, this is basically the mechanism that Isaac is using. The places defined by a Petri network are the scenes in our game/application the transitions between the scenes are made by the so called Isaac Triggers and the arcs are the transition directions. This is basically the model that every application is trying to obtain in one way or another.
Isaac Code
Assuming that you already know how to put in place an Isaac project, (if not you can read Getting Started with Isaac) I will just go to coding from now on. Because we are not going to create a big project or so, we are going to create just a few classes in order to understand the basics. So, create a new project in visual studio and link all the needed library as described in the first tutorial, after you do this, create a class in your project and name it “MyGame“.
In the header of your class this code is needed :
#pragma once
#include <defines.h>
#include <IStaticAspect.h>
#include <IDynamicAspect.h>
#include <Game.h>
class EXPORT_API StaticAspect : public isaac::IStaticAspect
{
public:
StaticAspect();
void mp_DefineScenes() const;
void mp_DefineTriggers() const;
void mp_DefineTransientData() const;
~StaticAspect();
};
class EXPORT_API DynamicAspect : public isaac::IDynamicAspect
{
public:
DynamicAspect();
void mp_DefineScenesTransitions() const;
void mp_DefineInitialScene() const;
~DynamicAspect();
};
extern "C"
{
EXPORT_API int CreateGame();
};
In order to fire up Isaac you need to have defined two objects: a static aspect and a dynamic one. This is what you could see in the example above. First lets take a look at the StaticAspect class, where you see that three methods are defined (you can find more info at the wiki page) :
- mp_DefineScenes – this is where you will define your scenes that are going to be involved in you game/application. Those are the places equivalent from the Petri network theory.
- mp_DefineTriggers – the triggers that will be involved in the game dynamics will be defined inside this method.
- mp_DefineTransientData – data that is needed to be available/passed for all the scenes it will be defined in this section
And the DynamicAspect:
- mp_DefineScenesTransitions – inside this method you are going to define the transitions between the scenes using the already defined triggers.
- mp_DefineInitialScene – define the name of the initial scene
In this file you can see that there is also an external method defined, inside this method we will start Isaac, just look at the source code bellow 🙂
And the source :
#include "MyGame.h"
StaticAspect::StaticAspect()
{
;
}
void StaticAspect::mp_DefineScenes() const
{
;
}
void StaticAspect::mp_DefineTriggers() const
{
;
}
void StaticAspect::mp_DefineTransientData() const
{
;
}
StaticAspect::~StaticAspect()
{
;
}
/////////////////////////////////////////////////////////////////////////////////////////
DynamicAspect::DynamicAspect()
{
;
}
void DynamicAspect::mp_DefineScenesTransitions() const
{
;
}
void DynamicAspect::mp_DefineInitialScene() const
{
;
}
DynamicAspect::~DynamicAspect()
{
;
}
/////////////////////////////////////////////////////////////////////////////////////////
int CreateGame()
{
std::shared_ptr< isaac::IDynamicAspect> lv_xDynAspect = std::make_shared<DynamicAspect>();
lv_xDynAspect->mp_InsertStaticAspect(std::make_shared<StaticAspect>());
isaac::CGame::instance().mp_DefineGameAspect(lv_xDynAspect);
isaac::CGame::instance().mp_Start(("Game"), 800, 600);
return 0;
}
Now we can compile and no errors should be generated (in case we see same errors we need to go back and check what we did wrong). In order to run this code we need to pass this DLL file that we just created to the Isaac_Loader.exe, we can do this from a CMD or from the Visual Studio. For those who work in Visual Studio, you just need to add the Isaac_Loader.exe from the Isaac output file, as an existing project inside your solution (right click to your solution – > Add – > Existing project and then navigate to the Isaac_Loader.exe), once you see the Isaac_Loader in your solution set it as start up project. After that right click on it and go to the properties and add as arguments the path to the dll file (without the DLL extension) and the name of the external function in the following form: -b Path_to_the_DLL _file -f External_function (see example in the picture bellow)
As you can see in the above picture the arguments that I have are : -b D:\Projects\Isaac_Tutorial\GettingStarted\IsaacTest -f CreateGame. First argument is the path to the DLL including the DLL name, but without the extension (-b comes from binary), the second argument is the name to the external method (-f comes from function).
You need to make sure that besides the Isaac DLLs are the SFML DLLs.
If everything is OK you can now try to run Loader with the specific arguments. A black window should open with “Game” as title and a resolution if 800×600. It is just a black window because we did not define any scenes yet.
Isaac Scene
In this section you are going to see how a scene is working. First of all you need to add a new class in your project and name it “SceneOne”, the class header:
#pragma once
#include <IStaticScene.h>
class SceneOne : public isaac::IStaticScene
{
private:
sf::RectangleShape* mv_pRect;
public:
SceneOne(std::string ac_szSceneName);
void mp_InitScene(std::shared_ptr<sf::RenderWindow> av_xMainWindow,
std::shared_ptr<const isaac::CTransientDataCollection>& av_xTransientData);
void mp_InitTriggers(std::shared_ptr<isaac::CTriggerCollection>& ac_xGlobalTriggersColl);
void mp_UpdateScene(std::shared_ptr<sf::RenderWindow> av_pMainWindow,
std::shared_ptr<const isaac::CTransientDataCollection>& av_xTransientData,
sf::Event av_eventSFMLEvent,
bool& av_bReturnedBool_WindowClosed);
void mp_DrawScene(std::shared_ptr<sf::RenderWindow> av_pMainWindow) const;
void mp_Release(std::shared_ptr<const isaac::CTransientDataCollection>& av_xTransientData, std::string ac_szTriggerName);
~SceneOne();
};
We have a couple of methods that we need to implement (for more information please see the wiki):
- mp_InitScene – this method is the first one that is going to be disturbed when this scene is going to “go live”
- mp_InitTriggers – in this method we are going to define all the triggers, but you will learn more about this in another tutorial
- mp_UpdateScene – this is the method that is going to run continuously as long as the scene it will be active
- mp_DrawScene – all the object that you need to be displayed are going to be draw in this section
- mp_Release – this method is the last one that it will disturbed before the dead of the scene
Now the source code:
#include "SceneOne.h"
#include <ITrigger.h>
#include <iostream>
SceneOne::SceneOne(std::string ac_szSceneName) :
isaac::IStaticScene(ac_szSceneName)
{
;
}
void SceneOne::mp_InitScene(std::shared_ptr<sf::RenderWindow> av_xMainWindow,
std::shared_ptr<const isaac::CTransientDataCollection>& av_xTransientData)
{
mv_pRect = new sf::RectangleShape(sf::Vector2f(40, 40));
std::cout<< "Scene one - Init" << std::endl;
}
void SceneOne::mp_InitTriggers(std::shared_ptr<isaac::CTriggerCollection>& ac_xGlobalTriggersColl)
{
;
}
void SceneOne::mp_UpdateScene(std::shared_ptr<sf::RenderWindow> av_xMainWindow,
std::shared_ptr<const isaac::CTransientDataCollection>& av_xTransientData,
sf::Event av_eventSFMLEvent,
bool& av_bReturnedBool_WindowClosed)
{
av_bReturnedBool_WindowClosed = false;
mv_pRect->move(1, 0);
}
void SceneOne::mp_DrawScene(std::shared_ptr<sf::RenderWindow> av_xMainWindow) const
{
av_xMainWindow->draw(*mv_pRect);
}
void SceneOne::mp_Release(std::shared_ptr<const isaac::CTransientDataCollection>& av_xTransientData, std::string ac_szTriggerName)
{
delete mv_pRect;
}
SceneOne::~SceneOne()
{
;
}
In order to add this scene into Isaac, we need to define it into the static aspect and then (because we have only one scene) define it as the initial stage. Go back to the MyGame file and modify it as bellow.
In the “mp_DefineScenes” you need to add:
void StaticAspect::mp_DefineScenes() const
{
const auto firstScene = std::make_shared<SceneOne>("SceneOne");
mp_AddScene(firstScene);
}
And “mp_DefineInitialScene” should look:
void DynamicAspect::mp_DefineInitialScene() const
{
mv_pInitialScene = mf_xDefineInitialScene(("SceneOne"));
}
After doing this you should see a white square on the screen, that is moving from left to right.