Isometric engine drawing issue - javascript

I'm trying to write a game engine in js (canvas). So far so good.
But i got one problem my world is diamond shaped and i render the tiles from top to bottom.
The problem is when i have a tile that's bigger than 1 tile (so 2x2 as example) this will happen:
The house is defined on tile (2,1).
The left rock is placed on (1,0)
The tile (1,0) is rendered first and the next tile is (2,1) because it's on the same row and on the right.
How can you solve this?

You should be able to avoid the problem by breaking your graphics down into smaller pieces - one piece per tile on the grid. A good way to think of it is like this: If you could view the grid from directly above, each sprite should not overflow the edges of the cell they're allocated to.
For example, this cell below should probably only contain the front section of the house shown by the smaller cube:
At some point you may need to also micromanage multiple sprites in the same cell, but that's the same concept in a smaller space.

For this specific example there's a simpler solution.
Right now the house occupies these spaces: 2x0, 3x0, 2x1, 3x1
And you're drawing the house from position 2x1
If you instead drew the house from position 2x0 (and still occupy the same original 4 tiles) all the tiles would draw in correct order.
As long as you're drawing tiles top (back) to bottom (front) in screen rows, you can use oversized tiles that are 2x2, 3x3, 4x4, or any square size easily without slicing. Just draw these larger tiles along their middle row position. I often use the left corner as the grid anchor for these large tiles. It makes sense in my head this way because as soon as you draw the leftmost (or right) corner of a big isometric square, you separate everything already drawn behind it from what comes in front of it.
Rectangular oversized tiles (e.g. 2x1, 2x3, 2x4, 3x4, 4x5) usually require a more complex draw order algorithm than just screen rows top to bottom. I opt to slice these into square tiles.
Side note, that medieval house tile does already have original parts split into vertical slices if you want to go that route (my originals are on OpenGameArt).

I think the best solution here is clearly to divide your graphics using a pre-defined metric (width of a tile for instance).
The tile-based system is widely used for 2D-game, including isometric games.
Example: http://www.spriters-resource.com/pc_computer/fallouttactics/

My solutions (Also thanks to Marty Wallace!)
I can cut the sprite in 3 pieces shown on the image below
The first part gets drawed on coord (2, 0)
The second part gets drawed on coord (2, 1)
The third part gets drawed on coord (3, 1)
So we slice it vertically on the bottom tiles (the drawed tiles are like a V shape)
This should work for every tile size like 4x4
We can forgot about the tile (3, 0)
Blue: The actual png
Red: the cut lines
The lines are a bit off, but it's about the idea
And i need some sleep (that last 2 is 3 ofcourse)
This also gives us a simple calculation:
sizeX - 1 = The number of sides on the right of the middle section (the big one)
sizeY - 1 = The number of sides on the left side of the middle section
And every slice is half the tile width, and the middle slice is the full tile width.
The right slices contain only the most right part of the tile, and the left the most left side.
We can easily use tiles like 3x1 or 1x4 etc

Related

Terrain map THREE.JS

Tell me who knows about THREE.JS. I implement a map grid similar to the game SimCity 50x50. I rendered blocks of ground with different heights (as in the screenshot), now I want to smooth out the height transitions so that there is a smooth relief, while maintaining the 50x50 grid. As planned, everything should be like in the SimCity game: the user changes the type of landscape, change the relief of the landscape.
Image: How it looks now - Image: What I want to get
Implemented as follows: there is a json array where for each cell its XZ coordinates are listed for placement on a 50x50 map and a Y coordinate for the height of the land block, as well as the type of terrain (grass, earth, stone, etc.). Each block of land is a BoxGeometry.
There were thoughts to somehow add vertices to the upper side and smooth out the transitions by their positions, but I did not find this.
This could be accomplished with BlockGeometry objects, but that would be far more trouble than it's worth. For example, you'd have to analyze the surrounding terrain to know how to rotate the BlockGeometry to modify it correctly, and you might have to rotate it again when the terrain is edited. < That's not to mention the number of objects; at your size of 50×50, you'd be adding 2,500 objects to the scene. > A single mesh is much more natural in these circumstances.
First, notice that the map only comprises four cell shapes, not counting rotations: flat cells, cells that slope parallel opposite edges, cells that bend up to form a corner, and cells that bend down to form a corner.
Think of your terrain not as a set of blocks, but as one continuous surface quilted from square cells. Each cell is a mesh of four triangles.
Looking at a cell down the negative y-axis, starting at the top-left corner and moving clockwise around the cell, we'll label the vertices at the cell's corners a, b, c, and d. We'll place a fifth vertex, e, at the exact middle of the cell when viewed from this perspective. These vertices form four triangles, △abe, △bce, △cde, and △dae.
For any cell, the Y value that you're currently storing becomes the y-position of vertex a< e >.
To get the y-position of vertex b, we look at the cell one column to the right. The y-position of that cell's top-left corner (a), i.e., that cell's stored Y value, is also the y-position of the top-right corner (b) of the cell we're currently building. Similarly, the y-position of our current bottom-right corner (c) is the stored Y value of the cell down one row and to the right one column. The y-position of our current bottom-left corner (d) is the stored Y value of the cell down one row.
Vertex e is a bit trickier, but it can be solved like this:
If any three of a, b, c, and d have the same y-position, then e shares that y-position (this is a level cell or a diagonal slope).
If two adjacent vertices of a, b, c, and d share one y-position and the other two share another y-position, then the y-position of e is the average of those two y-positions.
If neither 1 nor 2 is true, we have an error (because the SimCity 2000 map didn't allow other types of geometry).
Now, actually generating the mesh is another matter, but there are plenty of tutorials available (via Google: Procedural terrain meshes in ThreeJS).
< I've created a proof of concept that you can see at https://pnichols04.github.io/sc2k-map/, with source code at https://github.com/pnichols04/sc2k-map. >

Find all free rectangular regions in a 2-D box with random positioned obstacles inside

I have a pre-defined-size rectangular area with some other rectangles inside, which represents filled regions, or, let say, obstacles.
All the rectangles are axis-aligned.
Origin of the axis (i.e. 0,0) is top-left.
The X and Y coordinates of all the rectangles, as well as the horizontal and vertical size is known.
Information about the rectangles inside the main area is contained in an already-sorted array, where i[0],i[1] are the X,Y coordinates of the upper-left corner and i[2],i[3] are respectively the x and y size:
[
[10,1,14,7],
[34,1,14,15],
[16,22,27,44]
]
How can i get all the rectangles covering the free remaining space, like in the image below?
(credits: Jukka Jylänki, A Thousand Ways to Pack the Bin - A Practical Approach to Two-Dimensional Rectangle Bin Packing http://clb.demon.fi/)
I don't need an algorithm for optimal bin-packing, as the rectangles are already placed, nor to find the biggest rectangle, but i'm aware that these can be related arguments.
I have also read some papers about the line-sweep algorithm, but i'm not able to get a working implementation, and so i cannot imagine if this would be the right solution for my problem.
My first attempt (clearly wrong) was to gather all the cuts generated by all the sides (inverse intersection):
[
[24,8,37,8],
[1,16,60,6],
[1,22,15,44],
[43,22,18,44],
[1,66,60,5],
[1,1,9,70],
[10,16,6,55],
[24,1,10,21],
[34,8,9,14],
[43,8,5,63],
[48,1,13,70]
]
...but this would require an additional step to to join adjacent rectangles and then filter out those inside a bigger one. See for example, the red-marked rectangles in this picture:
Could be this a way to go, though not optimized?

How to calculate a region of an SVG image to be used for circles?

I have an SVG image that is of size 200px x 300px but based on some conditions, I need to draw small circles that have a radius value of 30 whenever a condition fails and want this to be layered evenly (no overlapping), over a specified region of my image.
There could be multiple circles drawn but obviously don't want these to go off the image but instead wrapped around to the next line down when 5 circles have already been drawn on the first row of the image.
The same would then apply on row 2, once 5 circles of radius 30 are drawn, wrap to the next line.
I'm using snapsvg.io but unsure how to calculate the region of the image, where I want my circles drawn and unsure how to perform the wrap down to the next line.
Managed to work out how to achieve this by calculating the boundary of the region I wanted to target and also using javascript's modulo operator as suggested by Robert Longson.
By boundary, I manually determined each corner position of my image region that I wanted to use, i.e. left to right to bottom and to left again - basically width/height.
Further to this, I knew that I would only display 7 small circles from left to right across this image region and so when loop index % 7 = 0 then I reset my (x,y) coordinates to start on the next ine - 2r.

Dividing a rectangle into four clickable (or hover-able) triangles

Before reading on, my issue is to know what are the optimal methods to find an objects height/width/position as there seems to be some conflict about this.
After that I'll need help with how to use the previously obtained data to do number 4 in the following list. And after that I'll need help with number 5. I was hoping to do this gradually so please bear with me.
I found code for how to divide a square into two equal triangular clickable areas (Two triangular clickable area within a square). I didn't really understand much of what the code was doing to be honest. My question was about subdividing the rectangle that represents the visible screen area into four clickable areas, imagine its diagonals are drawn.
I did find this very useful (pseudo)-pseudocode :
Create a div and style it to be a square. Use a background image to illustrate the triangles
Create a variable, square, in javascript to hold the square element
Get the position, height, and width of square in your js
Do some math to determine the coordinates of each triangle's vertices
Write a function, getQuadrant(), that determines which triangle any given point within the square is in
Add an event listener to click events on the square. The event listener should call the getQuadrant function
Use a switch/case to execute whatever code you need to call conditional upon which quadrant the click lands in
I'm not going to ask for the full code right away, I'd like to learn in the process. Could someone please help in just pointing me towards which methods to use for numbers 3 and 4? And I'll most probably need help with number 5 as well.
Thanks a for the help! =)
K
If you translate everything so that the center of the square is the origin, then the borders of the triangle are defined by the lines x == y and x == -y. You can base your quadrant classification on that relationship:
If x > Math.abs(y), then you are in the right triangle
If y > Math.abs(x), then you are in the top triangle
If -x > Math.abs(y), then you are in the left triangle
If -y > Math.abs(x), then you are in the bottom triangle
Ties can be resolved arbitrarily between the two (or four, if x == y == 0) closest triangles.

Isometric Screen to Map

I'm trying to figure out how I can get the correct "active" tile under the mouse when I have "ramp" and +1 height tiles (see picture below).
When my world is flat, everything works no problem. Once I add a tile with a height of say +1, along with a ramp going back to +0, my screen -> map routine is still looking as if everything is "flat".
In the picture above, the green "ramp" is the real tile I want to render and calculate mouse -> map, however the blue tile you see "below" it is the area which gets calculated. So if you move your mouse into any of the dark green areas, it thinks you're on another tile.
Here is my map render (very simple)
canvas.width = canvas.width; // cheap clear in firefox 3.6, does not work in other browsers
for(i=0;i<map_y;i++){
for(j=0;j<map_x;j++){
var xpos = (i-j)*tile_h + current_x;
var ypos = (i+j)*tile_h/2+ current_y;
context.beginPath();
context.moveTo(xpos, ypos+(tile_h/2));
context.lineTo(xpos+(tile_w/2), ypos);
context.lineTo(xpos+(tile_w), ypos+(tile_h/2));
context.lineTo(xpos+(tile_w/2), ypos+(tile_h));
context.fill();
}
}
And here is my mouse -> map routine:
ymouse=( (2*(ev.pageY-canvas.offsetTop-current_y)-ev.pageX+canvas.offsetLeft+current_x)/2 );
xmouse=( ev.pageX+ymouse-current_x-(tile_w/2)-canvas.offsetLeft );
ymouse=Math.round(ymouse/tile_h);
xmouse=Math.round(xmouse/(tile_w/2));
current_tile=[xmouse,ymouse];
I have a feeling I'll have to start over and implement a world based map system rather than a simple screen -> map routine.
Thanks.
Your assumption is correct. In order to "pick" against world geometry, your routine needs to be aware of the world (and not just the base-level tile configuration). That is, without any concept of the height of the tiles near the one that is currently picked (by your current algorithm), there's no way to determine whether a neighboring tile (or one even further away, depending on the permitted height) should be intercepted by picking ray.
You've got the final possible point of your picking ray, already. What remains is to define the remainder of the ray, in world-space, and to check that ray for intersections with world geometry.
If, like the picture, your view angle is always 45 degrees and always from the same direction, your mouse -> map routine could use an algorithm something like:
calculate i,j of tile as you're doing currently (your final value of xmouse, ymouse)
look up height and angle of tile at i,j
given the height and angle, does this tile intersect the picking ray? If so, set lasti, lastj = i, j
increment/decrement i,j one step diagonally toward viewer
have we fallen off the edge of the map? If so, return lasti, lastj. Otherwise go back to 2.
Depending on the maximum height of a tile, you might have to check only 2 tiles, rather than going all the way to the edge of the map.
3 is the tricky part, and depends on your world geometry. Draw some triangles and you should be able to figure it out. Or you might try looking at the function intersect_quadrilateral_ray() here.

Categories

Resources