# Unity Void* Messaging for Classes (Now W/ Source Code)

## Recommended Posts

Drew_Benton    1861
(Sorry for all the source boxes - only way to read it nicely [headshake]) Ok so I've had quite a few ideas recently that just have not worked out. So I decided to start at the point at which they failed - messaging. Now I would like to make an easy way to be able to have all my objects interact with each other. Don't ask me why, I just want to see if this concept is possible. So before I begin, I know a lot of people who see this code are going to have heart attacks - I am 100% aware of safety issues, I just want you to know that. Here's the idea - I start out with having a generic messaging structure -

struct IMessage
{
std::vector<void*> ptr;
};

I then have my base class that acts as the "Root" of all other classes:
class Actor
{
private:
typedef void (Actor::*ActorPtr)( const IMessage *data );
ActorPtr mActorPtr;
std::map< std::string, Actor::ActorPtr> Functions;

public:
Actor();
~Actor();

virtual void Create( const IMessage *data = 0 );
virtual void Render( const IMessage *data = 0 );
virtual void Destroy( const IMessage *data = 0 );
virtual void Message( std::string message, const IMessage *data = 0);
};


Now some important things to point out. It contains a std::map of function pointers. This is part of the messaging system, as you will see shortly. Now as for function implemtations, there are two main functions that are the most important -

void Actor::Message( std::string message, const IMessage *data )
{
if( Functions.find( message ) != Functions.end() )
{
(this->*Functions[ message ])( data );
}
else
{
std::cout << std::endl << "Non processed message: " <<  message << std::endl;
}
} 
This function is what receives a message and then calls the appropriate function with the passed data. Next -

Actor::Actor()
{
Functions["Create"] = &Actor::Create;
Functions["Render"] = &Actor::Render;
Functions["Destroy"] = &Actor::Destroy;
}

The ctor, for the time being, registers what function calls are mapped to what string. Now as for use, it is very simple. The first way in messaging a class, is without data:

Actor A1;
A1.Message("Create");
A1.Message("Render");
A1.Message("Destroy");

To which Actor::Create, Actor::Render, and Actor::Destroy are called. The second way in messaging a class, is with data:

IMessage tmp;
tmp.ptr.resize( 1, 0 );
tmp.ptr[0] = (void*)("This is a test");
A1.Message("Print", &tmp );

Now as for handling the data sent in, it is up to the class to properly handle it. Once again I am aware of safety issues, but that is not my concern as of now. Now that was just the Base Actor class, other classes are derived from there and work in a similar way. The basic idea is that any class can interact with any other class without knowing about it. This means that you can send unit data to your GUI wihtout having to include in the header files for your units and GUI - thus greatly reducing any dependecies. Here is a sample Main.CPP file:
#include "Actor.h"

Actor *Actor1;
Box B1;

int main( int argc, char* argv[] )
{
Actor1 = &B1;

Actor1->Message( "Create" );

IMessage tmp;

tmp.ptr.resize( 1, 0 );
tmp.ptr[0] = (void*)("File1.bmp");
Actor1->Message( "SetTexture", &tmp );

Actor1->Message( "12345" );

tmp.ptr.resize( 3, 0 );
tmp.ptr[0] = (void*)("5");
tmp.ptr[1] = (void*)("0");
tmp.ptr[2] = (void*)("-8");
Actor1->Message( "SetPosition", &tmp );

tmp.ptr.resize( 3, 0 );
float x = 0.25f, y = 1.0f, z = -0.75f;
tmp.ptr[0] = (void*)(&x);
tmp.ptr[1] = (void*)(&y);
tmp.ptr[2] = (void*)(&z);
Actor1->Message( "SetRotate", &tmp );

Actor1->Message( "Render" );

Actor1->Message( "Destroy" );

return 0;
}


When ran, the output is:
Call to Box::Create

Call to Box::SetTexture
FileName: File1.bmp

Non processed message: 12345

Call to Box::SetPosition
X: 5
Y: 0
Z: -8

Call to Box::SetRotate
X: 0.25
Y: 1
Z: -0.75

Call to Box::Render

Call to Box::Destroy
Press any key to continue


Now idealy, an Object Factory would be used so there would not be an explicit Box variable, but this is just a quick demo. What are your reactions to this idea? Sound promising or too much hassal (in terms of use, I'm the one programming it [wink]). Thanks for your time! Unlike my other posts, I will try not to actively defend anything brought up - I will let a few responses be made then reply to them, just so it doesn't grow to such a large size that no one want's to read it (like my last post [lol]) I am open to ALL comments and suggestions - I want to hear what you developers think. - Drew [Edited by - Drew_Benton on March 31, 2005 6:24:59 PM]

##### Share on other sites
Deyja    920
You may want to consider using boost's signal/slot library. It was designed for exactly this sort of thing. Also; you can tack-on some runtime type safety using a class like boost::any.

##### Share on other sites
nilkn    960
Yep, messaging is a very good and reliable way for communication between disparate objects. Your method will certainly work, and there is nothing wrong with it. Here's a few suggestions though:

1. Try to seperate the messaging system from the game entities (Actor's in this case). This gives more flexibility and also easier reuse of the system.

2. Maybe allow certain messages to have a "delay", in that they aren't handled immediately but wait to be executed until a timer has expired.

3. Periodic messages (or so I call them) -- messages that are executed repeatedly on a set time interval forever, until otherwise specified.

I have literally just finished writing my own messaging system, so I it could be fun to look at each others implementations. I know I did mine considerably differently. [smile] Here's the code for my system (not that it's not fully optimized yet -- I literaly just finished this):

Class.hpp
#ifndef __XRGAME_CLASS_H__#define __XRGAME_CLASS_H__#include <list>#include <string>using std::list;using std::string;struct xrRTTI;class xrClass;typedef list<xrClass*> xrClassList;struct xrRTTI{	xrRTTI() :		ClassName(""),		Parent(NULL)	{	}	xrRTTI(const string& ClassName_, const xrRTTI* Parent_) :		ClassName(ClassName_),		Parent(Parent_)	{	}	const string ClassName;	const xrRTTI* Parent;};#define CLASS_DECLARE 	private: 		static xrRTTI* m_RTTI; 	public: 		static const xrRTTI& GetRTTI(); 		static const string& GetClassName();#define CLASS_DEFINE_BASE(classname) 	xrRTTI* classname::m_RTTI = new xrRTTI(#classname, NULL); 	const xrRTTI& classname::GetRTTI() { 		return *m_RTTI; 	} 	const string& classname::GetClassName() { 		return m_RTTI->ClassName; 	}#define CLASS_DEFINE(classname, parentname) 	xrRTTI* classname::m_RTTI = new xrRTTI(#classname, &(parentname::GetRTTI())); 	const xrRTTI& classname::GetRTTI() { 		return *m_RTTI; 	}class xrClass{	CLASS_DECLARE;public:	xrClass();	virtual ~xrClass();	inline const int AddRef();	inline const int Release();	inline const int GetRefCount() const;	const bool IsExactly(xrClass& Class) const;	const bool DerivesFrom(xrClass& Class) const;	inline const int GetUID() const;	static const xrClassList& GetClassList();	static void CollectGarbage();	static void CollectRemainingObjects();	static const int CreateNewUID();private:	static xrClassList s_ClassList;	static int s_NextUID;	const bool DerivesFrom(xrRTTI& RTTI1, xrRTTI& RTTI2) const;protected:	int m_UID;	int m_RefCount;};inline const int xrClass::AddRef(){	return ++m_RefCount;}inline const int xrClass::Release(){	--m_RefCount;	int RefCountCpy = m_RefCount;	if (m_RefCount <= 0) {		s_ClassList.remove(this);		delete this;	}	return RefCountCpy;}inline const int xrClass::GetRefCount() const{	return m_RefCount;}inline const int xrClass::GetUID() const{	return m_UID;}#endif

Class.cpp
#include "Class.hpp"xrClassList xrClass::s_ClassList = xrClassList();int xrClass::s_NextUID = 0;CLASS_DEFINE_BASE(xrClass);xrClass::xrClass() :	m_RefCount(0),	m_UID(xrClass::CreateNewUID()){	s_ClassList.push_back(this);}xrClass::~xrClass(){	s_ClassList.remove(this);}int const xrClass::CreateNewUID(){	return ++s_NextUID;}void xrClass::CollectGarbage(){	xrClassList::iterator ClsIt;	for (ClsIt = s_ClassList.begin(); ClsIt != s_ClassList.end(); ++ClsIt) {		xrClass* Cls = (*ClsIt);		if (Cls->GetRefCount() <= 0) {			ClsIt = s_ClassList.erase(ClsIt);			delete Cls;		}	}}void xrClass::CollectRemainingObjects(){	xrClassList::iterator ClsIt;	for (ClsIt = s_ClassList.begin(); ClsIt != s_ClassList.end(); ++ClsIt) {		xrClass* Cls = (*ClsIt);		ClsIt = s_ClassList.erase(ClsIt);		delete Cls;	}}const bool xrClass::IsExactly(xrClass& Class) const{	return (m_RTTI == &(Class.GetRTTI()));}const bool xrClass::DerivesFrom(xrClass& Class) const{	if (IsExactly(Class))		return true;	return DerivesFrom(const_cast<xrRTTI&>(*m_RTTI->Parent), 		               const_cast<xrRTTI&>(Class.GetRTTI()));}const bool xrClass::DerivesFrom(xrRTTI& RTTI1, xrRTTI& RTTI2) const{	if (&RTTI1 == &RTTI2)		return true;	return DerivesFrom(const_cast<xrRTTI&>(*RTTI1.Parent), RTTI2);}

Ptr.hpp
#ifndef __XRGAME_PTR_H__#define __XRGAME_PTR_H__#include <assert.h>template <class Type>class xrPtr{public:	xrPtr() :	  m_Object(NULL)	{	}	xrPtr(const Type* Object) :		m_Object(const_cast<Type*>(Object))	{		if (m_Object) m_Object->AddRef();	}	xrPtr(const xrPtr<Type>& Object) :		m_Object(Object.m_Object)	{		if (m_Object != NULL) m_Object->AddRef();	}	~xrPtr()	{		if (m_Object != NULL)			m_Object->Release();	}	inline operator =(Type* Object)	{		if (m_Object) m_Object->Release();		m_Object = Object;		if (m_Object) m_Object->AddRef();		return 1;	}	inline operator =(const xrPtr<Type>& Object)	{				if (m_Object) m_Object->Release();		m_Object = Object.m_Object;		if (m_Object) m_Object->AddRef();		return 1;	}	inline operator Type*() const	{		return m_Object;	}	inline Type& operator *() const	{		assert(m_Object);		return *m_Object;	}	inline Type* operator ->() const	{		assert(m_Object);		return m_Object;	}	inline const bool Valid() const	{		return (m_Object != NULL);	}	inline const bool operator !() const	{		return (!m_Object);	}	inline const bool operator ==(Type* Object) const	{		return (m_Object == Object);	}	inline const bool operator ==(const xrPtr<Type>& Object) const	{		return (m_Object == Object.m_Object);	}	inline const bool operator !=(Type* Object) const	{		return (m_Object != Object);	}	inline const bool operator !=(const xrPtr<Type>& Object) const	{		return (m_Object != Object.m_Object);	}private:	Type* m_Object;};#endif

MessageSystem.hpp
#ifndef __XRGAME_MESSAGESYSTEM_H__#define __XRGAME_MESSAGESYSTEM_H__#include <vector>#include <map>#include <string>#include "Class.hpp"#include "Ptr.hpp"using std::vector;using std::map;using std::string;class xrCallback;struct xrMessageDef;struct xrMsgListenerReg;struct xrMessage;class xrMessagePump;typedef xrPtr<xrCallback> xrCallbackPtr;typedef xrPtr<xrMsgListenerReg> xrMsgListenerRegPtr;typedef xrPtr<xrMessageDef> xrMessageDefPtr;typedef xrPtr<xrMessage> xrMessagePtr;typedef map<int, xrMsgListenerRegPtr> xrMsgListenerRegMap;typedef map<string, xrMessageDefPtr> xrMessageDefMap;typedef vector<xrMessagePtr> xrMessageList;typedef map<xrMessagePtr, float> xrPeriodicMsgDelayMap;class xrCallback : public xrClass{	CLASS_DECLARE;public:	virtual void Function(xrMessagePtr Msg, xrClass* Parent) = 0; // must use unmanaged pointer for typecasting};struct xrMessageDef : public xrClass{	CLASS_DECLARE;public:	xrMessageDef() :		Name(""),		MsgListenerRegMap(xrMsgListenerRegMap())	{	}	string Name;	xrMsgListenerRegMap MsgListenerRegMap;};struct xrMsgListenerReg : public xrClass{	CLASS_DECLARE;public:	xrMsgListenerReg() :		Parent(NULL),		CBack(NULL)	{	}	xrPtr<xrClass> Parent;	xrCallbackPtr CBack;};struct xrMessage : public xrClass{	CLASS_DECLARE;public:	xrMessage() :		TimeCreated(0),		DestID(-1),		Priority(MESSAGE_PRIORITY_MEDIUM),		Sender(NULL),		Message(""),		Arg1(NULL),		Arg2(NULL),		Delivered(false)	{	}	enum 	{		MESSAGE_PRIORITY_AMBIENT = 0,		MESSAGE_PRIORITY_LOW,		MESSAGE_PRIORITY_MEDIUM,		MESSAGE_PRIORITY_HIGH,		MESSAGE_PRIORITY_IMMEDIATE,		MESSAGE_PRIORITY_PERIODIC,	};	float TimeCreated;	float Delay;	int DestID;	int Priority;	bool Delivered;	xrPtr<xrClass> Sender;	string Message;	xrPtr<xrClass> Arg1;	xrPtr<xrClass> Arg2;};class xrMessagePump{public:	static void Init(bool ArbitrateMessages = true);	static void Shutdown();	static void SendMessage(xrMessagePtr Message);	static void SendMessage(int DestID, xrPtr<xrClass> Sender, string Message, float Delay, int Priority, xrPtr<xrClass> Arg1);	static void RegisterMessage(string Message);	static void UnregisterMessage(string Message);	static void RegisterClassAsListener(int UID, xrPtr<xrClass> Parent, xrCallbackPtr CBack, string Message);	static bool IsClassListeningFor(int UID, string Message);	static void UnregisterClassAsListener(int UID, string Message);	static void UnregisterAllListenersFor(string Message);	static void ProcessMessages(float TimeStep);	static const bool GetArbitrateMessages();	static void EnableMessageArbitration();	static void DisableMessageArbitration();private:	static xrMessageDefMap s_MessageDefMap;	static xrMessageList s_MessageList;	static xrMessageList s_PeriodicMessageList;	static xrPeriodicMsgDelayMap s_PeriodicMsgDelayMap;	static bool s_ArbitrateMessages;	static xrMessageDefPtr ExtractMessageDef(string Message);	static xrMsgListenerRegPtr ExtractMsgListenerReg(int UID, xrMessageDefPtr MsgDef);	static void ProcessMessage(xrMessagePtr Message);	class MessagePrioritySort	{	public:		bool operator()(xrMessagePtr Msg1, xrMessagePtr Msg2) const;	};	class MessageCompare	{	public:		bool operator()(xrMessagePtr Msg1, xrMessagePtr Msg2) const;	};	class RemoveMessageIf	{	public:		bool operator()(xrMessagePtr Msg) const;	};};#endif

MessageSystem.cpp
#include <ClanLib/core.h>#include <assert.h>#include <set>#include "MessageSystem.hpp"CLASS_DEFINE_BASE(xrCallback);CLASS_DEFINE_BASE(xrMessageDef);CLASS_DEFINE_BASE(xrMsgListenerReg);CLASS_DEFINE_BASE(xrMessage);xrMessageDefMap xrMessagePump::s_MessageDefMap = xrMessageDefMap();xrMessageList xrMessagePump::s_MessageList = xrMessageList();xrMessageList xrMessagePump::s_PeriodicMessageList = xrMessageList();xrPeriodicMsgDelayMap xrMessagePump::s_PeriodicMsgDelayMap = xrPeriodicMsgDelayMap();bool xrMessagePump::s_ArbitrateMessages = true;void xrMessagePump::Init(bool ArbitrateMessages){	s_ArbitrateMessages = ArbitrateMessages;}void xrMessagePump::Shutdown(){	xrMessageDefMap::iterator MsgDefIt;	for (MsgDefIt = s_MessageDefMap.begin(); MsgDefIt != s_MessageDefMap.end(); ++MsgDefIt) {		xrMessageDef* MsgDef = (*MsgDefIt).second;		UnregisterAllListenersFor(MsgDef->Name);		MsgDefIt = s_MessageDefMap.erase(MsgDefIt);		delete MsgDef;	}	s_MessageDefMap.clear();	xrMessageList::iterator MsgIt;	for (MsgIt = s_MessageList.begin(); MsgIt != s_MessageList.end(); ++MsgIt) {		xrMessage* Msg = (*MsgIt);		MsgIt = s_MessageList.erase(MsgIt);		delete Msg;	}	s_MessageList.clear();	for (MsgIt = s_PeriodicMessageList.begin(); MsgIt != s_PeriodicMessageList.end(); ++MsgIt) {		xrMessage* Msg = (*MsgIt);		MsgIt = s_PeriodicMessageList.erase(MsgIt);		delete Msg;	}}void xrMessagePump::SendMessage(xrMessagePtr Message){	assert(&Message);	if (Message->Priority == xrMessage::MESSAGE_PRIORITY_IMMEDIATE)		ProcessMessage(Message);	else if (Message->Priority == xrMessage::MESSAGE_PRIORITY_PERIODIC) {		s_PeriodicMessageList.push_back(Message);		s_PeriodicMsgDelayMap[Message] = Message->Delay;	}	else		s_MessageList.push_back(Message);}void xrMessagePump::SendMessage(int DestID, xrPtr<xrClass> Sender, string Message, float Delay, int Priority, xrPtr<xrClass> Arg1){	xrMessagePtr Msg = new xrMessage();	Msg->DestID = DestID;	Msg->Sender = Sender;	Msg->Message = Message;	Msg->Delay = Delay;	Msg->Priority = Priority;	Msg->Arg1 = Arg1;	Msg->TimeCreated = CL_System::get_time();	SendMessage(Msg);}xrMessageDefPtr xrMessagePump::ExtractMessageDef(string Message){	xrMessageDefMap::iterator MsgDefIt = s_MessageDefMap.find(Message);	if (MsgDefIt != s_MessageDefMap.end())		return (*MsgDefIt).second;	return NULL;}xrMsgListenerRegPtr xrMessagePump::ExtractMsgListenerReg(int UID, xrMessageDefPtr MsgDef){	assert(MsgDef != NULL);	xrMsgListenerRegMap::iterator ListRegIt = MsgDef->MsgListenerRegMap.find(UID);	if (ListRegIt != MsgDef->MsgListenerRegMap.end())		return (*ListRegIt).second;	return NULL;}void xrMessagePump::RegisterMessage(string Message){	if (ExtractMessageDef(Message) == NULL) {		xrMessageDefPtr MsgDef = new xrMessageDef();		MsgDef->Name = Message;		s_MessageDefMap[Message] = MsgDef;	}}void xrMessagePump::UnregisterMessage(string Message){	xrMessageDefPtr MsgDef = ExtractMessageDef(Message);	if (MsgDef != NULL) {		s_MessageDefMap.erase(Message);		delete MsgDef;	}}void xrMessagePump::RegisterClassAsListener(int UID, xrPtr<xrClass> Parent, xrCallbackPtr CBack, string Message){	xrMessageDefPtr MsgDef = ExtractMessageDef(Message);	if (MsgDef != NULL) {		xrMsgListenerRegPtr ListReg = ExtractMsgListenerReg(UID, MsgDef);		if (ListReg == NULL) {			ListReg = new xrMsgListenerReg();			ListReg->Parent = Parent;			ListReg->CBack = CBack;			MsgDef->MsgListenerRegMap[UID] = ListReg;		}	}}bool xrMessagePump::IsClassListeningFor(int UID, string Message){	xrMessageDefPtr MsgDef = ExtractMessageDef(Message);	xrMsgListenerRegPtr ListReg = ExtractMsgListenerReg(UID, MsgDef);	return (ListReg != NULL);}void xrMessagePump::UnregisterClassAsListener(int UID, string Message){	xrMessageDefPtr MsgDef = ExtractMessageDef(Message);	if (MsgDef != NULL) {		xrMsgListenerRegPtr ListReg = ExtractMsgListenerReg(UID, MsgDef);		if (ListReg != NULL) {			MsgDef->MsgListenerRegMap.erase(UID);			delete ListReg;		}	}}void xrMessagePump::UnregisterAllListenersFor(string Message){	xrMessageDefPtr MsgDef = ExtractMessageDef(Message);	if (MsgDef != NULL) {		xrMsgListenerRegMap::iterator ListRegIt;		for (ListRegIt = MsgDef->MsgListenerRegMap.begin(); ListRegIt != MsgDef->MsgListenerRegMap.end(); ++ListRegIt) {			xrMsgListenerRegPtr ListReg = (*ListRegIt).second;			MsgDef->MsgListenerRegMap.erase(ListRegIt);			delete ListReg;		}	}}const bool xrMessagePump::GetArbitrateMessages(){		return s_ArbitrateMessages;}void xrMessagePump::EnableMessageArbitration(){	s_ArbitrateMessages = true;}void xrMessagePump::DisableMessageArbitration(){	s_ArbitrateMessages = false;}bool xrMessagePump::MessagePrioritySort::operator()(xrMessagePtr Msg1,													xrMessagePtr Msg2) const{	if (Msg1->Priority == Msg2->Priority)		return (Msg1->TimeCreated > Msg2->TimeCreated);	return (Msg1->Priority > Msg2->Priority);}bool xrMessagePump::MessageCompare::operator()(xrMessagePtr Msg1,											   xrMessagePtr Msg2) const{	return (Msg1 == Msg2);}bool xrMessagePump::RemoveMessageIf::operator()(xrMessagePtr Msg) const{	return Msg->Delivered;}void xrMessagePump::ProcessMessage(xrMessagePtr Message){	xrMessageDefPtr MsgDef = ExtractMessageDef(Message->Message);	if (MsgDef != NULL) {		xrMsgListenerRegMap::iterator ListRegIt;		for (ListRegIt = MsgDef->MsgListenerRegMap.begin(); ListRegIt != MsgDef->MsgListenerRegMap.end();++ListRegIt) {			xrMsgListenerRegPtr ListReg = (*ListRegIt).second;			if (ListReg != NULL) {				if (Message->DestID == -1 || Message->DestID == ListReg->Parent->GetUID()) {					ListReg->CBack->Function(Message, ListReg->Parent);					Message->Delivered = true;				}			}		}	}}void xrMessagePump::ProcessMessages(float TimeStep){	float TimeTaken = 0.0f;	float StartTime = CL_System::get_time();	float EndTime = 0.0f;	xrMessageList::iterator MsgIt;	if (s_PeriodicMessageList.size() <= 0)		goto NormalMessageProcessing;	for (MsgIt = s_PeriodicMessageList.begin(); MsgIt != s_PeriodicMessageList.end(); ++MsgIt) {		xrMessagePtr Msg = (*MsgIt);		if (Msg->Delay > 0.0f) {			Msg->Delay -= TimeStep;			continue;		}		float InitialMsgDelay = (s_PeriodicMsgDelayMap.find(Msg))->second;		Msg->Delay = InitialMsgDelay;		ProcessMessage(Msg);	}NormalMessageProcessing:	if (s_ArbitrateMessages)		sort(s_MessageList.begin(), s_MessageList.end(), xrMessagePump::MessagePrioritySort());	for (MsgIt = s_MessageList.begin(); MsgIt != s_MessageList.end(); ++MsgIt) {		if (s_ArbitrateMessages) {			if (TimeTaken >= TimeStep)				break;			StartTime = CL_System::get_time();		}		xrMessagePtr Msg = (*MsgIt);		if (Msg->Delay > 0.0f) {			Msg->Delay -= TimeStep;			continue;		}		ProcessMessage(Msg);		if (s_ArbitrateMessages) {			EndTime = CL_System::get_time();			TimeTaken += EndTime - StartTime;		}	}	xrMessageList::iterator End = s_MessageList.end();	xrMessageList::iterator NewEnd = remove_if(s_MessageList.begin(), s_MessageList.end(), xrMessagePump::RemoveMessageIf());	if (End != NewEnd)		s_MessageList.erase(NewEnd, End);	if (s_ArbitrateMessages && s_MessageList.size() > 0) {		xrMessageList::iterator MsgIt;		for (MsgIt = s_MessageList.begin(); MsgIt != s_MessageList.end(); ++MsgIt) {			xrMessagePtr Msg = (*MsgIt);			if (Msg->Priority != xrMessage::MESSAGE_PRIORITY_AMBIENT &&				Msg->Priority != xrMessage::MESSAGE_PRIORITY_HIGH)				++Msg->Priority;		}	}}

I apologize for the xrClass and xrPtr stuff, but the message system is so heavily based on them I felt it was necessary for the sake of comprehension. If you don't want to read through them, just know that xrClass is a class that unifies garbage collection, reference counting and custom RTTI, and that xrPtr is just a simple smart pointer for anything derived from xrClass. [smile]

Edit: Sorry for the immense size of this post. If I knew another way to do it, I would.

Edit2: I just realized I uploaded an older version of Class.hpp and Class.cpp. The RTTI functions IsExactly() and DerivedFrom() aren't any good in this version, so just ignore them!

[Edited by - nilkn on March 28, 2005 6:35:59 PM]

##### Share on other sites
Zahlman    1682
1) Instead of the void*s that you know are so ugly ;), how about Boost::Any?

2) I don't see your implementation for Actor::Message with no arguments, but I assume it is similar. There's some duplication here, though, since you could just as easily create an IMessage with a zero-size vector.

3) The whole process of constructing an IMessage seems rather annoying.

My recommendation would be to wrap the function name into the IMessage itself, and give it functionality for appending arguments. Something like:

class IMessage {  std::string procedure;  std::vector<Boost::any> arguments;  public:  IMessage(const std::string & procedure) : procedure(procedure), arguments() {}  // Let's use operator() to append arguments. There are several options. You  // might find operator, to "look nicest", except you will probably need extra  // pairs of parentheses...  template <typename T>  IMessage& operator() (const T& arg) {    arguments.push_back(Boost::any(arg));    return *this;  }  // We'll make the procedure name read-only, so that the dispatcher can check it  const std::string& proc() const { return procedure; }  // OTOH, it would probably be better OOP to have the Message()  // function pass the Functions map to an IMessage method like "dispatchFrom"  // ("lookUpSelfIn"?)... i.e. have the message do the lookup work, rather than  // providing a dumb accessor.  // We'll provide read-only access to the arguments  const Boost::any& operator[] (int index) const { return arguments[index]; }  // OTGH, since we pretty much have to have read access to the arguments -  // this is really a container object after all - we may as well have it for  // the procedure too... :s}// You're on your own for fixing up the Message() dispatcher etc.// BTW, shouldn't that Functions map be static? :)// later:Actor* Actor1 = new Box();Actor1->Message( IMessage("Create") );Actor1->Message( IMessage("SetTexture")("File1.bmp") );Actor1->Message( IMessage("12345") ); // will complain, no such procedureActor1->Message( IMessage("SetPosition")("5")("0")("-8") );Actor1->Message( IMessage("SetRotate")(0.25f)(1.0f)(-0.75f) );Actor1->Message( IMessage("Render") );Actor1->Message( IMessage("Destroy") );

##### Share on other sites
Telastyn    3777
Direct object messaging seems icky to me. I think I'd prefer the design where all messages are passed through a chokepoint [read: message handler] and divvied down to individual objects [or groups of objects].

Also, having a map like that seems also kinda icky. If there's any sort of loop where the message is constant and the data changes, you're spending a lot of time in Functions.find. Doing something like:

Function *f=Functions.find( something );// use f a lot...

just seems like unnecissary hassle.

Further you describe this as solving many dependancy issues, and I'm skeptical. I mean just because the thing compiles doesn't mean there aren't dependancies. Classes still need to know about other classes, or else they're sending bogus messages slowing your app down.

That said, I also have something sort of like this setup for some of the messages you deal with like SetPosition and the such. It doesn't work so well, so I wouldn't recommend it. I'd likely go with a messaging system in my next big project myself.

Anyways, your posts are great and thought provoking, keep em coming.

##### Share on other sites
Drew_Benton    1861
Wow thanks everyone for answering so quickly! I appologize for the way it looks, I just can't find a 'nice and clean' way that is not long - I will be in the GDNet suggestion forum soon.

@Deyja - Thanks, it seems like everyday that passes, there is more of a reason for me to use boost! I was trying to avoid it, but now I guess I might need to give in and learn it.

@nilkn - WOW I will begin looking at all of that, thanks! And it's fine about the size, I'm in the same position [smile]

@Zahlman - Oooo, now that's nice! I'd love to be able to do something like that, thanks

@Telastyn - Thanks too. I know what you are saying in terms of redudant messages and such. However, in terms of the dependicies, it's more of a the programmer knows, but not the compiler kind of thing [wink]. I will definitly work on a better demo demonstrating this - I know it sounds too good to be true, but it's possible! [smile]

Ok thanks everyone once again! I am still trying to figure out how to make a neater post [lol] - so that's why this one is a little short, I will re-address all of this when I get this fixed and start comparing posts to my class right now.

- Drew

##### Share on other sites
antareus    576
Err if you want to do all this why not use a language like Smalltalk?

You send an object a message (e.g. call a method). If the object doesn't know how to interpret the message, it returns doesNotUnderstand or something like that. All dynamic.

Just suggesting that you choose a higher-level language if you want this sort of dynamic behavior, because its like pulling teeth to get C++ to take it.

##### Share on other sites
IFooBar    906
I like this system, your code is most certainly food for thought. Anyway, the problem with it is that Actor will have to know about the functions to be called right? Why not make either a message dispatcher, or maybe make use of templates. Make the Actor a templated class and have the Actor define:

typedef void (T::*ActorPtr)( const IMessage *data );

so that class Box : Actor<Box> will allow you to define custom Box specific functions. No harm in trying it out right. Or in the case of a Dispatcher, you can make Actor a pure interface, with just a Dispatch function. Keep the map of function pointers class specific so that Actor dosent have to know about the functionality of each class.

Also, about the speed issues. You can use some type of hashing to make it extremely faster. One options is to make the map key an unsigned 32 bit integer and use an algo like 32 bit pkzip or something to turn a string into IDs.

Another option to make it faster is if you are strictly using msvc, you can turn string pooling on and use the address of each string as an ID. I've never done anything like this and don't know how stable it would be though.

##### Share on other sites
Drew_Benton    1861
First, Zahlman, you are my hero! I had NO IDEA you could do what you just did. That is the most amazing thing I've ever seen! Thanks to you this is what the main.cpp looks like now ( I still want to wait on boost [wink]):
#include "Actor.h"Actor *Actor1;Box B1;int main( int argc, char* argv[] ){	Actor1 = &B1;	Actor1->Message( "Create" );	Actor1->Message( "SetTexture", &IMessage("File1.bmp") );	Actor1->Message( "12345" );	Actor1->Message( "SetPosition", &IMessage("5")("0")("-8") );	float x = 0.25f, y = 1.0f, z = -0.75f;	Actor1->Message( "SetRotate", &IMessage(&x)(&y)(&z) );	Actor1->Message( "Render" );	Actor1->Message( "Destroy" );	return 0;}

And the output is the same and no crashes! Thanks! One question though -
Quote:
 I don't see your implementation for Actor::Message with no arguments, but I assume it is similar

I don't have one of those defined - are you talking about an void parameter, or using the same technique as you did with the () operator?

Quote:
 Original post by antareusErr if you want to do all this why not use a language like Smalltalk?

To be honest, I am language ignorant. Right now I only know C/C++ and have about 10 mins worth of Java experience. I am trying to change that though! I started to learn a little Python and have plans to do some C# some time soon as well. I will definitly take a look at smalltalk though, I've only heard of it's name a few times, I don't know anything about it.

As to why I want to do this in C++, it's more of a 'learning' thing. I'd like to have a system that I've made so when the time comes to do it in something else, I at least know the 'hard' way to do it [wink]. I'd really like to use it for our game, but I do not know if it will be applicable or not with what we are using (OGRE & co.)

Thank you for your suggestion though! I really do need to catch up to modern day [lol] I've been in the past a bit too long.

Quote:
 Original post by IFooBarI like this system, your code is most certainly food for thought. Anyway, the problem with it is that Actor will have to know about the functions to be called right? Why not make either a message dispatcher, or maybe make use of templates. Make the Actor a templated class and have the Actor define:typedef void (T::*ActorPtr)( const IMessage *data );so that class Box : Actor will allow you to define custom Box specific functions. No harm in trying it out right. Or in the case of a Dispatcher, you can make Actor a pure interface, with just a Dispatch function. Keep the map of function pointers class specific so that Actor dosent have to know about the functionality of each class.

Well right now, as you can see from the new Main.cpp on top, the Actor class does not *know* about the Box class. The way I have it, through polymorphism, I just need to call the Message function, of which is part of the Actor class and thus overloaded in all derived classes, and the messages are handled in that specific class the way they need to.

When I get it right - there will be no Box class or any other class in the main.cpp, just an ObjectFactory that is ready to create any class on demand [smile] I will try to add that in tonight.

Quote:
 Also, about the speed issues. You can use some type of hashing to make it extremely faster. One options is to make the map key an unsigned 32 bit integer and use an algo like 32 bit pkzip or something to turn a string into IDs.

Thank you for the speed suggestiosn as well [smile] When I start off, I always use some sort of map just so I can see how it all works without worry about optimizations, when I get ready for speed, I do switch over to hash_map for strings. That does make it O(n) run time for access, so I am happy with that. This is what I did in my OpenAL audio manager - at first I had just used map, then I learned how to use the Standard C++ Library a bit better, so I added iterators, hash_map, and typenames for the reused names

Anyways, I am stil learning [smile] Thank you everyone for your comments and suggestions! They are much appreciated! Keep 'em coming.

- Drew

Another idea I was thinking about the use of this was perhaps something along the lines of being used in scripting.[/edit]

[Edited by - Drew_Benton on March 27, 2005 10:11:35 PM]

##### Share on other sites
Drew_Benton    1861
Ok, I think I have a base code to work with that does EXACTLY what I want it too [smile] Thanks for all the help!

I see no reason not to share this with everyone, so feel free to take a look at what I have been raving about for so long [lol]. I added comments to everything so you can follow. Just know this is by no means finished for 'optimized'. This is just one part of my original idea I could get figure out the first 2 times around. Tell me what you think!

- Drew Benton

##### Share on other sites
Zahlman    1682
Quote:
Original post by Drew_Benton
Quote:
 I don't see your implementation for Actor::Message with no arguments, but I assume it is similar

I don't have one of those defined - are you talking about an void parameter, or using the same technique as you did with the () operator?

What I meant was, how does "Actor1->Message( "Render" );" work, given that you're not passing any IMessage to Message(), and there doesn't appear to be a default parameter or function overload to take care of it?

Quote:
 To be honest, I am language ignorant. Right now I only know C/C++ and have about 10 mins worth of Java experience. I am trying to change that though! I started to learn a little Python and have plans to do some C# some time soon as well. I will definitly take a look at smalltalk though, I've only heard of it's name a few times, I don't know anything about it.

You can do it in Python too:

class Actor(object):  def Message(self, name, *args, **kwargs):    self.__class__.__dict__[name](*args, **kwargs)     # throws KeyError for bad message names

Although the language is dynamic in so many other ways that it's hard to imagine why you'd need this. :)

##### Share on other sites
Drew_Benton    1861
Ok awesome! Thanks. How/where did you learn that overloading the () operator as well as ctor trick at? That was really the most interesting thing I've ever seen.

##### Share on other sites
Genjix    100
	/*create node messenger handler*/	World myworld;	/*create nodes and reference them	with their parents.*/	AreaNode area(&myworld);	BackgroundNode bg(&myworld);	StartUpResourceNode sur(&myworld);	/*register the nodes with	node handler*/	myworld.Register("area",area);	myworld.Register("background",bg);	myworld.Register("startup",sur);

hopefully you can see what i'm doing here (the names i've chosen for this example are just random).
Now lets say the "startup" node wants to message "background" with the screen width and height.
void StartUpResourceNode::StartScreen(){	int w = 800 , h = 600 , bpp = 16;	Screen::Init(w,h,bpp);	/*now we send background node, the screen dimensions*/	Signal dim("screen width and height settings");	dim("screenw" , w)	   ("screenh" , h);	SendSignal("background" , "set screen" , dim);}

notice my Signal object can be declared as follows
Signal sig("this is the debug message in case of a problem");sig("name" , (const char*)"aaat")   ("age"  , 10)   ("pi"   , 3.141592654);  // from memory :)   ("chr"  , 'a')   ("obj"  , obj)   ("ptr"  , &obj);

now the sendsignal above would call its parent pointer and call from it SendSignal.
#ifndef WORLD_H#define WORLD_H#include "node.h"/**Application's Communication Server SystemThis is the main server, whereby each section of theprogram has to register with this class, to enablesignalling with the other 'nodes'.*/class World{    public:	/***/	World();	/***/	~World();	/**Add a node to our server*/	void Register(const string &name , Node &newnode);	/**Send signal and identifier to all nodes		An example could be Broadcast("KeyPress",buttons_pressed)*/	void Broadcast(const string &id , const Signal &sig);	/**Same as broadcast, except a specific node can be signalled*/	void SendSignal(const string &name , const string &id , const Signal &sig);    private:	/**Nodes are stored in a hash (named array)*/	map<string , Node*> nodes;};#endif

SendSignal would do a lookup first of all for "background", then send it the id command and the signal.
so in background it would do something like this in its RecieveSignal
void BackgroundNode::RecieveSignal(const string &id , Signal &sig){	if(id == "set screen")		SetScreen(sig);}void BackgroundNode::SetScreen(Signal &sig){	try	{		screenw = sig.Load<int>("screenw");		screenh = sig.Load<int>("screenh");	}	catch(const bad_any_cast&)	{		cerr << "error : bad cast... unable to function\n"			<< sig << Exit(-1);	}}

as you can see, all objects i registered with 'World' have access to each other, via signalling methods... but there are problem's here
a) its not type safe (it will compile if you missspell a message id command)
b) rising complexity - reimplementation of function calls, but with no type safety and higher speed let off.
c) this is no better than making those messenger classes global, and having them explicitly call each other.

the last one you may argue with, if your messaging system is encapsulated in a very deep part of your program, but then I'd ask why you would do something like this for such a minor part of your program.

##### Share on other sites
snk_kid    1312
Quote:
 Original post by DeyjaYou may want to consider using boost's signal/slot library.

Signals & slots are not exactly message based.

Personally I’m not very fond of message based systems, there to indirect for an internal/core why do you need such loose coupling and high level of indirection? What I mean is that it seems like going over board. I prefer event systems based on some form of observer pattern or signals & slots framework.

[Edited by - snk_kid on March 28, 2005 4:31:42 AM]

##### Share on other sites
Programmer16    2321
Hey Drew, I think your system looks awesome, but when I download the code and try to run it, it tells me that there aren't enough actual parameters for MACRO_REPEAT_15.

I'm using Microsoft Visual C++ 6.0

##### Share on other sites
Houdini    266
Quote:
 Original post by Programmer16Hey Drew, I think your system looks awesome, but when I download the code and try to run it, it tells me that there aren't enough actual parameters for MACRO_REPEAT_15.I'm using Microsoft Visual C++ 6.0

Programmer16, I can answer this one. Drew is using the Object Factory class from an article here on GameDev. The problem is that VC++ 6.0 doesn't support 'template specialization' or 'explicit template argument specification for member functions', which the Object Factory class uses.

There's a VC++ 6.0 friendly version of the Object Factory Drew could use, but the syntax is a bit strange because of the hacks required to get VC++ 6.0 to work. IE, instead of writing this:

int BoxRegister = Actor::Factory.Register<Box>("Box");

You'd need to write this
int BoxRegister = Actor::Factory.Register("Box", Type2Type<Box>());

- Houdini

##### Share on other sites
Drew_Benton    1861
Quote:
 Original post by HoudiniThere's a VC++ 6.0 friendly version of the Object Factory Drew could use, but the syntax is a bit strange because of the hacks required to get VC++ 6.0 to work. IE, instead of writing this:

Ahh yes! Thank you very much for that. I should have mentioned that [embarrass]. Credit to this GameDev article for that object factoy: Creating a Generic Object Factory [smile]

Ok Genjix, your bring up some very good points, but I must ask you, in your opinion, what would a better solution to this? I can see how a system like this could get to that point where it just gets to a point where it could get kind of unmanagable, to which large projects is not something I am that experienced with.

snk_kid - You know you are always right [smile] This could be a little too overboard for somemore complex, so saying that and going with what Genjix has said, perhaps instead of worrying about objects messaging each other, do as you like, and make this more of an "event system framework"? I will work with that idea definitly for it could make this a little better, tight now it's just all experimentation.

Also I have not ever heard of signals & slots, so I guess it's time I do some reading on that topic. [wink] Thanks everyone again! I will see if I can make a Dev-CPP version of this as well, so for people that do not have VS7 can run it withouhg moddifying it, thanks for asking about that Programmer16.

- Drew

##### Share on other sites
Genjix    100
well, bear with me im about to mention the umentionables and programmers will be at my neck.
the advantage to this system is no globals... but as i said, its really no different. you may as well group those 'nodes' into a global namespace then have a declaration in the header file (using a macro like NODE_DECLARATION(Background background) ), and then calling the methods explicitly (such as Node::background.SetScreen(w,h) ). the type safety, and loss of code (not to mention loss of info is bad, since variables lose their type) far outweighs the risks of globals.
#ifndef BACKGROUND_H#define BACKGROUND_H#include <boost/multi_array.hpp>#include "scene/image.h"#include "types/xml/xml.h"/***/class Background{    public:	/***/	Background();	/***/	~Background();	/**set screen variables*/	void SetScreen(int w , int h);	/**parse backgrounds in	scene xml file*/	void Parse(Xml::Element bgroot);	/**draw screen, when offsetted	by (offx,offy).	draws backgrounds that intersect	(offx,offy)->(screenw,screenh)*/	void Draw(int offx , int offy);    private:		/**2 dimensional array of background	images.*/	boost::multi_array<int , 2> background_images;	int screenw;		///< screens height	int screenh;		///< screens width};NODE_DECLARATION(Background background);#endif

but this of course applies to mapping based design structure versus something like a tree for example.
I don't think globals are quite the black hole everyone makes of them, as long as they're used properly (and its not worth all the extra code, required to make them work). more Encapsulation being better does not imply all globals are bad. Using a lot of globals can wreck and make a mess of your design - but thats when they're misused.

##### Share on other sites
Drew_Benton    1861
Ahh I see. Very interesting. I will keep that in mind during development. I mean I really don't know how this will turn out on larger scales. I mean right now I say 'message' all objects, but perhaps what I am doing is what you have done, with more of an events - simply because of how I process the messages in each class. I will see how it goes. I am not sure what I will do next today with it, I think I should evaluate some before I do anything too irrational.[lol] Thanks for our suggestions and time! Oh and I wanted to ask, did you revamp your system or just abadon it for your project? I'd like to see where it went wrong in hopes of avoiding that for this one, that is if it's not already on that path [wink]

- Drew

##### Share on other sites
Guest Anonymous Poster
on a side node, please do see this (rather old) article on coding for the lithtech engine (shogo in that case). it deals a lot with the messaging system underneath lithtech. i find this quite inspiring.. seeing how monolith did it.

in general i'm a strong proponent of messaging systems. i also really like what you came up with, drew. the idea of a central messaging hub in an application (aside from the windows message pump (which could be integrated into the hub) was always sound to me.

here's the article:
http://www.planetshogo.com/features/lithtech1.shtml

i don't say you should resemble lith's method.. but it might be interesting to see what they're using the messaging system for and the capabilities they built into it (not commenting on the issues regarding type safety and close relationships between objects for now.. i don't want to scratch the beautiful surface of the messaging system :-) .

##### Share on other sites
evolutional    1393
This is similar to the system I'm using as a basis for my scripting environment. Instead of void *, however, I'm keeping track of type information allowing variable typing on your messages. This is necessary in my case as you never kow what's coming along in a loose-typed system, but I think it's probably unnecessary for the system Drew's describing. Interesting though as Metaclassing is something I'm interested in. All you need to do now is implement propertysets and you're in Metaclass heaven....

##### Share on other sites
Drew_Benton    1861
Quote:
 Original post by Anonymous Posteron a side node, please do see this (rather old) article on coding for the lithtech engine (shogo in that case). it deals a lot with the messaging system underneath lithtech. i find this quite inspiring.. seeing how monolith did it. in general i'm a strong proponent of messaging systems. i also really like what you came up with, drew. the idea of a central messaging hub in an application (aside from the windows message pump (which could be integrated into the hub) was always sound to me. here's the article:http://www.planetshogo.com/features/lithtech1.shtmli don't say you should resemble lith's method.. but it might be interesting to see what they're using the messaging system for and the capabilities they built into it (not commenting on the issues regarding type safety and close relationships between objects for now.. i don't want to scratch the beautiful surface of the messaging system :-) .keep on your work, drew. i'm really enjoying your posts.

Thank you for that information on the Lithtech Engine. I have never heard of it before. I didn't even know it was Monolith's until you said so, so I know this will be quite inspiring [smile]. Thanks for sharing that! I'm glad you you like hte posts [wink] sometimes I worry if I'm just insane [grin] (jk) but I will keep on working with this. Still not sure what I want to do with it today though

Quote:
 Original post by evolutionalThis is similar to the system I'm using as a basis for my scripting environment. Instead of void *, however, I'm keeping track of type information allowing variable typing on your messages. This is necessary in my case as you never kow what's coming along in a loose-typed system, but I think it's probably unnecessary for the system Drew's describing. Interesting though as Metaclassing is something I'm interested in. All you need to do now is implement propertysets and you're in Metaclass heaven....

I promise I will get around to looking at your code. I've said that like a 100 times already, but this time I mean it [wink] That would a very interesting idea, to let whatever data types be passed as well. I think I just had a quick flash of an idea of doing that so I will give that a try soon. That term, Metaclassing, is another one that I am not familure with, so I will take a look into that as well as the signals and slots.

- Drew

##### Share on other sites
Zahlman    1682
Quote:
 Original post by Drew_BentonOk awesome! Thanks. How/where did you learn that overloading the () operator as well as ctor trick at? That was really the most interesting thing I've ever seen.

I kinda came up with it on my own. The general technique is just method chaining, which is used for the named parameter idiom, and that's all common knowledge stuff. And operator() is the obvious way to "anonymize" a named function, so I just put it together. :) (WRT the example on that page, of course you don't necessarily *need* to shuffle the creation off to another class; in the example, they do it because there's stuff to be done after all the parameters have been chosen, and also in order to "lock things down" so that you can't change stuff after object creation.)

This is influenced by some of my evil Python hackery BTW. If I make a class with only one method in it, you can bet it's __call__ ^^;

##### Share on other sites
Drew_Benton    1861

Quote:
 This is influenced by some of my evil Python hackery BTW. If I make a class with only one method in it, you can bet it's __call__ ^^;

More the reason to learn Python now. Thanks for sharing that. I was looking on that site and I saw that the object chaining was how: cout << x << y worked. I never knew that! I just thought 'that was the way it is'...[lol]

##### Share on other sites
I am using a similar method for my own message passing system, except that I use integers for event identification, rather than strings. I like the idea of using strings because it is easier to create custom messages, however, I am worried about the performance hit this will incur. Recieving messages will require a bunch of if/else statements along with string comparisons. Also, it seems that using a string as the key in a std::map is more costly than using an integer. Is there a significant performance hit to using strings instead of integers, or am I just being overly anal? Thanks!

## Create an account

Register a new account

• ### Similar Content

• Main menu in Feudal Alloy.

• Hi guys,i m looking for someone that can work with me on a "top-down" multiplayer fps as 2d and 3d artist.I used photon server and i can take the part of programming.For now i made only the basic gameplay of the game that include shooting,switch weapon and and damage player.If someone can help me please contact me via e mail: 270514974@libero.it.
I really appreciate your collaboration and hope you have a good day.....
Thanks for you time to read the post
At the bottom i attach some screenshot of the current game,i m sorry that i can't attach a video...

• By Raptor42
I'm looking to form a new game development team, mostly for training purposes.
I'm a student - Unity C# developer, who worked part-time in this industry for a couple of years already. I've been a lead developer in many "random collab groups" as well as a few companies. I specialize in creating 2D games for Android, but I'm looking forward to trying out new things - especially 3D development.
Currently, I've got one Android game close to a release so I'd work for this team in my spare time.
I've been thinking about creating a simple tycoon-like simulation game for Android (and PC eventually), inspired by the Game Dev Story (initially released by Kairosoft in 1997) https://en.wikipedia.org/wiki/Game_Dev_Story
I haven't done much planning though, therefore I'm looking forward to hearing out your ideas.
Right now, I've only created a test 3D scene using placeholder models and implemented a simple pathfinding system for me to play around with:
https://i.imgur.com/nHZerOT.png
I'm looking to work with people who are:
- willing to take a position of a: 3D modeller/2D artist/Designer
- not necessarily very experienced, but eager to learn and improve their skills
- active - check in at least once a day
If you'd like to apply for a different position which I didn't list here, you are welcome to contact me as well.
While this project is created mostly for learning purposes, if we ever get to release it and generate any revenue - you will recieve a certain percentage of it.

To Apply:
Send an email to rk.softwaredev@gmail.com
Introduce yourself and attach an example of your work (if you have any)

• Hello.

We're a two-member team and I'd like to introduce you our game - Feudal Alloy.
It's a metroidvania-style action RPG with fishbowl-powered medieval robots.

We've been working on the project for a few months. I'd be happy for any feedback.
teaser
homepage

Our Steam page is public now:

• Ballimals is a work in progress physics-based fighting game where your only source of movement is some form of a "Grappling Gun" or jump, be it a chicken's tongue or an elephant's trunk. It features a similar system to Super Smash Bros, where you have to knock other players off the screen. However there are no "percentage-health" system yet, and I probably won't add something like that either. All aspects of the game is currently made by me, and even though I might believe it is amazing I desperately need someone else to take a look at it and give me some feedback. Therefore, I would appreciate it if you could take a look at the screenshots, try the concept/movement demo and potentially check out my devlog.
The game currently features three playable characters, three playable levels, a character select and a level select. All of the character's share the same basic moves, but have one unique special attack.

Character select

Level select

Level 1 displaying the elephant performing his special attack: Elphquake.

Level 2 displaying the pig performing his special attack: Megafart or something... Also, the egg is the chickens special attack and can eggsplode.

Level 3 displaying the pig swinging by his snot.