// Unity学习笔记,自己会记录一些学习中遇到的问题,可以给自己看也可以给需要的人看,希望能帮助掉在同样坑里的小伙伴们
// 初学者,没太多编程基础,所以难免可能会有写错的地方,请见谅
课后心得:unity还真是像之前一个程序同事说的,可能不算难,但是东西真**的多啊:D,很容易学完就忘啊。。
开始学第二课啦!
第二课是一个3D打飞机游戏,恩
视频地址:http://unity3d.com/cn/learn/tutorials/projects/space-shooter-tutorial
前言 初始设定
切换为目标分辨率的竖屏模式:
edit --> project settings --> player 分辨率设定为600*900
第一步 建立player物体
1.拖入飞船模型
2.加rigidbody
3.加collider
*关于collider形态的选择:
原始形态的collider(unity自带的box、sphere、capsule)- 性能更好,能用就用
mesh collider (自定形状)- 更贴合物体形状,但比较费,所以教程中做了一个简化版的物体模型来作为mesh
*player会有碰撞触发效果,要勾选Trigger
4.player下添加飞机的火焰作为子物体
第二步 调整镜头、灯光,添加背景
camera调到正上方,做出俯视效果(projection-->orthographic)
关掉默认的环境光(ambient colour),自建3个新的directional light
加背景图:
新建quad -- 把背景图的texture拖到quad上 -- 把quad的shader设为unlit(不受光源影响)
第三步 让飞船动起来
开始写script了,此处只记重点
1. 怎么动?
上次让球动,是推动力,用的 rb.AddForce (movement * speed);
这次不是推动,是移动,使用 rb.velocity = movement * speed;
2. 限定动的范围
rb.position = new Vector3 (
Mathf.Clamp(rb.position.x, 最小值,最大值),
Mathf.Clamp(rb.position.y, 最小值,最大值),
Mathf.Clamp(rb.position.z, 最小值,最大值)
);
*Mathf是个数学函数库
*最大/最小值设成public变量,在unity中直接敲进去
3. 自建新的class
此处教程中是说,为了在inspector有层级更清楚的显示所以新建了个class
建类:public class 类名 { 类里的内容,比如变量 };
建类的例子:public 类名 例子名;
引用类中变量:例子名.变量名
4. 最后给飞船加了个倾斜效果,不细述了
2016.7.19 学习进度有点慢
第四步 让飞船发射子弹
1. 先要把子弹做出来
- 这里先建了一个空object做容器(Bolt),把所有的设定和逻辑都做在了Bolt上,这大概是为了之后可以随意替换里面的子弹样式而不影响子弹的设定。
- 在Bolt下面加quad(VFX),然后把material丢上去
- 加material的时候,这次教程没有直接拖texture到物体上,不过其实直接拖上去再改属性也是可以的。material的shader默认是standard,是这样的效果:
改成 particle-additive后,黑色背景会消失(关于material的各种shader的特性之后再仔细研究)
最后选用的是mobile下的particle-additive,mobile模式可以节省资源
2.子弹自身的运动逻辑 —— 让子弹飞
- 凡是有物理逻辑的肯定都是先把rigidbody加上,带碰撞的都是把collider加上,碰撞后有反应的都要把trigger勾上。
- collider加在Bolt上,所以要把挂在Bolt下面的物体的collider删掉。
- 脚本很简单,让子弹往前飞:
rb.velocity = transform.forward * speed;
transform.forward的说明上写的是
The blue axis of the transform in world space.
我理解应该就是一个方向的设定了,蓝轴的话就是z轴了,但是我还不知道为什么不直接写z轴
3. 子弹转换为prefab
Bolt拖到prefab目录下,然后删掉场景中的Bolt
@.@会飞的子弹就做好了,剩下的发射逻辑就回到player上写了
4. 子弹的发射逻辑
(1)产生子弹:从原型-生成->例子,使用Instantiate
如果后期需要对所生成的例子进行操控,就需要返回例子的object:
GameObject clone = Instantiate(projectile, transform.position, transform.rotation) as GameObject;
如果没有对例子的操控(如本项目),就可以简单写:
Instantiate(projectile, transform.position, transform.rotation)
(2)子弹的触发与时间间隔控制
public GameObject shot;
public Transform shotSpawn;
public float fireRate;
private float nextFire;
void Update() {
if (Input.GetButton("Fire1") && Time.time > nextFire) {
nextFire = Time.time + fireRate;
Instantiate(shot, shotSpawn.position, shotSpawn.rotation);
}
}
逻辑大概就是:接收到按键并且时间超过了预定时间(上次射击时间+指定时间间隔),则生成子弹,并同时更新预定时间
这里做了4个变量,这里引用Zui(简书作者)的一段说明:
首先我们定义一个私有变量nextFire,用来记录下一发子弹的时间
然后我们定义一个公共变量fireRate,用来控制子弹发射的间隔时间
接着我们定义一个公共变量shot,用来保存我们发射的子弹Prefab,也就是告诉程序,我们发射的是哪个子弹
然后我们定义一个公共变量shotSqawn,用来保存子弹的发射点,也就是告诉程序,子弹从哪里发射出去
文/Zui(简书作者)
fireRate、shot和shotSpawn都在player的Inspector中设置
关于GetButton和Getkey:
Getkey可直接使用键盘键的代号(例,Input.GetKey(KeyCode.Spacebar)),GetButton只能使用input manager中设定的button名(Fire1在里面就有)。官方推荐使用GetButton。
Time.time是当前时间。
@-@至此一个会放弹的飞船就做好了
2016.7.20
最后还有一件事,就是把飞出屏幕外的子弹清理掉(免得费资源嘛)
这里做了一个立方体的边界盒子(cube),把游戏区域框起来,飞出去的就消掉
判定离开物体区域的api:onTriggerExit(collider other)
销毁物体 Destroy(other.gameObject)
void OnTriggerExit(Collider other)
{ Destroy(other.gameObject); }
第五步 加入“敌人”
敌人就是这个大陨石了
搞出来这个大陨石要做几件事:
1 做出陨石的形象,2 让陨石翻滚,3 让陨石被子弹击中后爆掉,4 让陨石向下冲向飞船, 5 把陨石做成prefab
1. 做出陨石的形象
还是新建空物体做载体(Asteroid),把陨石的FXB模型直接拖到它的子节点上
Rigidbody、Collider都加在Asteroid上
2. 让陨石翻滚
Asteroid上加脚本RandomRotator:
public float tumble;
private Rigidbody rb;
void Start ()
{
rb = GetComponent();
rb.angularVelocity = Random.insideUnitSphere * tumble;
}
为什么用Random.insideUnitSphere? 教程里只说是为了获得一个Vector3,恩,只是为了获得一个Vector3。好吧~
tumble在inspector里设定为5
空气阻力AngularDrag要调成0 (好精细还有空气阻力~)
#一个滚动的陨石诞生了!#
3 让陨石被子弹击中后爆掉
再在Asteroid上加脚本DestroyByContact
public GameObject explosion; // 装陨石爆破动画
public GameObject playerExplosion; // 装飞船爆破动画
void OnTriggerEnter (Collider other) //“一触即发”
{
if (other.tag == "Boundary") // 把boundary排除掉哦
{
return;
}
Instantiate(explosion, transform.position, transform.rotation); // 陨石爆破动画
if (other.tag == "Player") // 飞船爆破动画
{
Instantiate(playerExplosion, other.transform.position, other.transform.rotation);
}
Destroy(other.gameObject); // 爆了就清掉啊,Destroy排名不分先后,当前帧结束时一起清
Destroy(gameObject);
}
4. 让陨石向下冲向飞船
直接把之前子弹用的mover C#拖到Asteroid里,调下speed为负数
5. 把陨石做成prefab
和子弹一样,拖进prefab,删掉场景里的
#至此,陨石自己的任务就完成了#
第六步 让敌人一波一波地来
噼里啪啦
新建一个“”控场”专属空物体 Game Controller,加脚本
还是直接截图吧
关于Coroutine(协程):
协程的作用一共有两点:1)延时(等待)一段时间执行代码;2)等某个操作完成之后再执行后面的代码。总结起来就是一句话:控制代码在特定的时机执行。
A coroutine is a function that is executed partially and, presuming suitable conditions are met, will be resumed at some point in the future until its work is done.
即协程是一个分部执行,遇到条件(yield return 语句)会挂起,直到条件满足才会被唤醒继续执行后面的代码。
个人理解:
- 协程与主程序间有一定的独立性,这也是它存在的意义
- 有局部暂停、等待的时候就要用协程
*理解可能不透彻,但感觉也够用了
粘一张很赞的图(同样来自 http://dsqiu.iteye.com/blog/2029701)
2016.7.22 进展不快,但感觉还是学了很多的