Skip to content

Commit

Permalink
Merge pull request #142 from boozook/api/sprite-examples
Browse files Browse the repository at this point in the history
Improve sprite examples, fix metadata for docs.rs
  • Loading branch information
boozook authored Sep 30, 2023
2 parents 9056613 + 2ea0051 commit 655d2fc
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 182 deletions.
8 changes: 5 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion api/gfx/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "playdate-graphics"
version = "0.3.5"
version = "0.3.6"
readme = "README.md"
description = "High-level graphics API built on-top of Playdate API"
keywords = ["playdate", "sdk", "api", "gamedev"]
Expand Down Expand Up @@ -57,6 +57,7 @@ features = [
"sys/bindings-derive-ord",
"sys/bindings-derive-partialeq",
"sys/bindings-derive-partialord",
"sdk_2_1"
]
rustdoc-args = ["--cfg", "docsrs", "--show-type-layout"]
default-target = "thumbv7em-none-eabihf"
Expand Down
3 changes: 2 additions & 1 deletion api/playdate/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "playdate"
version = "0.1.8"
version = "0.1.9"
readme = "README.md"
description = "High-level Playdate API"
keywords = ["playdate", "sdk", "api", "gamedev"]
Expand Down Expand Up @@ -127,6 +127,7 @@ features = [
"sys/bindings-derive-ord",
"sys/bindings-derive-partialeq",
"sys/bindings-derive-partialord",
"sdk_2_1"
]
rustdoc-args = ["--cfg", "docsrs", "--show-type-layout"]
default-target = "thumbv7em-none-eabihf"
Expand Down
13 changes: 9 additions & 4 deletions api/sprite/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "playdate-sprite"
version = "0.2.1"
version = "0.2.2"
readme = "README.md"
description = "High-level sprite API built on-top of Playdate API"
keywords = ["playdate", "sdk", "api", "gamedev"]
Expand All @@ -21,7 +21,7 @@ repository.workspace = true
[features]
default = ["sys/default", "gfx/default"]

sdk_2_1 = []
sdk_2_1 = ["gfx/sdk_2_1"]

# playdate-sys features, should be shared because it's build configuration:

Expand All @@ -37,18 +37,22 @@ bindings-derive-debug = [
sys = { workspace = true, default-features = false }
gfx = { workspace = true, default-features = false }

[dev-dependencies]
display = { workspace = true, default-features = false }
system = { workspace = true, default-features = false, features = [ "try-trait-v2" ] }


[[example]]
name = "simple"
crate-type = ["dylib", "staticlib"]
path = "examples/simple.rs"
required-features = ["sys/lang-items"]
required-features = ["sys/lang-items", "sys/entry-point"]

[[example]]
name = "handler"
crate-type = ["dylib", "staticlib"]
path = "examples/handler.rs"
required-features = ["sys/lang-items"]
required-features = ["sys/lang-items", "sys/entry-point"]

[package.metadata.playdate]
bundle-id = "rs.playdate.sprite"
Expand All @@ -65,6 +69,7 @@ features = [
"sys/bindings-derive-ord",
"sys/bindings-derive-partialeq",
"sys/bindings-derive-partialord",
"sdk_2_1"
]
rustdoc-args = ["--cfg", "docsrs", "--show-type-layout"]
default-target = "thumbv7em-none-eabihf"
Expand Down
6 changes: 2 additions & 4 deletions api/sprite/examples/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
# Examples

⚠️ All of the examples here are very low-level, except for the parts that directly demonstrate the functionality of this package.


# How to run

```bash
cargo playdate run -p=playdate-sprite --example=simple --features=sys/lang-items
cargo playdate run -p=playdate-sprite --example=simple --features=sys/lang-items,sys/entry-point

cargo playdate run -p=playdate-sprite --example=handler --features=sys/lang-items
cargo playdate run -p=playdate-sprite --example=handler --features=sys/lang-items,sys/entry-point
```

More information how to use [cargo-playdate][] in help: `cargo playdate --help`.
Expand Down
196 changes: 93 additions & 103 deletions api/sprite/examples/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ extern crate gfx;
extern crate playdate_sprite as sprite;

use core::ffi::*;
use alloc::boxed::Box;
use core::ptr::NonNull;

use sys::EventLoopCtrl;
use sys::ffi::*;
use gfx::*;
use gfx::color::*;
use gfx::bitmap::Bitmap;
use display::Display;
use sprite::prelude::*;
use sprite::callback::update::*;
use system::event::*;
use system::update::*;

use self::updater::*;

Expand All @@ -23,66 +27,71 @@ const CENTER_X: u32 = LCD_COLUMNS / 2;
const CENTER_Y: u32 = LCD_ROWS / 2;


/// App state
struct State {
/// Game state
struct Game {
rotation: c_float,
bitmap: Option<Bitmap>,
sprite: Option<Handle<true, Sprite<UpdState>, Upd>>,
bitmap: Bitmap,
sprite: Handle<true, Sprite<UpdState>, Upd>,
}

impl State {
const fn new() -> Self {
Self { rotation: 0.,
bitmap: None,
sprite: None }
}
impl Game {
fn new() -> Self {
let bitmap = Bitmap::new(50, 50, Color::BLACK).expect("bitmap");
let sprite = Sprite::new().into_update_handler::<Upd>();

sprite.set_draw_mode(BitmapDrawMode::Copy);
sprite.set_image(&bitmap, BitmapFlip::Unflipped);
sprite.move_to(CENTER_X as _, CENTER_Y as _);
sprite.add();

/// Updates the state
fn update(&mut self) -> Option<()> {
sprite::update_and_draw_sprites();
sprite.set_userdata(UpdState::new());

// reuse bitmap:
if let Some(bitmap) = self.bitmap.as_ref() {
self.rotation += 1.0;
if self.rotation > 360.0 {
self.rotation = 0.0;
}

bitmap.draw_rotated(CENTER_X as _, CENTER_Y as _, self.rotation, 0.5, 0.5, 1.0, 1.0);
}

Some(())
Self { rotation: 0.,
bitmap,
sprite }
}


/// Event handler
fn event(&'static mut self, event: PDSystemEvent) -> Option<()> {
fn event(&'static mut self, event: SystemEvent) -> EventLoopCtrl {
match event {
// initial setup
PDSystemEvent::kEventInit => {
unsafe { (*(*sys::API).display).setRefreshRate?(60.0) };
// Initial setup
SystemEvent::Init => {
// Set FPS to maximum possible
Display::Default().set_refresh_rate(0.);

// Register our update handler that defined below
self.set_update_handler();
},
_ => {},
}

let bitmap = Bitmap::new(50, 50, Color::BLACK).expect("bitmap");
let sprite = Sprite::new().into_update_handler::<Upd>();
EventLoopCtrl::Continue
}
}

sprite.set_draw_mode(BitmapDrawMode::Copy);
sprite.set_image(&bitmap, BitmapFlip::Unflipped);
sprite.move_to(CENTER_X as _, CENTER_Y as _);
sprite.add();

sprite.set_userdata(UpdState::new());
impl Update for Game {
/// Updates the state
fn update(&mut self) -> UpdateCtrl {
sprite::update_and_draw_sprites();

self.sprite = Some(sprite);
self.bitmap = Some(bitmap);
},
_ => {},
// Reuse bitmap:
self.rotation += 1.0;
if self.rotation > 360.0 {
self.rotation = 0.0;
}
Some(())

// Draw bitmap rotated
self.bitmap
.draw_rotated(CENTER_X as _, CENTER_Y as _, self.rotation, 0.5, 0.5, 1.0, 1.0);

UpdateCtrl::Continue
}
}


/// Here is our "update sprite" behavior.
mod updater {
use core::ffi::c_float;

Expand All @@ -96,34 +105,6 @@ mod updater {

pub struct Upd<UD = UpdState, T: AnySprite = SpriteRef>(Sprite<UD, T::Api, false>);

impl<T: AnySprite, UD> AsRef<Sprite<UD, T::Api, false>> for Upd<UD, T> {
fn as_ref(&self) -> &Sprite<UD, T::Api, false> { &self.0 }
}

impl<T: AnySprite, UD> From<T> for Upd<UD, T> where Sprite<UD, T::Api, false>: From<T> {
fn from(ptr: T) -> Self { Self(Sprite::from(ptr)) }
}

impl<T: AnySprite, UD> TypedSprite for Upd<UD, T> {
type Userdata = UD;
const FREE_ON_DROP: bool = false;
}
impl<T: AnySprite, UD> AsRaw for Upd<UD, T> {
type Type = <T as AsRaw>::Type;
unsafe fn as_raw(&self) -> *mut Self::Type { self.0.as_raw() }
}
impl<T: AnySprite, UD> SpriteApi for Upd<UD, T> {
type Api = <T as SpriteApi>::Api;

fn api(&self) -> Self::Api
where Self::Api: Copy {
self.0.api()
}

fn api_ref(&self) -> &Self::Api { self.0.api_ref() }
}


impl<T, UD> SpriteUpdate for Upd<UD, T>
where T: AnySprite,
Self: From<SpriteRef>,
Expand All @@ -141,12 +122,45 @@ mod updater {
velocity.y = -velocity.y;
}

// Move sprite
sprite.move_by(velocity.x, velocity.y);
}
}
}


mod impls_needed_to_combine_handlers {
use super::*;

impl<T: AnySprite, UD> AsRef<Sprite<UD, T::Api, false>> for Upd<UD, T> {
fn as_ref(&self) -> &Sprite<UD, T::Api, false> { &self.0 }
}

impl<T: AnySprite, UD> From<T> for Upd<UD, T> where Sprite<UD, T::Api, false>: From<T> {
fn from(ptr: T) -> Self { Self(Sprite::from(ptr)) }
}

impl<T: AnySprite, UD> TypedSprite for Upd<UD, T> {
type Userdata = UD;
const FREE_ON_DROP: bool = false;
}
impl<T: AnySprite, UD> AsRaw for Upd<UD, T> {
type Type = <T as AsRaw>::Type;
unsafe fn as_raw(&self) -> *mut Self::Type { self.0.as_raw() }
}
impl<T: AnySprite, UD> SpriteApi for Upd<UD, T> {
type Api = <T as SpriteApi>::Api;

fn api(&self) -> Self::Api
where Self::Api: Copy {
self.0.api()
}

fn api_ref(&self) -> &Self::Api { self.0.api_ref() }
}
}


#[derive(Default)]
pub struct UpdState {
velocity: Point<c_float>,
Expand All @@ -172,43 +186,19 @@ mod updater {
}


/// Entry point / event handler
#[no_mangle]
/// Proxy event handler, calls `State::event`
pub extern "C" fn eventHandlerShim(api: *const PlaydateAPI, event: PDSystemEvent, _arg: u32) -> c_int {
static mut STATE: Option<Box<State>> = None;

match event {
PDSystemEvent::kEventInit => unsafe {
// register the API entry point
sys::API = api;

// create game state
if STATE.is_none() {
STATE = Some(Box::new(State::new()));
}
let state = STATE.as_mut().unwrap().as_mut() as *mut State;

// get `setUpdateCallback` fn
let f = (*(*api).system).setUpdateCallback.expect("setUpdateCallback");
// register update callback with user-data = our state
f(Some(on_update), state.cast());
},
_ => {},
}

if let Some(state) = unsafe { STATE.as_mut() } {
state.event(event).and(Some(0)).unwrap_or(1)
} else {
1
fn event_handler(_: NonNull<PlaydateAPI>, event: SystemEvent, _: u32) -> EventLoopCtrl {
// Unsafe static storage for our state.
// Usually it's safe because there's only one thread.
pub static mut GAME: Option<Game> = None;
if unsafe { GAME.is_none() } {
let state = Game::new();
unsafe { GAME = Some(state) }
}
}


/// Proxy update callback, calls `State::update`
unsafe extern "C" fn on_update(state: *mut c_void) -> i32 {
let ptr: *mut State = state.cast();
let state = ptr.as_mut().expect("missed state");
state.update().and(Some(1)).unwrap_or_default()
// Call state.event
unsafe { GAME.as_mut() }.expect("impossible").event(event)
}


Expand Down
Loading

0 comments on commit 655d2fc

Please sign in to comment.