Inheriting actor/renderablefactory

mcaden

14-09-2008 05:18:06

In my project I have various classes of character and entity I'll be colliding and interacting with. I just figured I'd inherit my various classes from Actor to make my own bodies. My problem though is that it seems like you can only set one type of body to be created through Scene::CreateBody(...);

I have a player and enemies, but I also have items that I'll want to push to solve various puzzles which will need callbacks and the like. Will I have to use the same body, or is there a way to set different body types. If I collide with an enemy I'll want to use functions like modifying HP...but if I'm pushing a cube onto a floor plate I won't want that function. I'll want to modify things based on whether the cube is colliding with the front of my mesh so I can play my player's pushing animation, and if I collide with a storyline NPC I don't want to hurt or push him, but I want to know if I'm in contact for communication. Putting these all in the same body would be a mess so I'd like to be able to classify my bodies.

I have been really busy so I haven't been able to do much in the way of research or experimentation with the code. I wanted to ask the question here in order to make it easier on myself when I can get to the code, but I'm also asking because I think a clear answer would be helpful to the community. I might've missed an obvious answer someplace. If I have I apologize.

betajaen

14-09-2008 10:06:16

No longer in '22, we have this limitation:

http://nxogre.org/Inheriting_Actor_and_ ... type_class

http://nxogre.org/How_the_Rendersystem_works

mcaden

14-09-2008 11:24:37

No longer in '22, we have this limitation:
Not sure I understand this. Are you saying that as of '22 we don't have this limitation any more, or that currently we DO have this limitation?

I read up on the links a bit more...It seems CreateBody creates only whatever body you set up in the RenderableFactory class. So, do you have to ignore CreateBody to make your various bodies and instantiate them another way, or do you have to make several different RenderableFactory classes for each of your inherited actors and create your inherited classes in phases calling setRenderableFactory in between phases?

betajaen

14-09-2008 11:41:31

We don't have this limitation in '22 anymore.

Like in the article it says you can get NxOgre to instead of using NxOgre::Body you can use MyGame::Body with the createBody function (and related ones) using the RenderableFactory.

mcaden

14-09-2008 14:16:57

But say I put my PlayerActor* in the RenderableFactory...what do I then do about my EnemyActor*, NPCActor*, PlainOldOrdinaryActor*, and the other actors I want to be able to create?

Sorry if I didn't explain it well, that's what I've been getting at from the start.

Would I simply use the constructor:

EnemyActor* = new EnemyActor( name, pose, shape, scene, renderParams, actorParams );
instead of Scene::CreateBody(...)?

betajaen

14-09-2008 14:22:56

Ahh.

You could use the mIntent string in NodeRenderableParams or abuse the identifier until I come up with something better.

mcaden

14-09-2008 15:22:04

Does using that constructor do the same thing as CreateBody or is there a lot of node parenting and such involved?

betajaen

14-09-2008 16:13:26

CreateBody locks the Actor pointer for garbage collection from the MutableMap. Using the new operator won't do this; so when NxOgre kills the Scene it won't kill the actor.

mcaden

14-09-2008 16:23:12

I suppose CreateBody could be overloaded to accept a RenderableFactory, and otherwise revert to whatever was passed in using

mScene->setRenderableFactory(mTestFactory);


whereas

mScene->createBody("Test;cube.1m.mesh", new Cube(1), Vector3(0, 3 + i, 0), "mass: 10", mTestFactory2);

Would use a different mTestFactory2 instead? I guess I need to look at the code more to see how easily this would be possible. I've been really busy so I haven't been able to, although I suppose I could've been looking at code instead of reading forums. I'm surprised this situation hasn't been brought up before since we're not really expected to seriously use Body.

betajaen

14-09-2008 18:19:18

Your the first really.

I have two ideas:

1. How would you feel if I create an extra function, and expand RenderableFactory a little by adding an "integer/enum" to the createBody function. The values of the integer/enum is up to the user, and when the request gets to the RenderableFactory you can use a simple switch to create the variations of Actor you wanted.


2. Add an addActor(Actor*) function, for any actors created via the new operator. This would avoid the RenderableFactory concept though and anything that uses Body creation code would use NxOgre's and not yours.


I would prefer to implement the first.

KevinMulder

14-09-2008 21:00:36

I prefer the first one too. I think that is much more flexible (from my POV)

Do it :)

betajaen

14-09-2008 21:05:41

Alright. I'll add a second createBody function for you.

mcaden

15-09-2008 04:50:59

That sounds like a good plan.

Your the first really.
Usually when this is true, it means I'm doing something in a roundabout/convoluted fashion. I'm not in this case?

I look forward to trying it out. #1 seems the most reasonable and clean method, and simple to implement.

Prophet

15-09-2008 09:19:35

Your the first really. I asked this this week, but, apparently, I'm hopeless at explaining my self. This was exactly was I was wondering. Thanks.

xadh00m

15-09-2008 09:43:35

Btw Ogre does the same thing with its SceneManagers. But instead of enums they are using strings. The factories are registered and looked up with this name...

I really look forward to see this integrated :-)

betajaen

15-09-2008 10:42:59

Btw Ogre does the same thing with its SceneManagers. But instead of enums they are using strings. The factories are registered and looked up with this name...

I really look forward to see this integrated :-)


Strings are a good idea but integers are faster. Shall I take a vote?

Prophet

15-09-2008 11:45:07

I guess if it is during loading time (the creation) and not during gameplay strings will be easier and the speed won't make that difference. Otherwise integers as they are faster.
My two cents (worth less than that).

mcaden

15-09-2008 15:21:17

I tend to go on the side of speed.

betajaen

15-09-2008 15:48:50

I'm thinking that it may be able to be achieved using templates.

mScene->createBody<Game::TwinkleBody>("Ghost01;ghost1.mesh", new Cube(13.37f), Vector3(1,3,37), "mass: 1337");

I may have to brush up my C++ on this, but it would be just as fast or faster than comparing an integer. If I can't get virtual template functions; I have a more dastardly solution up my very short sleeves.

xadh00m

15-09-2008 22:58:33

Could anybody verify how hard the performance impact would be with Strings?
I suppose string comparison stops if the first mismatching character is found.
This can occure very fast depending on the names used.

I remember some kind of save enum extension at gamedev which allowed to
extend existing enumerations to avoid ambiguity. But I suppose this approach was limited and some kind of hacky.

A neat template solution would be very interesting. Maybe it can be solved with traits. But to be honest I´m not very familiar with this technique.

Edit: This conceptual question was doubtlessly addressed by some Ogre experts (at least Sinbad). Maybe they would share their knowledge...

KevinMulder

15-09-2008 23:07:11

My 2 cent is on integer (enum). Speed is everything :)

mcaden

16-09-2008 13:56:42


class TestFactory : public NxOgre::RenderableFactory {

public:
enum actorTypes{
Player=0,
HarmlessNPC,
VillageIdiot,
Enemy,
Betajaen,
YourMom
};
~TestFactory() {}

//...and all the fun switch statements

};



mScene->createBody("Test1;cube.1m.mesh", new Cube(1), Vector3(0, 1, 0), "mass: 10", mTestFactory::Player);
mScene->createBody("Test2;cube.1m.mesh", new Cube(1), Vector3(0, 4, 0), "mass: 10", mTestFactory::HarmlessNPC);
mScene->createBody("Test3;cube.1m.mesh", new Cube(1), Vector3(0, 7, 0), "mass: 10", mTestFactory::VillageIdiot);


Would that not have the speed of an int, and the readability of a string?

As for string comparison you're right. However, that means 1234 and 1235 will have 1 comparison...and 'gobbletygook1' and 'gobbletygook2' would have 13 comparisons before it realized the 2 were unequal even though 'apple' and 'orange' would only take 1. Granted this is c++ and that's plenty fast...but that's still more cpu cycles.

So yeah...we're talking nanoseconds, but I still like speed. I'm not going to go around programming subroutines in ASM for my game, but at the same time there's some obvious speed-ups that come in handy for maintaining that higher FPS ability.

EDIT: Re: Templates -- It's been a few years since I studied templates, but if I remember right they are basically a compiler directive...so it would affect the build speed, but I'm pretty sure it doesn't affect program speed or load time. It depends I suppose on whether the template is applied at build or at run-time...I just don't remember which.

Prophet

16-09-2008 14:31:16

We're dealing with physics, so I say speed. I think you are right about the templates (it seems right). As I see it, templates will be easier as we get rid of the switches, right? (I'm not to familiar with templates. :oops:)
And leave my mum out of this.

mcaden

16-09-2008 14:35:48

And leave my mum out of this.

Apparently I get lost on my own humor...that last comment REALLY confused me thinking I'd thrown around an insult without realizing and actually pissed somebody off...before I realized it was the random variable I threw into the enum :P

xadh00m

16-09-2008 14:58:15

The problem with enums is the consistent extensibility.
If a user want to add his own factory for his class he has to extense the existing,
enum list shipped with NxOgre. The problem could become worse if you
want to support some kind of Plugin architecture, where new types are
registered on the fly.
RakNet works with enums for its packet IDs and forces the user to start
the first custom user packet ID with the last enum of RakNets list (CUSTOM_ID or so).

Maybe a better approach would be some kind of runtime ID generation,
but instead of Strings with integer or short or byte etc. .

I´m not sure how this should be implemented. The factory could register
itself on creation and would get a new ID from NxOgre. Every type of Actor requests would then look like this:

inline
const unsigned char* Actor::getType() const
{
return mCreator->getID();
}


This should be really fast and the only indirection would be the virtual
method getID() in the factory. But I think this would be negligible compared
to the following std::map lookUp...

The factory should be a singleton so to ask if the Actor is of a specific type would look like this:


// is this actor of type myActor?
if(actor->getType() == MyActorFactory::getSingletonPtr()->getType())
{ do it }


Maybe this could be made faster with templates?!

betajaen

16-09-2008 15:21:44

We have "getType" in Actor - two a string and a hash.

Templates would not require any comparison. As I understand template programming and simply put, this would be translated from:

mScene->createBody<Game::Body>(...)
Into:
mScene->createBody_GameBody(...)

I'm working on something for BetaGUI right now (it's a heavy compression image algorithm turning RAW images into 1/10th of there size), but I'll open up another fresh Visual Studio project and come up with something involving templates.

betajaen

01-10-2008 18:12:04

Alright, took me 15 minutes.

#include <map>
#include <iostream>

class Actor
{
public:
virtual void id()
{
std::cout << "Actor(base)" << std::endl;
}
};

class Body : public Actor
{
public:
enum
{
FactoryIdentifier = 0x0
};
virtual void id()
{
std::cout << "Body(inherit)" << std::endl;
}
};

class RenderableActorFactory
{
public:
virtual Actor* create() {return 0;}
};

class BodyFactory : public RenderableActorFactory
{
public:
Actor* create()
{
return new Body();
}
};

class Scene
{
public:

void registerFactory(unsigned int ID, RenderableActorFactory* factory)
{
mFactories[ID] = factory;
}

template<class ActorDerivedClass> Actor* createBody()
{
return mFactories[ActorDerivedClass::FactoryIdentifier]->create();
}

protected:
std::map<unsigned int, RenderableActorFactory*> mFactories;
};


void main()
{
Scene* scene = new Scene();
scene->registerFactory(Body::FactoryIdentifier, new BodyFactory());
Actor* actor = scene->createBody<Body>();
actor->id();
delete actor;
delete scene;
}


N:\Furry\Furry\Debug>Furry.exe
Body(inherit)


Works fine.

mcaden

01-10-2008 18:20:19

Great!

If we need this should we simply copy over the changes you just posted or will you have an updated '22 soon?

betajaen

01-10-2008 18:29:23

No, it was just a test to see if it was possible to use templates and it is. I have the NxOgre solution open right now, but I'm working on something else. If I get that working, I'll put the code in based on what I pasted above.

mcaden

01-10-2008 18:31:13

Understood.

Thanks.