徐麟家的博客

站在巨人的肩膀上

通过批处理脚本设置图集的一些参数

一下代码可以自动找到相关的目录下的图集,根据统一的参数配置一键设置好图集参数,并且立即打包所有图集,开发中比较实用的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class AtlasCompressEditor
{
[MenuItem("Package/Atlas/图集压缩_设置矫正", priority = 1003)]
public static void AltasCompress()
{
string[] guids = AssetDatabase.FindAssets("t:spriteatlas",new[] {"Assets/Art/UIAtlas"});
foreach (var id in guids)
{
var p = AssetDatabase.GUIDToAssetPath(id);
SpriteAtlas atlas = AssetDatabase.LoadAssetAtPath(p, typeof(SpriteAtlas)) as SpriteAtlas;
atlas.SetIncludeInBuild(true);

var packSetting = atlas.GetPackingSettings();
packSetting.blockOffset = 1;
packSetting.enableRotation = false;
packSetting.padding = 2;
packSetting.enableTightPacking = false;
atlas.SetPackingSettings(packSetting);

var texture_setting = atlas.GetTextureSettings();
texture_setting.generateMipMaps = false;
texture_setting.filterMode = FilterMode.Bilinear;
atlas.SetTextureSettings(texture_setting);

TextureImporterPlatformSettings android_setting = new TextureImporterPlatformSettings();
android_setting.name = BuildPipeline.GetBuildTargetName(BuildTarget.Android);
android_setting.overridden = true;
android_setting.textureCompression = TextureImporterCompression.Compressed;
android_setting.format = TextureImporterFormat.ASTC_6x6;

TextureImporterPlatformSettings phone_setting = new TextureImporterPlatformSettings();
phone_setting.name = BuildPipeline.GetBuildTargetName(BuildTarget.iOS);
phone_setting.overridden = true;
phone_setting.textureCompression = TextureImporterCompression.Compressed;
phone_setting.format = TextureImporterFormat.ASTC_6x6;
atlas.SetPlatformSettings(android_setting);
atlas.SetPlatformSettings(phone_setting);

AssetDatabase.SaveAssetIfDirty(atlas);
}
SpriteAtlasUtility.PackAllAtlases(EditorUserBuildSettings.activeBuildTarget);
}
}

解释和要点

1、AssetDatabase.FindAssets中可以增加对应的图集目录,代码会依次设置
2、SpriteAtlas.SetPackingSettings 设置图片资源的相关格式,图集也是一张图片可以使用图片压缩格式
3、SpriteAtlasUtility.PackAllAtlases 打包所有的图集
4、Sprite Atlas 版本 1 和 Sprite Atlas 版本 2 不兼容
5、本代码只能用于spriteatlasv1版本的图集设置,关于新版本的v2图集目前官方不支持通过脚本编辑 Sprite Atlas 版本 2,至此2024/07还未提供。
博主也是查了好多网上的资料才发现这点的,不是很明显,到目前为止博主还是建议使用Sprite Atlas 版本 1。希望能帮助到各位开发者。官网截图如下:

点评

迭代器是很好的访问聚集对象的方法,为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的接口。/br 实现迭代器模式的实现,应该把重点放在两个点:/br 1、实现迭代器接口的类方法 2、聚集类存放原始数据,同时有一个方法可以创建出某个迭代器实例(为什么是某个,迭代器可以有多种实现,比如正序的迭代器,倒叙迭代器,某些赛选功能的迭代器等等)。

迭代器在语言中的实现

迭代器在各种高级语言中都以嵌入到语法中,如lua中的选好 for k,v in ipairs(table) do end ;C# 中 foreach (var item in list) { }等等。
而且 .NET的迭代器实现了上述的迭代器接口。 也就是我们不需要设计上述的类方法了。直接使用IEumerator(顶层迭代器抽象上方1) 和 IEnumerable(顶层创建迭代器类上方2)、 C#中foreach语法糖,会被编译器实现未一个while循环语法,以访问迭代器中的元素。

Unity携程中的迭代器

MonoBehaviour.StartCoroutine接收的参数为什么是IEnumerator,IEnumerator和协程有什么关系?

知识点1:迭代器函数

yield是C#的关键字,其实就是快速定义迭代器的语法糖。只要是yield出现在其中的方法就会被编译器自动编译成一个迭代器,对于这样的函数可以称之为迭代器函数。迭代器函数的返回值就是自动生成的迭代器类的一个对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Test : MonoBehaviour
{
Animator animator = null;

void Start()
{
IEnumerator enumerator = DoSomeThing();

while(enumerator.MoveNext())
{
Debug.Log("T:"enumerator.Current);
}
}

IEnumerator DoSomeThing()
{
yield return 1;
yield return 2;
yield return 3;
yield return new WaitForSeconds(0.5f);
yield return "haha";
}
}
上方输出是:
T:1
T:2
T:3
T:UnityEngine.WaitForSeconds
T:haha

知识点2:Unity协程机制

下方文字引用一位网友的理解,原理描述得比较清晰!

Unity每通过MonoBehaviour.StartCoroutine启动一个协程,就会获得一个IEnumerator(StartCoroutine的参数就是IEnumerator,参数是方法名的重载版本也会通过反射拿到该方法对应的IEnumerator)。并在它的游戏循环中,根据条件判断是否要执行MoveNext方法。而这个条件就是根据IEnumerator的Current属性获得的,即yield return返回的值。

在启动一个协程时,Unity会先调用得到的IEnumerator的MoveNext一次,以拿到IEnumerator的Current值。所以每启动一个协程,协程函数会立即执行到第一个yield return处然后“停住”。
对于不同的Current类型(一般是YieldInstruction的子类),Unity已做好了一些默认处理,比如:

如果Current是null,就相当于什么也不做。在下一次游戏循环中,就会调用MoveNext。所以yield return null就起到了等待一帧的作用

如果Current是WaitForSeconds类型,Unity会获取它的等待时间,每次游戏循环中都会判断时间是否到了,只有时间到了才会调用MoveNext。所以yield return WaitForSeconds就起到了等待指定时间的作用

如果Current是UnityWebRequestAsyncOperation类型,它是AsyncOperation的子类,而AsyncOperation有isDone属性,表示操作是否完成,只有isDone为true时,Unity才会调用MoveNext。对于UnityWebRequestAsyncOperation而言,只有请求完成了,才会将isDone属性设置为true。

unity文档中的一些参考

untiy官方文档关于执行顺序的一些说明 ExecutionOrder

点评

策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。
策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。
在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象。

Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。Originator可根据需要决定Memento存储Originator的哪些内部状态。

Memento(备忘录):负责存储Originator对象的内部状态,并可防止Originator以外的其他对象访问备忘录Memento。备忘录有两个接口,Caretaker只能看到备忘录的窄接口,它只能将备忘录传递给其他对象。Originator能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。

Caretaker(管理者):负责保存好备忘录Memento,不能对备忘录的内容进行操作或检查

命令模式可以使用备忘录模式来存储可撤销操作的状态[DP]。有时一些对象的内部信息必须保存在对象以外的地方,但是必须要由对象自己读取,这时,使用备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来[DP],从而可以恰当地保持封装的边界。

点评

备忘录模式很好的展示单一职责。程序设计中注重的边界,合理组织类功能的协作,对未来程序的拓展和使用有着至关重要的的影响!

点评

当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。优点:状态模式的好处是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。

本文总结个人使用unity调试第三方库的些许经验

解释关于dll,pdb,mdb的概念
dll:
DLL(Dynamic Link Library)是一种微软Windows操作系统中常见的文件类型,它包含了一系列的可执行代码以及数据,这些代码和数据可以被其他程序调用和使用。 DLL文件通常包含了一个或多个函数、数据、类、资源等,这些可以被其他程序使用。与静态链接库(Static Link Library)不同,DLL在运行时动态加载到内存中,因此它们被称为动态链接库。 DLL文件的主要优点是可以共享代码和数据,避免了代码的重复编写,提高了代码的重用性。通过将常用的功能封装到DLL中,其他程序可以通过简单地调用DLL中的函数或使用其中的类来实现相应的功能,从而简化了开发过程。
pdb:
PDB(Program Database)是一种微软Windows平台上的调试信息文件格式,用于存储编译后的程序的调试信息。这些调试信息包括了源代码文件、行号、变量名称、函数名称等。

PDB文件通常与编译后的可执行文件(例如.EXE或.DLL)一起生成。它们包含了编译器产生的调试符号信息,以及源代码的位置信息,使得调试器能够将执行的机器码映射回源代码,并且在调试器中显示正确的变量名称和行号等信息。

在调试过程中,开发者可以使用PDB文件来设置断点、查看变量值、跟踪函数调用等操作,以帮助定位和解决代码中的问题。
mdb:
在上下文中,"mdb"通常指的是Mono调试符号文件(Mono Debug Symbol),它是用于在Mono环境中进行调试的文件。

在Unity中,当你编译C#脚本时,Unity会生成相应的可执行文件(例如.dll文件),以及用于调试的.mdb文件。这个.mdb文件包含了调试信息,如源代码文件、行号、变量名称等,以便在Unity编辑器中进行调试。调试信息使得你能够在代码中设置断点、查看变量的值以及执行其他调试操作。

因此,在Unity中,如果你想要在编辑器下调试C#脚本,你通常需要对应的.mdb文件。
unity Editor是运行在mono环境中的,应该使用mdb文件获取调试信息的,为什么也能使用pdb文件获取调试信息?
Unity的脚本(主要是C#脚本)在运行时是通过Mono运行时来执行的。Mono是一个跨平台的.NET实现,它与微软的.NET框架兼容,并且提供了一些额外的功能和扩展。

PDB(Program Database)是微软.NET编译器产生的调试信息文件,通常用于在Windows平台上进行调试。这些文件包含了源代码的位置、变量和函数的调试信息等。

尽管Unity使用Mono作为脚本的运行时环境,但是Unity编辑器和Mono运行时之间并没有直接关联,因此Unity编辑器默认情况下不会使用PDB文件进行调试。

然而,你可以使用一些第三方工具或技术来进行调试,比如你可以使用Visual Studio作为外部调试器来调试Unity项目,这样你就可以利用PDB文件进行调试了。不过,这种方法相对复杂,而且在跨平台开发中可能不是最佳选择。

在Unity中,通常使用的是.mdb文件来进行调试,因为Unity编辑器和Mono运行时直接支持.mdb文件,这使得在Unity编辑器中进行调试更加方便。
在unity中使用pdb特别注意点
在unity中使用的pdb要求是便携的pdb文件,如果使用通常意义的pdb,unity是没有办法读取的。会有如下警告图示,无法读取源程序符号信息。 Unity is only able to load mdb or portable-pdb symbols. Assets/GameFramework/Libraries/GameFramework.pdb is using a legacy pdb format. 我们可以通过修改dll库源代码(当然是需要有源码,或者已经提供配套的pdb文件)编译器选项解决这个问题。调试信息选择可移植,如下图:

实操

按以上方法生成好pdb文件一并放入unity资产目录中(任意Assets目录下)。 断点调试截图:

点评

好处是请求者不用管哪个对象来处理。 接收者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构。结果是职责链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用,而不需保持它所有的候选接受者的引用[DP]。

点评

模板方法定义统一的流程骨架,部分差异的方法可在超类中修改。在效果上,有点类似于创建者模式。模板方法更侧重行为流程控制,结构简洁明了;创建者更侧重对象的灵活创建;创建者是多个类组合完成一个整体的功能,结构会复杂些。模板方法让我想到了unity中的可编程渲染管线,在整理流程不变的情况下,为用户开放了顶点着色器(vertex)和片元着色器(fragment),这大大丰富了用户的可操作性,又不失整体流程的控制。从某种程度讲设计模式或许就是现实方法论的总结,它一直都在我们身边。

主要是想分下集成A* Pathfinding Project作为Behavior Designer作为寻路的解决方案,官方文档记录的不是很详细,希望能帮助到大家。
首先概述下两个插件

(1)A* Pathfinding Project Pro,主要是用来做寻路,包括自动寻路网格生成的方案,能为开发省很多时间,当然也买的非常好用!

(2)Behavior Designer 做ai行为树插件,unity项目中使用的比较多。还有子集成包:

a、movement 运动包

b、Tactical Pack 战术包

c、Formations Pack 编队包

我想集成的是MovePack能支持 A* Pathfinding Project 的寻路。当我导入MovePack的插件包时候并没有发现有A* pathfinding的支持,只有默认的unity navemesh支持?

和文档上说的不一样,只有默认的集成,没有Pathfinding Project的集成,我的项目截图如下:

反复研究完文档后,原来需要在官网下载movement对A* 相关行为树的集成,并不是默认包中带的,需要输入序列号后要再下载下图第一个集成包(A* Pathfinding Project 相关集成)。

下载完,导入项目后,终于有关于pathfing的Task了。如下图:

最后,想说下在上文提到的这几个软件都是需要付费购买序列号的,希望大家使用和支持正版软件!

阴影生成的实现方式

1.实时光照
2.脚底放置假的阴影面片模拟阴影
3.变幻模型在某个平面上投影形成2d压缩模型
4.project投影器

具体做法

这里我们使用方案3方案实现,因为spine是2d的投影替换为使用旋转和切变来实现。具体实现通过两个pass,第一个pass做模型的变换处理,生成一个阴影;第二个pass使用默认的渲染画出本身的模型。

github工程连接:https://github.com/xulinjia/spine_shadow
shader代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184

Shader "Spine/SkeletonShadow" {
Properties {
_ShadowColor("ShadowColor",Color) = (0,0,0,0.5) //阴影颜色
_ShadowDdge("ShadowDege",range(0,1.507)) = 0 //绕x轴的旋转角度
_ShadowCutD("_ShadowCutD",float) = 0.5 //切变距离
_ShadowHeightScale("_ShadowHeight",float) = 1 // 影子高度比例
_Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1
[NoScaleOffset] _MainTex ("Main Texture", 2D) = "black" {}
[Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
[HideInInspector] _StencilRef("Stencil Reference", Float) = 1.0
[HideInInspector][Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil Comparison", Float) = 8 // Set to Always as default

// Outline properties are drawn via custom editor.
[HideInInspector] _OutlineWidth("Outline Width", Range(0,8)) = 3.0
[HideInInspector] _OutlineColor("Outline Color", Color) = (1,1,0,1)
[HideInInspector] _OutlineReferenceTexWidth("Reference Texture Width", Int) = 1024
[HideInInspector] _ThresholdEnd("Outline Threshold", Range(0,1)) = 0.25
[HideInInspector] _OutlineSmoothness("Outline Smoothness", Range(0,1)) = 1.0
[HideInInspector][MaterialToggle(_USE8NEIGHBOURHOOD_ON)] _Use8Neighbourhood("Sample 8 Neighbours", Float) = 1
[HideInInspector] _OutlineOpaqueAlpha("Opaque Alpha", Range(0,1)) = 1.0
[HideInInspector] _OutlineMipLevel("Outline Mip Level", Range(0,3)) = 0
}

SubShader {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" }

Fog { Mode Off }
Cull Off
ZWrite Off
Blend One OneMinusSrcAlpha
Lighting Off

Stencil {
Ref[_StencilRef]
Comp[_StencilComp]
Pass Keep
}

Pass {
Name "FastShadow"
Blend SrcAlpha Zero
CGPROGRAM
#pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/Spine-Common.cginc"
sampler2D _MainTex;
fixed _Cutoff;
float4 _ShadowColor;
float _ShadowDdge;
float _ShadowCutD;
float _ShadowHeightScale;

struct VertexInput {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 vertexColor : COLOR;
};

struct VertexOutput {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 vertexColor : COLOR;
};

VertexOutput vert (VertexInput v) {
VertexOutput o;
float4 vt = v.vertex;
float4 vtn;
vt.y *= _ShadowHeightScale;
vtn.x = vt.x + vt.y * _ShadowCutD;
vtn.y = cos(_ShadowDdge) * vt.y;
vtn.z = sin(_ShadowDdge) * vt.y;
vtn.w = vt.w;
o.pos = UnityObjectToClipPos(vtn);
o.uv = v.uv;
o.vertexColor = PMAGammaToTargetSpace(v.vertexColor);
return o;
}

float4 frag (VertexOutput i) : SV_Target {
float4 texColor = tex2D(_MainTex, i.uv);

#if defined(_STRAIGHT_ALPHA_INPUT)
texColor.rgb *= texColor.a;
#endif
float4 shadowColor = _ShadowColor;
clip(texColor.a * i.vertexColor.a - 0.01);
return shadowColor;

}
ENDCG
}


Pass {
Name "Normal"

CGPROGRAM
#pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/Spine-Common.cginc"
sampler2D _MainTex;

struct VertexInput {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 vertexColor : COLOR;
};

struct VertexOutput {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 vertexColor : COLOR;
};

VertexOutput vert (VertexInput v) {
VertexOutput o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.vertexColor = PMAGammaToTargetSpace(v.vertexColor);
return o;
}

float4 frag (VertexOutput i) : SV_Target {
float4 texColor = tex2D(_MainTex, i.uv);

#if defined(_STRAIGHT_ALPHA_INPUT)
texColor.rgb *= texColor.a;
#endif

return (texColor * i.vertexColor);
}
ENDCG
}

Pass {
Name "Caster"
Tags { "LightMode"="ShadowCaster" }
Offset 1, 1
ZWrite On
ZTest LEqual

Fog { Mode Off }
Cull Off
Lighting Off

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed _Cutoff;

struct VertexOutput {
V2F_SHADOW_CASTER;
float4 uvAndAlpha : TEXCOORD1;
};

VertexOutput vert (appdata_base v, float4 vertexColor : COLOR) {
VertexOutput o;
o.uvAndAlpha = v.texcoord;
o.uvAndAlpha.a = vertexColor.a;
TRANSFER_SHADOW_CASTER(o)
return o;
}

float4 frag (VertexOutput i) : SV_Target {
fixed4 texcol = tex2D(_MainTex, i.uvAndAlpha.xy);
clip(texcol.a * i.uvAndAlpha.a - _Cutoff);
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
CustomEditor "SpineShaderWithOutlineGUI"
}

0%