|
Post by silderan on Dec 29, 2014 14:04:41 GMT
Hello Ness.
I'm trying to create a more complex tile map and need more information per Sprite.
I tried to sub-class Sprite, but realized that I cannot do this because of smart-pointers that makes classes incompatibles. (if I'm not wrong) and cannot use new Sprites sub-class on NodeAPI virtual functions.
I peeped in Sprite.h/Entity.h looking for some kind of custom property and didn't see anything.
Of course, for efficency, sub-classing is better than custom property, but don't know if there is any workarround for this.
Everything that comes to my mind, uses standard pointers... but don't blame me! I'm old C coder! XDD
Regards!
Silderán.
|
|
|
Post by Admin on Dec 29, 2014 14:15:11 GMT
hello silderan if you want to attach simple data to a node or an entity you can use set_user_data() and get_user_data(), defined in the renderable api class: github.com/RonenNess/ness-engine/blob/master/source/NessEngine/renderable/renderable_api.hbut what did you mean about not being able to inherit from sprite and use it in the tilesmap? smart pointers should not prevent polymorphism, perhaps I did something wrong or you misused it. can you please post some code so I'll understand better the type of error you got? thanks! EDIT: I made a test code and here's how you create a tilesmap with a custom sprite type. try to use it if set_user_data() is not enough for you: // global pointer to the renderer - un ugly patchy patch! Ness::Renderer* g_renderer;
// my custom sprite type class MySprite : public Ness::Sprite { public: MySprite(Ness::Renderer* renderer, const Ness::String& TextureFile) : Sprite(renderer, TextureFile) {} };
// function to generate my own customized sprites Ness::SpritePtr CreateCustomSprites(const Ness::Pointi& index) { return ness_make_ptr<MySprite>(g_renderer, "tilemap.jpg"); }
int _tmain(int argc, _TCHAR* argv[]) { // init and create a renderer Ness::init(); Ness::Renderer renderer("Tilemap demo!", Ness::Sizei(800, 600));
g_renderer = &renderer;
// create a new scene Ness::ScenePtr scene = g_renderer->create_scene();
// create the tilemap with the customized sprites and add it to the scene Ness::TileMapPtr map = ness_make_ptr<Ness::TileMap>(g_renderer, "", Ness::Sizei(100, 100), Ness::Size(32.0f, 32.0f), Ness::Size::ZERO, &CreateCustomSprites); scene->add(map);
... ..
|
|
|
Post by silderan on Dec 29, 2014 19:50:02 GMT
Hello Ness. I appology because I didn't go deeper into your code. Didn't see user_data() But, it's a pointer! (I like it! xDD) So, it must be deleted manually. Is there any callback function to know when Entity is going to be deleted? Or... what's the way to delete that pointer? (Sorry for all this questions, but as there is no docs for API it's hard to me. ) On the polymorphism... This is the first time I write C++ code that uses std. I'm familliar with Qt . So, it's my fault. Thats the code: class TileMapSprite : public Sprite { public: TileMapSprite(Ness::Renderer* renderer, const Ness::String& TextureFile) : Sprite(renderer, TextureFile) { } }; typedef SharedPtr<TileMapSprite> TileMapSpritePtr; ... inline SpritePtr& get_sprite(const Pointi& index) { return m_tiles[index.x][index.y];
And the error is... Changing typedef to typedef SharedPtr<Sprite> TileMapSpritePtr; This error dissapears.
|
|
|
Post by Admin on Dec 29, 2014 22:51:59 GMT
no need to apologize, the tutorials and docs are far from complete and it takes some time to get used to a new library. never hesitate to ask any question, through them I get a useful feedback which I appreciate very much! now regarding your error, the line: inline SpritePtr& get_sprite(const Pointi& index) { return m_tiles[index.x][index.y]; } raise the error because you return SpritePtr by reference instead of by value. if you don't understand why this is an issue, here's a long, boring explanation: the pointers ness-engine uses are std::shared_ptr, which are basically a template class that gets a raw pointer and count how many other shared_ptrs hold it (when a shared_ptr pass the value to another it increase the counter, and when a shared_ptr dies is decrease the counter). the shared_ptr delete the raw pointer automatically when the last reference dies.
now when you try to assign one shared ptr into the other it calls the assignment operator (implemented by std::shared_ptr), which eventually try to assign one raw pointer into the other, meaning the normal pointer cast rules of cpp apply. but when you pass the shared pointer by reference and not by value, it's like you are doing the following:
TileMapSpritePtr* a; SpritePtr* b; a = b; // <-- error!
now why is this a problem if they are both shared_ptr type? because its a template class, they are not recognized as the same type nor as inheritance of one another. its like two separated classes that has nothing to do in common. long story short, remove the reference from the return value, i.e.: inline SpritePtr get_sprite(const Pointi& index) { return m_tiles[index.x][index.y];}
now about what you said with having to remove the user data, I was thinking in an approach of "you created it, you will delete it", but since it can be a hassle in some cases, I added a boolean you can set when calling set_user_data() which will delete the pointer when the sprite is deleted. as usual, this change will be part of the next version but already in git if you want it. PS. why aren't you using the example code I provided to use custom sprites in a tilemap? are you trying to create a custom tilesmap as well? or change the one in ness-engine?
|
|
|
Post by silderan on Dec 30, 2014 19:52:01 GMT
Ok, it works. I've changed all references returned and is Ok I'm usually coding with Qt. This has a awesome signal-slot calls that makes such thinks, like deleting data stored in objects, trivial. The best aproach without this tool, is a callback function. But, it breaks some OOP because the need to pass a static function pointer. If you prefer adding a bool parameter to "delete yes/no", will be ok too. All objects in Qt has a "property" variant variable to store anything. It could be as simple as a key:value dicctionary (or mapping) to store an integer. add_property("property_name", 1234); get_property("property_name"); // Will return 1234. mixing this property data with callback function at Entity destruct could be great! I think! What I'm trying is to create a new Tilemap with, at least, two more features: 1. I wanna add/remove tiles dynamically to matrix. So, matrix will remain at minimun size to fill camera view. Thinking in huge world maps, it will save RAM used. Tile's texture and render possition will change with camera movement. 2. Add more tiles per matrix index. To draw hills, houses, etc. to improve 3D looking. Maybe, I can do it using your tilemap/isomap example. Don't know. But, for sure, I need to know much better how your engine works. Regards! Silderán.
|
|
|
Post by Admin on Dec 30, 2014 22:04:39 GMT
well I'm glad I asked about your purpose because ness-engine has a solution for you: just use a nodesmap! you can check out the API here: github.com/RonenNess/ness-engine/blob/master/source/NessEngine/renderable/nodes/nodes_map.hbasically a nodesmap works just like tilesmap, but instead of sprites you have nodes. in every node you can create as many sprites as you want. I used it to create the following example (sorry no source yet for this one, but I can send it to you in private if you like): as for loading just parts of the map for huge maps: usually RAM is not an issue, but if you are talking about REALLY huge maps I think the best solution would be to use a matrix of nodesmap and load/delete nodesmap as whole when necessary (i.e. divide your whole map into smaller maps and load only the maps that are currently in screen or near it). I assume you want this for seamless maps transitions right? I hope this helps, if you have any more questions feel free to ask PS. the QT properties design pattern is interesting (and quite easy to implement with a hash table) I assume the return value is a union? Anyway I will consider it for possible future versions, but dont count on it for the near future.. thanks!
|
|
|
Post by silderan on Dec 31, 2014 9:56:43 GMT
Haha... I was thinking to implement it with nodes too.
I saw other picture of this example in your tutorials, but didn't pay attention.
Looking at it carefully... it seems exactly what i was looking for! ^_^
If I'm not wrong. It is tile based for "fixed" objects (walls, bridge, water (awesome water, indeed!), lawn, grass, flowers... In various layers (seems that flowers/grass are on a different layer from lawn. And bridge is, for sure, in different one from lawn) And, some fixed sprites such barrels, trees and (perhaps) torchs?
One thing that I wanna do is simulate player's line of sight (not just where it looks at but all 360º arround). For that, I wanna make translucid some parts of scene. If node-map builds scene to increase performance, can I do that? Or can I only do it with sprites?
About your solution using portions of world map (100x100, for example) and load it when it is close to camera is a good point. I Didn't think on it because I believe is not possible to modify tile's properties as your map is static. For example, as I asked before, to make tiles translucid. Think about a Dragon's ball adventure game, where the hills and buildings breaks down when Goku hits. Haahaha, just kidding, but illustrates the need of dynamic tiles.
About custom data properties. Yes, an union with void* and int, long, bool, etc. is soo easy to implement to. And yes, I agree that hash table is the best choise. I will try to write code for it.
|
|
|
Post by Admin on Dec 31, 2014 18:32:57 GMT
you were mostly right about your assumptions: 1. the dirt, grass, water, bridge etc.. are indeed layers of sprites, just like you said. I used a nodesmap and for every node in the map I created as many sprites as I needed on top of each other (every sprite == layer). so the result is like it was tilesmap but with as many sprites as needed for every tile spot. this way you can have dirt (bottom layer) with grass over it, and combine pretty much anything (you can even do dirt with grass and water over it to give a swampy look). 2. the walls are on a separated nodesmap then the tiles, because they use z ordering while the floor tiles don't need it. every piece of wall is a node in the walls nodesmap, and composed of several sprites combined together (wall side sprite, wall top sprite, and shadow). the advantage here is that you can create wall you can see-thought. for example, see the metal-bars gate behind the player? this is actually a wall type. if the player would stand behind it, the bars will be rendered on top of the player. in front of it, and the player will be rendered on top of the bars. 3. the objects (barrels, torch, trees, blood..) are individual sprites arranged in the same z-node as the walls and the player. I added the texture of the wall and the bridge to clear things up: you said: ""For that, I wanna make translucid some parts of scene. If node-map builds scene to increase performance, can I do that? Or can I only do it with sprites?"" what do you mean "if node-map builds scene to increase performance"? nodes map is basically a matrix of nodes, arranged by the size and distance you defined. but the "size" of those nodes is only relevant to the culling mechanism (which is extremely efficient in nodesmap). so.. nodesmap is totally dynamic and editable, except for moving nodes or putting objects larger then the defined size, which will result in wrong culling. so in short: don't worry, your Guku will be able to break walls, and you can play with the nodesmap on the fly (add cloudy sprite to hide things outside the line of sight and even make the other entities invisible below it for better performance. PS if you are looking for a real static nodesmap, there is the staticNode entity that convert as many objects as you like into a matrix of textures and can render thousands of entities at the speed of light (but those entities are 100% static and can't be moved or changed). the StaticNode can be good for a static none-animated tilesmap, but its kind of premature optimization if you ask me
|
|
|
Post by silderan on Jan 1, 2015 2:23:16 GMT
Great! tilemap and isomap builds static scenes, didn. it? So, I thought this one uses the same . Awesome if it doesn't!!!! Tomorrow I will try to explain you my idea of how to create the "dynamic" tile map and, maybe you like it and like to include into API or, at least, desing API (if it isn't) to allow to make it by overloading some functions. By the way. Have you ever seen Tiled? Regards and happy new year to every body! Silderán.
|
|
|
Post by Admin on Jan 1, 2015 10:03:21 GMT
nope, nothing static about tilesmap/nodesmap, stop insulting them I'm familiar with Tiled, though I never used it. looks nice though, from the gallery it looks like they did a good job feel free to share your ideas and insights from your project, as well as interesting screenshots and WIP stages and have a happy new year!
|
|
|
Post by silderan on Jan 1, 2015 11:53:25 GMT
Ok, here I go.
This is just first line of a 6x6 matrix. X==0, if you prefer. The number is the index and I will call it "local_map_index"
local_map_index.x = 0 1 2 3 4 5
I'll include a second number called "real_map_index"
local_map_index.x = 0 1 2 3 4 5 real_map_index.x = 0 1 2 3 4 5 Assume anchor at 0,0 Now, camera moves to 1,0 and indexes become...
local_map_index.x = 0 1 2 3 4 5 real_map_index.x = 6 1 2 3 4 5
Camera at 10,0 and indexes become...
local_map_index.x = 0 1 2 3 4 5 real_map_index.x = 6 7 8 9 10 5
Arranging tiles must look at real_map_index instead local. With this, we'll have a small-sized matrix for an undetermined world map size. Furthermore, much less memory allocation/deletion and, hopefully, similar performance. Maths involved to camera-local-real index transformations are almost done in pseudo-code by myself. There is one thing to do: API must be aware on changing node's position. If not wrong, your design assumes that tiles didn't change their possition.
|
|
|
Post by Admin on Jan 2, 2015 15:01:57 GMT
to be honest I didn't fully understand your proposition (maybe a code would help) but I think you worry about memory and performance too much. first, you need to understand how nodesmap work so you'll worry less about performance: the nodesmap calculate the first and last nodes that are inside the screen based on the distance and "size" of the nodes. so if you have a nodesmap in the size of 1000000x1000000 or a nodesmap in the size of 100x100, it doesn't matter; the performance would still be the same. only the nodes that are inside the screen boundaries will be rendered, while the others will not even be tested for visibility because they are assumed to be outside the screen. that's why if you move nodes in nodesmap it mess up the culling. so if your resolution is 800x600 and your tiles are 32x32 in size, rest assure every frame will only go over 25x19 nodes that will be processed and rendered. as for RAM, an average computer today have at least 8GB of RAM. a sprite in ness-engine takes about 240 bytes (rough estimation), and a node about 200. so lets say every tile in your map is a node + 1 to 4 sprites, the RAM taken for every tile in your map would be 440 to 1169 bytes. lets say a map is in the size of 100x100 tiles, worse case will take 11.69 MB (worse case means every tile will have 4 layers of graphics). so if you implement smooth map transition in the most naive way, i.e. you load the current map and all its surrounding maps, it means that at every single moment you will have 9 maps of 100x100 loaded into your RAM. that's about 108 MB (in the worse case of 4 sprites per tile!). just for comparison: google chrome with about 10 tabs open can easily take over 130 MB (at least on this PC...) what I'm basically saying is this: I think you should focus more on your gameplay and less on cool yet complex optimizations, especially if your focus is on memory optimizations which are less critical. my best advice is to start from creating a class that represent a single map batch, lets say 100x100 tiles in size, and focus on make that work. after that add the smooth transition (naive implementation with loading all the neighbors) and if that's not enough - time for optimizations but that's just my personal opinion good luck on your project! PS if you do write it I'd love to see the code, I just think you should save this optimization for later phases
|
|
|
Post by silderan on Jan 2, 2015 22:48:08 GMT
You're right: RAM is not a problem; tried 1000x1000 and gets just 300Mb. Performance on rendering... cannot see differences with smaller maps. Except zoom, but 1M tiles map is soooo huge, I think. xD On closing application, takes almost 20 seconds (release build) with 1M tiles. Of course, is not necessary the cleanup at exit. So it's, in fact, irrelevant. But... xD For sure, will be necessary to create some logic to avoid handling all world at once. So, I decided to handle camera viewport x2 So... why to load a huge empty/dead map if only nearest tiles/sprites on camera will be "alive"?. The same logic used to animate sprites, read tilemap data and so on, will adapt map. Anyway. I appology again... I see now, at least in tilemap API, tiles are arranged at initial map creation. After that, I can move the tiles freely and works fine. It will take long until I can show some code as my main job is on a WISP and family. P.S. I have many things to cover: parsing files, IA/pathfinding, GUI, collision... (And addapt myself to VS. I miss QtCreator! xD) So, don't need nodemap API for now. I can play with tilemap until you get this new awesome API ready. Thank you for your kindness. Regards! Silderán.
|
|