use bevy::prelude::*;
use spine_runtime::animation::{AnimationState, Event};
use spine_runtime::render::SkeletonRenderer;
use spine_runtime::skeleton::{Physics, Skeleton, SkinNotFound};
use crate::asset::SpineSkeletonAsset;
use crate::material::{SpineMaterial, SpineMaterial3d};
#[derive(Component, Debug, Clone, Copy, Default)]
pub struct SpineRender2d;
#[derive(Component, Debug, Clone, Copy, Default)]
pub struct SpineRender3d;
#[derive(Component)]
#[require(Transform, Visibility)]
pub struct SpineSkeleton {
pub asset: Handle<SpineSkeletonAsset>,
pub state: Option<SpineSkeletonState>,
pub time_scale: f32,
pub physics: Physics,
pub paused: bool,
pub pending_animation: Option<PendingAnimation>,
pub pending_skin: Option<String>,
}
pub struct SpineSkeletonState {
pub skeleton: Skeleton,
pub animation_state: AnimationState,
pub renderer: SkeletonRenderer,
pub events: Vec<Event>,
pub meshes: Vec<Handle<Mesh>>,
pub materials: Vec<Handle<SpineMaterial>>,
pub materials_3d: Vec<Handle<SpineMaterial3d>>,
pub children: Vec<Entity>,
}
#[derive(Clone, Debug)]
pub struct PendingAnimation {
pub track: usize,
pub name: String,
pub looping: bool,
}
impl SpineSkeleton {
#[must_use]
pub fn new(asset: Handle<SpineSkeletonAsset>) -> Self {
Self {
asset,
state: None,
time_scale: 1.0,
physics: Physics::Update,
paused: false,
pending_animation: None,
pending_skin: None,
}
}
#[must_use]
pub fn with_initial_animation(
mut self,
track: usize,
name: impl Into<String>,
looping: bool,
) -> Self {
self.pending_animation = Some(PendingAnimation {
track,
name: name.into(),
looping,
});
self
}
pub fn play(&mut self, track: usize, name: impl Into<String>, looping: bool) {
let pending = PendingAnimation {
track,
name: name.into(),
looping,
};
if let Some(state) = self.state.as_mut() {
if let Err(err) = state.animation_state.set_animation_by_name(
pending.track,
&pending.name,
pending.looping,
) {
warn!(
"spine_bevy: set_animation_by_name({}, {:?}, {}) failed: {err:?}",
pending.track, pending.name, pending.looping
);
}
} else {
self.pending_animation = Some(pending);
}
}
#[must_use]
pub fn animation_state(&self) -> Option<&AnimationState> {
self.state.as_ref().map(|s| &s.animation_state)
}
pub fn animation_state_mut(&mut self) -> Option<&mut AnimationState> {
self.state.as_mut().map(|s| &mut s.animation_state)
}
#[must_use]
pub fn skeleton(&self) -> Option<&Skeleton> {
self.state.as_ref().map(|s| &s.skeleton)
}
pub fn skeleton_mut(&mut self) -> Option<&mut Skeleton> {
self.state.as_mut().map(|s| &mut s.skeleton)
}
#[must_use]
pub fn with_initial_skin(mut self, name: impl Into<String>) -> Self {
self.pending_skin = Some(name.into());
self
}
pub fn set_skin(&mut self, name: impl Into<String>) -> Result<(), SkinNotFound> {
let name = name.into();
if let Some(state) = self.state.as_mut() {
state.skeleton.set_skin_by_name(&name)?;
state.skeleton.setup_pose_slots();
Ok(())
} else {
self.pending_skin = Some(name);
Ok(())
}
}
#[must_use]
pub fn available_animations(&self) -> Option<Vec<&str>> {
let skel = self.skeleton()?;
Some(
skel.data()
.animations
.iter()
.map(|a| a.name.as_str())
.collect(),
)
}
#[must_use]
pub fn available_skins(&self) -> Option<Vec<&str>> {
let skel = self.skeleton()?;
Some(skel.data().skins.iter().map(|s| s.name.as_str()).collect())
}
}