Browse Source

Merge branch '3.8-beta' of https://github.com/EsotericSoftware/spine-runtimes into 3.8-beta

badlogic 6 years ago
parent
commit
80971172ef
2 changed files with 236 additions and 123 deletions
  1. 1 1
      spine-csharp/spine-csharp.csproj
  2. 235 122
      spine-monogame/example/ExampleGame.cs

+ 1 - 1
spine-csharp/spine-csharp.csproj

@@ -71,11 +71,11 @@
     <Compile Include="src\BlendMode.cs" />
     <Compile Include="src\Bone.cs" />
     <Compile Include="src\BoneData.cs" />
+    <Compile Include="src\ConstraintData.cs" />
     <Compile Include="src\Triangulator.cs" />
     <Compile Include="src\Event.cs" />
     <Compile Include="src\EventData.cs" />
     <Compile Include="src\ExposedList.cs" />
-    <Compile Include="src\IConstraint.cs" />
     <Compile Include="src\IkConstraint.cs" />
     <Compile Include="src\IkConstraintData.cs" />
     <Compile Include="src\IUpdatable.cs" />

+ 235 - 122
spine-monogame/example/ExampleGame.cs

@@ -33,159 +33,272 @@ using Microsoft.Xna.Framework.Graphics;
 using Microsoft.Xna.Framework.Input;
 
 namespace Spine {
-	public class Example : Microsoft.Xna.Framework.Game {
-		GraphicsDeviceManager graphics;
-		SkeletonRenderer skeletonRenderer;
+
+	public abstract class Screen {
+		protected Example game;
+		protected SkeletonRenderer skeletonRenderer;
+		private MouseState lastMouseState;
+		protected Boolean mouseClicked = false;
+
+		public Screen(Example game) {
+			this.game = game;
+			skeletonRenderer = new SkeletonRenderer(game.GraphicsDevice);
+			skeletonRenderer.PremultipliedAlpha = false;			
+		}
+
+		public void UpdateInput() {
+			MouseState state = Mouse.GetState();
+			mouseClicked = lastMouseState.LeftButton == ButtonState.Pressed && state.LeftButton == ButtonState.Released;
+			lastMouseState = state;
+		}
+
+		public abstract void Render(float deltaTime);
+	}
+
+	/// <summary>
+	/// The raptor screen shows basic loading and rendering of a Spine skeleton.
+	/// </summary>
+	internal class RaptorScreen : Screen {
+		Atlas atlas;
 		Skeleton skeleton;
-		Slot headSlot;
 		AnimationState state;
-		SkeletonBounds bounds = new SkeletonBounds();
 
-		private string assetsFolder = "data/";
+		public RaptorScreen(Example game) : base (game) {		
+			// Load the texture atlas
+			atlas = new Atlas("data/raptor.atlas", new XnaTextureLoader(game.GraphicsDevice));
 
-		public Example() {
-			IsMouseVisible = true;
+			// Load the .json file using a scale of 0.5
+			SkeletonJson json = new SkeletonJson(atlas);
+			json.Scale = 0.5f;
+			SkeletonData skeletonData = json.ReadSkeletonData("data/raptor-pro.json");
 
-			graphics = new GraphicsDeviceManager(this);
-			graphics.IsFullScreen = false;
-			graphics.PreferredBackBufferWidth = 800;
-			graphics.PreferredBackBufferHeight = 600;
+			// Create the skeleton and animation state
+			skeleton = new Skeleton(skeletonData);
+			AnimationStateData stateData = new AnimationStateData(skeleton.Data);
+			state = new AnimationState(stateData);
+
+			// Flip the skeleton on the y-axis and center it within the viewport
+			skeleton.ScaleY = -1;
+			skeleton.X = game.GraphicsDevice.Viewport.Width / 2;
+			skeleton.Y = game.GraphicsDevice.Viewport.Height;
+
+			// Set the "walk" animation on track one and let it loop forever
+			state.SetAnimation(0, "walk", true);			
 		}
 
-		protected override void LoadContent() {
-			// Two color tint effect, comment line 80 to disable
-			var spineEffect = Content.Load<Effect>("Content\\SpineEffect");
-			spineEffect.Parameters["World"].SetValue(Matrix.Identity);
-			spineEffect.Parameters["View"].SetValue(Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), Vector3.Zero, Vector3.Up));
-
-			skeletonRenderer = new SkeletonRenderer(GraphicsDevice);
-			skeletonRenderer.PremultipliedAlpha = false;
-			skeletonRenderer.Effect = spineEffect;
-
-			// String name = "spineboy-ess";
-			// String name = "goblins-pro";
-			String name = "raptor-pro";
-			// String name = "tank-pro";
-			// String name = "coin-pro";
-			String atlasName = name.Replace("-pro", "").Replace("-ess", "");
-			bool binaryData = false;
-
-			Atlas atlas = new Atlas(assetsFolder + atlasName + ".atlas", new XnaTextureLoader(GraphicsDevice));
-
-			float scale = 1;
-			if (name == "spineboy-ess") scale = 0.6f;
-			if (name == "raptor-pro") scale = 0.5f;
-			if (name == "tank-pro") scale = 0.3f;
-			if (name == "coin-pro") scale = 1;
-
-			SkeletonData skeletonData;
-			if (binaryData) {
-				SkeletonBinary binary = new SkeletonBinary(atlas);
-				binary.Scale = scale;
-				skeletonData = binary.ReadSkeletonData(assetsFolder + name + ".skel");
-			}
-			else {
-				SkeletonJson json = new SkeletonJson(atlas);
-				json.Scale = scale;
-				skeletonData = json.ReadSkeletonData(assetsFolder + name + ".json");
-			}
-			skeleton = new Skeleton(skeletonData);
-			if (name == "goblins-pro") skeleton.SetSkin("goblin");
+		public override void Render(float deltaTime) {
+			// Update the animation state and apply the animations
+			// to the skeleton
+			state.Update(deltaTime);
+			state.Apply(skeleton);
+
+			// Update the transformations of bones and other parts of the skeleton
+			skeleton.UpdateWorldTransform();
+
+			// Clear the screen and setup the projection matrix of the skeleton renderer
+			game.GraphicsDevice.Clear(Color.Black);
+			((BasicEffect)skeletonRenderer.Effect).Projection = Matrix.CreateOrthographicOffCenter(0, game.GraphicsDevice.Viewport.Width, game.GraphicsDevice.Viewport.Height, 0, 1, 0);
+			
+			// Draw the skeletons
+			skeletonRenderer.Begin();
+			skeletonRenderer.Draw(skeleton);
+			skeletonRenderer.End();
+
+			// Check if the mouse button was clicked and switch scene
+			if (mouseClicked) game.currentScreen = new TankScreen(game);
+		}
+	}
+
+	/// <summary>
+	/// The tank screen shows how to enable two color tinting.
+	/// </summary>
+	internal class TankScreen : Screen {
+		Atlas atlas;
+		Skeleton skeleton;
+		AnimationState state;
+
+		public TankScreen(Example game) : base(game) {
+			// Instantiate and configure the two color tinting effect and
+			// assign it to the skeleton renderer
+			var twoColorTintEffect = game.Content.Load<Effect>("Content\\SpineEffect");
+			twoColorTintEffect.Parameters["World"].SetValue(Matrix.Identity);
+			twoColorTintEffect.Parameters["View"].SetValue(Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), Vector3.Zero, Vector3.Up));
+			skeletonRenderer.Effect = twoColorTintEffect;
+
+			// The remaining code loads the atlas and skeleton data as in the raptor screen
+			atlas = new Atlas("data/tank.atlas", new XnaTextureLoader(game.GraphicsDevice));			
+			SkeletonJson json = new SkeletonJson(atlas);
+			json.Scale = 0.25f;
+			SkeletonData skeletonData = json.ReadSkeletonData("data/tank-pro.json");
 
-			// Define mixing between animations.
+			skeleton = new Skeleton(skeletonData);
 			AnimationStateData stateData = new AnimationStateData(skeleton.Data);
 			state = new AnimationState(stateData);
 
-			if (name == "spineboy-ess") {
-				skeleton.SetAttachment("head-bb", "head"); // Activate the head BoundingBoxAttachment.
-
-				stateData.SetMix("run", "jump", 0.2f);
-				stateData.SetMix("jump", "run", 0.4f);
-
-				// Event handling for all animations.
-				state.Start += Start;
-				state.End += End;
-				state.Complete += Complete;
-				state.Event += Event;
-
-				state.SetAnimation(0, "run", false);
-				TrackEntry entry = state.AddAnimation(0, "jump", false, 0);
-				entry.End += End; // Event handling for queued animations.
-				state.AddAnimation(0, "run", true, 0);
-			}
-			else if (name == "raptor-pro") {
-				state.SetAnimation(0, "walk", true);
-				state.AddAnimation(1, "gun-grab", false, 2);
-			}
-			else if (name == "coin-pro") {
-				state.SetAnimation(0, "animation", true);
-			}
-			else if (name == "tank-pro") {
-				state.SetAnimation(0, "drive", true);
-			}
-			else {
-				state.SetAnimation(0, "walk", true);
-			}
-           
-			skeleton.X = 400 + (name == "tank-pro" ? 300 : 0);
-			skeleton.Y = GraphicsDevice.Viewport.Height;
-            skeleton.ScaleY = -1;
-			skeleton.UpdateWorldTransform();
+			skeleton.ScaleY = -1;
+			skeleton.X = game.GraphicsDevice.Viewport.Width / 2 + 200;
+			skeleton.Y = game.GraphicsDevice.Viewport.Height;
 
-			headSlot = skeleton.FindSlot("head");
+			state.SetAnimation(0, "shoot", true);
 		}
 
-		protected override void Update(GameTime gameTime) {
-			base.Update(gameTime);
+		public override void Render(float deltaTime) {
+			state.Update(deltaTime);
+			state.Apply(skeleton);
+
+			skeleton.UpdateWorldTransform();
+
+			// Clear the screen and setup the projection matrix of the custom effect through the
+			// "Projection" parameter.
+			game.GraphicsDevice.Clear(Color.Black);
+			skeletonRenderer.Effect.Parameters["Projection"].SetValue(Matrix.CreateOrthographicOffCenter(0, game.GraphicsDevice.Viewport.Width, game.GraphicsDevice.Viewport.Height, 0, 1, 0));
+
+			skeletonRenderer.Begin();
+			skeletonRenderer.Draw(skeleton);
+			skeletonRenderer.End();
+			
+			if (mouseClicked) game.currentScreen = new SpineboyScreen(game);
 		}
+	}
 
-		protected override void Draw(GameTime gameTime) {
-			GraphicsDevice.Clear(Color.Black);
+	/// <summary>
+	/// The Spineboy screen shows how to queue up multiple animations via animation state,
+	/// set the default mix time to smoothly transition between animations, and load a 
+	/// skeleton from a binary .skel file.
+	/// </summary>
+	internal class SpineboyScreen : Screen {
+		Atlas atlas;
+		Skeleton skeleton;
+		AnimationState state;
+
+		public SpineboyScreen(Example game) : base(game) {			
+			atlas = new Atlas("data/spineboy.atlas", new XnaTextureLoader(game.GraphicsDevice));
 
-			state.Update(gameTime.ElapsedGameTime.Milliseconds / 1000f);
+			SkeletonBinary binary = new SkeletonBinary(atlas);
+			binary.Scale = 0.5f;
+			SkeletonData skeletonData = binary.ReadSkeletonData("data/spineboy-pro.skel");
+
+			skeleton = new Skeleton(skeletonData);
+			AnimationStateData stateData = new AnimationStateData(skeleton.Data);			
+			state = new AnimationState(stateData);
+
+			skeleton.ScaleY = -1;
+			skeleton.X = game.GraphicsDevice.Viewport.Width / 2;
+			skeleton.Y = game.GraphicsDevice.Viewport.Height;
+
+			// We want 0.2 seconds of mixing time when transitioning from
+			// any animation to any other animation.
+			stateData.DefaultMix = 0.2f;
+
+			// Set the "walk" animation on track one and let it loop forever
+			state.SetAnimation(0, "walk", true);
+
+			// Queue another animation after 2 seconds to let Spineboy jump
+			state.AddAnimation(0, "jump", false, 2);
+
+			// After the jump is complete, let Spineboy walk
+			state.AddAnimation(0, "run", true, 0);
+		}
+
+		public override void Render(float deltaTime) {
+			state.Update(deltaTime);
 			state.Apply(skeleton);
 			skeleton.UpdateWorldTransform();
-			if (skeletonRenderer.Effect is BasicEffect) {
-				((BasicEffect)skeletonRenderer.Effect).Projection = Matrix.CreateOrthographicOffCenter(0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, 0, 1, 0);
-			}
-			else {
-				skeletonRenderer.Effect.Parameters["Projection"].SetValue(Matrix.CreateOrthographicOffCenter(0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, 0, 1, 0));
-			}
+			
+			game.GraphicsDevice.Clear(Color.Black);
+			((BasicEffect)skeletonRenderer.Effect).Projection = Matrix.CreateOrthographicOffCenter(0, game.GraphicsDevice.Viewport.Width, game.GraphicsDevice.Viewport.Height, 0, 1, 0);
+			
 			skeletonRenderer.Begin();
 			skeletonRenderer.Draw(skeleton);
 			skeletonRenderer.End();
+			
+			if (mouseClicked) game.currentScreen = new MixAndMatchScreen(game);
+		}
+	}
+
+	/// <summary>
+	/// The mix-and-match screen demonstrates how to create and apply a skin
+	/// composed of other skins. This method can be used to create customizable
+	/// avatar systems.
+	/// </summary>
+	internal class MixAndMatchScreen : Screen {
+		Atlas atlas;
+		Skeleton skeleton;
+		AnimationState state;
+
+		public MixAndMatchScreen(Example game) : base(game) {
+			atlas = new Atlas("data/mix-and-match.atlas", new XnaTextureLoader(game.GraphicsDevice));
+
+			SkeletonJson json = new SkeletonJson(atlas);
+			json.Scale = 0.5f;
+			SkeletonData skeletonData = json.ReadSkeletonData("data/mix-and-match-pro.json");
+
+			skeleton = new Skeleton(skeletonData);
+			AnimationStateData stateData = new AnimationStateData(skeleton.Data);
+			state = new AnimationState(stateData);
 
-			bounds.Update(skeleton, true);
-			MouseState mouse = Mouse.GetState();
-			if (headSlot != null) {
-				headSlot.G = 1;
-				headSlot.B = 1;
-				if (bounds.AabbContainsPoint(mouse.X, mouse.Y)) {
-					BoundingBoxAttachment hit = bounds.ContainsPoint(mouse.X, mouse.Y);
-					if (hit != null) {
-						headSlot.G = 0;
-						headSlot.B = 0;
-					}
-				}
-			}
-
-			base.Draw(gameTime);
+			skeleton.ScaleY = -1;
+			skeleton.X = game.GraphicsDevice.Viewport.Width / 2;
+			skeleton.Y = game.GraphicsDevice.Viewport.Height;
+			
+			state.SetAnimation(0, "dance", true);
+
+			// Create a new skin, by mixing and matching other skins
+			// that fit together. Items making up the girl are individual
+			// skins. Using the skin API, a new skin is created which is
+			// a combination of all these individual item skins.
+			var mixAndMatchSkin = new Spine.Skin("custom-girl");
+			mixAndMatchSkin.AddSkin(skeletonData.FindSkin("skin-base"));
+			mixAndMatchSkin.AddSkin(skeletonData.FindSkin("nose/short"));
+			mixAndMatchSkin.AddSkin(skeletonData.FindSkin("eyes/eyelids-girly"));
+			mixAndMatchSkin.AddSkin(skeletonData.FindSkin("eyes/violet"));
+			mixAndMatchSkin.AddSkin(skeletonData.FindSkin("hair/brown"));
+			mixAndMatchSkin.AddSkin(skeletonData.FindSkin("clothes/hoodie-orange"));
+			mixAndMatchSkin.AddSkin(skeletonData.FindSkin("legs/pants-jeans"));
+			mixAndMatchSkin.AddSkin(skeletonData.FindSkin("accessories/bag"));
+			mixAndMatchSkin.AddSkin(skeletonData.FindSkin("accessories/hat-red-yellow"));
+			skeleton.SetSkin(mixAndMatchSkin);
 		}
 
-		public void Start(TrackEntry entry) {
-			Console.WriteLine(entry + ": start");
+		public override void Render(float deltaTime) {
+			state.Update(deltaTime);
+			state.Apply(skeleton);
+			skeleton.UpdateWorldTransform();
+
+			game.GraphicsDevice.Clear(Color.Black);
+			((BasicEffect)skeletonRenderer.Effect).Projection = Matrix.CreateOrthographicOffCenter(0, game.GraphicsDevice.Viewport.Width, game.GraphicsDevice.Viewport.Height, 0, 1, 0);
+
+			skeletonRenderer.Begin();
+			skeletonRenderer.Draw(skeleton);
+			skeletonRenderer.End();
+
+			if (mouseClicked) game.currentScreen = new RaptorScreen(game);
 		}
+	}
+
+	public class Example : Microsoft.Xna.Framework.Game {
+		GraphicsDeviceManager graphics;
+		public Screen currentScreen;
+
+		public Example() {
+			IsMouseVisible = true;
 
-		public void End(TrackEntry entry) {
-			Console.WriteLine(entry + ": end");
+			graphics = new GraphicsDeviceManager(this);
+			graphics.IsFullScreen = false;
+			graphics.PreferredBackBufferWidth = 800;
+			graphics.PreferredBackBufferHeight = 600;
 		}
 
-		public void Complete(TrackEntry entry) {
-			Console.WriteLine(entry + ": complete ");
+		protected override void LoadContent() {
+			currentScreen = new MixAndMatchScreen(this);
 		}
 
-		public void Event(TrackEntry entry, Event e) {
-			Console.WriteLine(entry + ": event " + e);
+		protected override void Update(GameTime gameTime) {
+			currentScreen.UpdateInput();
 		}
+
+		protected override void Draw(GameTime gameTime) {
+			currentScreen.Render(gameTime.ElapsedGameTime.Milliseconds / 1000.0f);
+		}		
 	}
 }