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 501773 times)

0 Members and 9 Guests are viewing this topic.

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #345 on: November 28, 2015, 11:49:10 am »
I created pretty cool shadow system


Previously shadows were drawn by hand, but I realized that this is pretty boring to do, so I created a simple shadow system. Here's how it works:


First, I apply a simple fragment shader to the object's silhouette.
Here it is:
uniform sampler2D texture;

void main()
{
    float alpha = texture2D(texture, gl_TexCoord[0].xy).a;
    if(alpha < 0.1) {
        discard;
    }
    vec4 color = vec4(0.098, 0.098, 0.098, 0.5);
    gl_FragColor = color;      
}
 
So, I just set all non-transparent pixels to one color.

Then I skew the silhouette using sf::VertexArray (this is done by simply moving two top verticles (marked as 0 and 1)  like this:


and then I draw the original sprite on top of the shadow. That's it! I still don't know how much I have to skew each shadow to get the best looking shadows (anyone has ideas?). Maybe there are ways to make this even cooler? (I don't want to make anything complex, there won't be "true" lightning system in my game, no directed lights, etc.)

Another aspect of shadows is interaction with other objects and I'll write about it when I implement it (basically it's per-pixel collision test with shadow and object's collision rect)
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

shadowmouse

  • Sr. Member
  • ****
  • Posts: 302
    • View Profile
Re:creation - a top down action adventure about undeads
« Reply #346 on: November 28, 2015, 12:22:46 pm »
How about making a day and night system and having the shadows gradually move from left to right? I think you'll have to rotate the top edge a bit to stop it looking weird though, but I might be wrong as I've never tried it myself.

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #347 on: November 28, 2015, 12:28:02 pm »
How about making a day and night system and having the shadows gradually move from left to right? I think you'll have to rotate the top edge a bit to stop it looking weird though, but I might be wrong as I've never tried it myself.
Yeah, I thought about that, changing a pallete a bit by using different shaders would also work pretty good with this (changing brightness + hue). I think that I may make day/night system where day and night wouldn't change gradually, but during some events (you go inside the dungeon, it's day outside, you go out - night already!) :D
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

lezebulon

  • Full Member
  • ***
  • Posts: 235
    • View Profile
Re:creation - a top down action adventure about undeads
« Reply #348 on: November 28, 2015, 02:06:34 pm »
Hi!
from experience, when you do shadows this way you really cannot skew them that much without it looking really bad
the reason is simply that the shadow should be the projection of the outline of the 3D object, and with a 2D sprite you only know the outline of the object when you see it from the camera point of view.

This can work fine in any direction as long as the original 3D object has a circulat symmetry (like a pine tree), where the outline from all angles is the same. But basically if you want the shadow to be drawn on the upper-right of the object, the shape of the shadow should be the outline of the object seen from the lower-left... there is no way to tell what it should be just based on the sprite since it's just a view of the object from the top

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #349 on: November 28, 2015, 09:19:30 pm »
Hi! That's a valid concern, but I'll try not to skew them too much. Most of the shadow is overlapped by the object itself, shadows are low-res/not to big, so I don't think that this will be too obvious if you don't try to think about it. Even if I drew shadows myself, I wouldn't be able to imagine how the other side of the object would look! But if some objects (especially bigger once) have really bad shadows, I can still draw them by hand to make them look better.

So, I'm okay with not perfect shadows :)
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 #350 on: November 29, 2015, 08:54:57 am »
Okay, it's time to write another part about the refactoring I've done. It's also contains the hardest part of the refactoring!
Here's some small stuff I've done:

1. Lua enums. I've written about those already. They work pretty good
2. LuaEntityHandle. Read more about this technique here.
This thing allows me to write stuff like this:
entity:setAnimation("walk")
instead of
setAnimation(this, talk)
A minor change, but the one I like very much, because it makes scripts easier to read
3. Lua/C++ events. Written about it here.
4. And now for the hardest part. I didn't really think too much about header dependencies because I try to not include stuff in headers, use forward declaration as possible, try to decouple classes, etc. But changes in some classes caused lots of recompile for no reason. You now, like file changing A.h caused B.cpp which didn't include A.h in any way to recompile. I've searched for hours for the root of the problem. I've created a big graph of includes in my code but found no reason for this thing. And here comes the worst part: when I created new functions and registered them in LuaBridge, this caused almost full recompilation of the whole project (which took 4-5 minutes!). This was not acceptable. This would limit me a lot, because I wouldn't want to add C++ functions which can be called for Lua or change some classes which caused a lot.

And then I found it. It was LuaBridge.h. I created a quick project to test things out. So, suppose you have two classes A and B which are not related in any way. You register A class in A.cpp and do some stuff with LuaBridge in B.cpp. So, A.cpp and B.cpp have LuaBridge.h included in them. And now suppose you make some changes in class A and compile. B.cpp compiles too! What? This is what LuaBridge does. I think that compiler generates some weird template code again which causes LuaBridge.h to recompile causing ALL files which include LuaBridge.h to recompile.

As you may imagine, lots of files in my project included LuaBridge.h. This is what caused the problem! And I've found the solution. It was to make LuaScriptManager which included LuaBridge.h in LuaScriptManager.cpp but not in LuaScriptManager.h, so if you change some stuff and LuaBridge.h needs to recompile, only LuaScriptManager.cpp is recompiles, but all the files which use LuaScriptManager.h are not recompiled!
But making good interface for LuaScriptManager was a big challenge. There were two problems:

Problem A. I needed to make some templated functions which used LuaRef in them (like T getData<T>). This meant that I needed to include LuaBridge.h in LuaScriptManager.h which would cause the same problems again!

Here's how I solved it:
You can declare a template function in .h but define it in C++ if you do template specialization. But this proved to be bad, because a lot of the code had mostly common stuff in it and I didn't want to just copy-paste stuff. And then I've found out about one of the coolest template features ever. It's called explicit template instantiation. So, all you have to do is to define the template function in .cpp and then put explicit instantiations in .cpp for all the types you want this function to use with. This limits you to only those types, but I was okay with that, because getData was used with only a number of classes like int, float, bool, etc.
So, here's how the code looks:
// LuaScriptManager.h
class LuaScriptManager {
    ...
    template <typename T>
    T getData<T>(...);
    ...
};

// LuaScriptManager.cpp
...
template <typename T>
T LuaScriptManager::getData<T>(...) {
    // template code
}

// explicit instantiations
template bool LuaScriptManager<bool> getData(...);
template int LuaScriptManager<int> getData(...);
... // etc.
So, this causes compiler to generate all this functions in .cpp file, so you don't have to have template function definition in your .h file which enables you to not put code into headers.

Problem B. I needed to find some wrapper around LuaRef which could act like LuaRef, but didn't require files where it's used to include LuaBridge.h. This one was easy, I just created class like this:
namespace luabridge {
    class LuaRef;
} // forward declaration

class LuaRefWrapper {
    template <class T>
    T cast() const;
    ...
    std::unique_ptr<luabridge::LuaRef> refPtr;
};
This also allowed me to get rid of some explicit template instantiations in LuaScriptManager which made code a lot better. I can just use LuaRefWrapper.h in LuaScriptManager.h without including LuaBridge.h! So, I got rid of the dependency, but I also got a few good things out of LuaScriptManager too, of course.

1) Hiding stuff/error checking inside the scripts
Suppose I have entity definition that looks like this:
someEntity = {
    ...,
    GraphicsComponent = {
        filename = "res/images/someEntity.png",
        ...
    }
}
Suppose I want to get someEntity.CollisionComponent.collide. Here's what I had to do previously:
auto entityTable = luabridge::getGlobal(L, "someEntity");
auto componentTable = entityTable["GraphicsComponent"];
auto filenameRef = componentTable["filename"];
std::string filename;
if(filenameRef.isString()) {
    filename = filenameRef.cast<std::string>();
} else {
    filename = "res/images/error.png";
}
There are lots of things that can go wrong. What if one of the tables is not found? Should add checks for this too?
Here's what I can do now:
auto componentTable = scriptManager.getTable("someEntity.GraphicsComponent");
auto filename = scriptManager.getData<std::string>("filename", "res/images/error.png", componentTable);
Much better! If one of the tables is not found, the program doesn't crash and LuaScriptManager tells me about the error. If the value is of the wrong type, the script manager tells me about this too. If there are some errors, getData returns the default value which I provided as the second argument.

2) Overwrite
One functionality I've implemented a long ago was entity "inheritance". Suppose I have one entity with lots of components. And now I decide to create almost the same entity, but with another sprite. Here's how I can do that:
base_entity = {
    ...
}

derived_entity = {
    template_init = function(this)
        this:copyEntity("base_entity") // copies everything from base_entity
    end,
    GraphicsComponent = {
        filename = "res/images/newSprite.png"
    }
}
The difference between default loading and this type of loading is that I don't care if the value is not found in derived entity. If this is a case, I just don't change the value. If the value is found, I replace it with a new one. And here's what I had to do previously:
auto entityTable = luabridge::getGlobal(L, "someEntity");
auto componentTable = entityTable["GraphicsComponent"];
auto filenameRef = componentTable["filename"];
if(!filenameRef.isNil()) {
    filename = filenameRef.cast<std::string>();
}
Notice how it's almost the same as the previous example. Can I implement it better with new scriptManager? Sure
auto componentTable = scriptManager.getTable("someEntity.GraphicsComponent");
auto filenameRef = scriptManager.getRef("filename", componentTable);
if(filenameRef.isNil()) {
    filename = filenameRef.cast<std::string>();
}
This almost looks like LuaBridge code above! So I came up with a better solution, now I can do stuff like this:
scriptManager.getData(filename, "res/images/error.png", "filename", componentTable, isOverwrite);
Here's what's going on. Fist of all, notice that I don't have to specify template argument because of the type deduction which happens because I pass the first argument.
If isOverwrite == false, this works as getData<std::string> that I showed above. If isOverwrite == true, then I rewrite the value only if it's found. But I don't write about errors that some tables or values are not wrong, because some values may be missing in derived entities and that's fine.
So, this one line works for two cases: loading value expecting to find it and trying to overwrite value only if the value is found.

So, all this stuff lead me to great simplification of script loading code, allowed me not to worry about the errors in scripts and reduced a lot of header dependencies. This also allows me to replace LuaBridge with some other library in the future only by changing LuaScriptManager and not bothering about other code which is very good.

I'm very glad I did this epic refactoring/engine improvement. Now I'm a lot more confident in the engine and I'm already making some progress about the game. :)
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 #351 on: November 30, 2015, 10:31:39 pm »
Today I've integrated new shadow system in the game (previously I used it in a test project). Here's how it looks!

Notice, that I don't draw shadows when I turn on the fog.

There's a lot of stuff to improve, of course. Here's one of such improvements:

The shadow on the left doesn't look as it should because the dirt/grass and tombstone were one sprite, so shadow system used all that sprite to create a shadow. But if I separate them and tell the system to generate a shadow only from the tombstone, it all becomes much better! (The shadow must be smaller though, but it's easy to change!)

Here's another screenshot for fun. There are still lots of things I have to make better (like don't skew shadows very much in some objects and there's other stuff) but I'm very satisfied with the result. The game looks more alive now!
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re:creation - a top down action adventure about undeads
« Reply #352 on: November 30, 2015, 11:39:58 pm »
This is looking better and better every time. Good work!

Notice, that I don't draw shadows when I turn on the fog.
The absence of the 'dynamic' shadows makes the presence of the hand-animated shadows look odd (at the bottom of the lamps).
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #353 on: December 01, 2015, 06:10:52 am »
Thanks a lot!

Oh, I completely forgot about those. They are drawn directly with the sprite itself, I guess I have to separate them  and draw shadow and torch sprites separately :D
Or I can use a shader which will remove "shadow" pixels from the texture (this is even easier, ha-ha!)
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 #354 on: December 01, 2015, 01:47:47 pm »
If you love Re:creation, please vote for it, so maybe it wins Indie of The Year indiedb award!  ;D
Vote now!
Do it, so you can spend more time talking with this cat in the future
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 #355 on: December 01, 2015, 06:21:29 pm »
Voted. You and your awesome game deserve it :-)

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #356 on: December 01, 2015, 06:34:25 pm »
Thanks a lot, Jesper!  ;D
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

shadowmouse

  • Sr. Member
  • ****
  • Posts: 302
    • View Profile
Re:creation - a top down action adventure about undeads
« Reply #357 on: December 01, 2015, 07:01:12 pm »
Same. I think I'm possibly anticipating this more than any professionally made game at the moment.

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re:creation - a top down action adventure about undeads
« Reply #358 on: December 01, 2015, 07:03:48 pm »
Voted, here  ;)

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re:creation - a top down action adventure about undeads
« Reply #359 on: December 01, 2015, 07:13:27 pm »
I think I'm possibly anticipating this more than any professionally made game at the moment.
I know what you mean. All the cool screenshots, details about implementation, etc etc, has built up a huge amount of suspense.
I want to play this now! ;-) (for Linux, please).

 

anything