This is a project I have been working on recently. I am converting SkyWyre v10 to isometric for my game Era Online 3.
I’ve always wanted an isometric 2D engine for many many years now after being inspired by Dark Ages several years ago.
This project isn’t an engine I will be releasing, the project is purely a system within an already existing project but I felt that it deserved it’s own thread as it’s a feature that a lot of people request all the time, and I’d like to share my progress with it, and hope to shed some light on how to do this in your own engine.
I will split this project up into stages, and discuss the processes, whether they are right or wrong. I am learning as I go along, and I hope you will learn with me as this thread progresses.
Firstly, we need to establish the correct size of the tiles. For this I will be using 64x32 tiles. With the isometric angle, the width is always double the height, so whatever size tiles you are working with, the width is always double the height. If I was using a tile with the height of 48px, the width would be 96px.
So, I go through the majority of the source code and update the bits that are important to the placement of tiles on the map. And that results in the following: I’ve placed a grass tile on every other tile so you can see it as a grid.
We can now work from this grid, and adjust the movement of the player. You need to bear in mind that tiles will not be placed separately like this, to get the true isometric system, tiles will overlap each other on each corner of the rectangle, but we will look into that later.
Unlike the way the current system works on square grids, the player will move from the centre of one of those tiles, to only half way across, here is an example of WHY that is.
If you were to place tiles normally, like you do now, you would get the following…
So in this case, we need to give a Y offset of 16px, and an X offset of 32px, so moving each tile half down and half across, which will produce the following result:
Now we have that system in place, we need to do the same thing with the player as they move. Since the player will move diagonal, rather than moving them 64px across, and 32px up or down, we need to half those measurements so they are centred on each tile.
The following picture explains why we need to do this in more detail. The red dots show where the player stops each time they move, the red line is basically the correct line of travel. As you can see, the player doesn’t just move to each grid, if they are in the centre of a grid, they only move half down, and half across, so they are in between grids, which makes this a little more complicated than normal square tiles, and this is why we use offsets, and we will use these offsets for various things in the source, such as finding out if we are standing on top of an item or event etc.
We are no longer using conventional square tiles, and every single piece of code in the source that references to the location on the map needs to be adjusted to take into account these new offsets we are using.
So, now that we have our 64x32 tiles, and we’ve established how our character needs to move and what offsets we need, we can move onto Stage 2.
Stage 2 is all about getting the character to move correctly. Currently, the source code knows the sizes of your tiles. So in normal circumstances, lets use 32x32 as an example. Every bit of code that tells the character sprite how far to move, uses PIC_X and PIC_Y, which are your tile sizes, and it will move the character up, down, left, right the amount of pixels specified by PIC_X and PIC_Y. So right now, my character is moving from the left side of a tile, diagonally, then horizontally to the next position as seen below in the video, 32px up/down, and 64px left/right. This is no good…
So what we need to do here, is go through all of the movement code in the source (Which, as always, is scattered around in several places and you have to dig around trying to find it all), and half that movement. We also need to centre the sprite onto the grid, otherwise it won’t look right.
"Where do I find all this code to adjust the movement?"
Good question, I have no idea. So the first thing I will do is start right from the beginning, the moment we press the movement keys. In this case we have vbKeyW/A/S/D, so we will search for that key press procedure, and follow the code through until we find the bits that focus on how the sprite is moved across the screen.
So first of all, we already know that the Eclipse Engine is tile-based movement, and not pixel based movement. This makes getting isometric to work much more difficult, because throughout the code, almost everything in terms of tiles and sprites wants to use PIC_X and PIC_Y to decide where everything goes.
You’ll notice that throughout the code, we have GetPlayerX/Y and SetPlayerX/Y, now, the X and Y values are based on the tile number you are on, rather than the amount of pixels.
So if you moved down twice, your Y would = 2 (number of tiles), rather than 64 (Number of pixels)
As far as I can tell, the current system only deals with whole numbers, and has absolutely no idea how to comprehend the fact that the sprite needs to move half a tile… so 0.5, 1, 1.5, 2, 2.5 simply isn’t going to accomplish this task. This is where offsets come in to play.
We still want our 64x32 tile sizes, but we want our movement to be half those values. So we now need to figure out how to get this done.
We could add a pair of new variables called MOV_X and MOV_Y which would be used to calculate movement in replacement of parts of the movement code that use PIC_X and PIC_Y, or we could go to the movement sections of the code and divide by 2, where those values are used.
Ultimately, we want the movement system to think that our tiles are 32x16.
So after some digging around, we eventually come to Sub DrawPlayer, and it’s within this procedure where it calculates the X and Y coordinates of where the sprite should be when we move.
I decided to set up a couple of new Constants called TILE_X and TILE_Y which have values of 32 and 16 respectively. After replacing some of the PIC_X’s and PIC_Y’s with our new constants, we get a much more accurate movement in regards to our isometric system. Now we just need to centre our sprite and we have this result:
You’ll notice the movement is jerky, and that’s because the sprite is moving up/down 16px, and left/right 32px, so it still has 16px left to move left/right by the time it has finished moving up/down. This is ultimately the reason I don’t like this system being tile-based movement. I may have a solution to the problem but for now, sprite movement is more or less complete and we are heading in the right direction for a true isometric graphics system.
This stage involves editing the current tile system to work isometrically. Stage 2 and 3 do overlap slightly, as adjustments to some of the tile system need to be made in order to get movement working properly by adjusting the offsets. This is briefly outlined above by using 2 new constants, TILE_X and TILE_Y.
At this stage, I’m unable to select my sprite, and I am unable to place tiles onto the map, well at least not in the right place. The cursor data is also inaccurate. I think the easiest thing to do here is go around the code and see what is using PIC_X and PIC_Y, and if it relates to the features that need to be updated, we can replace those constants with our new TILE_X and TILE_Y accordingly. However, we also need to consider that we want to try and get TRUE isometric, so the cursor no longer counts tiles by moving it left - right, up - down.
And as you can see from the below image, we would also need to figure out a way of identifying the edges of the map. This shouldn’t be any different from the normal tile system, however, the way we have managed to get isometric working so far is by what is known as “fake isometric”, in which we still use the original square grid on the left, but we double the width of the tiles, and move the player up/down by 0.5 a tile, and left/right by 1 tile at the same time. This is where it gets incredibly tricky, as we’re only moving half a tile up/down, but need it to count as one whole tile.
The problem with Eclipse, is that it uses the same data to calculate drawing, as it does positions of sprites and objects. We need to separate the 2 somehow and recalculate it. To simplify, we have 64x32 tiles, but we need to have 32x16 movement.
So after some playing around I figured out that I can use TILE_X and TILE_Y for the map editor and other things. PIC_X and PIC_Y are set to 64x32, so we know to use this whenever we’re talking about map tile sizes in regards to the graphics, and we use TILE_X and TILE_Y when talking about the movement of the player. So to adjust the code for the map editor, we just need to look at the MouseDown and MouseMove sub procedures and adjust them accordingly. This was really simple to do, and only required changing PIC_X and PIC_Y.
The box that appears on the main screen when selecting a tile to replace, is called Tex_Misc (In SkyWyre v10), and draws the selection box, we need to go into Sub DrawTileOutline and adjust the code.
The issue with this code, is that it uses the tile size for the position of the cursor, so we need to adjust some code here.
After that’s done the box draws correctly.
Now we need to work on our map edges. I took a look at Dark Ages which is a popular isometric MMORPG, and it seems that they only have warp points at the edge of the map where a path is laid down, so I’m going to go with this concept and disable the feature that automatically teleports you to the next map. I believe they have done this because they have large maps, and certain parts of the same edge will take you to a different location. You can do this by simply not entering a map number in the map editor but I’ll go ahead and comment out the code anyway as it’s a feature I don’t want.
To establish the edges of the map we need to change how our X and Y locations work. Currently, if you move up on the screen, your Y value changes, and if you move across, your X value changes.
Since our character moves diagonally, both the X and Y value change at the same time, but we don’t want this. If you look at the above picture, we need to change our grid layout to recognise movement isometrically.