鸟语天空
Spine骨骼动画制作工具
post by:追风剑情 2021-9-17 10:06

Spine官网
Spine-Unity 运行时文档

2D骨骼动画(2D Skeleton Animation)

皮肤与皮肤占位符

1、新建皮肤1(skin1)、皮肤2(skin2),并选择skin1

1111.png

2、在骨骼下新建皮肤占位符(placeholder)

选中skin1,并在placeholder下放置图片gum。再选中skin2,并在placeholder下放置图片goggles。

22222.png3333.png

3、两套皮肤制作完成,现在可以通过切换skin1和skin2来显示不同的皮肤了。


libgdx 运行库

[官网] libGDX
[GitHub] libgdx
libgdx wiki

使用 gdx-setup.jar 创建 libgdx Android Studio 工程
java -jar gdx-setup.jar
[百度网盘] gdx-setup.jar 提取码 pmx8
111111.png

3D Graphics (3D图形)
  ● 3D animations and skinning (3D动画与蒙皮)
  ● 3D Particle Effects (3D粒子特效)
  ● 3D Picking (3D拾取)
  ● Decals (贴花,一种贴图技术)
  ● Importing Blender models in LibGDX (导入Blender模型)
  ● Material and environment (材质与环境)
  ● ModelBatch (模型批处理,一种GPU优化技术)
  ● ModelBuilder,MeshBuilder and MeshPartBuilder (模型构建器,网格构建器与网格部分构建器)
  ● ModelCache (模型缓存)
  ● Models (模型)
  ● Quick start (快速启动)
  ● Virtual Reality (VR) (虚拟现实)

.fbx 转 .g3db or .g3dj 工具
fbx-conv (命令行版本)
https://github.com/libgdx/fbx-conv
下载 fbx-conv.exe

libgdx-fbxconv-gui (Java GUI 版本),运行环境需要Java 7
https://github.com/ASneakyFox/libgdx-fbxconv-gui

自己编译生成fbx-conv.exe
1、 安装 Autodesk FBX Software Developer Kit (FBX SDK 2019.0)
通过fbx-conv工程中的generate_vs2015.bat生成Visual Studio工程时需要用到FBX SDK,需要配置环境变量FBX_SDK_ROOT={FBX SDK 安装目录}

2、 将 fbx-conv visual studio 2015 工程升级到 visual studio 2019
  1) 项目右键->属性->平台工具集-选择Visual Studio 2019(v142)
  2) 项目->重定目标解决方案
  3) 生成->重新生成 fbx-conv

fbx-conv命令行:
-? -显示帮助信息
-o <type> -设置输出文件类型 <type>:FBX, G3DJ(json) or G3DB(二进制)
-f -纹理贴图垂直翻转
-p -顶点颜色值转成一个float
-m <size> -网格可能包含的顶点或索引的最大数量(默认值:32k)
-b <size> -未分离可包含的最大骨骼数量(默认值:12)
-w <size> -每个顶点的最大骨骼权重(默认值:4)
-v -详细信息:打印其他进度信息
示例
fbx-conv-win32.exe -f -v myModel.fbx convertedModel.g3db

示例代码:替换附件

//(1) 找插槽
Slot slot = skeleton.findSlot("插槽名")

//(2) 获取附件
Attachment attachment = slot.getAttachment();

//(3) 创建并加载纹理
Texture texture = new Texture(Gdx.files.internal("纹理地址"));
//设置纹理参数
texture.setFilter(Texture.TextureFilter.MipMap, Texture.TextureFilter.MipMap);
texture.setWrap(Texture.TextureWrap.ClampToEdge, Texture.TextureWrap.ClampToEdge);

//(4) 创建纹理区域对象
TextureAtlas.AtlasRegion region = new TextureAtlas.AtlasRegion(new TextureRegion(texture));

//(5) 替换附件
//区域附件
if (attachment instanceof RegionAttachment)
{
	RegionAttachment regionAttachment = (RegionAttachment) attachment;
	regionAttachment.setRegion(region);
	regionAttachment.updateOffset();
}
//网格附件
else if (attachment instanceof MeshAttachment)
	{
	MeshAttachment meshAttachment = (MeshAttachment) attachment;
	meshAttachment.setRegion(region);
	meshAttachment.updateUVs();
}

//(6) 移除附件(卸装)
slot.setAttachment(null);


WebGL 运行库

(1) 用VS Code打开Spine-ts工程
(2) 在VS Code的终端运行
npm install
npm run dev
(3) 然后就可以按F5运行demo了。VS Code会启动一个本地Web服务器。
(4) 执行发布命令 npm build,会在工程下生成dist目录,将dist丢到Web服务器上即可访问。
注意:使用 npm 命令需要安装node.js,并配置环境变量。

需要在html页面中嵌入必要的js文件
<script src="../dist/iife/spine-webgl.js"></script>
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>;

示例代码:获取webgl对象

//(1) 获取画布
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

//(2) 获取webgl
var config = { alpha: false };
var gl = canvas.getContext("webgl", config) || canvas.getContext("experimental-webgl", config);
if (!gl) {
	alert('WebGL is unavailable.');
}


示例代码:加载骨骼和图集

//骨骼文件名
var skeletonName = "2dnansheng.skel";
//图集文件名
var atlasName = "2dnansheng.atlas";

//创建资源管理器
var assetManager = new spine.AssetManager(gl, "assets/");
//加载二进制骨骼文件
assetManager.loadBinary(skeletonName);
//加载图集
assetManager.loadTextureAtlas(atlasName);
//执行加载
requestAnimationFrame(load);

//加载函数
function load() {
	if (assetManager.isLoadingComplete()) {
		//获取图集
		var atlas = assetManager.require(atlasName);
		//创建图集附件加载器
		var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
		//创建骨骼加载器
		var skeletonLoader = new spine.SkeletonBinary(atlasLoader);
		skeletonLoader.scale = 1;
		//读取骨骼数据
		var skeletonData = skeletonLoader.readSkeletonData(assetManager.require(skeletonName));
		//创建骨骼
		var skeleton = new spine.Skeleton(skeletonData);
	} else {
		//继续调用load(),直到所有资源加载完成
		requestAnimationFrame(load);
	}
}


示例代码:替换附件

//加载换装图片assets/xxx.png
assetManager.loadTexture("xxx.png");
//执行加载
requestAnimationFrame(load);

//加载函数
function load() {
	if (assetManager.isLoadingComplete()) {
		//获取插槽
		var slot = skeleton.findSlot("插槽名");
		//获取插槽下面的当前附件
		var attachment = slot.getAttachment();
		//提取图片
		var texture = assetManager.require("xxx.png");
		//获取Image对象
		var image = texture.getImage();
		//创建GLTexture
		var glTexture = new spine.GLTexture(gl, image);
		//设置纹理滤波
		glTexture.setFilters(spine.TextureFilter.MipMap, spine.TextureFilter.MipMap);
		//设置纹理环绕模式
		glTexture.setWraps(spine.TextureWrap.ClampToEdge, spine.TextureWrap.ClampToEdge);
		
		//判断附件类型
		if (attachment instanceof spine.RegionAttachment) {
			//创建纹理区域
			var textureRegion = new spine.TextureRegion();
			textureRegion.renderObject = glTexture;//绑定要渲染的纹理
			//替换附件
			attachment.setRegion(textureRegion);
			//更新位置
			attachment.updateOffset();
		} else if (attachment instanceof spine.MeshAttachment) {
			//替换附件
			//attachment.setRegion(textureRegion);//没这个函数

			var image = glTexture.getImage();
			var page = new spine.TextureAtlasPage();
			page.name = glTexture.name;
			page.minFilter = spine.TextureFilter.MipMap;
			page.magFilter = spine.TextureFilter.MipMap;
			page.uWrap = spine.TextureWrap.ClampToEdge;
			page.vWrap = spine.TextureWrap.ClampToEdge;
			page.width = image.width;
			page.height = image.height;
			page.setTexture(glTexture);

			var region = new spine.TextureAtlasRegion();
			region.page = page;
			region.width = image.width;
			region.height = image.height;
			region.originalWidth = image.width;
			region.originalHeight = image.height;
			region.u = region.v = 0;
			region.u2 = region.v2 = 1;
			region.renderObject = glTexture;
						
			//替换附件
			attachment.region = region;
			//更新UV
			attachment.updateUVs();
		}
	} else {
		//继续调用load(),直到所有资源加载完成
		requestAnimationFrame(load);
	}
}


Unity 运行库

下载 spine-unity.unitypackage

[Spine菜单]->[导出...]  *.skel.bytes、*.atlas.txt、*.png
11111.png

打包设置
222222.png

Unity Runtime 需要加载的文件:skeleton-name.jsonskeleton-name.skel.bytesskeleton-name.atlas.txtskeleton-name.png

说明   skeleton-name.json或skeleton-name.skel.bytes,包含了skeleton和animation数据。skeleton-name.atlas.txt,包含了texture atlas的信息。一个或多个.png文件,每个文件代表包含了texture atlas中的一页,而texture atlas包含了skeleton所需的全部图片。当资源导入Unity工程时将自动生成skeleton-name_Atlas.assetskeleton-name_Material.mat

重要提示   只要在atlas texture导出时启用了Premultiply alpha,Unity中Material的 Straight Alpha Texture 参数和Texture的 Alpha Is Transparency 设置都要禁用,反之亦然。[Edit]->[Preferences]-Spine配置页面中找到Atlas Texture Settings项,如果使用的Texture为预乘Alpha,则设置为PMATexturePreset.preset,反之,则设置为StraightAlphaPreset.preset

33333.png

正确的Texture打包器导出和Texture & Material导入设置:

1. Premultiplied Alpha

4444.png

Texture 打包器启用 Premultiply alpha
Unity Texture 设置中启用 sRGB (Color Texture) 且禁用 Alpha Is Transparency
Unity Material 参数中禁用 Straight Alpha Texture

2. Straight Alpha

55555.png

Texture 打包器禁用 Premultiply alpha , 启用 Bleed
Unity Texture 设置启用 sRGB (Color Texture) 且启用 Alpha Is Transparency
Unity Material 参数中启用 Straight Alpha Texture

using UnityEngine;
using Spine;
/// <summary>
/// Spine辅助工具类
/// </summary>
public sealed class SpineHelper
{
    /// <summary>
    /// 使用外部图片替换插槽下的附件图片
    /// </summary>
    /// <param name="skeleton">骨骼对象</param>
    /// <param name="slotName">插槽名称</param>
    /// <param name="texture2D">外部图片</param>
    public static void ReplaceSkin(Skeleton skeleton, string slotName, string attachmentName, Texture2D texture2D)
    {
        if (skeleton == null || texture2D == null)
            return;

        Slot slot = skeleton.FindSlot(slotName);
        if (slot == null)
            return;

        //当插槽下的所有附件处于隐藏状态时,slot.Attachment为null
        //如果想要获取到隐藏状态的附件,可以使用skeleton.GetAttachment()方法
        Attachment attachment = skeleton.GetAttachment(slotName, attachmentName);
        if (attachment == null)
            return;

        Material material = new Material(Shader.Find("Spine/Skeleton"));
        material.mainTexture = texture2D;

        AtlasPage page = new AtlasPage();
        page.rendererObject = material;
        page.name = texture2D.name;
        page.minFilter = TextureFilter.MipMap;
        page.magFilter = TextureFilter.MipMap;
        page.uWrap = TextureWrap.ClampToEdge;
        page.vWrap = TextureWrap.ClampToEdge;
        page.width = texture2D.width;
        page.height = texture2D.height;

        AtlasRegion region = new AtlasRegion();
        region.page = page;
        region.width = texture2D.width;
        region.height = texture2D.height;
        region.originalWidth = texture2D.width;
        region.originalHeight = texture2D.height;
        region.u = region.v = 0;
        region.u2 = region.v2 = 1;
        region.rotate = false;

        if (attachment is RegionAttachment)
        {
            RegionAttachment regionAttachment = attachment as RegionAttachment;
            regionAttachment.RendererObject = region;
            regionAttachment.SetUVs(0f, 1f, 1f, 0f, 0);
            regionAttachment.UpdateOffset();
        }
        else if (attachment is MeshAttachment)
        {
            MeshAttachment meshAttachment = attachment as MeshAttachment;
            meshAttachment.RendererObject = region;
            meshAttachment.RegionU = 0f;
            meshAttachment.RegionV = 1f;
            meshAttachment.RegionU2 = 1f;
            meshAttachment.RegionV2 = 0f;
            meshAttachment.UpdateUVs();
        }
    }
}

评论:
发表评论:
昵称

邮件地址 (选填)

个人主页 (选填)

内容