PDA

View Full Version : Entity Layouts via XML!!!



BigBMan
10-27-2011, 05:19 AM
Hello fellow Proton-ites! I'm sharing with you a development I hope you'll find very useful...

Entity Layouts via XML!!!

I have modified the RTSimpleApp example to load everything via XML.
Click here to get the files (http://www.mediafire.com/?cls2o8mcr6o46)

As an example, here is what the MainMenu looks like:


<Entity name="MainMenu">

<!-- AddFocusIfNeeded()-->
<EntityComponent class="FocusUpdateComponent">
<EntityComponent class="FocusRenderComponent">
<EntityComponent class="FocusInputComponent">
<EntityComponent class="CustomInputComponent" keycode="(uint32)500000">

<Entity name="ParticleTest" pos2d="(vec2f)50,40">
<EntityComponent class="TextRenderComponent" text="Particle Test">
<EntityComponent class="TouchHandlerComponent">
<EntityComponent class="Button2DComponent">
</Entity>

<Entity name="InputTest" pos2d="(vec2f)50,85">
<EntityComponent class="TextRenderComponent" text="Text Input Test">
<EntityComponent class="TouchHandlerComponent">
<EntityComponent class="Button2DComponent">
</Entity>

<Entity name="TouchTest" pos2d="(vec2f)50,130">
<EntityComponent class="TextRenderComponent" text="Multitouch Input Test">
<EntityComponent class="TouchHandlerComponent">
<EntityComponent class="Button2DComponent">
</Entity>

<Entity name="Debug" pos2d="(vec2f)50,175">
<EntityComponent class="TextRenderComponent" text="Debug and music test">
<EntityComponent class="TouchHandlerComponent">
<EntityComponent class="Button2DComponent">
</Entity>

<Entity name="About" pos2d="(vec2f)50,220">
<EntityComponent class="TextRenderComponent" text="About (scroll bar test)">
<EntityComponent class="TouchHandlerComponent">
<EntityComponent class="Button2DComponent">
</Entity>

</Entity>


It is loaded like this:


Entity * MainMenuCreate(Entity *pParentEnt)
{
CreateXMLLayoutEntity(pParentEnt, GetBaseAppPath() + "interface/MainMenuLayout.xml");

Entity* pBG = pParentEnt->GetEntityByName("MainMenu");

EntityComponent *pComp = pBG->GetComponentByName("CustomInput");
pComp->GetFunction("OnActivated")->sig_function.connect(1, boost::bind(&App::OnExitApp, GetApp(), _1));

Entity* pButtonEntity;

pButtonEntity = pBG->GetEntityByName("ParticleTest");
pButtonEntity->GetShared()->GetFunction("OnButtonSelected")->sig_function.connect(&MainMenuOnSelect);

pButtonEntity = pBG->GetEntityByName("InputTest");
pButtonEntity->GetShared()->GetFunction("OnButtonSelected")->sig_function.connect(&MainMenuOnSelect);

pButtonEntity = pBG->GetEntityByName("TouchTest");
pButtonEntity->GetShared()->GetFunction("OnButtonSelected")->sig_function.connect(&MainMenuOnSelect);

pButtonEntity = pBG->GetEntityByName("Debug");
pButtonEntity->GetShared()->GetFunction("OnButtonSelected")->sig_function.connect(&MainMenuOnSelect);

pButtonEntity = pBG->GetEntityByName("About");
pButtonEntity->GetShared()->GetFunction("OnButtonSelected")->sig_function.connect(&MainMenuOnSelect);

SlideScreen(pBG, true);
return pBG;
}


I added these functions to EntityUtils.cpp:




//
// XML Layout functions
//

//
// Create a specific EntityComponent instance from class name string
//
EntityComponent* CreateEntityComponent( string strClassName, EntityComponent* (*CustomComponentClassFunction)(string) )
{
if( strClassName == "ArcadeInputComponent" ) return new ArcadeInputComponent();
else if( strClassName == "Button2DComponent" ) return new Button2DComponent();
else if( strClassName == "CustomInputComponent" ) return new CustomInputComponent();
else if( strClassName == "DPadComponent" ) return new DPadComponent();
else if( strClassName == "EmitVirtualKeyComponent" ) return new EmitVirtualKeyComponent();
else if( strClassName == "FilterComponent" ) return new FilterComponent();
else if( strClassName == "FilterInputComponent" ) return new FilterInputComponent();
else if( strClassName == "FocusInputComponent" ) return new FocusInputComponent();
else if( strClassName == "FocusRenderComponent" ) return new FocusRenderComponent();
else if( strClassName == "FocusUpdateComponent" ) return new FocusUpdateComponent();
else if( strClassName == "HTTPComponent" ) return new HTTPComponent();
else if( strClassName == "InputTextRenderComponent" ) return new InputTextRenderComponent();
else if( strClassName == "InterpolateComponent" ) return new InterpolateComponent();
else if( strClassName == "LogDisplayComponent" ) return new LogDisplayComponent();
else if( strClassName == "OverlayRenderComponent" ) return new OverlayRenderComponent();
else if( strClassName == "ProgressBarComponent" ) return new ProgressBarComponent();
else if( strClassName == "RectRenderComponent" ) return new RectRenderComponent();
else if( strClassName == "RenderClipComponent" ) return new RenderClipComponent();
else if( strClassName == "ScrollBarRenderComponent" ) return new ScrollBarRenderComponent();
else if( strClassName == "ScrollComponent" ) return new ScrollComponent();
else if( strClassName == "SelectButtonWithCustomInputComponent" ) return new SelectButtonWithCustomInputComponent();
else if( strClassName == "SliderComponent" ) return new SliderComponent();
else if( strClassName == "TapSequenceDetectComponent" ) return new TapSequenceDetectComponent();
else if( strClassName == "TextBoxRenderComponent" ) return new TextBoxRenderComponent();
else if( strClassName == "TextRenderComponent" ) return new TextRenderComponent();
else if( strClassName == "TouchDragComponent" ) return new TouchDragComponent();
else if( strClassName == "TouchHandlerComponent" ) return new TouchHandlerComponent();
else if( strClassName == "TouchStripComponent" ) return new TouchStripComponent();
else if( strClassName == "TrailRenderComponent" ) return new TrailRenderComponent();
else if( strClassName == "TyperComponent" ) return new TyperComponent();
else if( strClassName == "UnderlineRenderComponent" ) return new UnderlineRenderComponent();
else if( CustomComponentClassFunction != NULL )
{
return CustomComponentClassFunction(strClassName);
}
else
return NULL;
}

//
// parses custom string formats for defining Variant values
//
//enum eType
//{
// TYPE_UNUSED,
// TYPE_FLOAT, = "(float)23.45"
// TYPE_STRING, = "This is a string"
// TYPE_VECTOR2, = "(vec2f)34.5, 23.4444"
// TYPE_VECTOR3, = "(vec3f)3.4, 5.4, 2"
// TYPE_UINT32, = "(uint32)3"
// TYPE_ENTITY, = UNSUPPORTED
// TYPE_COMPONENT, = UNSUPPORTED
// TYPE_RECT, = "(rec2f)3.4, 4.5, 5.4, 3"
// TYPE_INT32 = "(int32)234"
//
//};
void SetVariantFromFormattedString(Variant* pVar, string strVariant)
{
// TYPE_FLOAT, = "(float)23.45"
if( strVariant.substr(0, 7) == "(float)" )
{
string strValue = strVariant.substr(7, strVariant.size());

float f = atof(strValue.c_str());

pVar->Set(f);
}
// TYPE_VECTOR2, = "(vec2f)34.5, 23.4444"
else if( strVariant.substr(0, 7) == "(vec2f)" )
{
string strValues = strVariant.substr(7, strVariant.size());

vector<string> v = StringTokenize(strValues, ",");

string strX = (string)v[0];
string strY = (string)v[1];

float f1 = atof(strX.c_str());
float f2 = atof(strY.c_str());

pVar->Set(f1, f2);
}
// TYPE_VECTOR3, = "(vec3f)3.4, 5.4, 2"
else if( strVariant.substr(0, 7) == "(vec3f)" )
{
string strValues = strVariant.substr(7, strVariant.size());

vector<string> v = StringTokenize(strValues, ",");

string strX = (string)v[0];
string strY = (string)v[1];
string strZ = (string)v[3];

float f1 = atof(strX.c_str());
float f2 = atof(strY.c_str());
float f3 = atof(strZ.c_str());

pVar->Set(f1, f2, f3);
}
// TYPE_UINT32, = "(uint32)3"
else if( strVariant.substr(0, 8) == "(uint32)" )
{
string strValue = strVariant.substr(8, strVariant.size());

uint32 n = atoi(strValue.c_str());

pVar->Set(n);
}
// TYPE_RECT, = "(rec2f)3.4, 4.5, 5.4, 3"
else if( strVariant.substr(0, 7) == "(rec2f)" )
{
string strValues = strVariant.substr(7, strVariant.size());

vector<string> v = StringTokenize(strValues, ",");

float f1 = atof(((string)v[0]).c_str());
float f2 = atof(((string)v[1]).c_str());
float f3 = atof(((string)v[2]).c_str());
float f4 = atof(((string)v[3]).c_str());

pVar->Set(CL_Rectf(f1, f2, f3, f4));
}
// TYPE_INT32 = "(int32)234"
else if( strVariant.substr(0, 7) == "(int32)" )
{
string strValue = strVariant.substr(8, strVariant.size());

int32 n = atoi(strValue.c_str());

pVar->Set(n);
}
// it is a string
else
{
pVar->Set(strVariant);
}

LogMsg("Set Variant %s", pVar->GetString().c_str());

}

//
// Recursive function for parsing an Entity or EntityComponent
//
void ReadLayoutXML( Entity* pParentEnt, IrrXMLReader* xml, EntityComponent* (*CustomComponentClassFunction)(string))
{
while(xml && xml->read())
{
switch(xml->getNodeType())
{
case EXN_ELEMENT:
{
string strName = xml->getNodeName();

if( strName == "Entity" )
{
//ReadEntityXML(pParentEnt, xml);
Entity* pEnt = pParentEnt->AddEntity(new Entity());

for( int i=0; i<xml->getAttributeCount(); i++ )
{
string strName = xml->getAttributeName(i);
string strValue = xml->getAttributeValue(i);
LogMsg("Adding Entity Attribute: %s=%s", strName.c_str(), strValue.c_str());

if( strName == "name" )
{
// special case for name field which isn't actually in the variant db
pEnt->SetName(strValue);
}
else
{
// sets the variant based on how the value is cast as a string
SetVariantFromFormattedString(pEnt->GetVar(strName), strValue);
}
}

if( xml->read() )
{
ReadLayoutXML(pEnt, xml, CustomComponentClassFunction);
}
}
else if( strName == "EntityComponent" )
{
string strClass = xml->getAttributeValue("class");
EntityComponent* pComp = CreateEntityComponent(strClass, CustomComponentClassFunction);

pParentEnt->AddComponent(pComp);

if( pComp != NULL )
{
//ReadEntityComponentXML(pParentEnt, xml);
for( int i=0; i<xml->getAttributeCount(); i++ )
{
string strName = xml->getAttributeName(i);
string strValue = xml->getAttributeValue(i);

/*if( strName == "fileName" )
{
if( strValue.substr(0, 1) == "./" )
strValue = GetBaseAppPath() + strValue.substr(2, strValue.size());
else
strValue = GetBaseAppPath() + strValue;
}*/

LogMsg("Adding EntityComponent Attribute: %s=%s", strName.c_str(), strValue.c_str());

SetVariantFromFormattedString(pComp->GetVar(strName), strValue);
}
}
}
else
{
LogError("Unknown Element type: %s", strName.c_str());
}

break;
}
case EXN_CDATA:
{
break;
}
case EXN_ELEMENT_END:
{
//break;
return;
}
case EXN_NONE:
case EXN_TEXT:
case EXN_COMMENT:
case EXN_UNKNOWN:
{
break;
}
}
}
}

//
// Starts parsing XML lauyout file and creates Entities and Entity components within the parent
//
void CreateXMLLayoutEntity(Entity* pParentEnt, string strFile, EntityComponent* (*CustomComponentClassFunction)(string))
{
// create the reader using one of the factory functions
//std::string strFile = GetBaseAppPath() + "interface/layout.xml";
LogMsg("XML FILE: %s", strFile.c_str());
IrrXMLReader* xml = createIrrXMLReader(strFile.c_str());

ReadLayoutXML(pParentEnt, xml, CustomComponentClassFunction);

// delete the xml parser after usage
delete xml;
}

BigBMan
10-27-2011, 06:29 AM
BTW, I would love to contribute more of this directly to a dev branch if you're willing to open this up. Let's go open source!

ideas for updates:
* setting forced resolutions from any Entity
* percent values for sizes and positions (i.e. size2d="(vec2f)80%,70%")
* better support for existing enum values
* a GUI layout tool written in Proton!!!
* HTML5 converter
* basic scripting, source code generators

Seth
10-28-2011, 01:48 PM
Nice work BigB,

Sure, I can give you write access to the svn, but only one serious rule.. :prophet: a requirement is that any changes that break existing builds must be talked about first - otherwise all examples on all platforms must be tweaked and tested (plus my 10 or so private projects) and that's best avoided when at all possible!! :sweatdrop:

Your changes use Irrlicht's XML right? I don't want that to be a requirement for the other examples, so what if we remove your functions from EntityUtils.* and create a .. XMLLayoutUtils.h instead?

Then apps that need it can include it and the irrlicht xml stuff only if needed. The shared code could go in shared/addons/XMLLayout, and the example could go in /addons/XMLLayout. I've been using this addon dir already for some new iPhone joystick-addon code.

Do you IRC at all? I hang out all day so it might be nice to chat about stuff when needed.. can find me in #clanlib on irc.freenode.net as SethR.

BigBMan
10-28-2011, 05:48 PM
I'll find you on IRC (just joined as BigBMan.) I agree with your conditions :sweatdrop:, but I think setting up a dev branch might be our best bet.

... and what about setting this up on GitHub or Google code?