猴子请来的逗比项目流水总结

一、美工相关

  因为2D sprite图片使用rotate翻转之后貌似碰撞器不会随之改变位置,所以使用scale * -1 来实现翻转,这样一来,3渲2出来的图片最好关于中心轴对称,否则翻转之后会出现如下问题:

  从游戏画面角度来说这个人物就好像瞬移旋转一样。

  使用程序初步解决了这个问题,在翻转前先往预定的方向移动一段距离来抵消翻转造成的瞬移。

  

二、场景相关

  场景部分:

    水果buff 产生两次:因为人物有两个colider component,所以产生两次trigger enter event.

        

  

  人物运动部分  

  人物采用2D rigidbody component 的时候,才冰块组成的Wall上移动的时候,如果使用box colider 2d 碰撞器,由于物理模拟造成的棱角,导致运动的的中端。这个问题原因寻找了好久,因为当时的ice wall 拼接的时候非常严密,最后使用circle colider 2d 碰撞器解决这个问题。

三、构架相关、

  

  后期重构参考:

    考虑地图,怪物,Boss在多个关卡有着惊人的相似度,如果考虑后期重构可以用一个场景来实现6个关卡,使用脚本动态加载6个关卡的不同个元素,6个关卡之间共享相同的元素。比如相同的怪物与Boss控制脚本等等,只在渲染上做动态加载。

  

      对象池:

      工作流程如下:

     

  注意,如果你采用了对象池来实现一个重复使用对象的方案,那么,当你把对象使用完毕后放入池子里的时候,拿出来时要注意恢复成你最初开始拿出来的样子,也就是恢复对象的变量,恢复对象的状态,断开与其他脚本的联系等等(当然你也可以再放入的时候恢复,这些都是一样的)

     总之一句话,你使用对象池拿出来容易,再放回去没这么容易,注意要恢复成原来的样子,最好自己内部脚本有一个放回的方法,里面实现一些恢复,然后由控制器来调用放回,比如与单例脚本断开链接,恢复子物体的样子。

   同样的,如果你产生也有一些初始化工作,我建议你也在内部脚本中实现,然后由控制器调用。(也就是在对象池产生回收的时候调用)

    

     你必须要记住的是OnEnable,OnDestory ,OnDisable 这些回调函数只是在物体active 变换的时候调用,本质上并不是对象池产生回收的时候调用的。这两者不是等价的。

    举个例子,OnEnable联系单例脚本,OnDisable断开单例脚本,退出编辑器,OnDisable 中访问已经销毁的单例脚本,是不正确的。

     又或者冰块被碰撞 active = false的时候你在单例脚本中记录下其坐标,那么你退出编辑器或者父物体set active false的时候同样也会有这些逻辑操.

      再或者当你生成对象池的时候,你却要在OnEnable OnDisable的时候进行对象初始化,完全没必要,浪费性能。

      本质上也好分析,这些函数也不是为了你回收创建的。

      总之对象池中的对象初始化和销毁工作功能写在对象自己的脚本中,由控制器调用,而不是系统函数调用,当然你也可以设定过一段时间自己回收然后调用,本质也是一样的。

  

    单例脚本:

  单例脚本分为如果自己带有一些资源比如贴图,预设,像BuffManager,或者不带预设,类似人物与怪物交互之间的单例脚本(可以做距离判断)。

  对于不是动态创建的单例脚本,如果访问单例脚本,不要一开始就访问, 因为你不能保证这个单例脚本会先于你之前创建。

  PS:一般第一种脚本我都不是动态创建 ,因为资源不是动态加载。

      如果需要一个跨场景并且带有资源的单例脚本,可以采用如下

 

当加载一个新关卡时,所有场景中所有的物体被销毁,然后新关卡中的物体被加载进来。
为了保持在加载新关卡时物体不被销毁,使用DontDestroyOnLoad保持,
如果物体是一个组件或游戏物体,它的整个transform层次将不会被销毁,全部保留下来。

// survive when loading a new scene.
// Make this game object and all its transform children
//当加载新场景的时候,使游戏物体和它所有的transform子物体存活下来
function Awake () {
    DontDestroyOnLoad (transform.gameObject);
}

      多平台信号控制

  考虑到游戏的跨平台性,我们必须要适应不同的输入设备,这个时候,如果输入设备的检测放在逻辑代码里是不现实的,那么如果输入设备改了,我们的逻辑代码也要去改,会很麻烦。那么我们可以把这个输入设备封装出来,不同输入设备有不同输入设备的信号,我们可以用c# 的委托来根据不同输入设备进行监听。

  引发对怪物的AI思考

     怪物的AI可以分为传感器的输入,内部的逻辑处理,最后的对应的表现,我们可以把传感器的输入独立出来,传感器的检测逻辑由怪物的行为逻辑监听,行为逻辑就可以喝检测逻辑分开,松耦合。

 

 

四、脚本

  一:Awake在Enable之前。

  

  二、Time.scale = 0 的时候 游戏的声音不会停止播放,依旧会播放,比如游戏结束场景出来的时候,人物的的战斗声音如果比较长或者设置为loop,那么他就会一直播放。

      三、www 类不能ref 传入

      四、c#的委托对set active false的物体同样有效

  五、DateTime.Parse的日期格式如果要和服务器交互,那么你需要to String后转换成服务器的日期格式 本地默认的格式是不能直接提交的。

    项目中我都是先将本地的默认日期格式变为如下

    string DateTime = String.Format("{0:u}", Globals.Me.VipTime.Value);  //2010-10-04 17:38:01Z 再进行提交

    否则服务器不认识本地默认的日期格式就会变为 0000-00-0 00:00:00 同时DateTime.Parse 不能解析0000-00-0 00:00:00 就会发生异常

    

    日期格式大全如下:

    http://www.cnblogs.com/shaocm/archive/2012/08/15/2639998.html

  六、注意,如果你的按钮按下后会消失或者会加载场景,你这时候在按下的函数中处理服务器数据提交是不明智的,所以我们应该分层,等待数据提交完毕后按钮再消失。

 五、游戏引擎

  一、注意你不要在Animation Event 中试图 this.gameobject.SetActive(false),这会导致引擎奔溃。我花了差不多几个小时才找出来。

      注: 7.12 追加

             这个BUG在4.5 版本中已经修复

  解决办法如下:

      http://answers.unity3d.com/questions/590945/gameobjectsetactivefalse-crashes-unity.html

     http://answers.unity3d.com/questions/595459/unity-43-crashes-setting-objects-inactive.html

     

    

      Unity apparently does not like it when the gameObject the animationEvent happens on gets disabled in the middle of the event or something similar.

A workaround could be to use a coroutine to wait for the end of the frame instead of disabling immediately:

public void OnAnimationEnd() {
   StartCoroutine("Delay");
}
 
private IEnumerator Delay() {
   yield return new WaitForEndOfFrame();
 
   foreach (Transform level in levels.transform) {
      level.gameObject.SetActive(false);
   }
}

或者Or only disable the SpriteRenderer to hide the Sprite:

private bool isAnimationCompleted;
 
public void AnimationCompleted()
{
    isAnimationCompleted = true;
}
 
void Update() 
{
    if(isAnimationCompleted)
    {
        isAnimationCompleted = false;
        gameObject.SetActive(false);
    }
}

 

  二、Sprite Packer 能不用就最好不用,他本身的在编译器运行的时候会计算一次,非常消耗内存,Draw Call 不到100不用搞这个。

      下面这些人也碰到了这些问题

  http://issuetracker.unity3d.com/issues/sprite-pack-crash

      http://forum.unity3d.com/threads/sprite-packer-runs-out-of-memory.229999/

  三、Application.persistentDataPath is empty

  自己多个判断即可

  

static string FileName
{
	
	get {
		if (Application.persistentDataPath.Length == 0)
		{
			return GameConfig.PresistentDataPath+"/game_9.dat";
		}
		else { 
			
		}
		return Application.persistentDataPath + "/game_9.dat"; }
} 

   三、关于Unity3D内存加载

  网上流程很广的那篇有关两篇Unity3D内存加载,看完之后,受益颇多,但是其实有几句话我亲自测试是有问题的,如下:

   

存在3种加载prefab的方式:
一是静态引用,建一个public的变量,在Inspector里把prefab拉上去,用的时候instantiate
二是Resource.Load,Load以后instantiate
三是AssetBundle.Load,Load以后instantiate 
    三种方式有细节差异,前两种方式,引用对象texture是在instantiate时加载,而assetBundle.Load会把perfab的全部assets都加载,
    instantiate时只是生成Clone。所以前两种方式,除非你提前加载相关引用对象,否则第一次instantiate时会包含加载引用类assets的操作,
导致第一次加载的lag。 官方论坛有人说Resources.Load和静态引用是会把所有资源都预先加载的,反复测试的结果,静态引用和Resources.Load也是OnDemand的,用到时才会加载。

 亲自测试,第一种静态引用在场景加载的时候就会把所有的资源加载进内存,然后Instantiate的时候再进行克隆+引用

 

 结果场景一加载就闪退。ProFiler

 

 第二种说法,我倒是比较纠结的是Resources.load的时候他是把整个目录下的资源打包做一个缺省AssertBundle引入内存load呢?

 还是只是把自己引入内存load呢?

 经过测试,应该是只有自己引入内存。

写了一段脚本,测试如下

public class testtest : MonoBehaviour
{

    public float Timer = 0;
    GameObject[] tmp = new GameObject[Enum.GetValues(typeof(EnemyId)).Length];
    void Awake() {
        StartCoroutine(MyUpdate());
    }

    IEnumerator MyUpdate() {
        while (true)
        {
            int RandomIndex = UnityEngine.Random.Range((int)EnemyId.YangJian, Enum.GetValues(typeof(EnemyId)).Length);

            string MonsterName = "_Monster/" + ((EnemyId)RandomIndex).ToString();
            print(MonsterName);

            if (tmp[RandomIndex] == null)
            {
                tmp[RandomIndex] = Resources.Load(MonsterName) as GameObject;
                
            }
            yield return new WaitForSeconds(5.0f);
            Debug.Log(Time.time.ToString());

            GameObject tmp1 = Instantiate(tmp[RandomIndex]) as GameObject;

            yield return new WaitForSeconds(2.0f);
            Debug.Log(Time.time.ToString());
            
        }
    }
    

}

 

 

 

从上图可以看出,他的内存是一个峰值接着一个峰值,测试结果是对的。

 

 

六、项目管理

  一些底层的数据比如怪物的属性等等,随着策划的不同而修改,有可能会降低难度,有可能有时候会提高难度,我们可以提供给策划一个excel表格,让策划填写,我们只需要拿着这个excel表格解析出来即可……

posted @ 2014-06-26 21:18  灵魂重新  阅读(520)  评论(0编辑  收藏  举报