全是在学官教时遇到的坑,然后数小时后爬出来.同时会添加到处学来的的Unity技巧
----------------------------------------------------------
代码:
1.使游戏对象运动的N种方式
1、rigidbody.addforce(Vector3 * speed) (见roll-a-ball)
2、rigidbody.velocity(vector3 * speed);(见space shooter,改变位置向量,非常的生硬)
3、rigidbody.MovePositon(vector 3) & rigidbody.MoveRotation(quaternion)(包含移动和转向,细腻,常用。见survival shooter player的移动)(转向包括光标指向转向,和键盘输入转向(见Tanks!))
4、transform.translate(方向 * 速度 * Time.deltatime)
见survival shooter内敌人被消灭后下沉并回收
MoveTowards vs. Lerp vs. Slerp vs. SmoothDamp
2.镜头跟随移动游戏对象移动,脚本内用LateUpdate()而非FixedUpdate()(见roll-a-balll)。
3.Random.Range(min,max),如果两个数都是float则前后都算,如果两个数是int则包前不包后。
4.FindObjectOfType<Inventory>() vs GetComponent<Inventory>()
FindObjectOfType①为外部调用,在所有场景和游戏对象里找,不是很快;通常放在Start()内。
GetComponent②为内部调用,只在挂着该脚本的游戏对象上的其他组件找,他们都在一个inspector里,比如rigidbody,renderer,collider等。
②替代①的方式是给脚本所在的对象选择tag,再用【FindGameObjectWithTag("tag的名字“).GetComponent<脚本>()】放入Awake()内。
也可以直接 在脚本开头public Inventory inventory; 然后在inspecotor界面把挂着Inventory脚本的游戏对象拖入框。[Adventure Game]
5.Debug
在space shooter中,行星在游戏界面内存在,运行尝试是否子弹能消除时发现行星不见了,这时候在行星的脚本里加了一段“Debug.Log(other.name)”把将行星摧毁的物体的名称发送到了unity的console内。
6.coroutine
在土豆视频内spaceshoot官方教程中行星波生成里解释的很清楚(因为有中文字幕)。coroutine就是等某一段或几段设定好的时间后循环方法内的代码。
调用这种异步(即有时间间隔)的方法要用语句 startCoroutine (方法()); 。含异步的方法要用IEnumerator来代替void,yield return new WaitForSecond(设定好的时间);是指等这段时间后继续循环,写在for语句内表示等N秒后i++,写在循环前表示等N秒后开始while循环(把for循环放入while循环内,判断条件可以按需求写,在 space shooter里想要的是行星不停出直到飞船被毁为止,所以判断条件写的是true(意思是true就是true,一直循环),再在while循环的末尾加上for循环的间隔)。
关于yield return,这篇文章就说的很清楚了:http://www.cnblogs.com/wangchengfeng/p/3724377.html
7.游戏对象的回收(包括落下的行星,行星被射击时的爆炸效果)
在space shooter内行星和飞行的子弹靠的是外置一个boundary(cube,box collider)并用脚本(onTriggerExit)来回收游戏对象。
但行星的爆炸特效并不与boundary碰撞,所以会冗余。在所有爆炸特效下挂载脚本在void start内用Destroy(gameObject, 设定的时间);语句使每帧检测超过设定的时间后被挂载的游戏对象销毁,在survival shooter内对敌人的回收也是如此(先下沉,下沉N秒后摧毁)。
8.unity内脚本的实例化关系
见space shooter内计分板的讲解,即在脚本内写一个public的方法,要在其他脚本内调用要先指定是要调用脚本的哪个实例(即使在只有一个实例的情况下)。
9.Input.GetAxisRaw(); vs Input.GetAxis ();
Raw是指非平滑输入,即输入值只有-1,0,1(左,无输入,右)。这种输入的作用让角色单位输入变小,在操作感官上更有灵敏性,角色反应更快更流畅。(见survival shooter内Player的移动)
10.Time.time vs Time.deltaTime
Time.time是游戏从开始到此刻的持续时间
Time.deltaTime是游戏上一帧的持续时间(增量时间),每一帧完成的秒数都不同,所以在需要固定增量时(比如每秒移动10,每0.5秒攻击一次),要在数值上乘以Time.deltaTime(即单位),否则将变成(每帧移动10,每帧攻击2次)
11.在脚本实例(A)内引用其他脚本实例(B)内方法
【】如果A和B脚本实例都挂在同一对象上(比如survival shooter的敌人上同时挂载Enemy Health和Enemy Attack),则在A内private EnemyAttack enemyAttack;
然后在Awake()内 enemyAttack = GetComponent<EnemyAttack> ();就可以直接调用了。
【】如果A和B在不同对象上(比如survival shooter的Enmey Attack和Player Health)则在A内private PlayerHealth playerHealth;
然后在Awake()内先要player = GameObject.FindGameObjectWithTag<"Player">();找到挂B脚本的对象(该对象必须添加“Player”的Tag)。
然后再playerHealth = player.GetComponent<PlayerHealth>();,才可调用。
如果是在场景里的GameObject上挂的脚本则可直接Public GameObject xx; 然后在Inspector里拖入。但如果是存成Prefab的的游戏对象上挂的脚本,引用方式必须是上面通过Tag寻找,而且要注意脚本加载顺序找不到引用报错。
还有一种见Adventure Game(仅用于Editor继承),下面的target是Editor特有的字段,表示该Editor的对象,第二个红框里的句子只是把target转换成了ConditionCollection类型,在这之前target仅仅是个Object类型不明
【】如果B在A的子对象上(比如survival shooter的PlayerShooting挂在游戏对象Player的子对象GunBarrelEnd上),在A内引用B的则private PlayerShooting playerShooting;
然后在Awake内playerShooting = GetComponentInChildren<PlayerShooting>();
【】还有一种情况,玩家开枪射击敌人,使敌人生命值降低。player下的游戏对象枪上所挂的脚本PlayerShooting要引用某个敌人上的EnemyHealth来调用生命值降低方法。
则以射线碰撞Raycast,撞到层为“shootable”后返回碰撞点信息,在信息点上再找其脚本实例。(见survival shoot的playershooting脚本)
【】如果一个变量声明方式为public static xx(类型) xx(名称),则在其他脚本内调用不用先声明上面的那一套,直接使用就是。(一般用于分数等不可改元素)