If some are curious, there's a post here about the technique I'm using to handle events on the interface (characters and NPCs movements, attacks and such). Now the real question is how much the browser will be able to deal with before everything becomes too laggy.
So far I've only tested with 1 character and 4 NPCs at the same time and I had no problem at all. I'm guessing that if there was 50 avatars on the screen at the same time things would be a bit different but if that's ever the case, I guess that'd be a "happy problem".
I won't wait long before testing this since it might have an impact on how movements/events are dealt with.
Friday, October 26, 2007
How I handle action on the interface
Wednesday, October 24, 2007
Getting a responsive interface
Now that one was a nice challenge. In the first 3 demos, you might notice that movements of avatars were done one after the other and not in a fluid way. Of course, it was ok for the prototype but it was clear it would be unacceptable for the final interface.
After 4 days of pulling my hairs, I finally got it.
My first shot was to lock the keyboard while movement was processing. What I mean is that when a move event was received, the interface couldn't send more /move commands to the server until the animation was done. You need to remember that avatars are moving inside a grid and there's no "free" movements here. So when you move your avatar, you're moving from one cell to another.
Of course, this first shot gave strange results. The avatars were lagging and strangely, I had some difficulties to reset the keyboard state (well, the keypress event) to allow back movements. Anyway, it gave me some weird looking code so I knew I was off track.
After I tried to separate client movement from server events, meaning that a player's avatar movements were handled only by the client while events were sent to server for other players to see. Again, that wasn't a good approach. I ended up with the avatar not showing where it should really be on the server and again weird code for synchronizing events sent with movement on the client. That and all my collision logic was thrown out the window this way (to prevent the current avatar to walk over buildings and such).
Finally, it came to me. All events really need to be raised by the server (as movement) to make sure that what is seen is really what is on the server (assuming there's no lag). Any avatar (current player included) should really only move when the server allow it. And an event sent to the server should really not be aware of what's happening on the screen. The delay between each moves is handled on the server anyway.
So now, each events received are added to an array of events linked to an avatar. Then each avatar has its own thread looking for events inside its array. If an event is available, it is executed and the loop is on hold to prevent 2 events being shown at the same time on screen. This way, I've been able to get fluid avatar movement while a key is hold.
All of this seems pretty trivial now that it's working but I had a hard time figuring out the good way of doing it. Now, I need to do some heavy cleaning on this code and will probably have to tweak some interval values for the loops.
The demo will have to wait since I want to have at least what I had in the last one before showing it. One's thing sure now, things are really getting exciting!
Friday, October 19, 2007
When to do the boring stuff
In every project, there's always some part that are just not that exciting. Some people like to get it done first to keep the icing for the end. Me, I say "do it when it's needed whatever it's fun or not". Well, I just "finished" the login/create account/forgot password system...
I've done more "login interface" than I can count. Each time I tell myself "Great, I'll use this in every other projects". Unfortunately, this is rarely the case. There's always some different twists, different needs and while you could probably come with some generic module to start, sometimes it's just easier to start from scratch. Some might say it's my design skills that are just terrible but if I take a look back at what I've been working on, I don't feel ashamed.
So the work on the interface is started and it's probably be as much job as doing the server side. That's ok, I'm gonna learn a lot. That will be the first time I'll use that much things like Script.aculo.us and Prototype and it will be just exciting to see what will become the "final product" evolving.
Sunday, October 14, 2007
A taste of what I have in mind for the interface
I'm at that point where I have most of the basic systems done and I need a more robust interface to test them all (my prototype is real dirty).
I'll start work on the interface and I need to figure some basic things as what will be the dimension. I'll be using the graphics found on Lost Garden and sprites generated from the Charas Project for the characters and NPCs. It's already in my plans to allow players to build their own sprites so they don't end up looking all the same. All they'll have to do is to upload the sprite and wait for approbation before setting it up to their character.
The chat and skills zones doesn't look really good but I'm hoping to find someone later to refine those parts unless I'm able to make something good out of it.
After reading this post from Elder Game, I'm now seriously thinking of adding some kind of on-screen map.
So here it is (click to zoom)
Wednesday, October 10, 2007
Range attacks in
The range attack is really just an upgrade of the melee attack. Since the "range check" was already in, the only new mechanism needed was something to check line of sight. Again, there was plenty of resources on the web to understand how to accomplish this.
To determine if a target is in the line of sight, I used Bresenham's line algorithm. All I really wanted to achieve is to make sure there is no obstacles between the attacker and its target.
There's many explanation of this algorithm but what was most helpful for me was this article on Code Project. My function receives coordinates of the attacker and the target as well as an array for "collision coordinates" which are coordinates of props with an elevation higher than 0 (characters are always on elevation 0 while buildings, trees and such have an elevation of 1.). So instead of drawing a pixel, there is a check against the array (which is in fact a generic dictionary). If the current calculated coordinate is found in the array, the function simply returns false, meaning you "can't see" your target. The checks are made at the same time as the line is "drawn" so it doesn't have to calculate it all if a collision is found before it reach the target.
To test this I only needed to create a new prop type (crossbow) part of the range weapon family and create a skill called "Basic range attack" part of the family "Range attack". Just like melee attack, when the skill is used, we make sure the weapon type currently equipped match the skill type. Everything else is just the same code used for melee attack.
This took about 3 hours to code and get it running which is great. Being able to "finish" a system in little time really does help for motivation.
The more it goes, the more I only see some tweaking tasks on my to-do list which mean that after I'm done with the crafting mechanic, I'll probably start to work on the interface. We're getting there and I can't wait to invite some people to help me test this.
Saturday, October 6, 2007
Demo 3 : It's alive! Basic NPC AI, A* pathfinding
When I wanted to add some basic AI to NPCs, I add no idea how to start. Hopefully, many people before me had already think of it all.
I first started to elaborate the controller class that would become the "brain" of each NPC. I knew I needed some kind of loop and a way to decide what action a NPC is to perform. From there, I built a really basic action called "random move" which really only do what it is named after, move randomly without any purpose. That was a start.
What if I attack this NPC? I want him to follow me and attack if he's range. But how does he get around those buildings? Didn't took long before I concluded I was lacking some basic knowledge on the subject. I then asked Uncle Google.
I found many resource and was lucky enough to find some code in C# to help me understand. Here's some link:
http://www.codeproject.com/cs/algorithms/PathFinderLibrary.asp
http://www.codeproject.com/cs/algorithms/PathFinder.asp
http://www.gamedev.net/reference/articles/article2003.asp
I'm using a very basic A* algorithm that should be good enough for what I want to achieve which is allowing a NPC to follow a target around buildings and other objects. With the tutorials I read, it was easy enough to come to a result. In fact, the hardest part was to remove diagonal movement that for some reason, I've been unable to remove the way it should have been done. Having spent enough time already on this, I simply removed the diagonal coordinates from the coordinates available (instead of raising the cost for diagonal like it should have worked). It shouldn't bring any problem. Anyway, all the pathfinding code is isolated so it's really just a matter of recompiling a DLL if I want to improve it one day.
I'm quite happy with the final result but I feel I'll have to revisit this, performance wise. For now, each "non-static" NPC is running it's AI in it's own thread. When the server starts, each AI is started separately and is independent. In the long run, if I end up with lot of NPCs, I think this might cause some problems.
I don't quite know what is the usual way to deal with this but another way to do this would be to have 1 thread per zone. Each zone is responsible for the NPCs in it and the thread is looping through all NPC. Do NPC 1 action, do NPC 2 action, .... , returns to NPC 1...
Again, the code would have to be optimized to make sure a NPC isn't waiting for another one. The advantage of having each NPC in it's own thread is that it's easy to set cool down time for each action and making sure it is exactly as expected. The commoner in the demo have a cool down time of 1.5 seconds between each moves and attacks. So what's happening is that I let the NPC do whatever action he is currently doing than set the thread to sleep for X seconds, which is the cool down time of the action that have been performed, then function calls back itself to check for next action.
So here's the link to the demo:
Demo 3
Thursday, October 4, 2007
More Metaplace stuff
Each time I read about Metaplace, it sounds quite like I could have used it for my project. Of course, I guess it would take away some of the fun of starting something from scratch but it would surely remove some of the pain too.
I haven't bumped into anything terribly difficult yet but I can see that a long beta will be needed as there's a lot of possible scenarios. The current basic AI system I'm building might be a system that will require more time than expected. That's why I'll keep it basic to make sure I don't create a monster getting out of control. A demo for the AI should be available soon.