Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411279 Posts in 69323 Topics- by 58380 Members - Latest Member: bob1029

March 28, 2024, 01:38:20 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityDevLogsRe:creation - action adventure game about undeads
Pages: 1 ... 7 8 [9]
Print
Author Topic: Re:creation - action adventure game about undeads  (Read 27269 times)
eliasdaler
Guest
« Reply #160 on: April 19, 2017, 01:37:52 PM »

hi elias
sup. :D

It's alive!

Sorry for the silence: I wanted to make a huge dev log once I've made a lot of new art and gameplay stuff, but the semester got hard and I only had time to work on engine stuff, but still made a significant progress.
Here's the latest dev log: https://eliasdaler.github.io/re-creation-devlog-december-march/

Hope you like it! And I hope that the next one will contain a lot more new things in it, not just engine/code related details.
Logged
JobLeonard
Level 10
*****



View Profile
« Reply #161 on: April 20, 2017, 07:49:43 AM »

Quote
It's alive undead!
Wink
Logged
eliasdaler
Guest
« Reply #162 on: April 20, 2017, 12:09:12 PM »

Quote
It's alive undead!
Wink
Ha-ha. Grin
But it was never dead in the first place, just left on the shelf for some time!
Logged
eliasdaler
Guest
« Reply #163 on: May 19, 2017, 01:38:22 PM »

I'm back! Starting out slowly, but with lots of confidence. Can't promise much, as I have lots of study to do (3 homeworks, 3 big projects, exams coming up, ugh!)
So, first of all, I was a little afraid that I'll look at the code and be either confused or have a burning desire to fix a lot of stuff in it. But thankfully major recent refactorings came out pretty well, so I started working on the game right away.

First, I decided to make platforms which follow waypoints.Waypoints are just entities without any components. All I need is to position them in the world and have platform follow them. Of course, this will also be used for other entities, like humans following predetermined paths.

I still have no idea how to store paths in a good way, so right now I just hardcode them by their tags. Each waypoint just gets a tag like "waypoint1", "waypoint2", etc. The path is stored in Lua like this:
Code: (lua)
somePath = { "waypoint1", "waypoint2", "waypoint3" }
Maybe I'll store them in map files at some point.

And then I realized that I can't set tag via level editor interface (previously I set them by hand in map file, ha-ha), so I quickly made this:

ImGui is trully brilliant for such quick and useful interfaces.

And then I've made platforms follow the path. It was very easy. I created FollowPathAction which is an action which can be used in action lists. It stores an array of waypoint tags and then iterates over them, creating GoToAction for each waypoint until the end of the path is reached. I've also made some additional conditions there:
  • Path following starts with the waypoint to which entity is closest too (e.g. guard chases the player and then returns to his idle path afterwards)
  • If path following starts from beginning of the path, it goes forward, otherwise - backward (e.g. 1->2->3 vs 3->2->1).
The result looks like this:


Nice! It took me 3-4 hours to make all this stuff (with some small bug-fixing of other stuff that I discovered was broken), so it's pretty nice how quickly I can make some gameplay things. Smiley

Next up is tile collision porting from my test project which has diagonal collisions. I also want to some simple tile interactions. For example, running on grass vs rock will have different sounds, running on tiles with sand will leave footprints, etc. I'll also use this system to implement falling into pits, because right now you can't do that! :D

P.S. I've also made some nice organization change: now all transform related things (position, facing direction) are grouped in Transform component, all child-parent related things are in HierarchyComponent, so now I do something like this:
Code: (cpp)
auto pos = someEntity.transform.getPosition();
/* ... */
someEntity.hierarchy.addChild(otherEntity);
I made this components public, so I could have easy access to them, instead of calling template get<ComponentType> function every time. Not much difference, but the code looks less noisy. Smiley
Logged
JobLeonard
Level 10
*****



View Profile
« Reply #164 on: May 20, 2017, 12:50:38 AM »

Nice indeed! Tools for quick iteration are always a good thing Smiley
Logged
eliasdaler
Guest
« Reply #165 on: June 01, 2017, 11:10:48 AM »

Nice indeed! Tools for quick iteration are always a good thing Smiley
Thanks!

Okay, I've made lots of stuff again!
Tile collision, falling into pits
First of all, you can fall into pits now.


For that to work, I had to integrate previously implemented collision detection into my game. This only took several hours, but now I have diagonal collision as well, which will allow me to make more interesting levels!


I also used cornering system from tiles to entities as well, so that hero now doesn't get stuck on corners of another objects and gets pushed it needed direction automatically.


So, how does falling work?
Now in every frame I check if common tile of entity changes. As most entities don't occupy many tiles, I just iterate over all tiles it intersects and if all of them are of the same type (e.g. "fall tiles"), I send CommonTileChanged event. Hero's state machine catches that event, sees that common tile is now a "fall tile" and changes hero's current state to FallState.
This will work with other systems good as well, for example if I add footstep sounds, I will use CommonTileChanged event to change walking sounds from "grass" footsteps to "rock" footsteps, depending on which tiles player walks on.

Platforms
Things don't stop here. If player steps on the platform, he doesn't fall! This is not as simple as it looks, but I'm glad I didn't have to hardcode it.


When player collides with platform, CollisionEvent is queued. CommonTileChanged event is queued too, but as CollisionSystem checks collisions before TileCollisionSystem, CollisionEvents is processed first afterwards. When something collides with moving platform, it adds it to child list (if it wasn't added before). Hero becomes a child of moving platform and inherits platform's position (moves with it).
Then CommonTileChanged event is checked, but in the callback I check if entity has a parent or not. If it does, it won't go to FallState, so the hero doesn't fall into a pit, as it has moving platform as a parent. I'll probably make it event more explicit, maybe I'll add isFlying flag which will be true for moving platform and later in CommonTileChanged callback I'll check it on parent of entity which is about to fall down. I'll see what works the best. Smiley

Huge changes to ECS implementation
I've also checked out EntityX source code and found some very useful things which I liked about its implementation. In my implementation references to components of currently active entities were stored in corresponding systems. So, CollisionSystem had vector of references to CollisionComponents. But now I implemented the system which allows me just to do this:

Code: (cpp)
for(auto& e : entityManager.getEntitiesWith<CollisionComponent, MovementComponent>()) {
    auto& mc = e.get<MovementComponent>();
    auto& cc = e.get<CollisionComponent>();
    ... // do something
}
Nice! This allows me to always get active entities with needed components and not have to store and constantly update component lists as entities get deactivated or components get disabled.

The implementation is pretty simple. First of all, each component type get unique id from 0 to N. And I also started to store all entity's components in a std::array<std::unique_ptr<Component>, MAX_COMPONENTS>. I don't have a lot of component types (I'm only counting those implemented in C++, low level ones!), so this array is very small and holds pointers after all. But this means that I can get needed components very fast just by using their unique id as an array index! I previously used unordered_map for getting storing components and getting components was pretty slow, especially when done frequently, it became noticeable in profiler.
I can also now very quickly test if entity has needed components or not. I store activeComponentMask bitset which has bits set to 1 in corresponding positions if component is active (not only present, but active, as I can sometimes disable collision, for example!).

EntityManager stores ids of currently active entities (entities nearby the hero which need to be updated) and getEntitiesWith returns EntityView object which is then used for easy iteration. Iterator just checks if entity has needed components by comparing mask being searched to activeComponentMask. It all works very fast even with lots of entities, which makes me very happy, as I don't have to store component lists anywhere now.

Iterating over tile indices
I've also implemented cool range/iterator for 2d indices. I had lots of code like this:
Code: (cpp)
const auto tilesIdx = TileMap::getTileIndicesInRect(boundingBox);
// tilesIdx is sf::IntRect which has (left-top) set to left-top tile in range and (width, height) correspond number of X and Y tiles
for (int y = 0; y < tilesIdx.height; ++y) {
    for (int x = 0; x < tilesIdx.width; ++x) {
    // for all tiles that entity intersects...
        const TileIndex tileIndex(tilesIdx.left + x, tilesIdx.top + y); // this is sf::Vector 2f
        const auto& tile = tileMap.getTile(tileIndex);
        ... // do something
But this got really annoying as I wrote this code again and again... And then I realized, what if I make range which returns indices like this?
Code: (none)
(0, 0), (1, 0), (2, 0), ... (5, 0), (0, 1), (1, 1), ...
And I've made this range by implementing RowColumnIterator which operator++ increases x if it's less than number of X tiles in range. And it worked perfectly! Now I just write code like this:
Code: (cpp)
for(const auto& tileIndex : TileMap::getTileIndicesInRect(boundingBox)) {
    const auto& tile = tileMap.getTile(tileIndex);
    ... // do something
}
Much better!
Logged
foofter
Level 4
****


MAKE THAT GARDEN GROW


View Profile WWW
« Reply #166 on: June 01, 2017, 12:27:16 PM »

I always fantasized about playing/making a "possession" game like this. Animating the dead enemies sounds/looks fun. Good luck with it!
Logged


@_monstergarden (game) and @williamzwood (me)
See Monster Garden progress here: https://forums.tigsource.com/index.php?topic=56012.0
eliasdaler
Guest
« Reply #167 on: June 01, 2017, 11:37:58 PM »

I always fantasized about playing/making a "possession" game like this. Animating the dead enemies sounds/looks fun. Good luck with it!
Thank you! Smiley
Logged
eliasdaler
Guest
« Reply #168 on: June 12, 2017, 01:54:09 PM »

And it's time for another dev log!
Things are progressing quite well. I don't have much time to work on the game, but it's great to see how much I'm able to do with so little time!

AI field of view
I've used rectangles for AI previously, but it didn't work so well because human field of view doesn't quite work like that. I had no idea how to implement "circle sector" field of view, but it turned out to be pretty simple.
First of all, I wanted some way to visualize each entity's field of view. Thankfully, SFML has sf::PrimitiveType::TriangleFan for sf::VertexArray, so implementing CircleSector class was very simple.

Then came implementation for AI. So, here's what I do
1) Find all entities which are closer than circle's radius
2) Calculate dot product of enemy's heading and ||playerPosition - enemyPosition||. If it's less than cos(viewAngle/2) then player is in enemy's field of view.
Here's a picture for you to visualize what's happening:

3) Check if any obstacles or solid tiles are on the way between enemy and player. This is done via ray-tracing. Right now I use one ray which goes from enemy to player, but one ray may not be enough because of situations like this:

Instead, I'll use three parallel (or maybe at small angle?) rays so that this will happen instead:


Entity's heading
Previously entities could only face four directions only: up, left, down and right. This came from animations being drawn only in four directions, so I didn't have direction vector. But having it is certainly is useful, especially for AI! This also lets me easily compute current entity animation direction based on current heading.
As you can see here, archer's field of view turns smoothly instead of player's discrete heading movement (that's because I'm using keyboard to move, only gamepad's stick can be used to achieve smoother movement)

This seemed like a pretty big change for me, but good encapsulation helped me implement this in a few minutes! There were lots of calls like this:
Code: (cpp)
someEntity.transform.setDirection(Direction::Down);
So, all I had to do now is to set needed heading to (0, 1) instead of setting direction variable to Direction.Down. I've also had to rewrite getDirection function to return one of four directions based on heading variable, but that was pretty easy to do, of course. And that's all I had to change in regards to previous direction variable being used by some systems. Imagine if this field was public? Ohh, I'd have to change a lot! So, getters and setters can be an important time-saver sometimes!

Improved archer's AI
After field of view improvement I decided to tune archer's AI a bit. I made archer follow the player so that he could get a better shot. The algorithm is pretty simple right now, so I won't explain it.

As AI gets more complex, I'm thinking about trying to use behaviour trees, but we'll see, maybe state machines with some simple goal based system will work just fine! If anyone dealt with behaviour trees before, please tell me your experience with them.

Small (but important) changes
First of all, I've finally started to render stuff to sf::RenderTexture so that I will always get pixel perfect rendering even when rotating/scaling stuff. It also prevents tearing even at weird resolutions and also allows me to easily apply shaders to the whole frame.
And by the way, when I enter level editor, I start to render to window which can be of any size, so that I can zoom level out and still see things clearly.

I also finally moved origin of entity's position from top-left sprite corner to center of its collision box like this:

This makes a lot of things easier to handle as I usually want to position entities next to each other based on this origin, not on top-left corner of the sprite which is not useful at all. Using center of collision box as origin allows me to only store collision box size and I can easily calculate worldBoundingBox from player's current position and this size. This origin is also used for AI's field of view which works just fine. I'll also change sprite's origin based on current animation (e.g. if entity changes origin relative to frame boundaries).

And I also finally separated Animation from GraphicsComponent making AnimationComponent. This simplified a lot of things and I should have done it sooner, but oh well. The new AnimationSystem takes care of all animation related things and makes RenderingSystem's code even more simple.
Logged
JobLeonard
Level 10
*****



View Profile
« Reply #169 on: June 13, 2017, 01:54:20 AM »

Have you ever played the original Commandos: Behind Enemy Lines?



Logged
eliasdaler
Guest
« Reply #170 on: June 13, 2017, 06:56:52 AM »

Nope. What about it? :D
Logged
JobLeonard
Level 10
*****



View Profile
« Reply #171 on: June 13, 2017, 07:01:43 AM »

Sneaking around and avoiding the enemy's field of view is the main gameplay element. The funny thing is (IIRC) that it originally was a debug mode, like your overlay, and that the developers liked it as a gameplay element and then built the game around it.
Logged
eliasdaler
Guest
« Reply #172 on: June 13, 2017, 11:21:35 AM »

Ha-ha, interesting. Now it seems like such a standard practice that I didn't even think twice about it. :D
Logged
JobLeonard
Level 10
*****



View Profile
« Reply #173 on: June 14, 2017, 07:07:53 AM »

They kind of invented the mechanic Smiley
Logged
Pages: 1 ... 7 8 [9]
Print
Jump to:  

Theme orange-lt created by panic