2:10:29 I have no idea what any of the Blend modes in Raylib does outside Depth Test enabled, but I needed to discard alpha value to get illusion of depth thing going on too.
The easy way to sort: If you have no elevation in your maps and your sprites are not wider then they are tall, keep all your entities in a matrix where each entity can belong to one map tile. Then within each tile which needs updating sort all entities by Y-position by a circle which roughly corresponds to it's "footprint". If you need something more accurate then you need to make functions which map screen position to world position and they sort on world position by adding the X,Y components together. The secret to 2D isometric is to not make arbitrary sprite sizes. If you want to support Z-coordinates and layers it's better to go 3D. I learned this all the hard way, believe me. :)
As I kept saying on the stream, I do not want to sort on the CPU, I wanted to sort with the depth buffer. Sorting on the CPU is trivial but why should I need to do that in this case?
Here's a WorldToView to function which takes a world space coord and translates to view (screen) space given the world tile sizes. In Pascal: function WorldToView(x, y, z: TScalar): TVec2; var tileCoord: TVec3; begin with World do begin // convert pixel coords to tile coords tileCoord := V3(x / tileDim, y / tileDim, z / tileDepth); // convert tile coord to screen coords result.x := (tileCoord.x - tileCoord.y) * halfTileWidth; result.y := (tileCoord.x + tileCoord.y) * halfTileHeight; result.y -= (tileCoord.z * tileDepth); // offset to top-left of bottom result.y += halfTileHeight; end; end;
hey bill, awesome live. I was trying to copy it but right from the start the load_directory_of_textures proc fails unless I comment the line delete(match). since it seems to work on windows and not on ubuntu do you think there is a memory related bug somewhere in odin? package main import "core:fmt" import "core:path/filepath" import "core:strings" import rl "vendor:raylib" TILE_WIDTH :: 256 TILE_HEIGHT :: 128 TILE_FULL_HEIGHT :: 512 load_directory_of_textures :: proc(pattern: string) -> map[string]rl.Texture2D { textures := make(map[string]rl.Texture2D) matches, _ := filepath.glob(pattern) for match in matches { path, _ := strings.replace_all(match, "\\", "/") textures[path] = rl.LoadTexture(fmt.ctprintf("%s", path)) } for match in matches { // delete(match) } delete(matches) return textures } unload_directory_of_textures :: proc(textures: map[string]rl.Texture2D) { for path, tex in textures { rl.UnloadTexture(tex) } delete(textures) } main :: proc() { rl.SetTraceLogLevel(.ERROR) rl.SetConfigFlags({.MSAA_4X_HINT, .WINDOW_HIGHDPI, .VSYNC_HINT, .FULLSCREEN_MODE}) rl.InitWindow(1920, 1080, "The Dunjun 2D") defer rl.CloseWindow() scene_textures := load_directory_of_textures("res/image/scene/*.png") defer unload_directory_of_textures(scene_textures) camera := rl.Camera2D { target = {0, 0}, rotation = 0, zoom = 1, } rl.SetTargetFPS(144) for !rl.WindowShouldClose() { free_all(context.temp_allocator) rl.BeginDrawing();defer rl.EndDrawing() rl.ClearBackground({128, 180, 255, 255}) floor, ok := scene_textures["res/image/scene/stone_N.png"] fmt.println(ok) for i in 0 ..< i32(1000) { x, y := i32(i % 16), i32(i / 16) x *= TILE_WIDTH y *= TILE_HEIGHT y -= TILE_FULL_HEIGHT - TILE_HEIGHT rl.DrawTexture(floor, x, y, rl.WHITE) } rl.DrawFPS(10, 10) } }
This is great! Very glad to see you streaming again.
Isometric is the superior projection.
Great video! Its a very nice learning experience to see you do game dev in Odin.
glad to see you back!
the MAN is back!
I really like how you just import Raylib or any other vendor lib without the headache of setuping things, is a blessing
wtf?! missed this, but glad you're back to streaming.
2:10:29 I have no idea what any of the Blend modes in Raylib does outside Depth Test enabled, but I needed to discard alpha value to get illusion of depth thing going on too.
Dunjen 2.0 let's go :)
The easy way to sort: If you have no elevation in your maps and your sprites are not wider then they are tall, keep all your entities in a matrix where each entity can belong to one map tile. Then within each tile which needs updating sort all entities by Y-position by a circle which roughly corresponds to it's "footprint". If you need something more accurate then you need to make functions which map screen position to world position and they sort on world position by adding the X,Y components together. The secret to 2D isometric is to not make arbitrary sprite sizes. If you want to support Z-coordinates and layers it's better to go 3D. I learned this all the hard way, believe me. :)
As I kept saying on the stream, I do not want to sort on the CPU, I wanted to sort with the depth buffer. Sorting on the CPU is trivial but why should I need to do that in this case?
💯🙌❤
first
Here's a WorldToView to function which takes a world space coord and translates to view (screen) space given the world tile sizes. In Pascal:
function WorldToView(x, y, z: TScalar): TVec2;
var
tileCoord: TVec3;
begin
with World do
begin
// convert pixel coords to tile coords
tileCoord := V3(x / tileDim, y / tileDim, z / tileDepth);
// convert tile coord to screen coords
result.x := (tileCoord.x - tileCoord.y) * halfTileWidth;
result.y := (tileCoord.x + tileCoord.y) * halfTileHeight;
result.y -= (tileCoord.z * tileDepth);
// offset to top-left of bottom
result.y += halfTileHeight;
end;
end;
hey bill, awesome live. I was trying to copy it but right from the start the load_directory_of_textures proc fails unless I comment the line delete(match). since it seems to work on windows and not on ubuntu do you think there is a memory related bug somewhere in odin?
package main
import "core:fmt"
import "core:path/filepath"
import "core:strings"
import rl "vendor:raylib"
TILE_WIDTH :: 256
TILE_HEIGHT :: 128
TILE_FULL_HEIGHT :: 512
load_directory_of_textures :: proc(pattern: string) -> map[string]rl.Texture2D {
textures := make(map[string]rl.Texture2D)
matches, _ := filepath.glob(pattern)
for match in matches {
path, _ := strings.replace_all(match, "\\", "/")
textures[path] = rl.LoadTexture(fmt.ctprintf("%s", path))
}
for match in matches {
// delete(match)
}
delete(matches)
return textures
}
unload_directory_of_textures :: proc(textures: map[string]rl.Texture2D) {
for path, tex in textures {
rl.UnloadTexture(tex)
}
delete(textures)
}
main :: proc() {
rl.SetTraceLogLevel(.ERROR)
rl.SetConfigFlags({.MSAA_4X_HINT, .WINDOW_HIGHDPI, .VSYNC_HINT, .FULLSCREEN_MODE})
rl.InitWindow(1920, 1080, "The Dunjun 2D")
defer rl.CloseWindow()
scene_textures := load_directory_of_textures("res/image/scene/*.png")
defer unload_directory_of_textures(scene_textures)
camera := rl.Camera2D {
target = {0, 0},
rotation = 0,
zoom = 1,
}
rl.SetTargetFPS(144)
for !rl.WindowShouldClose() {
free_all(context.temp_allocator)
rl.BeginDrawing();defer rl.EndDrawing()
rl.ClearBackground({128, 180, 255, 255})
floor, ok := scene_textures["res/image/scene/stone_N.png"]
fmt.println(ok)
for i in 0 ..< i32(1000) {
x, y := i32(i % 16), i32(i / 16)
x *= TILE_WIDTH
y *= TILE_HEIGHT
y -= TILE_FULL_HEIGHT - TILE_HEIGHT
rl.DrawTexture(floor, x, y, rl.WHITE)
}
rl.DrawFPS(10, 10)
}
}