mirror of
https://github.com/danbulant/cushy
synced 2026-06-17 05:21:17 +00:00
Tilemap updates
This commit is contained in:
parent
16af20269e
commit
95555ce928
12 changed files with 258 additions and 54 deletions
27
Cargo.lock
generated
27
Cargo.lock
generated
|
|
@ -623,7 +623,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "figures"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/khonsulabs/figures#1809e94c19e56aaf9df36d7553ffa9e65523369d"
|
||||
source = "git+https://github.com/khonsulabs/figures#312f390a33025c902e94124cfab10c3dafdfb5f1"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"euclid",
|
||||
|
|
@ -968,9 +968,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "intentional"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "509e910bdc87b3c71b1085ae9659c11ea74f9667f1e15b54a6fe68a307600560"
|
||||
checksum = "bbc48117ac1523428c576e39831e93043112e2d2be0223bb0c0593af944e4e38"
|
||||
|
||||
[[package]]
|
||||
name = "interner"
|
||||
|
|
@ -1030,6 +1030,12 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "justjson"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7e253b574775d0ebd7975c471fc18f72f0775a4d42b563b5fbc3c4068aa1075"
|
||||
|
||||
[[package]]
|
||||
name = "kempt"
|
||||
version = "0.2.2"
|
||||
|
|
@ -1056,7 +1062,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
|||
[[package]]
|
||||
name = "kludgine"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/khonsulabs/kludgine#abb5625c3557c3c3b22e088fe1523b1544e7ddb7"
|
||||
source = "git+https://github.com/khonsulabs/kludgine#924198daa5fc9fe758efd7c56084f8a05ef783d1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"alot",
|
||||
|
|
@ -1067,6 +1073,7 @@ dependencies = [
|
|||
"figures",
|
||||
"image",
|
||||
"intentional",
|
||||
"justjson",
|
||||
"lyon_tessellation",
|
||||
"pollster",
|
||||
"smallvec",
|
||||
|
|
@ -1935,9 +1942,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.26"
|
||||
version = "0.38.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a"
|
||||
checksum = "bfeae074e687625746172d639330f1de242a178bf3189b51e35a7a21573513ac"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"errno",
|
||||
|
|
@ -3127,18 +3134,18 @@ checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697"
|
|||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.29"
|
||||
version = "0.7.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d075cf85bbb114e933343e087b92f2146bac0d55b534cbb8188becf0039948e"
|
||||
checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.29"
|
||||
version = "0.7.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86cd5ca076997b97ef09d3ad65efe811fa68c9e874cb636ccb211223a813b0c2"
|
||||
checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
|||
BIN
examples/assets/ferris-happy.png
Normal file
BIN
examples/assets/ferris-happy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
BIN
examples/assets/grass.aseprite
Normal file
BIN
examples/assets/grass.aseprite
Normal file
Binary file not shown.
45
examples/assets/grass.json
Normal file
45
examples/assets/grass.json
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
{ "frames": {
|
||||
"Swaying_0.aseprite": {
|
||||
"frame": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"sourceSize": { "w": 32, "h": 32 },
|
||||
"duration": 300
|
||||
},
|
||||
"Swaying_1.aseprite": {
|
||||
"frame": { "x": 32, "y": 0, "w": 32, "h": 32 },
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"sourceSize": { "w": 32, "h": 32 },
|
||||
"duration": 500
|
||||
},
|
||||
"Still_2.aseprite": {
|
||||
"frame": { "x": 64, "y": 0, "w": 32, "h": 32 },
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"sourceSize": { "w": 32, "h": 32 },
|
||||
"duration": 500
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"app": "http://www.aseprite.org/",
|
||||
"version": "1.2.17",
|
||||
"image": "grass.png",
|
||||
"format": "RGBA8888",
|
||||
"size": { "w": 96, "h": 32 },
|
||||
"scale": "1",
|
||||
"frameTags": [
|
||||
{ "name": "Swaying", "from": 0, "to": 1, "direction": "forward" },
|
||||
{ "name": "Still", "from": 2, "to": 2, "direction": "forward" }
|
||||
],
|
||||
"layers": [
|
||||
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" },
|
||||
{ "name": "Layer 2", "opacity": 255, "blendMode": "normal" }
|
||||
],
|
||||
"slices": [
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
examples/assets/grass.png
Normal file
BIN
examples/assets/grass.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 417 B |
BIN
examples/assets/isometric_tile.png
Normal file
BIN
examples/assets/isometric_tile.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1 KiB |
BIN
examples/assets/k.png
Normal file
BIN
examples/assets/k.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
examples/assets/stickguy.aseprite
Normal file
BIN
examples/assets/stickguy.aseprite
Normal file
Binary file not shown.
99
examples/assets/stickguy.json
Normal file
99
examples/assets/stickguy.json
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
{
|
||||
"frames": {
|
||||
"Idle_0.aseprite": {
|
||||
"frame": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"sourceSize": { "w": 32, "h": 32 },
|
||||
"duration": 500
|
||||
},
|
||||
"Idle_1.aseprite": {
|
||||
"frame": { "x": 32, "y": 0, "w": 32, "h": 32 },
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"sourceSize": { "w": 32, "h": 32 },
|
||||
"duration": 500
|
||||
},
|
||||
"Idle_2.aseprite": {
|
||||
"frame": { "x": 64, "y": 0, "w": 32, "h": 32 },
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"sourceSize": { "w": 32, "h": 32 },
|
||||
"duration": 500
|
||||
},
|
||||
"Idle_3.aseprite": {
|
||||
"frame": { "x": 96, "y": 0, "w": 32, "h": 32 },
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"sourceSize": { "w": 32, "h": 32 },
|
||||
"duration": 500
|
||||
},
|
||||
"WalkRight_4.aseprite": {
|
||||
"frame": { "x": 128, "y": 0, "w": 32, "h": 32 },
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"sourceSize": { "w": 32, "h": 32 },
|
||||
"duration": 200
|
||||
},
|
||||
"WalkRight_5.aseprite": {
|
||||
"frame": { "x": 160, "y": 0, "w": 32, "h": 32 },
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"sourceSize": { "w": 32, "h": 32 },
|
||||
"duration": 200
|
||||
},
|
||||
"WalkRight_6.aseprite": {
|
||||
"frame": { "x": 192, "y": 0, "w": 32, "h": 32 },
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"sourceSize": { "w": 32, "h": 32 },
|
||||
"duration": 200
|
||||
},
|
||||
"WalkLeft_7.aseprite": {
|
||||
"frame": { "x": 224, "y": 0, "w": 32, "h": 32 },
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"sourceSize": { "w": 32, "h": 32 },
|
||||
"duration": 200
|
||||
},
|
||||
"WalkLeft_8.aseprite": {
|
||||
"frame": { "x": 256, "y": 0, "w": 32, "h": 32 },
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"sourceSize": { "w": 32, "h": 32 },
|
||||
"duration": 200
|
||||
},
|
||||
"WalkLeft_9.aseprite": {
|
||||
"frame": { "x": 288, "y": 0, "w": 32, "h": 32 },
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
|
||||
"sourceSize": { "w": 32, "h": 32 },
|
||||
"duration": 200
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"app": "http://www.aseprite.org/",
|
||||
"version": "1.2.17",
|
||||
"image": "stickguy.png",
|
||||
"format": "RGBA8888",
|
||||
"size": { "w": 320, "h": 32 },
|
||||
"scale": "1",
|
||||
"frameTags": [
|
||||
{ "name": "Idle", "from": 0, "to": 3, "direction": "forward" },
|
||||
{ "name": "WalkRight", "from": 4, "to": 6, "direction": "pingpong" },
|
||||
{ "name": "WalkLeft", "from": 7, "to": 9, "direction": "pingpong" }
|
||||
],
|
||||
"layers": [{ "name": "Flattened", "opacity": 255, "blendMode": "normal" }],
|
||||
"slices": []
|
||||
}
|
||||
}
|
||||
BIN
examples/assets/stickguy.png
Normal file
BIN
examples/assets/stickguy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 592 B |
|
|
@ -1,3 +1,7 @@
|
|||
use std::array;
|
||||
use std::cmp::Ordering;
|
||||
use std::time::Duration;
|
||||
|
||||
use gooey::kludgine::app::winit::keyboard::Key;
|
||||
use gooey::kludgine::figures::units::Px;
|
||||
use gooey::kludgine::figures::{Point, Rect, Size};
|
||||
|
|
@ -6,42 +10,38 @@ use gooey::kludgine::shapes::Shape;
|
|||
use gooey::kludgine::tilemap::{Object, ObjectLayer, TileKind, TileMapFocus, Tiles, TILE_SIZE};
|
||||
use gooey::kludgine::Color;
|
||||
use gooey::value::Dynamic;
|
||||
use gooey::widget::MakeWidget;
|
||||
use gooey::widgets::{Label, Stack, TileMap};
|
||||
use gooey::widgets::TileMap;
|
||||
use gooey::{Run, Tick};
|
||||
use kludgine::app::winit::keyboard::NamedKey;
|
||||
use kludgine::figures::FloatConversion;
|
||||
use kludgine::DrawableExt;
|
||||
use kludgine::sprite::{Sprite, SpriteSource};
|
||||
use kludgine::{include_aseprite_sprite, DrawableExt};
|
||||
|
||||
const PLAYER_SIZE: Px = Px::new(16);
|
||||
|
||||
#[rustfmt::skip]
|
||||
const TILES: [TileKind; 64] = {
|
||||
const O: TileKind = TileKind::Color(Color::PURPLE);
|
||||
const X: TileKind = TileKind::Color(Color::WHITE);
|
||||
[
|
||||
X, X, X, X, X, X, X, X,
|
||||
X, O, O, O, O, O, O, X,
|
||||
X, O, X, O, O, X, O, X,
|
||||
X, O, O, O, O, O, O, X,
|
||||
X, O, X, O, O, X, O, X,
|
||||
X, O, O, X, X, O, O, X,
|
||||
X, O, O, O, O, O, O, X,
|
||||
X, X, X, X, X, X, X, X,
|
||||
]
|
||||
};
|
||||
|
||||
fn main() -> gooey::Result {
|
||||
let mut characters = ObjectLayer::new();
|
||||
|
||||
let mut sprite = include_aseprite_sprite!("assets/stickguy").unwrap();
|
||||
sprite.set_current_tag(Some("Idle")).unwrap();
|
||||
|
||||
let myself = characters.push(Player {
|
||||
color: Color::RED,
|
||||
sprite,
|
||||
current_frame: None,
|
||||
hovered: false,
|
||||
position: Point::new(TILE_SIZE.into_float(), TILE_SIZE.into_float()),
|
||||
});
|
||||
|
||||
let layers = Dynamic::new((Tiles::new(8, 8, TILES), characters));
|
||||
let sprite = include_aseprite_sprite!("assets/grass").unwrap();
|
||||
|
||||
let debug_message = Dynamic::new(String::new());
|
||||
let layers = Dynamic::new((
|
||||
Tiles::new(
|
||||
8,
|
||||
8,
|
||||
array::from_fn::<_, 64, _>(|_| TileKind::Sprite(sprite.clone())),
|
||||
),
|
||||
characters,
|
||||
));
|
||||
|
||||
let tilemap = TileMap::dynamic(layers.clone())
|
||||
.focus_on(TileMapFocus::Object {
|
||||
|
|
@ -70,24 +70,37 @@ fn main() -> gooey::Result {
|
|||
let cursor_pos = input.mouse.as_ref().map(|mouse| mouse.position);
|
||||
|
||||
layers.map_mut(|layers| {
|
||||
let pos = &mut layers.1[myself].position;
|
||||
*pos += one_second_movement.x * elapsed.as_secs_f32();
|
||||
let player = &mut layers.1[myself];
|
||||
|
||||
let rect = Rect::new(*pos - Size::squared(8.), Size::squared(16.));
|
||||
layers.1[myself].color =
|
||||
match cursor_pos.map_or(false, |cursor_pos| rect.cast().contains(cursor_pos)) {
|
||||
true => Color::RED,
|
||||
false => Color::BLUE,
|
||||
};
|
||||
let animation_tag = match direction.x.total_cmp(&0.) {
|
||||
Ordering::Less => "WalkLeft",
|
||||
Ordering::Equal => "Idle",
|
||||
Ordering::Greater => "WalkRight",
|
||||
};
|
||||
player
|
||||
.sprite
|
||||
.set_current_tag(Some(animation_tag))
|
||||
.expect("valid tag");
|
||||
|
||||
player.current_frame =
|
||||
Some(player.sprite.get_frame(Some(elapsed)).expect("valid tag"));
|
||||
|
||||
player.position += one_second_movement * elapsed.as_secs_f32();
|
||||
|
||||
let rect = Rect::new(player.position - Size::squared(8.), Size::squared(16.));
|
||||
layers.1[myself].hovered =
|
||||
cursor_pos.map_or(false, |cursor_pos| rect.cast().contains(cursor_pos));
|
||||
});
|
||||
}));
|
||||
|
||||
Stack::rows(tilemap.expand().and(Label::new(debug_message))).run()
|
||||
tilemap.run()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Player {
|
||||
color: Color,
|
||||
sprite: Sprite,
|
||||
current_frame: Option<SpriteSource>,
|
||||
hovered: bool,
|
||||
position: Point<f32>,
|
||||
}
|
||||
|
||||
|
|
@ -96,14 +109,30 @@ impl Object for Player {
|
|||
self.position.cast()
|
||||
}
|
||||
|
||||
fn render(&self, center: Point<Px>, zoom: f32, context: &mut Renderer<'_, '_>) {
|
||||
fn render(
|
||||
&self,
|
||||
center: Point<Px>,
|
||||
zoom: f32,
|
||||
context: &mut Renderer<'_, '_>,
|
||||
) -> Option<Duration> {
|
||||
let zoomed_size = PLAYER_SIZE * zoom;
|
||||
context.draw_shape(
|
||||
Shape::filled_rect(
|
||||
Rect::new(Point::squared(-zoomed_size / 2), Size::squared(zoomed_size)),
|
||||
self.color,
|
||||
)
|
||||
.translate_by(center),
|
||||
)
|
||||
if self.hovered {
|
||||
context.draw_shape(
|
||||
Shape::filled_rect(
|
||||
Rect::new(Point::squared(-zoomed_size / 2), Size::squared(zoomed_size)),
|
||||
Color::new(255, 255, 255, 80),
|
||||
)
|
||||
.translate_by(center),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(frame) = &self.current_frame {
|
||||
context.draw_texture(
|
||||
frame,
|
||||
Rect::new(center - zoomed_size / 2, Size::squared(zoomed_size)),
|
||||
);
|
||||
}
|
||||
|
||||
self.sprite.remaining_frame_duration().ok().flatten()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,14 +70,37 @@ where
|
|||
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
|
||||
let focus = self.focus.get();
|
||||
// TODO this needs to be updated to support being placed in side of a scroll view.
|
||||
self.layers.map(|layers| {
|
||||
tilemap::draw(layers, focus, self.zoom, context.gfx.inner_graphics());
|
||||
});
|
||||
let redraw_after = match &mut self.layers {
|
||||
Value::Constant(layers) => tilemap::draw(
|
||||
layers,
|
||||
focus,
|
||||
self.zoom,
|
||||
context.elapsed(),
|
||||
context.gfx.inner_graphics(),
|
||||
),
|
||||
Value::Dynamic(layers) => {
|
||||
let mut layers = layers.lock();
|
||||
layers.prevent_notifications();
|
||||
tilemap::draw(
|
||||
&mut *layers,
|
||||
focus,
|
||||
self.zoom,
|
||||
context.elapsed(),
|
||||
context.gfx.inner_graphics(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
context.draw_focus_ring();
|
||||
|
||||
if let Some(tick) = &self.tick {
|
||||
// When we are driven by a tick, we ignore all other sources of
|
||||
// refreshes.
|
||||
tick.rendered(context);
|
||||
} else {
|
||||
if let Some(redraw_after) = redraw_after {
|
||||
context.redraw_in(redraw_after);
|
||||
}
|
||||
self.focus.redraw_when_changed(context);
|
||||
self.layers.redraw_when_changed(context);
|
||||
}
|
||||
|
|
@ -168,10 +191,11 @@ where
|
|||
_location: Point<Px>,
|
||||
_device_id: DeviceId,
|
||||
button: kludgine::app::winit::event::MouseButton,
|
||||
_context: &mut EventContext<'_, '_>,
|
||||
context: &mut EventContext<'_, '_>,
|
||||
) -> EventHandling {
|
||||
if let Some(tick) = &self.tick {
|
||||
tick.mouse_button(button, ElementState::Pressed);
|
||||
context.focus();
|
||||
HANDLED
|
||||
} else {
|
||||
IGNORED
|
||||
|
|
|
|||
Loading…
Reference in a new issue