Working with scenes and triggers in Isaac

Isaac 2D  is a C++ framework based on SFML(Simple and Fast Multimedia Library) and Boost C++ Libraries that provides support for an easy development and understandable(flexible) structure even in complex scenarios for application and games.  Also, Issac it is a multi-platform library, your applications or games can compile and run on the most common operating systems: Windows, Linux, Mac OS X.

Welcome to the third Isaac tutorial, I hope you like to work to Isaac and for any information that you did not find in all the tutorials please do not hesitate to write a comment or contact me via email or complete the contact form.

Next you are going to see how to work with scenes and how to create your own trigger and use it into Isaac mechanism. Before we getting started you need to know that you should have the code from the previous tutorials in order to go further with this tutorial. So, if you did not read the previous tutorials, please do so and then get back here 🙂 .

So far we have a scene called “SceneOne” that only moves a rectangle from left to right and the definitions for the static and dynamic aspect. Next, we will create a new scene (as you can see I’m not trying to create AAA game, I just want to show you how you can work with Isaac). This new scene that we are going to create it’s called “SceneTwo”, the header is the same as for the first one(but with another private variable):

#pragma once
#include <IStaticScene.h>

class SceneTwo : public isaac::IStaticScene
{
private:
   sf::CircleShape* circle;
public:
   SceneTwo(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);

   ~SceneTwo();
};

We have all the needed method that are defined in IStaticScene, we can now start to implement them:

#include "SceneTwo.h"
#include <ITrigger.h>
#include <Frame.h>
#include <iostream>

SceneTwo::SceneTwo(std::string ac_szSceneName) :
isaac::IStaticScene(ac_szSceneName)
{
   ;
}

void SceneTwo::mp_InitScene(std::shared_ptr<sf::RenderWindow> av_xMainWindow,
std::shared_ptr<const isaac::CTransientDataCollection>& av_xTransientData)
{
   circle = new sf::CircleShape(30);
   circle->setPosition(300, 300);

   std::cout << "Scene Two - Init" << std::endl;
}

void SceneTwo::mp_InitTriggers(std::shared_ptr<isaac::CTriggerCollection>& ac_xGlobalTriggersColl)
{
   ;
}

void SceneTwo::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)
{
   
}

void SceneTwo::mp_DrawScene(std::shared_ptr<sf::RenderWindow> av_xMainWindow) const
{
   av_xMainWindow->draw(*circle);
}

void SceneTwo::mp_Release(std::shared_ptr<const isaac::CTransientDataCollection>& av_xTransientData, std::string ac_szTriggerName)
{
   delete circle;
}

SceneTwo::~SceneTwo()
{
   ;
}

So, this is our second scene, we are going to add it into the Isaac mechanism in the same mode as for the first one. Go to MyGame file and modify the DefineScenes method:

void StaticAspect::mp_DefineScenes() const
{
  const auto& firstScene = std::make_shared<SceneOne>("SceneOne");
  const auto& secondScene = std::make_shared<SceneTwo>("SceneTwo");

  mp_AddScene(firstScene);
  mp_AddScene(secondScene);
}

We have the scene added to the framework but we are unable to visualize it, because we don’t have any condition that will activate the scene. This condition that I’m talking about it is called a trigger. We can use a trigger that is already available in Isaac or we can create one of our own(we will do both). First let’s see how we can use a trigger that is already defined.

We will modify again MyGame.cpp file in order to create an instance of a trigger. The trigger that we are going to use is a positional trigger, this will activate when an element has an X or Y value, equal, grater or lesser then a given value.

void StaticAspect::mp_DefineTriggers() const
{
   const auto& firstTrigger = std::make_shared<isaac::CElementPositionTrigger>("FirstTrigger");

   mp_AddTrigger(firstTrigger);
}

The trigger is defined, is time to create a transition:

void DynamicAspect::mp_DefineScenesTransitions() const
{
   const auto& firstTransition = mf_xDefineTransition(("SceneOne"), ("FirstTrigger"), ("SceneTwo"));

   mp_AddTransition( "FirstTransition", firstTransition);
}

This transition is practically saying that when the SceneOne is active and trigger “FirstTrigger”(the positional trigger) is disturbed we will move to “SceneTwo”. Next we need to initialize the trigger inside the source scene. So, open the source code for the “SceneOne”:

void SceneOne::mp_InitTriggers(std::shared_ptr<isaac::CTriggerCollection>& ac_xGlobalTriggersColl)
{
   // This is an ITrigger instance
   const auto& lv_pTrigger = ac_xGlobalTriggersColl->mf_xGetTriggerByName(("FirstTrigger"));
   // we need to cast it to our trigger type
   const auto& lv_pPositionTrigger = std::static_pointer_cast<const isaac::CElementPositionTrigger>(lv_pTrigger);
   // define the properties that are going to define the trigger
   const isaac::PositionProp prop = { isaac::Position::en_GraterThen, isaac::Axis::en_X, 200.0 };
   // init the trigger
   lv_pPositionTrigger->mp_InitTrigger(mv_pRect, prop);
}

When you are going to run the application you should see that when the position of the X axis for the white rectangle passes 200 pixels relative to the window, scene two will be activated and scene one is going to be destroyed.

Let’s write a dummy trigger in order to get familiar with the syntax and then use it in our application. First,  add a new class in the project and name it “ElementScaleTrigger”. This trigger it will check the scale of an element an return true when the scale it will be equal with a given value. The header file should look similar to this one :

#pragma once
#include <ITrigger.h>
#include "SFML/Graphics.hpp"

class ElementScaleTrigger : public isaac::ITrigger
{
private:
   mutable sf::Transformable* mv_pElement;
   mutable sf::Vector2f mv_ScaleXY;

public:
   ElementScaleTrigger(std::string ac_szTriggerName);

   void mp_InitTrigger(sf::Transformable* ac_pShape, const sf::Vector2f& ac_scaleXY) const;

   const bool mf_bCheckTrigger(sf::Event) const override;

   virtual ~ElementScaleTrigger();
};

We have two private variable the element that will be checked and the scale value that will disturb the trigger. The source code is very simple:

#include "ElementScaleTrigger.h"

ElementScaleTrigger::ElementScaleTrigger(std::string ac_szTriggerName):
isaac::ITrigger(ac_szTriggerName)
{
   mv_ScaleXY = sf::Vector2f(1, 1);
}

void ElementScaleTrigger::mp_InitTrigger(sf::Transformable* ac_pShape, const sf::Vector2f& ac_scaleXY) const
{
   mv_pElement = ac_pShape;
   mv_ScaleXY = ac_scaleXY;
}

const bool ElementScaleTrigger::mf_bCheckTrigger(sf::Event) const
{
   const sf::Vector2f lc_Scale = mv_pElement->getScale();

   if (lc_Scale.x >= mv_ScaleXY.x && lc_Scale.y >= mv_ScaleXY.y)
     return true;
   return false;
}

ElementScaleTrigger::~ElementScaleTrigger()
{
   ;
}

In the init method we give the wanted values to the private variables and then the values are checked in “mf_bCheckTrigger” method. Let’s define the trigger and then use it in a transition from “SceneTwo” to “SceneOne”. This is going to be the same as for the first trigger, so move to MyGame.cpp and modify (don’t forget to include the ElementScaleTrigger header):

void StaticAspect::mp_DefineTriggers() const
{
   const auto& firstTrigger = std::make_shared<isaac::CElementPositionTrigger>("FirstTrigger");
   const auto& secondTrigger = std::make_shared<ElementScaleTrigger>("FirstTrigger");

   mp_AddTrigger(firstTrigger);
   mp_AddTrigger(secondTrigger);
}

And the “mp_DefineScenesTransitions” method:

void DynamicAspect::mp_DefineScenesTransitions() const
{
   const auto& firstTransition = mf_xDefineTransition(("SceneOne"), ("FirstTrigger"), ("SceneTwo"));
   const auto& secondTransition = mf_xDefineTransition(("SceneTwo"), ("SecondTrigger"), ("SceneOne"));

   mp_AddTransition( "FirstTransition", firstTransition);
   mp_AddTransition("SecondTransition", secondTransition);
}

Now all that we have to do is to init the trigger inside “SceneTwo”, and make the circle that we have added into the scene salable in time with the goal of having a functional trigger. Open SceneTwo.cpp file and do the trigger initialization like bellow:

void SceneTwo::mp_InitTriggers(std::shared_ptr<isaac::CTriggerCollection>& ac_xGlobalTriggersColl)
{
   // This is an ITrigger instance
   const auto& lv_pTrigger = ac_xGlobalTriggersColl->mf_xGetTriggerByName(("SecondTrigger"));
   // we need to cast it to our trigger type
   const auto& lv_pPositionTrigger = std::static_pointer_cast<const ElementScaleTrigger>(lv_pTrigger);
   // init the trigger
   lv_pPositionTrigger->mp_InitTrigger(circle, sf::Vector2f(4,4));
}

The trigger is initialized, the only thing left to do is to scale the circle in time to disturb the trigger when the condition is going to be fulfilled.

void SceneTwo::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)
{
   circle->setScale(circle->getScale().x + 0.025, circle->getScale().y + 0.025);
}

Isaac Game

When the scale of the circle will reach to the given values first scene will be activated and then all the things are rebooted. I hope this tutorial it was helpful for you and you understood how things are working inside Isaac framework. Cya next time.

Leave a Reply

Your email address will not be published. Required fields are marked *


*

WordPress SEO