First of all...
this thread have reached over 100,000 views!
I want to thank everyone for reading this thread and making it so awesome with your replies. I didn't think that my game will get so much attention and so I'm very glad it happened like this. Thank you.
What to you think of the thread so far? Anything that you want me to write more about or something unrelated to current progress that you want to discuss?
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 viewI'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 headingPreviously 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:
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 AIAfter 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) changesFirst 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.