Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Re:creation - a top down action adventure about undeads [hiatus]  (Read 440060 times)

0 Members and 1 Guest are viewing this topic.

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #405 on: December 14, 2015, 07:01:07 am »
MVC is a common pattern. Your C++ Json::Value class would then be the model, and the Qt table (or even the JSON file) the view. Changes in one will notify and update the other. Have a look at the Qt documentation to see how it provides such functionality.
I've already implemented a part of that. So, when the Qt table value changes, value in JSON changes automatically. But now I need to go further, I need to change C++ variable which corresponds to the changed JSON value, which is a harder problem, I think. :)

A library is the straightforward approach here.
Yeah, it seems like it.
I just have  no idea how they work because I never before had a need to do such code re-use. But I'll figure it out. (Btw, any good resources I can read about making libraries? Or is it so simple that I can't do wrong? :D)

There are well-established techniques for ports between languages (e.g. P/Invoke in this case). But you're dealing with a different problem ;)
Yep :D
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re:creation - a top down action adventure about undeads
« Reply #406 on: December 14, 2015, 07:04:31 am »
A library is the straightforward approach here.
Yeah, it seems like it.
I just have  no idea how they work because I never before had a need to do such code re-use. But I'll figure it out. (Btw, any good resources I can read about making libraries? Or is it so simple that I can't do wrong? :D)
This might be of use : http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re:creation - a top down action adventure about undeads
« Reply #407 on: December 14, 2015, 07:45:31 am »
If you only need the JSON <--> C++ mapping in the level editor, then you can use the Qt meta-object system, which allows class introspection.
Laurent Gomila - SFML developer

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #408 on: December 14, 2015, 07:59:03 am »
This might be of use : http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
Thanks!

If you only need the JSON <--> C++ mapping in the level editor, then you can use the Qt meta-object system, which allows class introspection.
I may be wrong, but doesn't it require me to inherit from QObject? I can't do this, because I don't want to change the existing classes from my game. Or maybe I can create some wrapper class which will inherit from QObject and use Qt's introspection? Something like:
class AnimationWrapper : public QObject {
    Q_OBJECT
   
public:
    AnimationWrapper(Animation& animation) :
        frameCount(animation.frameCount),
        frameTime(animation.frameTime),
        ...
    {}
   
private:
    int& frameCount;
    int& frameTime;
    ... // etc.
   
};
and then later...
Q_PROPERTY(int& frameCount READ frameCount)
so when the JSON value changes I can do something like this:
animWraper.setProperty(changedValueName, newValue); // both are taken from Json::Value which just changed

Btw, animations are working now :D
« Last Edit: December 14, 2015, 08:44:14 am by Elias Daler »
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #409 on: December 14, 2015, 04:26:37 pm »

Okay, got some basic introspection working. Here's how the wrapper looks.

class AnimationWrapper : public QObject {
    Q_OBJECT
    Q_PROPERTY(bool looped READ isLooped WRITE setLooped)
    Q_PROPERTY(int frameCount READ getFrameCount WRITE setFrameCount)
    Q_PROPERTY(int frameTime READ getFrameTime WRITE setFrameTime)

public:
    AnimationWrapper(Animation* anim) :
        anim(anim)
    { }

    // is looped
    bool isLooped() const {
        return anim->isLooped();
    }

    void setLooped(bool b) const {
        anim->setLooped(b);
    }

    // frame count
    int getFrameCount() const {
        return anim->getFrameCount();
    }

    void setFrameCount(int fc) {
        anim->setFrameCount(fc);
    }

    // frame time
    int getFrameTime() const {
        return anim->getFrameTime().asMilliseconds();
    }

    void setFrameTime(int frameTime) {
        anim->setFrameTime(sf::milliseconds(frameTime));
    }
private:
    Animation* anim;
};
That's not the coolest thing, yeah, but now I can do this:
animationWrapper->setProperty("frameCount", 10); // same as calling animation->setFrameCount(10);
So, when some value changes, I find corresponding animation wrapper, and then I can change animation parameters using this wrapper.

Update: but then I realized that this system kinda sucks and I shoudn't really use QObject, because some introspection will be useful when loading components in the game from Json in the future...
« Last Edit: December 14, 2015, 05:11:29 pm by Elias Daler »
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re:creation - a top down action adventure about undeads
« Reply #410 on: December 14, 2015, 05:47:27 pm »
Don't you already have a Lua binding of all your classes and objects? Can't you use it for introspection?
Laurent Gomila - SFML developer

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #411 on: December 14, 2015, 06:00:34 pm »
Don't you already have a Lua binding of all your classes and objects? Can't you use it for introspection?
I have one way binding like this:
void SomeComponent::loadFromScript()
{
     someVar = scriptManager.getData<SomeVarClass>(entityName + ".someVar");
     ...
}
So it's one way only. I can't set variables by name. I could change the value in Lua table and call loadFromScript again... but that's just silly.
And I think about moving most data stuff to JSON in near future.

Right now I think about implementing meta stuff like this:
void SomeComponent::SomeComponent()
{
     meta_register<SomeVarClass>("someVar", this, getSomeVar, setSomeVar); // getSomeVar/setSomeVar are get/set functions
     ...
}
and then I'll be able to do this:
someComponent->setProperty("someVar", newValue);
and this:
auto value = someComponent->getProperty("someVar");
So, it's basically like QObject meta stuff works, but without moc_....cpp and macros.

I can use it for
1) Loading from JSON (no need to write it by hand)
2) Saving to JSON
3) Displaying/changing component variables with Qt
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #412 on: December 15, 2015, 08:19:24 am »
Okay, so I got some basic meta-property system working which lets me do this:
class Animation : public MetaObject
{
public:
    Animation()
    {
        meta_register("name", name);
        meta_register("isLooped", isLooped);
        meta_register("frameCount", frameCount);
    }
private:
    std::string name;
    bool isLooped;
    int frameCount;
};

int main()
{
    Animation a;
    a.setProperty("name", std::string("walk"));
    a.setProperty("isLooped", true);
    a.setProperty("frameCount", 100);

    std::cout << "Name = "  << a.getProperty<std::string>("name") << std::endl;
    std::cout << "isLooped = "  << a.getProperty<bool>("isLooped") << std::endl;
    std::cout << "frameCount = " << a.getProperty<int>("frameCount") << std::endl;
}
Pretty neat! This will allow me to serialize to JSON /deserialize from JSON without any effort. One more thing I need to add is support for get/set member functions, but that's pretty easy to do, I just wanted to keep the code which I show brief.

Here's the source code:
http://pastebin.com/54ATGH9X
There's almost no error checking to keep the example brief.
Any suggestions to improve this are welcome! Maybe there's a better way do to it than to use dynamic_cast :P
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re:creation - a top down action adventure about undeads
« Reply #413 on: December 15, 2015, 08:58:19 am »
The "problem" here is that each instance of the class will register and store the (same) meta-properties, but properties should be attached to the class, not to its instances. Moreover, I think inheritance is too strong: almost every instance of your engine will be a meta-object, whereas they should have one (shared by all instances).

So I would rather do something along the lines of:

MetaObject meta("Animation");
meta.addProperty("name", &Animation::name); // or get/set functions
...
xxx.registerMetaObject<Animation>(meta);

auto meta = xxx.findMetaObject(a);
meta.setProperty(a, ...);
meta.getProperty(a, ...);

That's just for the idea. You can add a lot of syntactic sugar on top of that, and you can also find a smarter way of managing registration and ownership of meta-objects.
« Last Edit: December 15, 2015, 09:02:22 am by Laurent »
Laurent Gomila - SFML developer

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #414 on: December 15, 2015, 09:27:06 am »
I agree with all your points, Laurent. Version 2.0 coming soon! :D
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #415 on: December 15, 2015, 09:43:38 am »
Okay, it was a bit harder to implement, but it's so much cooler!
http://pastebin.com/SgAr78X3

it works like this:
class Animation
{
public:
    static void registerClass(MetaObject<Animation>& metaA)
    {
        metaA.addProperty("name", &Animation::name);
        metaA.addProperty("isLooped", &Animation::isLooped);
        metaA.addProperty("frameCount", &Animation::frameCount);
    }
   
private:
    std::string name;
    bool isLooped;
    int frameCount;
    FloatRect frame;
};

int main()
{
    MetaObject<Animation> metaA;
    Animation::registerClass(metaA);

    Animation a;
    metaA.setProperty(a, "name", std::string("walk"));
    metaA.setProperty(a, "isLooped", false);
    metaA.setProperty(a, "frameCount", 10);

    std::cout << "Name = " << metaA.getProperty<std::string>(a, "name") << std::endl;
    std::cout << "isLooped = " << metaA.getProperty<bool>(a, "isLooped") << std::endl;
    std::cout << "frameCount = " << metaA.getProperty<int>(a, "frameCount") << std::endl;
}
Of course, I'll need to have some MetaManager which will allow me to get Meta<Class> objects like this:
auto& meta = metaManager.get<Animation>();
I'll implement it soon.
Is it better now, Laurent? :D

P.S. a small improvement:
template <typename Class>
MetaObject::MetaObject()
{
     Class::registerClass(*this);
}

So now creating MetaObject<Animation> will automatically call Animation::registerClass
« Last Edit: December 15, 2015, 09:46:35 am by Elias Daler »
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re:creation - a top down action adventure about undeads
« Reply #416 on: December 15, 2015, 09:50:42 am »
Yes, much better, but let's see once you've completed the implementation ;)
Laurent Gomila - SFML developer

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #417 on: December 15, 2015, 09:53:59 am »
Yes, much better, but let's see once you've completed the implementation ;)
Nice. :)
I guess I'll just make std::unordered_map<std::type_index, std::unique_ptr<IMetaObject>> metaObjects and then I'll use dynamic_cast just as I did with properties. But yeah, I'll post the source code once this is implemented :D

... or maybe I can should just store static MetaObject<SomeClass> meta inside SomeClass? No need for MetaManager then. And then I'll just do this:
Animation::meta.setProperty(a, ...);

Update: ...and done
Animation a;
auto& metaA = Animation::meta;
metaA.setProperty(a, "name", std::string("walk"));
metaA.setProperty(a, "isLooped", false);
metaA.setProperty(a, "frameCount", 10);

std::cout << "Name = " << metaA.getProperty<std::string>(a, "name") << std::endl;
std::cout << "isLooped = " << metaA.getProperty<bool>(a, "isLooped") << std::endl;
std::cout << "frameCount = " << metaA.getProperty<int>(a, "frameCount") << std::endl;

And now I'll add JSON serialization/deserialization. :D
« Last Edit: December 15, 2015, 10:07:22 am by Elias Daler »
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re:creation - a top down action adventure about undeads
« Reply #418 on: December 15, 2015, 10:23:51 am »
I don't like this approach because:
- it is intrusive
- you can't control the lifetime of meta-objects, they all live in the global scope
- there's still a lof of static types involved

... however, it's good because you kept it simple and it really sticks to your needs. So I'd say leave it as it is, unless you think that one of the points above may be a problem in the future.
Laurent Gomila - SFML developer

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #419 on: December 15, 2015, 10:34:16 am »
Alright. :)
I think that meta-objects don't need to have their lifetime, because information about the class is compile-time (if only we had better constexpr, I'd like to generate all this stuff at compile time, hehe). So it's fine unless I would want to add/remove meta objects in runtime, but I don't see why I would need that. (or if one meta-object would depend on the other, but I think this will be pretty bad).
Global scope is not a problem too, because &SomeClass::SomeVar is global too (though it can be private, but SomeClass::meta can too) ;)

So yeah, I'll see if there are some problems with this approach.
I just don't want to search for meta-object in the map every time I need to access meta-info which is almost here, if you have SomeClass instance. :D
« Last Edit: December 15, 2015, 10:36:48 am by Elias Daler »
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler