最近用Laya2.0.2制作了3D【疯狂怪兽车】的跑酷 + 射击弹幕类微信小游戏项目,在做的过程中躺了不少次坑,最终还是躺过了,在这里分享一下我的一些躺坑经验和解决方案,同时也顺便疯狂打个广告2333333。
Laya客户端
微信小游戏性能优化
- 同屏物理组件个数超过10个性能会严重降低,测试3-5个没问题。如果是做飞行弹幕游戏类型的话,或许用距离 + 碰撞盒大小来判断是否击中,这种方案处理还比较理想;
- 相同音效同时播放超过5个性能会降低,必须限制播放个数;
- 粒子数量也要控制,同个粒子限制生成次数,间隔多久才能生成;
- 美术处理的汽车模型,为了能做好动画将模型分成了若干部件,导致每台车的dc都有5个,同屏出现多台汽车,DC会爆炸。最后我废除了敌人汽车动画,并且偷偷用unity的MeshBaker合并了敌人汽车,将敌人汽车的DC降到只有1;
- 美术提供的道路模型都是在unity里面用不同的物件摆出来的,个体都很分散,而且不利于合并,合并解决有两种方案:
一种是用Laya自带的静态合并方法Laya.StaticBatchManager.combine(),不过要将物体勾选static选项。另外一种是用unity插件MeshBaker来进行合并。这两种方法都可以达到合并的效果,个人觉得直接用unity插件合并更爽; - 根据实际需求减少模型的层次结构,这样可以直接优化Laya的绘制Sprite次数,我最多放3层,之前用Laya2.0的时候,Laya引擎多加了一层root,其实我也不知道为啥,但是Laya2.0.2减少了这一层,确实对性能会好点;
- 模型面数的控制,还是按照手游那套,由于后面同屏物体比较多,敌人汽车模型控制500-800三角面,主角的面数尽量1500-2000左右;其实感觉在微信上,同屏面数10W估计都能跑得起,虽然没测试过2333。
- 部分粒子效果最好能转为序列帧动画来表现效果,并且注意序列帧的位图大小;
- 子弹可以直接用面片和透贴来制作,控制同屏数量20个左右,应该都是没问题的;
物理系统(后面优化移除掉用物理检测了,只有主角车和boss车才有物理)
- 移除正在运动的物理会报错,可以先暂停移除或者暴力判断Laya物理为空的出错代码(缺少截图);
粒子制作
-
需要遵循Laya的粒子规则来制作粒子
-
注意Color over LifeTime的渐变色块不要超过4个,否则会有问题
插件导出
可使用unity2018.3.8.0f1, Laya2.0.2导出插件
导出管理
考虑到模型会有很多,我将要导出的资源分了多个场景来管理,这样的好处是可以减少导出时间并且能更好维护文件目录。
例如疯狂怪兽车中,我将角色汽车模型分到一个场景,道路一个场景,粒子场景,子弹效果场景,此时场景有4个可以分别导出管理lh模型,如下图
导出参数如下,以汽车模型为例:
最终导出到Laya客户端的效果:
LayaNative
突然收到要制作原生安卓包并对接渠道,顿时懵逼了的感觉,毕竟安卓没怎么写过,但有挑战才能有进步,还是硬着头皮上了。喵了一下Java的语法有点类似C#,突然心就安乐了。
音乐
- 原生只支持ogg和wav格式;
- 用Laya的SoundManager播放背景音乐会卡线程,需要等待3-4秒才能恢复;
- PlayMusic() 后并马上设置MusicMuted为停止,实测音乐不会停止播放;
- 如果先设置MusicMuted再PlayMusic(),音乐不执行播放;
原生和Laya2.0互相通讯
Laya的类必须要导出window静态类,并且要暴露静态方法给原生调用,如下JaveCallback.ts代码,必须要注意最后那个if语句,如果不写就会报undefine。
export default class JaveCallback {
public static CallbackTest() {
alert("Java 调用 Laya");
}
}
if (Laya.Browser.window) {
Laya.Browser.window.JaveCallback = JaveCallback;
}
仿效微信的onShow和onHide方法
游戏中有一个需求是当app进入后台或前台需要关闭或开启音效,如果直接执行回调onShow和onHide,是会报错的,因为此时游戏还没初始化完成,调用就会报undefine。
所以暂时想到了一个解决方案:Laya进入游戏后先通知原生初始化,然后原生记录初始化标记后再执行回调onShow和onHide函数,这样才能避免一开始就报错。具体代码如下:
Laya端的代码
export default class JaveCallback {
private static readonly conchIOS: string = "Conch-ios";
private static readonly conchAndroid: string = "Conch-android";
private static os: string = "";
private static bridge: Laya.IPlatformClass = null;
public static onShow() {
alert("Java 调用 Laya onShow()");
}
public static onHide() {
alert("Java 调用 Laya onHide()");
}
//进入游戏后,执行init函数
public static init(){
if (Laya.Browser.window.conch) {
this.os = conchConfig.getOS();
if (this.os == JaveCallback.conchIOS) {
this.bridge = Laya.PlatformClass.createClass("JSBridge");
this.bridge.call("initGame:");
}
else if (this.os == JaveCallback.conchAndroid) {
this.bridge = Laya.PlatformClass.createClass("demo.JSBridge");
this.bridge.call("initGame");
}
}
}
}
if (Laya.Browser.window) {
Laya.Browser.window.JaveCallback = JaveCallback;
}
android studio端的代码
先找到Laya的JSBridge代码,增加一个Init属性,并增加一个initGame方法给Laya调用
public class JSBridge {
public static Handler m_Handler = new Handler(Looper.getMainLooper());
public static Activity mMainActivity = null;
public static Boolean Init = false;
public static void initGame(){
Init = true;
}
//后面省略..........
//一堆代码..........
//后面省略..........
//一堆代码..........
}
找到Laya的MainActivity代码,然后找到onPause和onResume方法.
在onPause方法除插入
if(JSBridge.Init == true) {
ConchJNI.RunJS("JaveCallback.onHide()");
}
在onResume方法除插入
if(JSBridge.Init == true){
ConchJNI.RunJS("JaveCallback.onShow()");
}
最终代码如下:
protected void onPause()
{
super.onPause();
if(isLoad){
if(JSBridge.Init == true) {
ConchJNI.RunJS("JaveCallback.onHide()");
}
mPlugin.game_plugin_onPause();
}
}
protected void onResume()
{
super.onResume();
if(isLoad){
mPlugin.game_plugin_onResume();
if(JSBridge.Init == true){
ConchJNI.RunJS("JaveCallback.onShow()");
}
}
}
Laya2.0打安卓原生包
按文档处理即可正常发包
Laya1.0打包安卓后gradle报错
需要修改Gradle Script里面的build.gradle,并将下面两行代码增加到里面
google()
jcenter()
buildscript {
repositories {
mavenLocal()
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
}
}
然后修改 compileSdkVersion 26 和 targetSdkVersion 26,最后升级一下SDK即发布APK包
遗留问题
- 发热问题
- 苹果打包