JSBinding + SharpKit / 实战:转换 2DPlatformer
最后修改:2015年07月29日
2016年2月25日
2DPlatformer 是 Unity3D 的一个官方 Demo。本文将介绍使用 JSBinging + SharpKit 转换 2DPlatformer 的过程。
本文并不详细介绍每个步骤的细节。因为他们将在其他文章里做详细介绍。
准备工作:首先准备好 JSBinding 的工程,正确导入 JSBinding 插件。
2DPlatformer 是Unity3D的第一个示例代码,一共不到10M。麻雀虽小,五脏俱全。牵扯到很多东西,比如 prefab 的使用,结构体序列化,场景切换,音乐等等。
我们的目标是:在已有的工程中,不添加/更新任何C#代码,不用JS重写任何C#代码,花费最少的时间把整个 2DPlatformer 游戏添加进去。
分析:
- 2DPlatformer 的 C# 代码全部都在 2DPlatformer/Scripts/ 文件夹中。现在需求是不允许更新C#代码,那么我们最终得把这个文件夹删掉。
- 既然要删掉所有的C#代码,那功能由谁来实现呢?此时强大的 SharpKit 出场了。他可以自动把 C# 代码转换在 JS 代码。
下载:
Unity官方原版 2DPlatformer(版本2.1):https://www.assetstore.unity3d.com/en/#!/content/11228
以下是具体步骤:
1. 将 2DPlatformer 导入到工程中,并确定C#版本能够正确运行
为了方便,导入后,建立一个 2DPlatformer 文件夹,把东西都拖进去。如图。
放完后一定要确保能够正常运行,因为项目包含 Project Settings ,如果没有正确导入,C#版本本身就会出错。
2. 配置 JSBindingSettings.classes
这里给出的是 2D Platformer 所有用到的类型,可以将这个数组替换掉代码中的 JSBindingSettings.classes 数组。
应该是不改也可以,因为代码里 JSBindingSettings.classes 数组已经包含下面这些类了。
public static Type[] classes = new Type[]
{
typeof(PerTest),
typeof(PerTest.RefObject),
typeof(Debug),
typeof(Input),
typeof(GameObject),
typeof(Transform),
typeof(Vector2),
typeof(Vector3),
typeof(MonoBehaviour),
typeof(Behaviour),
typeof(Component),
typeof(UnityEngine.Object),
typeof(YieldInstruction),
typeof(WaitForSeconds),
typeof(WWW),
typeof(Application),
typeof(UnityEngine.Time),
typeof(Resources),
typeof(TextAsset),
typeof(IEnumerator),
typeof(List<>),
typeof(List<>.Enumerator),
typeof(Dictionary<,>),
typeof(Dictionary<,>.KeyCollection),
typeof(Dictionary<,>.ValueCollection),
typeof(Dictionary<,>.Enumerator),
typeof(KeyValuePair<,>),
typeof(System.Diagnostics.Stopwatch),
typeof(UnityEngine.Random),
typeof(StringBuilder),
typeof(System.Xml.XmlNode),
typeof(System.Xml.XmlDocument),
typeof(System.Xml.XmlNodeList),
typeof(System.Xml.XmlElement),
typeof(System.Xml.XmlLinkedNode),
typeof(System.Xml.XmlAttributeCollection),
typeof(System.Xml.XmlNamedNodeMap),
typeof(System.Xml.XmlAttribute),
// for 2d platformer
typeof(LayerMask),
typeof(Physics2D),
typeof(Rigidbody2D),
typeof(Collision2D),
typeof(RaycastHit2D),
typeof(AudioClip),
typeof(AudioSource),
typeof(ParticleSystem),
typeof(Renderer),
typeof(ParticleSystemRenderer),
typeof(DateTime),
typeof(GUIElement),
typeof(GUIText),
typeof(GUITexture),
typeof(SpriteRenderer),
typeof(Animator),
typeof(Camera),
typeof(Mathf),
typeof(Quaternion),
typeof(Sprite),
typeof(Collider2D),
typeof(BoxCollider2D),
typeof(CircleCollider2D),
typeof(Material),
typeof(Color),
typeof(PolygonCollider2D),
};
3. 生成JS绑定
配置好 JSBindingSettings.classes 后就可以生成绑定了。
点击菜单:【JSB | Generate JS and CS Bindings】
(如果 Unity 版本没改,JSBindingSettings.classes 的列表也没有变化,此步骤如已执行过不需要再执行)
4. 正确配置 JSBindingSettings 里的各个导出路径设置
需要配置以下几个参数,路径都是相对于 Assets 文件夹:
PathsNotToJavaScript:简称为A,是指哪些路径不要转换成JS。比如说类库,或者 JSBinding 本身。
PathsToJavaScript:简称为B,是指那些在A中路径下的,但是又要导出的。
有点乱,详细解释一下。A和B是用于配置哪些路径要保留C#,哪些路径下的C#要转换成JS。
通常来说,基础框架代码是不需要转换成JS的,而且是相对固定的,比较少会进行改动。所以把这些路径配置到A数组。
这样就表示,除了A以外的其他路径里的C#类,都将被标记 [JsType(...)]标签,之后将被 SharpKit 编译生成JS代码。
而B是啥呢?B只是用于对A做一些例外,就是属于A数组,但是又要被转换成JS的。比如说,JSBinding/ 因为是插件源代码,肯定不转换成JS,所以 JSBinding/ 会配置在A数组里。
但是我们在 JSBinding/Samples 里放了一些示例代码,我希望他们被编译成JS,所以我就把 JSBinding/Samples 配置在B里,表示要转换。
PathsNotToCheckOrReplace:简称C。
跟A和B类似,但是用于说明 prefab 的处理。对于脚本,我们是将其编译成JS,才可以进行更新。对于 prefab,因为 prefab 上绑定了脚本,那么必须要解除这个绑定关系才可以。
解除的方法是将脚本都替换成 JSComponent,这里不详细解释。C这个数组用于说明,不要对哪些路径下的 prefab 进行处理。比如说 "JSBinding/Prefabs/_JSEngine.prefab" 是不需要处理的。
NGUI/ 路径下的 prefab 也不需要处理,因为这些 prefab 不会绑定要被导成JS的C#脚本。
1 /*
2 * Formula:
3 * All C# scripts - PathsNotToJavaScript + PathsToJavaScript = C# scripts to export to javascript
4 * see JSAnalyzer.MakeJsTypeAttributeInSrc for more information
5 */
6 public static string[] PathsNotToJavaScript = new string[]
7 {
8 "JSBinding/",
9 "Stealth/",
10 "DaikonForge Tween (Pro)/",
11 "NGUI/",
12 };
13 public static string[] PathsToJavaScript = new string[]
14 {
15 "JSBinding/Samples/",
16 "DaikonForge Tween (Pro)/Examples/Scripts",
17 };
18 /// <summary>
19 /// By default, menu
20 ///
21 /// JSB | Check All Monos for all Prefabs and Scenes
22 /// JSB | Replace All Monos for all Prefabs and Scenes
23 ///
24 /// handles all Prefabs and Scenes in whole project
25 /// add paths(directory or file name) to this array if you want to skip them
26 /// </summary>
27 public static string[] PathsNotToCheckOrReplace = new string[]
28 {
29 "JSBinding/",
30 "JSBinding/Prefabs/_JSEngine.prefab",
31 "Plugins/",
32 "Resources/",
33 "Src/",
34 "StreamingAssets/",
35 "UnityVS/",
36 "DaikonForge Tween (Pro)/",
37 "NGUI/",
38 };
5. 往2DPlatformer 的所有 C# 源代码中的类和结构体添加 [JsType()] 标记。
上一个步骤已经配置好了,这一步只要运行菜单即可。
运行菜单:【JSB | Add JsType Attribute for all structs and classes】
修改后会有像下面这样的变化:
// 原来的代码
1 using UnityEngine;
2 using System.Collections;
3
4 public class Gun : MonoBehaviour
5 {
6 public GameObject rocketGO; // Prefab of the rocket.
7 public float speed = 20f; // The speed the rocket will fire at.
// 执行菜单后(生成了 [JsType()] 标记):
1 using UnityEngine;
2 using System.Collections;
3
4 using SharpKit.JavaScript;
5 [JsType(JsMode.Clr,"../../StreamingAssets/JavaScript/SharpKitGenerated/2DPlatformer/Scripts/Gun.javascript")]
6 public class Gun : MonoBehaviour
7 {
8 public GameObject rocketGO; // Prefab of the rocket.
9 public float speed = 20f; // The speed the rocket will fire at.
6. 对代码进行少量修改
6.1 往以下几个文件添加这段代码:
(为啥:请看 JSBinding + SharpKit / Coroutine支持)
// 文件
- BackgroundPropSpawner.cs
- Bomb.cs
- PickupSpawner.cs
- Remover.cs
- Spawner.cs
// 要添加的代码
void LateUpdate()
{
jsimp.Coroutine.UpdateCoroutineAndInvoke(this);
}
6.2 对 BackgroundPropSpawner.cs 文件做如下修改(明确从 while 中 break 出来,而不是判断 object 是不是变成空了):
(为啥:对GameObject go调用 Destroy后,在C#中 (go == null) 为 true,但 JavaScript 为 false。)
// 修改前:
1 // While the prop exists...
2 while(propInstance != null)
3 {
4 // ... and if it's facing left...
5 if(facingLeft)
6 {
7 // ... and if it's beyond the left spawn position...
8 if(propInstance.transform.position.x < leftSpawnPosX - 0.5f)
9 // ... destroy the prop.
10 Destroy(propInstance.gameObject);
11 }
12 else
13 {
14 // Otherwise, if the prop is facing right and it's beyond the right spawn position...
15 if(propInstance.transform.position.x > rightSpawnPosX + 0.5f)
16 // ... destroy the prop.
17 Destroy(propInstance.gameObject);
18 }
19
20 // Return to this point after the next update.
21 yield return null;
22 }
// 修改后:
1 while(propInstance != null)
2 {
3 // ... and if it's facing left...
4 if(facingLeft)
5 {
6 // ... and if it's beyond the left spawn position...
7 if(propInstance.transform.position.x < leftSpawnPosX - 0.5f)
8 {
9 // ... destroy the prop.
10 Destroy(propInstance.gameObject);
11 break;
12 }
13 }
14 else
15 {
16 // Otherwise, if the prop is facing right and it's beyond the right spawn position...
17 if(propInstance.transform.position.x > rightSpawnPosX + 0.5f)
18 {
19 // ... destroy the prop.
20 Destroy(propInstance.gameObject);
21 break;
22 }
23 }
24
25 // Return to this point after the next update.
26 yield return null;
27 }
6.3 往 JSComponent 添加以下代码到合适的位置(打开代码你就知道该怎么加了,也可能已经有了,只是注释掉了,取消注释即可)
(为啥需要这一步?2DPlatformer 从动画 SendMessage 去执行 MonoBehaviour 代码,我们不支持、也不推荐这种方式,但他既然用了,我们先暂时特殊支持一下)
// 1 添加这3个变量定义到类中
int idDestroyChildGameObject = 0;
int idDisableChildGameObject = 0;
int idDestroyGameObject = 0;
// 2 添加这3个成员初始化到函数 initMemberFunction 中
idDestroyChildGameObject = JSApi.getObjFunction(jsObjID, "DestroyChildGameObject");
idDisableChildGameObject = JSApi.getObjFunction(jsObjID, "DisableChildGameObject");
idDestroyGameObject = JSApi.getObjFunction(jsObjID, "DestroyGameObject");
// 3 添加这3个函数到类中
void DestroyChildGameObject()
{
callIfExist(idDestroyChildGameObject);
}
void DisableChildGameObject()
{
callIfExist(idDisableChildGameObject);
}
void DestroyGameObject()
{
callIfExist(idDestroyGameObject);
}
7. 生成一个 js 文件,记录每个脚本要对应哪个JSComponent。
执行菜单:【JSB | Generate MonoBehaviour to JSComponent_XX】
这一步是提前计算每个 MonoBehaviour 脚本,要使用哪个 JSComponent 文件进行对应。
生成了文件:StreamingAssets/JavaScript/MonoBehaviour2JSComponentName.javascript,打开这个文件看看就会明白了。
8. 【JSB | Compile Cs to Js】
编译完成后 将在 StreamingAssets/JavaScript/SharpKitGeneratedFiles.javascript 文件里找到 2DPlatformer 的所有代码。
9. 解除所有 prefab 及 场景中GameObject 对 C# 源代码的引用
因为我们最终的目的是要删除整个 2DPlatformer/Scripts 文件夹,那么任何 prefab 及 GameObject 显然都不可以引用任何 C# 源代码。
对于 MonoBehaviour 来说,我们的做法是将其替换成固定的类 JSComponent,这个类足够强大,可以支持绝大部分继承于 MonoBehaviour 的类,最主要的是支持序列化。
对于其他类或结构体,他们都将被转换为 JS 代码,GameObject 不会引用到。
在真正的项目中使用时,最好先备份,再执行这个菜单。
执行菜单:[JSB | Replace all monos for all Prefabs and Scenes]
Replace.txt 示例。显示了所有将要被处理的 prefab 及 scene。刚好包含了所有 2DPlatformer 的所有东西。确定好了就点OK吧!让奇迹发生~
1 2DPlatformer/Prefabs/Characters/enemy1.prefab
2 2DPlatformer/Prefabs/Characters/enemy2.prefab
3 2DPlatformer/Prefabs/Characters/hero.prefab
4 2DPlatformer/Prefabs/Environment/backgroundAnimation.prefab
5 2DPlatformer/Prefabs/Environment/backgrounds.prefab
6 2DPlatformer/Prefabs/Environment/Bus.prefab
7 2DPlatformer/Prefabs/Environment/Cab.prefab
8 2DPlatformer/Prefabs/Environment/env_clouds.prefab
9 2DPlatformer/Prefabs/Environment/env_fog.prefab
10 2DPlatformer/Prefabs/Environment/env_riverMid.prefab
11 2DPlatformer/Prefabs/Environment/env_riverTop.prefab
12 2DPlatformer/Prefabs/Environment/Foregrounds.prefab
13 2DPlatformer/Prefabs/FX/part_splash.prefab
14 2DPlatformer/Prefabs/FX/part_warp.prefab
15 2DPlatformer/Prefabs/FX/splash.prefab
16 2DPlatformer/Prefabs/killTrigger.prefab
17 2DPlatformer/Prefabs/mainCamera.prefab
18 2DPlatformer/Prefabs/pickupManager.prefab
19 2DPlatformer/Prefabs/platformEnd.prefab
20 2DPlatformer/Prefabs/Props/bomb.prefab
21 2DPlatformer/Prefabs/Props/bombCrate.prefab
22 2DPlatformer/Prefabs/Props/explosionCircle.prefab
23 2DPlatformer/Prefabs/Props/explosionParticle.prefab
24 2DPlatformer/Prefabs/Props/healthCrate.prefab
25 2DPlatformer/Prefabs/Props/rocket.prefab
26 2DPlatformer/Prefabs/Props/rocketExplosion.prefab
27 2DPlatformer/Prefabs/Props/swan.prefab
28 2DPlatformer/Prefabs/spawner.prefab
29 2DPlatformer/Prefabs/UI/ui_100points.prefab
30 2DPlatformer/Prefabs/UI/ui_bombHUD.prefab
31 2DPlatformer/Prefabs/UI/ui_healthDisplay.prefab
32 2DPlatformer/Scenes/Level.unity
菜单执行前:
执行菜单后:
添加一个 JSEngine.prefab 到场景中
场景是:2DPlatformer/Scenes/Level.unity
运行!
运行成功后,删除 2DPlatformer/Scripts/ 文件夹!
Windows版本可执行程序截图:
Unity代码热更新方案 JSBinding + SharpKit 首页
posted on 2015-05-04 21:24 AnswerWinner 阅读(1815) 评论(0) 编辑 收藏 举报