mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 01:36:02 +08:00
[sfml] Added IK demo. See #1532.
This commit is contained in:
parent
8a93522ce9
commit
0f401bfb44
@ -165,6 +165,80 @@ void spineboy (SkeletonData* skeletonData, Atlas* atlas) {
|
|||||||
SkeletonBounds_dispose(bounds);
|
SkeletonBounds_dispose(bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ikDemo (SkeletonData* skeletonData, Atlas* atlas) {
|
||||||
|
UNUSED(atlas);
|
||||||
|
|
||||||
|
// Create the SkeletonDrawable and position it
|
||||||
|
AnimationStateData* stateData = AnimationStateData_create(skeletonData);
|
||||||
|
SkeletonDrawable* drawable = new SkeletonDrawable(skeletonData, stateData);
|
||||||
|
drawable->timeScale = 1;
|
||||||
|
drawable->setUsePremultipliedAlpha(true);
|
||||||
|
|
||||||
|
Skeleton* skeleton = drawable->skeleton;
|
||||||
|
skeleton->x = 320;
|
||||||
|
skeleton->y = 590;
|
||||||
|
|
||||||
|
// Queue the "walk" animation on the first track.
|
||||||
|
AnimationState_setAnimationByName(drawable->state, 0, "walk", true);
|
||||||
|
|
||||||
|
// Queue the "aim" animation on a higher track.
|
||||||
|
// It consists of a single frame that positions
|
||||||
|
// the back arm and gun such that they point at
|
||||||
|
// the "crosshair" bone. By setting this
|
||||||
|
// animation on a higher track, it overrides
|
||||||
|
// any changes to the back arm and gun made
|
||||||
|
// by the walk animation, allowing us to
|
||||||
|
// mix the two. The mouse position following
|
||||||
|
// is performed in the render() method below.
|
||||||
|
AnimationState_setAnimationByName(drawable->state, 1, "aim", true);
|
||||||
|
|
||||||
|
sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - IK Demo");
|
||||||
|
window.setFramerateLimit(60);
|
||||||
|
sf::Event event;
|
||||||
|
sf::Clock deltaClock;
|
||||||
|
while (window.isOpen()) {
|
||||||
|
while (window.pollEvent(event))
|
||||||
|
if (event.type == sf::Event::Closed) window.close();
|
||||||
|
|
||||||
|
float delta = deltaClock.getElapsedTime().asSeconds();
|
||||||
|
deltaClock.restart();
|
||||||
|
|
||||||
|
// Update and apply the animations to the skeleton,
|
||||||
|
// then calculate the world transforms of every bone.
|
||||||
|
// This is needed so we can call Bone#worldToLocal()
|
||||||
|
// later.
|
||||||
|
drawable->update(delta);
|
||||||
|
|
||||||
|
// Position the "crosshair" bone at the mouse
|
||||||
|
// location. We do this before calling
|
||||||
|
// skeleton.updateWorldTransform() below, so
|
||||||
|
// our change is incorporated before the IK
|
||||||
|
// constraint is applied.
|
||||||
|
//
|
||||||
|
// When setting the crosshair bone position
|
||||||
|
// to the mouse position, we need to translate
|
||||||
|
// from "mouse space" to "local bone space". Note that the local
|
||||||
|
// bone space is calculated using the bone's parent
|
||||||
|
// worldToLocal() function!
|
||||||
|
sf::Vector2i mouseCoords = sf::Mouse::getPosition(window);
|
||||||
|
float boneCoordsX = 0, boneCoordsY = 0;
|
||||||
|
Bone* crosshair = Skeleton_findBone(drawable->skeleton, "crosshair"); // Should be cached.
|
||||||
|
Bone_worldToLocal(crosshair->parent, mouseCoords.x, mouseCoords.y, &boneCoordsX, &boneCoordsY);
|
||||||
|
crosshair->x = boneCoordsX;
|
||||||
|
crosshair->y = boneCoordsY;
|
||||||
|
crosshair->appliedValid = false;
|
||||||
|
|
||||||
|
// Calculate final world transform with the
|
||||||
|
// crosshair bone set to the mouse cursor
|
||||||
|
// position.
|
||||||
|
Skeleton_updateWorldTransform(drawable->skeleton);
|
||||||
|
|
||||||
|
window.clear();
|
||||||
|
window.draw(*drawable);
|
||||||
|
window.display();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void goblins (SkeletonData* skeletonData, Atlas* atlas) {
|
void goblins (SkeletonData* skeletonData, Atlas* atlas) {
|
||||||
UNUSED(atlas);
|
UNUSED(atlas);
|
||||||
SkeletonDrawable* drawable = new SkeletonDrawable(skeletonData);
|
SkeletonDrawable* drawable = new SkeletonDrawable(skeletonData);
|
||||||
@ -552,6 +626,7 @@ void testMixAndMatch(SkeletonData* skeletonData, Atlas* atlas) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main () {
|
int main () {
|
||||||
|
testcase(ikDemo, "data/spineboy-pro.json", "data/spineboy-pro.skel", "data/spineboy-pma.atlas", 0.6f);
|
||||||
testcase(testMixAndMatch, "data/mix-and-match-pro.json", "data/mix-and-match-pro.skel", "data/mix-and-match-pma.atlas", 0.5f);
|
testcase(testMixAndMatch, "data/mix-and-match-pro.json", "data/mix-and-match-pro.skel", "data/mix-and-match-pma.atlas", 0.5f);
|
||||||
testcase(goblins, "data/goblins-pro.json", "data/goblins-pro.skel", "data/goblins-pma.atlas", 1.4f);
|
testcase(goblins, "data/goblins-pro.json", "data/goblins-pro.skel", "data/goblins-pma.atlas", 1.4f);
|
||||||
testcase(test, "data/tank-pro.json", "data/tank-pro.skel", "data/tank-pma.atlas", 1.0f);
|
testcase(test, "data/tank-pro.json", "data/tank-pro.skel", "data/tank-pma.atlas", 1.0f);
|
||||||
|
|||||||
@ -160,6 +160,80 @@ void spineboy (SkeletonData* skeletonData, Atlas* atlas) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ikDemo (SkeletonData* skeletonData, Atlas* atlas) {
|
||||||
|
SP_UNUSED(atlas);
|
||||||
|
|
||||||
|
SkeletonBounds bounds;
|
||||||
|
|
||||||
|
// Create the SkeletonDrawable and position it
|
||||||
|
AnimationStateData stateData(skeletonData);
|
||||||
|
SkeletonDrawable drawable(skeletonData, &stateData);
|
||||||
|
drawable.timeScale = 1;
|
||||||
|
drawable.setUsePremultipliedAlpha(true);
|
||||||
|
drawable.skeleton->setPosition(320, 590);
|
||||||
|
|
||||||
|
// Queue the "walk" animation on the first track.
|
||||||
|
drawable.state->setAnimation(0, "walk", true);
|
||||||
|
|
||||||
|
// Queue the "aim" animation on a higher track.
|
||||||
|
// It consists of a single frame that positions
|
||||||
|
// the back arm and gun such that they point at
|
||||||
|
// the "crosshair" bone. By setting this
|
||||||
|
// animation on a higher track, it overrides
|
||||||
|
// any changes to the back arm and gun made
|
||||||
|
// by the walk animation, allowing us to
|
||||||
|
// mix the two. The mouse position following
|
||||||
|
// is performed in the render() method below.
|
||||||
|
drawable.state->setAnimation(1, "aim", true);
|
||||||
|
|
||||||
|
sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - IK Demo");
|
||||||
|
window.setFramerateLimit(60);
|
||||||
|
sf::Event event;
|
||||||
|
sf::Clock deltaClock;
|
||||||
|
|
||||||
|
while (window.isOpen()) {
|
||||||
|
while (window.pollEvent(event))
|
||||||
|
if (event.type == sf::Event::Closed) window.close();
|
||||||
|
|
||||||
|
float delta = deltaClock.getElapsedTime().asSeconds();
|
||||||
|
deltaClock.restart();
|
||||||
|
|
||||||
|
// Update and apply the animations to the skeleton,
|
||||||
|
// then calculate the world transforms of every bone.
|
||||||
|
// This is needed so we can call Bone#worldToLocal()
|
||||||
|
// later.
|
||||||
|
drawable.update(delta);
|
||||||
|
|
||||||
|
// Position the "crosshair" bone at the mouse
|
||||||
|
// location. We do this before calling
|
||||||
|
// skeleton.updateWorldTransform() below, so
|
||||||
|
// our change is incorporated before the IK
|
||||||
|
// constraint is applied.
|
||||||
|
//
|
||||||
|
// When setting the crosshair bone position
|
||||||
|
// to the mouse position, we need to translate
|
||||||
|
// from "mouse space" to "local bone space". Note that the local
|
||||||
|
// bone space is calculated using the bone's parent
|
||||||
|
// worldToLocal() function!
|
||||||
|
sf::Vector2i mouseCoords = sf::Mouse::getPosition(window);
|
||||||
|
float boneCoordsX = 0, boneCoordsY = 0;
|
||||||
|
Bone* crosshair = drawable.skeleton->findBone("crosshair"); // Should be cached.
|
||||||
|
crosshair->getParent()->worldToLocal(mouseCoords.x, mouseCoords.y, boneCoordsX, boneCoordsY);
|
||||||
|
crosshair->setX(boneCoordsX);
|
||||||
|
crosshair->setY(boneCoordsY);
|
||||||
|
crosshair->setAppliedValid(false);
|
||||||
|
|
||||||
|
// Calculate final world transform with the
|
||||||
|
// crosshair bone set to the mouse cursor
|
||||||
|
// position.
|
||||||
|
drawable.skeleton->updateWorldTransform();
|
||||||
|
|
||||||
|
window.clear();
|
||||||
|
window.draw(drawable);
|
||||||
|
window.display();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void goblins (SkeletonData* skeletonData, Atlas* atlas) {
|
void goblins (SkeletonData* skeletonData, Atlas* atlas) {
|
||||||
SP_UNUSED(atlas);
|
SP_UNUSED(atlas);
|
||||||
|
|
||||||
@ -540,6 +614,7 @@ int main () {
|
|||||||
DebugExtension dbgExtension(SpineExtension::getInstance());
|
DebugExtension dbgExtension(SpineExtension::getInstance());
|
||||||
SpineExtension::setInstance(&dbgExtension);
|
SpineExtension::setInstance(&dbgExtension);
|
||||||
|
|
||||||
|
testcase(ikDemo, "data/spineboy-pro.json", "data/spineboy-pro.skel", "data/spineboy-pma.atlas", 0.6f);
|
||||||
testcase(mixAndMatch, "data/mix-and-match-pro.json", "data/mix-and-match-pro.skel", "data/mix-and-match-pma.atlas", 0.5f);
|
testcase(mixAndMatch, "data/mix-and-match-pro.json", "data/mix-and-match-pro.skel", "data/mix-and-match-pma.atlas", 0.5f);
|
||||||
testcase(goblins, "data/goblins-pro.json", "data/goblins-pro.skel", "data/goblins-pma.atlas", 1.4f);
|
testcase(goblins, "data/goblins-pro.json", "data/goblins-pro.skel", "data/goblins-pma.atlas", 1.4f);
|
||||||
testcase(owl, "data/owl-pro.json", "data/owl-pro.skel", "data/owl-pma.atlas", 0.5f);
|
testcase(owl, "data/owl-pro.json", "data/owl-pro.skel", "data/owl-pma.atlas", 0.5f);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user