imgui_integration example initial commit, work in progress
mostly working, but the imgui ui part is only a placeholder. also this example project is kind of overly complicated, but i really wanted this to be something that somewhat resembles a real tool that i'd want to build.
This commit is contained in:
parent
d85293d9bf
commit
424c63d414
12
examples/imgui_integration/Cargo.toml
Normal file
12
examples/imgui_integration/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "imgui_integration"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "=1.0.55"
|
||||||
|
ggdt = { path = "../../ggdt" }
|
||||||
|
ggdt_imgui = { path = "../../ggdt_imgui" }
|
||||||
|
imgui = "0.11.0"
|
||||||
|
serde = { version = "1.0.136", features = ["derive"] }
|
||||||
|
serde_json = "1.0.79"
|
9
examples/imgui_integration/assets/arena.map.json
Normal file
9
examples/imgui_integration/assets/arena.map.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"width":40,
|
||||||
|
"height":30,
|
||||||
|
"layers":[
|
||||||
|
[96,96,96,96,96,96,96,96,96,96,96,96,16,17,16,16,16,16,16,17,17,32,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,33,16,16,96,96,96,96,96,96,96,96,96,96,96,16,17,17,17,32,17,32,16,16,32,16,16,32,16,16,16,16,16,16,32,16,33,16,16,16,16,16,16,16,96,96,96,96,96,96,96,96,96,96,181,178,178,178,178,183,32,16,17,181,178,178,178,178,178,178,178,178,178,178,178,183,16,16,16,16,16,16,16,32,96,96,96,96,96,96,96,96,96,181,195,16,32,17,17,193,178,178,178,195,16,16,16,16,16,16,32,16,16,16,16,193,178,183,16,32,16,16,16,16,96,96,96,96,96,96,96,181,178,195,16,16,16,32,17,17,17,17,32,16,16,16,33,16,16,16,16,16,16,16,16,16,16,193,183,16,16,16,33,16,96,96,96,96,96,96,181,195,32,16,16,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,32,16,193,183,16,16,16,16,16,17,16,16,96,181,195,16,16,33,16,16,16,16,16,32,16,16,16,16,16,32,16,16,16,16,48,48,48,48,16,16,16,16,16,196,16,16,16,16,32,16,17,16,181,195,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,48,48,48,48,48,48,48,48,16,16,32,193,183,16,32,16,8,8,8,181,195,16,16,16,16,17,32,16,16,16,16,16,16,16,16,16,16,16,16,48,48,48,48,48,48,48,48,48,48,16,16,16,196,16,16,16,7,7,7,196,16,32,16,32,17,17,17,16,16,16,16,33,16,16,16,16,16,16,48,48,48,48,48,48,48,48,48,48,48,48,16,16,193,183,16,32,7,7,181,195,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,32,16,16,16,48,48,48,48,48,48,48,48,48,48,48,48,32,16,196,32,16,7,7,196,16,16,32,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,48,48,48,48,48,48,48,48,48,48,16,16,16,196,16,16,7,7,196,8,8,16,16,16,16,16,16,32,16,16,16,16,32,16,16,16,16,16,16,16,16,48,48,48,48,48,48,48,48,16,16,16,16,196,17,16,7,7,196,7,7,16,16,16,16,16,16,16,16,16,33,16,16,16,16,16,16,16,16,16,32,16,16,48,48,48,48,48,16,16,16,16,16,196,17,16,7,7,196,7,7,16,16,16,16,16,33,16,16,16,16,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,196,17,16,16,16,196,16,33,16,16,16,16,16,16,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,196,32,16,16,16,196,16,16,16,16,16,32,16,16,16,16,32,16,16,16,32,16,16,16,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,196,16,16,16,16,196,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,32,16,16,16,16,16,16,16,33,16,16,16,16,16,32,16,196,16,33,32,16,196,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,33,16,16,16,16,16,16,16,16,16,16,16,16,16,196,32,16,16,16,196,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,32,16,16,16,16,16,196,16,16,16,16,193,183,16,16,48,48,48,48,48,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,196,32,16,32,16,33,196,48,48,48,48,48,48,48,48,16,16,16,16,16,16,16,16,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,196,16,16,16,16,16,193,183,48,48,48,48,48,48,48,48,16,16,32,16,16,16,16,16,16,16,16,33,16,16,16,16,16,16,16,16,16,16,16,16,196,16,16,16,32,16,16,196,48,48,48,48,48,48,48,48,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,32,16,196,16,16,16,16,16,16,193,183,48,48,48,48,48,48,48,48,48,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,181,195,16,16,16,16,16,16,16,193,183,48,48,48,48,48,48,48,48,48,48,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,196,16,16,16,16,16,16,16,16,16,193,178,178,183,48,48,48,48,48,48,48,48,16,16,16,16,16,32,16,16,16,16,32,16,16,16,16,16,16,181,195,16,16,16,32,16,16,33,16,32,16,16,16,193,178,178,178,178,178,178,178,178,183,16,32,16,16,16,16,16,33,16,16,16,16,16,16,181,178,195,32,16,16,16,16,16,16,16,16,16,16,32,16,16,16,16,16,16,16,33,32,17,193,178,178,178,178,178,178,178,178,178,178,178,178,178,178,195,16,16,16,16,16,48,16,16,32,16,16,16,16,16,16,16,16,16,16,32,16,16,16,16,32,16,16,32,16,16,16,32,16,16,16,33,16,16,32,16,16,16,16,16,48,48],
|
||||||
|
[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,87,-1,104,108,108,108,105,-1,-1,-1,-1,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,98,105,-1,-1,-1,26,-1,26,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,27,-1,-1,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,266,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,-1,266,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,-1,80,108,108,108,97,-1,-1,-1,-1,266,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,49,51,-1,-1,-1,-1,52,50,-1,-1,-1,-1,-1,-1,-1,-1,46,46,46,102,-1,-1,27,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,49,51,-1,-1,-1,-1,-1,-1,27,-1,52,50,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,51,-1,-1,26,-1,-1,-1,-1,-1,-1,27,52,50,-1,-1,-1,-1,-1,-1,-1,-1,-1,266,-1,-1,-1,-1,-1,-1,-1,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,278,-1,-1,-1,-1,52,-1,-1,-1,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,-1,-1,-1,67,-1,-1,27,279,-1,-1,294,-1,-1,-1,-1,-1,-1,-1,-1,-1,83,-1,-1,-1,46,46,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,65,67,-1,-1,-1,-1,-1,-1,26,-1,-1,266,68,-1,-1,-1,-1,86,-1,-1,-1,-1,-1,29,-1,-1,-1,-1,-1,-1,26,-1,-1,-1,-1,-1,-1,-1,-1,27,-1,65,67,-1,-1,-1,-1,-1,-1,-1,-1,68,66,-1,-1,-1,-1,86,-1,-1,-1,-1,-1,29,-1,27,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,65,67,-1,-1,-1,-1,-1,68,66,-1,-1,26,-1,-1,86,-1,-1,-1,-1,-1,29,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,86,14,14,-1,14,14,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,86,-1,282,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,-1,-1,-1,-1,104,-1,282,-1,-1,-1,259,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,-1,281,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,49,51,-1,-1,-1,-1,-1,52,-1,-1,-1,-1,-1,-1,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,52,-1,-1,-1,-1,-1,-1,-1,-1,-1,278,-1,-1,278,-1,-1,266,27,-1,278,-1,-1,-1,-1,26,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,-1,-1,-1,-1,27,-1,50,-1,-1,-1,-1,-1,-1,-1,-1,294,-1,-1,294,-1,27,279,-1,-1,294,-1,-1,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,52,50,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,278,-1,-1,-1,-1,266,-1,-1,-1,-1,-1,-1,-1,-1,52,50,-1,-1,-1,-1,-1,-1,-1,-1,278,-1,-1,278,-1,-1,278,-1,-1,80,-1,-1,-1,-1,26,-1,294,-1,-1,-1,-1,-1,27,-1,-1,-1,-1,-1,-1,-1,-1,266,52,-1,-1,-1,27,279,-1,-1,294,-1,-1,294,-1,-1,294,-1,266,-1,-1,-1,26,-1,-1,-1,-1,-1,260,-1,-1,-1,-1,-1,-1,27,-1,-1,-1,26,-1,-1,-1,50,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,27,-1,-1,27,-1,49,-1,278,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,266,-1,-1,-1,-1,-1,-1,-1,27,-1,-1,-1,-1,-1,49,51,-1,294,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,27,-1,49,51,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,49,51,27,-1],
|
||||||
|
[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,-1,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,-1,-1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-1,0,1,1,0,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,0,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,-1,-1,-1,-1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,-1,-1,-1,-1,-1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1,1,1,0,-1,-1,-1,-1,-1,-1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-1,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,0,1,0,0,-1,-1,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1,1,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]
|
||||||
|
]
|
||||||
|
}
|
BIN
examples/imgui_integration/assets/blue_slime.pcx
Normal file
BIN
examples/imgui_integration/assets/blue_slime.pcx
Normal file
Binary file not shown.
BIN
examples/imgui_integration/assets/db16.pal
Normal file
BIN
examples/imgui_integration/assets/db16.pal
Normal file
Binary file not shown.
BIN
examples/imgui_integration/assets/dp.fnt
Normal file
BIN
examples/imgui_integration/assets/dp.fnt
Normal file
Binary file not shown.
BIN
examples/imgui_integration/assets/green_slime.pcx
Normal file
BIN
examples/imgui_integration/assets/green_slime.pcx
Normal file
Binary file not shown.
BIN
examples/imgui_integration/assets/orange_slime.pcx
Normal file
BIN
examples/imgui_integration/assets/orange_slime.pcx
Normal file
Binary file not shown.
BIN
examples/imgui_integration/assets/small.fnt
Normal file
BIN
examples/imgui_integration/assets/small.fnt
Normal file
Binary file not shown.
BIN
examples/imgui_integration/assets/tiles.pcx
Normal file
BIN
examples/imgui_integration/assets/tiles.pcx
Normal file
Binary file not shown.
136
examples/imgui_integration/src/context.rs
Normal file
136
examples/imgui_integration/src/context.rs
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
use crate::entities::{AnimationDef, EntityActivity, Event};
|
||||||
|
use crate::support::{load_bitmap_atlas_autogrid, load_font, load_palette};
|
||||||
|
use crate::tilemap::TileMap;
|
||||||
|
use anyhow::Result;
|
||||||
|
use ggdt::prelude::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct CoreContext {
|
||||||
|
pub delta: f32,
|
||||||
|
pub camera_x: i32,
|
||||||
|
pub camera_y: i32,
|
||||||
|
pub transparent_color: u32,
|
||||||
|
pub system: System<Standard>,
|
||||||
|
pub palette: Palette,
|
||||||
|
pub font: BitmaskFont,
|
||||||
|
pub small_font: BitmaskFont,
|
||||||
|
pub entities: Entities,
|
||||||
|
pub event_publisher: EventPublisher<Event>,
|
||||||
|
pub tiles: Rc<BitmapAtlas<RgbaBitmap>>,
|
||||||
|
pub green_slime: Rc<BitmapAtlas<RgbaBitmap>>,
|
||||||
|
pub blue_slime: Rc<BitmapAtlas<RgbaBitmap>>,
|
||||||
|
pub orange_slime: Rc<BitmapAtlas<RgbaBitmap>>,
|
||||||
|
pub tilemap: TileMap,
|
||||||
|
pub slime_activity_states: Rc<HashMap<EntityActivity, Rc<AnimationDef>>>,
|
||||||
|
pub sprite_render_list: Vec<(EntityId, Vector2, RgbaBlitMethod)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CoreState<Standard> for CoreContext {
|
||||||
|
fn system(&self) -> &System<Standard> {
|
||||||
|
&self.system
|
||||||
|
}
|
||||||
|
|
||||||
|
fn system_mut(&mut self) -> &mut System<Standard> {
|
||||||
|
&mut self.system
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delta(&self) -> f32 {
|
||||||
|
self.delta
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_delta(&mut self, delta: f32) {
|
||||||
|
self.delta = delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CoreStateWithEvents<Standard, Event> for CoreContext {
|
||||||
|
fn event_publisher(&mut self) -> &mut EventPublisher<Event> {
|
||||||
|
&mut self.event_publisher
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SupportContext {
|
||||||
|
pub component_systems: ComponentSystems<CoreContext, CoreContext>,
|
||||||
|
pub event_listeners: EventListeners<Event, CoreContext>,
|
||||||
|
pub imgui: ggdt_imgui::ImGui,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SupportSystems for SupportContext {}
|
||||||
|
|
||||||
|
impl SupportSystemsWithEvents<Standard, Event> for SupportContext {
|
||||||
|
type ContextType = CoreContext;
|
||||||
|
|
||||||
|
fn event_listeners(&mut self) -> &mut EventListeners<Event, Self::ContextType> {
|
||||||
|
&mut self.event_listeners
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GameContext {
|
||||||
|
pub core: CoreContext,
|
||||||
|
pub support: SupportContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppContext<Standard> for GameContext {
|
||||||
|
type CoreType = CoreContext;
|
||||||
|
type SupportType = SupportContext;
|
||||||
|
|
||||||
|
fn core(&mut self) -> &mut Self::CoreType {
|
||||||
|
&mut self.core
|
||||||
|
}
|
||||||
|
|
||||||
|
fn support(&mut self) -> &mut Self::SupportType {
|
||||||
|
&mut self.support
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameContext {
|
||||||
|
pub fn new(mut system: System<Standard>) -> Result<Self> {
|
||||||
|
let palette = load_palette(Path::new("./assets/db16.pal"))?;
|
||||||
|
|
||||||
|
let font = load_font(Path::new("./assets/dp.fnt"))?;
|
||||||
|
let small_font = load_font(Path::new("./assets/small.fnt"))?;
|
||||||
|
|
||||||
|
let tiles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/tiles.pcx"))?);
|
||||||
|
let green_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/green_slime.pcx"))?);
|
||||||
|
let blue_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/blue_slime.pcx"))?);
|
||||||
|
let orange_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/orange_slime.pcx"))?);
|
||||||
|
|
||||||
|
let mut tilemap = TileMap::load_from(Path::new("./assets/arena.map.json"))?;
|
||||||
|
|
||||||
|
let entities = Entities::new();
|
||||||
|
let component_systems = ComponentSystems::new();
|
||||||
|
let event_publisher = EventPublisher::new();
|
||||||
|
let event_listeners = EventListeners::new();
|
||||||
|
let imgui = ggdt_imgui::ImGui::new();
|
||||||
|
|
||||||
|
let slime_activity_states = HashMap::from([
|
||||||
|
(EntityActivity::Idle, Rc::new(AnimationDef::new(&[1, 2], true, 1.0, Some(3)))),
|
||||||
|
(EntityActivity::Walking, Rc::new(AnimationDef::new(&[1, 0, 2, 0], true, 0.25, Some(3)))),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Ok(GameContext {
|
||||||
|
core: CoreContext {
|
||||||
|
delta: 0.0,
|
||||||
|
camera_x: 0,
|
||||||
|
camera_y: 0,
|
||||||
|
transparent_color: palette[0],
|
||||||
|
system,
|
||||||
|
palette,
|
||||||
|
font,
|
||||||
|
small_font,
|
||||||
|
entities,
|
||||||
|
event_publisher,
|
||||||
|
tiles,
|
||||||
|
green_slime,
|
||||||
|
blue_slime,
|
||||||
|
orange_slime,
|
||||||
|
tilemap,
|
||||||
|
slime_activity_states: Rc::new(slime_activity_states),
|
||||||
|
sprite_render_list: Vec::new(),
|
||||||
|
},
|
||||||
|
support: SupportContext { component_systems, event_listeners, imgui },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
767
examples/imgui_integration/src/entities.rs
Normal file
767
examples/imgui_integration/src/entities.rs
Normal file
|
@ -0,0 +1,767 @@
|
||||||
|
use crate::context::{CoreContext, GameContext};
|
||||||
|
use crate::tilemap::TileMap;
|
||||||
|
use ggdt::prelude::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub const FRICTION: f32 = 0.5;
|
||||||
|
pub const DEFAULT_PUSH_STRENGTH: f32 = 0.5;
|
||||||
|
pub const DEFAULT_PUSH_DISSIPATION: f32 = 0.5;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum EntityActivity {
|
||||||
|
Idle,
|
||||||
|
Walking,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum Direction {
|
||||||
|
South = 0,
|
||||||
|
West = 1,
|
||||||
|
East = 2,
|
||||||
|
North = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
pub fn new_random() -> Self {
|
||||||
|
use Direction::*;
|
||||||
|
match rnd_value(0, 3) {
|
||||||
|
0 => South,
|
||||||
|
1 => West,
|
||||||
|
2 => East,
|
||||||
|
3 => North,
|
||||||
|
_ => panic!("unknown random direction!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum SlimeColor {
|
||||||
|
Green = 0,
|
||||||
|
Blue = 1,
|
||||||
|
Orange = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SlimeColor {
|
||||||
|
pub fn new_random() -> Self {
|
||||||
|
use SlimeColor::*;
|
||||||
|
match rnd_value(0, 2) {
|
||||||
|
0 => Green,
|
||||||
|
1 => Blue,
|
||||||
|
2 => Orange,
|
||||||
|
_ => panic!("unknown random slime color!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub struct Force {
|
||||||
|
pub force: Vector2,
|
||||||
|
pub dissipation_factor: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
pub struct Activity(pub EntityActivity);
|
||||||
|
|
||||||
|
pub struct AnimateByActivity(pub Rc<HashMap<EntityActivity, Rc<AnimationDef>>>);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AnimationDef {
|
||||||
|
pub frames: &'static [usize],
|
||||||
|
pub loops: bool,
|
||||||
|
pub delay: f32,
|
||||||
|
pub multi_direction_offset: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnimationDef {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(frames: &'static [usize], loops: bool, delay: f32, multi_direction_offset: Option<usize>) -> Self {
|
||||||
|
AnimationDef { frames, loops, delay, multi_direction_offset }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AnimationInstance {
|
||||||
|
pub def: Rc<AnimationDef>,
|
||||||
|
pub frame_index: usize,
|
||||||
|
pub frame_timer: f32,
|
||||||
|
pub complete: bool,
|
||||||
|
pub delay_override: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnimationInstance {
|
||||||
|
#[inline]
|
||||||
|
pub fn from(def: Rc<AnimationDef>) -> Self {
|
||||||
|
AnimationInstance {
|
||||||
|
def, //
|
||||||
|
frame_index: 0,
|
||||||
|
frame_timer: 0.0,
|
||||||
|
complete: false,
|
||||||
|
delay_override: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn change_to(&mut self, def: Rc<AnimationDef>) {
|
||||||
|
self.def = def;
|
||||||
|
self.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.frame_index = 0;
|
||||||
|
self.frame_timer = 0.0;
|
||||||
|
self.complete = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Bounds {
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub radius: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FacingDirection(pub Direction);
|
||||||
|
|
||||||
|
pub struct Forces {
|
||||||
|
pub forces: Vec<Force>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Forces {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Forces { forces: Vec::with_capacity(5) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_force(&self) -> Vector2 {
|
||||||
|
let mut total_force = Vector2::ZERO;
|
||||||
|
for force in self.forces.iter() {
|
||||||
|
total_force += force.force;
|
||||||
|
}
|
||||||
|
total_force
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, force: Vector2, dissipation_factor: f32) {
|
||||||
|
self.forces.push(Force { force, dissipation_factor });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decay(&mut self) {
|
||||||
|
for force in self.forces.iter_mut() {
|
||||||
|
force.force *= force.dissipation_factor;
|
||||||
|
}
|
||||||
|
self.forces.retain(|f| !f.force.almost_zero(0.001));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IgnoresCollision;
|
||||||
|
|
||||||
|
pub struct IgnoresFriction;
|
||||||
|
|
||||||
|
pub struct MovementSpeed(pub f32);
|
||||||
|
|
||||||
|
pub struct Position(pub Vector2);
|
||||||
|
|
||||||
|
pub struct Pushable;
|
||||||
|
|
||||||
|
pub struct Pusher {
|
||||||
|
pub strength: f32,
|
||||||
|
pub push_force_dissipation: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pusher {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Pusher {
|
||||||
|
strength: DEFAULT_PUSH_STRENGTH, //
|
||||||
|
push_force_dissipation: DEFAULT_PUSH_DISSIPATION,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RandomlyWalksAround {
|
||||||
|
pub min_walk_time: f32,
|
||||||
|
pub max_walk_time: f32,
|
||||||
|
pub chance_to_move: u32,
|
||||||
|
pub min_cooldown: f32,
|
||||||
|
pub max_cooldown: f32,
|
||||||
|
pub cooldown_timer: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RandomlyWalksAround {
|
||||||
|
pub fn new(
|
||||||
|
min_walk_time: f32,
|
||||||
|
max_walk_time: f32,
|
||||||
|
chance_to_move: u32,
|
||||||
|
min_cooldown: f32,
|
||||||
|
max_cooldown: f32,
|
||||||
|
) -> Self {
|
||||||
|
RandomlyWalksAround {
|
||||||
|
min_walk_time,
|
||||||
|
max_walk_time,
|
||||||
|
chance_to_move,
|
||||||
|
min_cooldown,
|
||||||
|
max_cooldown,
|
||||||
|
cooldown_timer: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should_start_walking(&self) -> bool {
|
||||||
|
rnd_value(0, 100) < self.chance_to_move
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Slime;
|
||||||
|
|
||||||
|
pub struct Sprite {
|
||||||
|
pub atlas: Rc<BitmapAtlas<RgbaBitmap>>,
|
||||||
|
pub index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SpriteIndexByDirection {
|
||||||
|
pub base_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Velocity(pub Vector2);
|
||||||
|
|
||||||
|
pub struct WalkingTime(pub f32);
|
||||||
|
|
||||||
|
pub fn init_entities(entities: &mut Entities) {
|
||||||
|
entities.init_components::<Activity>();
|
||||||
|
entities.init_components::<AnimateByActivity>();
|
||||||
|
entities.init_components::<AnimationDef>();
|
||||||
|
entities.init_components::<AnimationInstance>();
|
||||||
|
entities.init_components::<Bounds>();
|
||||||
|
entities.init_components::<FacingDirection>();
|
||||||
|
entities.init_components::<Forces>();
|
||||||
|
entities.init_components::<IgnoresCollision>();
|
||||||
|
entities.init_components::<IgnoresFriction>();
|
||||||
|
entities.init_components::<MovementSpeed>();
|
||||||
|
entities.init_components::<Position>();
|
||||||
|
entities.init_components::<Pushable>();
|
||||||
|
entities.init_components::<Pusher>();
|
||||||
|
entities.init_components::<RandomlyWalksAround>();
|
||||||
|
entities.init_components::<Slime>();
|
||||||
|
entities.init_components::<Sprite>();
|
||||||
|
entities.init_components::<SpriteIndexByDirection>();
|
||||||
|
entities.init_components::<Velocity>();
|
||||||
|
entities.init_components::<WalkingTime>();
|
||||||
|
entities.remove_all_entities();
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
pub enum Event {
|
||||||
|
AnimationFinished(EntityId),
|
||||||
|
MoveForward(EntityId),
|
||||||
|
Remove(EntityId),
|
||||||
|
SetActivity(EntityId, EntityActivity),
|
||||||
|
TurnAndMove(EntityId, Direction),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_handler(event: &Event, context: &mut CoreContext) -> bool {
|
||||||
|
match event {
|
||||||
|
Event::AnimationFinished(entity) => {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
Event::MoveForward(entity) => {
|
||||||
|
if context.entities.has_entity(*entity) {
|
||||||
|
move_entity_forward(context, *entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::Remove(entity) => {
|
||||||
|
if context.entities.has_entity(*entity) {
|
||||||
|
remove_entity(&mut context.entities, *entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::SetActivity(entity, activity) => {
|
||||||
|
if context.entities.has_entity(*entity) {
|
||||||
|
set_entity_activity(&mut context.entities, *entity, *activity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::TurnAndMove(entity, direction) => {
|
||||||
|
if context.entities.has_entity(*entity) {
|
||||||
|
turn_and_move_entity(context, *entity, *direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_events(event_listener: &mut EventListeners<Event, CoreContext>) {
|
||||||
|
event_listener.clear();
|
||||||
|
event_listener.add(event_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
pub fn move_entity_forward(context: &mut CoreContext, entity: EntityId) {
|
||||||
|
let mut velocities = context.entities.components_mut::<Velocity>();
|
||||||
|
let facing_directions = context.entities.components::<FacingDirection>();
|
||||||
|
let movement_speeds = context.entities.components::<MovementSpeed>();
|
||||||
|
|
||||||
|
let velocity = velocities.get_mut(&entity).unwrap();
|
||||||
|
let facing_direction = facing_directions.get(&entity).unwrap();
|
||||||
|
let movement_speed = movement_speeds.get(&entity).unwrap();
|
||||||
|
|
||||||
|
let movement = match facing_direction.0 {
|
||||||
|
Direction::North => Vector2::UP * movement_speed.0,
|
||||||
|
Direction::East => Vector2::RIGHT * movement_speed.0,
|
||||||
|
Direction::West => Vector2::LEFT * movement_speed.0,
|
||||||
|
Direction::South => Vector2::DOWN * movement_speed.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
velocity.0 += movement;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_entity_with_collision(
|
||||||
|
position: &mut Position,
|
||||||
|
bounds: &Bounds,
|
||||||
|
velocity: Option<&Velocity>,
|
||||||
|
forces: Option<&Forces>,
|
||||||
|
map: &TileMap,
|
||||||
|
delta: f32,
|
||||||
|
) -> bool {
|
||||||
|
const NUM_STEPS: usize = 2;
|
||||||
|
const STEP_SCALE: f32 = 1.0 / NUM_STEPS as f32;
|
||||||
|
|
||||||
|
let mut collided = false;
|
||||||
|
|
||||||
|
// apply entity velocity + force (if any/either) and exit early with no collision if this entity
|
||||||
|
// has no movement ... no need to check collisions in such a case
|
||||||
|
let mut step_velocity = Vector2::ZERO;
|
||||||
|
if let Some(velocity) = velocity {
|
||||||
|
step_velocity += velocity.0 * delta;
|
||||||
|
}
|
||||||
|
if let Some(forces) = forces {
|
||||||
|
step_velocity += forces.current_force();
|
||||||
|
}
|
||||||
|
if step_velocity.nearly_equal(Vector2::ZERO, 0.00001) {
|
||||||
|
return collided;
|
||||||
|
}
|
||||||
|
|
||||||
|
// entity is actually moving, so check collisions and move accordingly
|
||||||
|
step_velocity *= STEP_SCALE;
|
||||||
|
for _ in 0..NUM_STEPS {
|
||||||
|
let old_position = position.0;
|
||||||
|
|
||||||
|
position.0.x += step_velocity.x;
|
||||||
|
if map.is_colliding(&Rect::new(position.0.x as i32, position.0.y as i32, bounds.width, bounds.height)) {
|
||||||
|
collided = true;
|
||||||
|
position.0.x = old_position.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
position.0.y += step_velocity.y;
|
||||||
|
if map.is_colliding(&Rect::new(position.0.x as i32, position.0.y as i32, bounds.width, bounds.height)) {
|
||||||
|
collided = true;
|
||||||
|
position.0.y = old_position.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
collided
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_entity(entities: &mut Entities, entity: EntityId) {
|
||||||
|
entities.remove_entity(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_entity_activity(entities: &mut Entities, entity: EntityId, new_activity: EntityActivity) {
|
||||||
|
let mut activities = entities.components_mut::<Activity>();
|
||||||
|
let mut activity = activities.get_mut(&entity).unwrap();
|
||||||
|
|
||||||
|
// only change the activity, and more importantly, the animation if we are actually applying
|
||||||
|
// an actual activity change from what it was before
|
||||||
|
if activity.0 != new_activity {
|
||||||
|
activity.0 = new_activity;
|
||||||
|
|
||||||
|
let animate_by_activitys = entities.components::<AnimateByActivity>();
|
||||||
|
if let Some(animate_by_activity) = animate_by_activitys.get(&entity) {
|
||||||
|
if let Some(new_animation_def) = animate_by_activity.0.get(&new_activity) {
|
||||||
|
let mut animations = entities.components_mut::<AnimationInstance>();
|
||||||
|
let animation = animations.get_mut(&entity).unwrap();
|
||||||
|
animation.change_to(new_animation_def.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn turn_and_move_entity(context: &mut CoreContext, entity: EntityId, direction: Direction) {
|
||||||
|
// can this entity currently move at all?
|
||||||
|
let activities = context.entities.components::<Activity>();
|
||||||
|
if let Some(activity) = activities.get(&entity) {
|
||||||
|
if activity.0 != EntityActivity::Idle && activity.0 != EntityActivity::Walking {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drop(activities);
|
||||||
|
|
||||||
|
// make the entity face in the direction specified
|
||||||
|
let mut facing_directions = context.entities.components_mut::<FacingDirection>();
|
||||||
|
let facing_direction = facing_directions.get_mut(&entity).unwrap();
|
||||||
|
facing_direction.0 = direction;
|
||||||
|
drop(facing_directions);
|
||||||
|
|
||||||
|
move_entity_forward(context, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
fn update_system_movement(context: &mut CoreContext) {
|
||||||
|
let mut positions = context.entities.components_mut::<Position>().unwrap();
|
||||||
|
let velocities = context.entities.components::<Velocity>();
|
||||||
|
let forces = context.entities.components::<Forces>();
|
||||||
|
let bounds = context.entities.components::<Bounds>();
|
||||||
|
let ignores_collision = context.entities.components::<IgnoresCollision>();
|
||||||
|
|
||||||
|
for (entity, position) in positions.iter_mut() {
|
||||||
|
if ignores_collision.contains_key(entity) {
|
||||||
|
if let Some(velocity) = velocities.get(entity) {
|
||||||
|
position.0 += velocity.0 * context.delta;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let velocity = velocities.get(entity);
|
||||||
|
let force = forces.get(entity);
|
||||||
|
|
||||||
|
if velocity.is_some() || force.is_some() {
|
||||||
|
move_entity_with_collision(
|
||||||
|
position,
|
||||||
|
bounds.get(entity).unwrap(),
|
||||||
|
velocity,
|
||||||
|
force,
|
||||||
|
&context.tilemap,
|
||||||
|
context.delta,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_system_friction(context: &mut CoreContext) {
|
||||||
|
let mut velocities = context.entities.components_mut::<Velocity>().unwrap();
|
||||||
|
let ignores_friction = context.entities.components::<IgnoresFriction>();
|
||||||
|
|
||||||
|
for (entity, velocity) in velocities.iter_mut() {
|
||||||
|
if !ignores_friction.contains_key(entity) {
|
||||||
|
velocity.0 *= FRICTION;
|
||||||
|
if velocity.0.almost_zero(0.001) {
|
||||||
|
velocity.0 = Vector2::ZERO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_system_force_decay(context: &mut CoreContext) {
|
||||||
|
let mut forces = context.entities.components_mut::<Forces>().unwrap();
|
||||||
|
for (_, force) in forces.iter_mut() {
|
||||||
|
force.decay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_system_pushing(context: &mut CoreContext) {
|
||||||
|
let positions = context.entities.components::<Position>();
|
||||||
|
let bounds = context.entities.components::<Bounds>();
|
||||||
|
let mut forces = context.entities.components_mut::<Forces>();
|
||||||
|
let pushers = context.entities.components::<Pusher>().unwrap();
|
||||||
|
let pushable = context.entities.components::<Pushable>().unwrap();
|
||||||
|
|
||||||
|
// TODO: this is slow
|
||||||
|
|
||||||
|
for (pusher_entity, pusher) in pushers.iter() {
|
||||||
|
let pusher_position = positions.get(pusher_entity).unwrap();
|
||||||
|
let pusher_bounds = bounds.get(pusher_entity).unwrap();
|
||||||
|
let pusher_circle = Circle::new(pusher_position.0.x as i32, pusher_position.0.y as i32, pusher_bounds.radius);
|
||||||
|
|
||||||
|
for (pushable_entity, pushable) in pushable.iter() {
|
||||||
|
// don't push ourself ...
|
||||||
|
if *pushable_entity == *pusher_entity {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pushable_position = positions.get(pushable_entity).unwrap();
|
||||||
|
let pushable_bounds = bounds.get(pushable_entity).unwrap();
|
||||||
|
let pushable_circle = Circle::new(
|
||||||
|
pushable_position.0.x as i32, //
|
||||||
|
pushable_position.0.y as i32,
|
||||||
|
pushable_bounds.radius,
|
||||||
|
);
|
||||||
|
|
||||||
|
if pusher_circle.overlaps(&pushable_circle) {
|
||||||
|
let mut push_direction = (pushable_position.0 - pusher_position.0).normalize();
|
||||||
|
// this can happen if the pusher's and pushable's positions are exactly the same. we just need to
|
||||||
|
// "break the tie" so to speak ...
|
||||||
|
if !push_direction.x.is_normal() || !push_direction.y.is_normal() {
|
||||||
|
push_direction = Vector2::UP; // TODO: use one of their facing directions? which one?
|
||||||
|
}
|
||||||
|
|
||||||
|
let pushable_force = forces.get_mut(pushable_entity).unwrap();
|
||||||
|
pushable_force.add(push_direction * pusher.strength, pusher.push_force_dissipation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_system_animation(context: &mut CoreContext) {
|
||||||
|
let mut animations = context.entities.components_mut::<AnimationInstance>().unwrap();
|
||||||
|
|
||||||
|
for (entity, animation) in animations.iter_mut() {
|
||||||
|
if animation.complete {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
animation.frame_timer += context.delta;
|
||||||
|
|
||||||
|
let delay = if let Some(delay_override) = animation.delay_override {
|
||||||
|
delay_override //
|
||||||
|
} else {
|
||||||
|
animation.def.delay
|
||||||
|
};
|
||||||
|
|
||||||
|
if animation.frame_timer >= delay {
|
||||||
|
// move to the next frame in the current sequence
|
||||||
|
animation.frame_timer = 0.0;
|
||||||
|
if animation.frame_index == (animation.def.frames.len() - 1) {
|
||||||
|
// we're at the last frame in the current sequence
|
||||||
|
if !animation.def.loops {
|
||||||
|
animation.complete = true;
|
||||||
|
context.event_publisher.queue(Event::AnimationFinished(*entity));
|
||||||
|
} else {
|
||||||
|
animation.frame_index = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
animation.frame_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_system_set_sprite_index_from_animation(context: &mut CoreContext) {
|
||||||
|
let animations = context.entities.components::<AnimationInstance>().unwrap();
|
||||||
|
let mut sprites = context.entities.components_mut::<Sprite>();
|
||||||
|
let facing_directions = context.entities.components::<FacingDirection>();
|
||||||
|
|
||||||
|
for (entity, animation) in animations.iter() {
|
||||||
|
if let Some(sprite) = sprites.get_mut(entity) {
|
||||||
|
// base animation sprite-sheet index for the current animation state
|
||||||
|
let mut index = animation.def.frames[animation.frame_index];
|
||||||
|
|
||||||
|
// add multi-direction offset if applicable
|
||||||
|
let multi_direction_offset = animation.def.multi_direction_offset;
|
||||||
|
let facing_direction = facing_directions.get(entity);
|
||||||
|
if multi_direction_offset.is_some() && facing_direction.is_some() {
|
||||||
|
index += multi_direction_offset.unwrap() * facing_direction.unwrap().0 as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprite.index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_system_set_sprite_index_by_direction(context: &mut CoreContext) {
|
||||||
|
let sprite_index_by_directions = context.entities.components::<SpriteIndexByDirection>().unwrap();
|
||||||
|
let mut sprites = context.entities.components_mut::<Sprite>();
|
||||||
|
let facing_directions = context.entities.components::<FacingDirection>();
|
||||||
|
|
||||||
|
for (entity, sprite_index_by_direction) in sprite_index_by_directions.iter() {
|
||||||
|
if let Some(sprite) = sprites.get_mut(entity) {
|
||||||
|
if let Some(facing_direction) = facing_directions.get(entity) {
|
||||||
|
sprite.index = sprite_index_by_direction.base_index + facing_direction.0 as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_system_walking_time(context: &mut CoreContext) {
|
||||||
|
let mut walking_times = context.entities.components_mut::<WalkingTime>().unwrap();
|
||||||
|
|
||||||
|
for (entity, walking_time) in walking_times.iter_mut() {
|
||||||
|
if walking_time.0 > 0.0 {
|
||||||
|
walking_time.0 -= context.delta;
|
||||||
|
context.event_publisher.queue(Event::MoveForward(*entity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove walking time components whose timers have elapsed
|
||||||
|
walking_times.retain(|_, comp| comp.0 > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_system_randomly_walk_around(context: &mut CoreContext) {
|
||||||
|
let mut randomly_walk_arounds = context.entities.components_mut::<RandomlyWalksAround>().unwrap();
|
||||||
|
let activities = context.entities.components::<Activity>();
|
||||||
|
let mut walking_times = context.entities.components_mut::<WalkingTime>().unwrap();
|
||||||
|
|
||||||
|
for (entity, randomly_walk_around) in randomly_walk_arounds.iter_mut() {
|
||||||
|
if let Some(activity) = activities.get(entity) {
|
||||||
|
if activity.0 == EntityActivity::Idle {
|
||||||
|
if randomly_walk_around.cooldown_timer > 0.0 {
|
||||||
|
randomly_walk_around.cooldown_timer -= context.delta;
|
||||||
|
if randomly_walk_around.cooldown_timer < 0.0 {
|
||||||
|
randomly_walk_around.cooldown_timer = 0.0;
|
||||||
|
}
|
||||||
|
} else if randomly_walk_around.should_start_walking() {
|
||||||
|
randomly_walk_around.cooldown_timer = rnd_value(
|
||||||
|
randomly_walk_around.min_cooldown, //
|
||||||
|
randomly_walk_around.max_cooldown,
|
||||||
|
);
|
||||||
|
|
||||||
|
let direction = Direction::new_random();
|
||||||
|
let walk_time = rnd_value(randomly_walk_around.min_walk_time, randomly_walk_around.max_walk_time);
|
||||||
|
|
||||||
|
walking_times.insert(*entity, WalkingTime(walk_time));
|
||||||
|
context.event_publisher.queue(Event::TurnAndMove(*entity, direction));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_system_current_entity_activity(context: &mut CoreContext) {
|
||||||
|
let activities = context.entities.components::<Activity>().unwrap();
|
||||||
|
let velocities = context.entities.components::<Velocity>();
|
||||||
|
|
||||||
|
for (entity, activity) in activities.iter() {
|
||||||
|
// try to detect current entity activity based on it's own movement speed
|
||||||
|
// (intentionally NOT checking force velocity!)
|
||||||
|
if let Some(velocity) = velocities.get(entity) {
|
||||||
|
match activity.0 {
|
||||||
|
EntityActivity::Idle => {
|
||||||
|
if velocity.0.length_squared() > 0.0 {
|
||||||
|
context.event_publisher.queue(Event::SetActivity(*entity, EntityActivity::Walking));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EntityActivity::Walking => {
|
||||||
|
if velocity.0.almost_zero(0.001) {
|
||||||
|
context.event_publisher.queue(Event::SetActivity(*entity, EntityActivity::Idle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_system_sprites(context: &mut CoreContext) {
|
||||||
|
context.sprite_render_list.clear();
|
||||||
|
|
||||||
|
let sprites = context.entities.components::<Sprite>().unwrap();
|
||||||
|
let positions = context.entities.components::<Position>().unwrap();
|
||||||
|
|
||||||
|
// build up list of entities to be rendered with their positions so we can sort them
|
||||||
|
// and render these entities with a proper y-based sort order
|
||||||
|
for (entity, _) in sprites.iter() {
|
||||||
|
let mut blit_method = RgbaBlitMethod::Transparent(context.transparent_color);
|
||||||
|
|
||||||
|
let position = positions.get(entity).unwrap();
|
||||||
|
context.sprite_render_list.push((*entity, position.0, blit_method));
|
||||||
|
}
|
||||||
|
context.sprite_render_list.sort_unstable_by(|a, b| (a.1.y as i32).cmp(&(b.1.y as i32)));
|
||||||
|
|
||||||
|
// now render them in the correct order ...
|
||||||
|
for (entity, position, blit_method) in context.sprite_render_list.iter() {
|
||||||
|
let sprite = sprites.get(entity).unwrap();
|
||||||
|
context.system.res.video.blit_atlas(
|
||||||
|
blit_method.clone(),
|
||||||
|
&sprite.atlas,
|
||||||
|
sprite.index,
|
||||||
|
position.x as i32 - context.camera_x,
|
||||||
|
position.y as i32 - context.camera_y,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_system_entity_ids(context: &mut CoreContext) {
|
||||||
|
let sprites = context.entities.components::<Sprite>().unwrap();
|
||||||
|
let positions = context.entities.components::<Position>().unwrap();
|
||||||
|
|
||||||
|
for (entity, _) in sprites.iter() {
|
||||||
|
let position = positions.get(entity).unwrap();
|
||||||
|
|
||||||
|
let x = position.0.x as i32 - context.camera_x;
|
||||||
|
let y = position.0.y as i32 - context.small_font.line_height() as i32 - context.camera_y;
|
||||||
|
context.system.res.video.print_string(
|
||||||
|
&entity.to_string(),
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
FontRenderOpts::Color(context.palette[15]),
|
||||||
|
&context.small_font,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_component_system(cs: &mut ComponentSystems<CoreContext, CoreContext>) {
|
||||||
|
cs.reset();
|
||||||
|
cs.add_update_system(update_system_current_entity_activity);
|
||||||
|
cs.add_update_system(update_system_walking_time);
|
||||||
|
cs.add_update_system(update_system_pushing);
|
||||||
|
cs.add_update_system(update_system_movement);
|
||||||
|
cs.add_update_system(update_system_friction);
|
||||||
|
cs.add_update_system(update_system_force_decay);
|
||||||
|
cs.add_update_system(update_system_randomly_walk_around);
|
||||||
|
cs.add_update_system(update_system_animation);
|
||||||
|
cs.add_update_system(update_system_set_sprite_index_from_animation);
|
||||||
|
cs.add_update_system(update_system_set_sprite_index_by_direction);
|
||||||
|
cs.add_render_system(render_system_sprites);
|
||||||
|
cs.add_render_system(render_system_entity_ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
pub fn init(context: &mut GameContext) {
|
||||||
|
init_entities(&mut context.core.entities);
|
||||||
|
init_component_system(&mut context.support.component_systems);
|
||||||
|
init_events(&mut context.support.event_listeners);
|
||||||
|
context.core.event_publisher.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_slime_entity(
|
||||||
|
context: &mut CoreContext,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
direction: Direction,
|
||||||
|
color: SlimeColor,
|
||||||
|
) -> EntityId {
|
||||||
|
let id = context.entities.new_entity();
|
||||||
|
|
||||||
|
let (
|
||||||
|
atlas, //
|
||||||
|
chance_to_move,
|
||||||
|
movement_speed,
|
||||||
|
min_walk_time,
|
||||||
|
max_walk_time,
|
||||||
|
min_walk_cooldown,
|
||||||
|
max_walk_cooldown,
|
||||||
|
) = match color {
|
||||||
|
SlimeColor::Green => (context.green_slime.clone(), 10, 8.0, 0.5, 2.0, 0.5, 5.0),
|
||||||
|
SlimeColor::Blue => (context.blue_slime.clone(), 40, 12.0, 0.5, 2.0, 0.5, 3.0),
|
||||||
|
SlimeColor::Orange => (context.orange_slime.clone(), 90, 24.0, 0.5, 1.0, 0.5, 2.0),
|
||||||
|
};
|
||||||
|
|
||||||
|
let activity = EntityActivity::Idle;
|
||||||
|
let animate_by_activity = AnimateByActivity(context.slime_activity_states.clone());
|
||||||
|
let animation = AnimationInstance::from(animate_by_activity.0.get(&activity).unwrap().clone());
|
||||||
|
|
||||||
|
context.entities.add_component(id, Slime);
|
||||||
|
context.entities.add_component(id, Position(Vector2::new(x as f32, y as f32)));
|
||||||
|
context.entities.add_component(id, Velocity(Vector2::ZERO));
|
||||||
|
context.entities.add_component(id, Forces::new());
|
||||||
|
context.entities.add_component(id, Bounds { width: 16, height: 16, radius: 8 });
|
||||||
|
context.entities.add_component(id, FacingDirection(direction));
|
||||||
|
context.entities.add_component(id, Sprite { atlas, index: 0 });
|
||||||
|
context.entities.add_component(id, Activity(activity));
|
||||||
|
context.entities.add_component(id, animate_by_activity);
|
||||||
|
context.entities.add_component(id, animation);
|
||||||
|
context.entities.add_component(
|
||||||
|
id,
|
||||||
|
RandomlyWalksAround::new(
|
||||||
|
min_walk_time, //
|
||||||
|
max_walk_time,
|
||||||
|
chance_to_move,
|
||||||
|
min_walk_cooldown,
|
||||||
|
max_walk_cooldown,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
context.entities.add_component(id, MovementSpeed(movement_speed));
|
||||||
|
context.entities.add_component(id, Pusher::new());
|
||||||
|
context.entities.add_component(id, Pushable);
|
||||||
|
|
||||||
|
id
|
||||||
|
}
|
108
examples/imgui_integration/src/main.rs
Normal file
108
examples/imgui_integration/src/main.rs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
mod context;
|
||||||
|
mod entities;
|
||||||
|
mod support;
|
||||||
|
mod tilemap;
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
|
use crate::context::GameContext;
|
||||||
|
use crate::tilemap::{TILE_HEIGHT, TILE_WIDTH};
|
||||||
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
|
pub struct DemoState;
|
||||||
|
|
||||||
|
impl AppState<GameContext> for DemoState {
|
||||||
|
fn update(&mut self, state: State, context: &mut GameContext) -> Option<StateChange<GameContext>> {
|
||||||
|
if context.core.system.res.keyboard.is_key_pressed(Scancode::Escape) {
|
||||||
|
return Some(StateChange::Pop(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ui = context.support.imgui.new_frame(&context.core.system.res.video);
|
||||||
|
ui.window("Entities").build(|| {
|
||||||
|
ui.text("TODO: display entity list or something");
|
||||||
|
});
|
||||||
|
|
||||||
|
let ui_focused = ui.is_window_hovered_with_flags(imgui::WindowHoveredFlags::ANY_WINDOW)
|
||||||
|
|| ui.is_window_focused_with_flags(imgui::WindowFocusedFlags::ANY_WINDOW);
|
||||||
|
|
||||||
|
if !ui_focused {
|
||||||
|
if context.core.system.res.mouse.is_button_down(1) {
|
||||||
|
context.core.camera_x -= context.core.system.res.mouse.x_delta() * 2;
|
||||||
|
context.core.camera_y -= context.core.system.res.mouse.y_delta() * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.support.do_events(&mut context.core);
|
||||||
|
context.support.component_systems.update(&mut context.core);
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, state: State, context: &mut GameContext) {
|
||||||
|
context.core.system.res.video.clear(context.core.palette[0]);
|
||||||
|
context.core.tilemap.draw(
|
||||||
|
&mut context.core.system.res.video,
|
||||||
|
&context.core.tiles,
|
||||||
|
context.core.camera_x,
|
||||||
|
context.core.camera_y,
|
||||||
|
context.core.transparent_color,
|
||||||
|
);
|
||||||
|
context.support.component_systems.render(&mut context.core);
|
||||||
|
|
||||||
|
context.support.imgui.render(&mut context.core.system.res.video);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transition(&mut self, state: State, context: &mut GameContext) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state_change(&mut self, new_state: State, old_state: State, context: &mut GameContext) {
|
||||||
|
if new_state == State::Pending {
|
||||||
|
entities::init(context);
|
||||||
|
for _ in 0..10 {
|
||||||
|
let (x, y) = context.core.tilemap.get_random_spawnable_coordinates();
|
||||||
|
entities::new_slime_entity(
|
||||||
|
&mut context.core,
|
||||||
|
x * TILE_WIDTH as i32,
|
||||||
|
y * TILE_HEIGHT as i32,
|
||||||
|
entities::Direction::new_random(),
|
||||||
|
entities::SlimeColor::new_random(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let config = StandardConfig::variable_screen_size(640, 480).scale_factor(2);
|
||||||
|
let mut system = SystemBuilder::new() //
|
||||||
|
.window_title("ImGui Example Integration")
|
||||||
|
.vsync(true)
|
||||||
|
.build(config)?;
|
||||||
|
system.res.cursor.enable_cursor(true);
|
||||||
|
let mut game = GameContext::new(system)?;
|
||||||
|
|
||||||
|
let mut states = States::new();
|
||||||
|
states.push(DemoState);
|
||||||
|
|
||||||
|
let mut last_ticks = game.core.system.ticks();
|
||||||
|
|
||||||
|
'mainloop: while !states.is_empty() {
|
||||||
|
game.core.system.res.update_event_state()?;
|
||||||
|
for event in game.core.system.event_pump.poll_iter() {
|
||||||
|
game.core.system.res.handle_event(&event)?;
|
||||||
|
game.support.imgui.handle_event(&event);
|
||||||
|
if event == SystemEvent::Quit {
|
||||||
|
break 'mainloop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last_ticks = game.core.update_frame_delta(last_ticks);
|
||||||
|
states.update(&mut game);
|
||||||
|
game.core.system.update()?;
|
||||||
|
states.render(&mut game);
|
||||||
|
game.core.system.display()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
18
examples/imgui_integration/src/support.rs
Normal file
18
examples/imgui_integration/src/support.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
use crate::tilemap::{TILE_HEIGHT, TILE_WIDTH};
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
|
pub fn load_palette(path: &std::path::Path) -> Result<Palette> {
|
||||||
|
Palette::load_from_file(path, PaletteFormat::Vga).context(format!("Loading palette: {:?}", path))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_font(path: &std::path::Path) -> Result<BitmaskFont> {
|
||||||
|
BitmaskFont::load_from_file(path).context(format!("Loading font: {:?}", path))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_bitmap_atlas_autogrid(path: &std::path::Path) -> Result<BitmapAtlas<RgbaBitmap>> {
|
||||||
|
let (bmp, _) = RgbaBitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?;
|
||||||
|
let mut atlas = BitmapAtlas::new(bmp);
|
||||||
|
atlas.add_grid(TILE_WIDTH, TILE_HEIGHT)?;
|
||||||
|
Ok(atlas)
|
||||||
|
}
|
110
examples/imgui_integration/src/tilemap.rs
Normal file
110
examples/imgui_integration/src/tilemap.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use ggdt::prelude::*;
|
||||||
|
|
||||||
|
pub const TILE_WIDTH: u32 = 16;
|
||||||
|
pub const TILE_HEIGHT: u32 = 16;
|
||||||
|
|
||||||
|
pub const TILE_FLAG_NONE: i32 = 1;
|
||||||
|
pub const TILE_FLAG_COLLISION: i32 = 0;
|
||||||
|
pub const TILE_FLAG_SPAWNABLE: i32 = 1;
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
pub struct TileMap {
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
layers: Vec<Box<[i32]>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TileMap {
|
||||||
|
pub fn load_from(path: &std::path::Path) -> Result<Self> {
|
||||||
|
let f = std::fs::File::open(path)?;
|
||||||
|
let reader = std::io::BufReader::new(f);
|
||||||
|
serde_json::from_reader(reader).context(format!("Loading json tilemap: {:?}", path))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn index_to(&self, x: i32, y: i32) -> Option<usize> {
|
||||||
|
if x >= 0 && y >= 0 && x < self.width as i32 && y < self.height as i32 {
|
||||||
|
Some(((y * self.width as i32) + x) as usize)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn collision(&self) -> &[i32] {
|
||||||
|
&self.layers[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_colliding(&self, rect: &Rect) -> bool {
|
||||||
|
let x1 = rect.x / TILE_WIDTH as i32;
|
||||||
|
let y1 = rect.y / TILE_HEIGHT as i32;
|
||||||
|
let x2 = rect.right() / TILE_WIDTH as i32;
|
||||||
|
let y2 = rect.bottom() / TILE_HEIGHT as i32;
|
||||||
|
|
||||||
|
for y in y1..=y2 {
|
||||||
|
for x in x1..=x2 {
|
||||||
|
match self.index_to(x, y) {
|
||||||
|
Some(index) => {
|
||||||
|
if self.collision()[index] == TILE_FLAG_COLLISION {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => return true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(
|
||||||
|
&self,
|
||||||
|
dest: &mut RgbaBitmap,
|
||||||
|
tiles: &BitmapAtlas<RgbaBitmap>,
|
||||||
|
camera_x: i32,
|
||||||
|
camera_y: i32,
|
||||||
|
transparent_color: u32,
|
||||||
|
) {
|
||||||
|
let xt = camera_x / TILE_WIDTH as i32;
|
||||||
|
let yt = camera_y / TILE_HEIGHT as i32;
|
||||||
|
let xp = camera_x % TILE_WIDTH as i32;
|
||||||
|
let yp = camera_y % TILE_HEIGHT as i32;
|
||||||
|
|
||||||
|
let tiles_y = (dest.height() as f32 / TILE_HEIGHT as f32).ceil() as i32 + 1;
|
||||||
|
let tiles_x = (dest.width() as f32 / TILE_WIDTH as f32).ceil() as i32 + 1;
|
||||||
|
|
||||||
|
for y in 0..tiles_y {
|
||||||
|
for x in 0..tiles_x {
|
||||||
|
if let Some(index) = self.index_to(x + xt, y + yt) {
|
||||||
|
let xd = (x * TILE_WIDTH as i32) - xp;
|
||||||
|
let yd = (y * TILE_HEIGHT as i32) - yp;
|
||||||
|
|
||||||
|
let lower = self.layers[0][index];
|
||||||
|
if lower >= 0 {
|
||||||
|
dest.blit_region(RgbaBlitMethod::Solid, tiles.bitmap(), &tiles[lower as usize], xd, yd);
|
||||||
|
}
|
||||||
|
let upper = self.layers[1][index];
|
||||||
|
if upper >= 0 {
|
||||||
|
dest.blit_region(
|
||||||
|
RgbaBlitMethod::Transparent(transparent_color),
|
||||||
|
tiles.bitmap(),
|
||||||
|
&tiles[upper as usize],
|
||||||
|
xd,
|
||||||
|
yd,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_random_spawnable_coordinates(&self) -> (i32, i32) {
|
||||||
|
loop {
|
||||||
|
let x = rnd_value(0, self.width as i32 - 1);
|
||||||
|
let y = rnd_value(0, self.height as i32 - 1);
|
||||||
|
if self.collision()[self.index_to(x, y).unwrap()] == TILE_FLAG_SPAWNABLE {
|
||||||
|
return (x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue