Unity官方 《Spaceshooter》学习笔记

2016-06-11  本文已影响780人  小海龟我们走

       在写学习笔记之前,我先说说为什么会做这么一个游戏DEMO吧,目前是在一家互联网广告公司任职3D设计组长一职,主要内容是CG这块,因为对AR和VR很感兴趣,并且暂时选择了Unity这款游戏引擎做为我的敲门砖,因为CG是我的优势,所以对Unity这款软件不陌生,上手很快,唯一比较陌生的是程序代码这块,但因为大学的时候学过C和游戏编程,所以倒不止于看到程序就放弃,只是工作一两年完全没接触程序了。因此我决定在上手了Unity软件后,准备啃下Unity中编程这块硬骨头。我选择了C#这门语言来学习,因为市面上大多数AR和VR教程都是用C#来写的,这样学习起来方便一些。

       我先在在网上学习了一些Unity的C#基础知识,比如GameObject,Transform,枚举,方法等,对Unity中编程的基础知识有了一些了解,接着我觉得还得跟着一些教程做练习,理解消化,跟着官方教程是一个很不错的选择,接下来就进入我的Spaceshooter笔记。                                                  

一:教程的前5个课时讲述的是3D知识,比如相机灯光背景和特效等,表示毫无压力跳过

二:角色控制、发射子弹

代码如下:

using UnityEngine;

using System.Collections;

public class spaceshooter : MonoBehaviour {

             public float speed;

             public float rt;

             public float xMin;

             public float xMax;

             public float zMin;

            public float zMax;

    publicGameObjectshot;

    publicTransformshotspawn;

    publicfloatfirerate;

    privatefloatnextfire=0.0f;

voidUpdate(){

      if(Input.GetButton("Fire1")&&Time.time>nextfire){

      nextfire=Time.time+firerate;

     Instantiate(shot,shotspawn.position,shotspawn.rotation);

}

}

   void FixedUpdate()   {

           float mh = Input.GetAxis ("Horizontal");

            float mv =  Input.GetAxis ("Vertical");

            Rigidbody rigidbody =GetComponent();

            Vector3 movement = new Vector3 (mh, 0.0f, mv);

             rigidbody.velocity = movement*speed;

             transform.rotation= Quaternion.Euler (0.0f, 0.0f, rigidbody.velocity.x* rt);

              transform.position = new Vector3 (

             Mathf.Clamp (transform.position.x,xMin,xMax),

             0.0F,

             Mathf.Clamp(transform.position.z,zMin,zMax)

             );

    }

}

这段代码主要实现了以下功能

1:这段代码首先没有写在Unity默认的Update函数中,而是写在了FixedUpdate()函数中,在网上搜了一下Update和FixedUpdate的区别,Update()和FixedUpdate()在游戏中都会在更新的时候自动循环调用。但是Update是在每次渲染新的一帧的时候才会调用,也就是说,这个函数的更新频率和设备的性能有关以及被渲染的物体(可以认为是三角形的数量)。在性能好的机器上可能fps 30,差的可能小些。这会导致同一个游戏在不同的机器上效果不一致,有的快有的慢。因为Update的执行间隔不一样了。而FixedUpdate,是在固定的时间间隔执行,不受游戏帧率的影响。所以处理Rigidbody的时候最好用FixedUpdate。

PS:FixedUpdate的时间间隔可以在项目设置中更改,Edit->Project Setting->time找到Fixed timestep。就可以修改了。

2:关于Rigidbody调用的问题,官方案例是4.6版本,可以直接调用rigidbody,但5.0以后的版本中,Unity将C#规范了,要调用rigidbody必须要定义,比如Rigidbody r=GetComponent<Rigidebody>()来定义。

3:通过键盘上的"W" "A" "S" "D"或者方向键来控制飞船的前后左右的移动,左右是通过Input.GetAxis ("Horizontal")和Input.GetAxis ("Vertical")来实现,它们都是Vector3类型并且值范围都是在-1到1之间 ,此时定义一个访问修饰符为public且float类型的speed来控制飞船的速度;

4:用Math.Clamp方法来控制飞船移动边界,通过查阅官方文档知道,Math.Clamp返回的是一个float类型value的值,写法是Math.Clamp(float value,float min,float max)

5:设定飞船的旋转边界值,rotation是一个Quaternion类型的值,用Quaternion.Euler来表示物体的旋转,物体在x和y方向上的旋转为0 ,在z方向上的旋转跟物体的速度正负值挂钩,所以用rigidbody.velovity.x来表示,这样,按下方向键,向左或者向右时,飞船就能够发生“侧翻”效果.

6:Instantiat方法的使用,参照官方文档,Instancitate是用来实例化物体的,使用方法是Instantiate (Object Original,Vector3 position,Quaternion rotation),Object是要实例化的对象,Vector是要复制到的位置,Quaternion是设置物体的旋转。

7:Time.time方法用来获取游戏运行的时间,在本例中用来设置按下鼠标左键后间隔多少时间发射下一发子弹。

8:Input.GetButton("FIre1")代码块中,Fire1表示鼠标左键,Fire0表示鼠标中间,Fire2表示鼠标右键,而且Input.GetButton表示持续按下Fire1,同类型的还有Input.GetButtonDown和Input.GetButtonUp也表示按下按键,但是只能计算一次按下或者弹起。

二:边界

voidOnTriggerExit(Colliderother){

Destroy(other.gameObject);

}

调用了Trigger中的一个方法,即离开这个边界的物体都会被“杀死”,条件就是只要触碰到了边界物体的Collider就会调用这个方法,Destroy中的参数是other.gameobjet,表明只会杀死其他的物体,而不会把自身给销毁掉,跟后面要用到的碰撞方法有区别。

三 创建激光

void Start() 

{

Rigidbodyrigidbody=GetComponent();

rigidbody.velocity=transform.forward*speed;

rigidbody.angularVelocity=Random.insideUnitSphere*tumble;

}

1:这个代码是用来表示行星的产生方向和起始随机的旋转速度,transform.forward用来表示z轴方向上的速度,为(0,0,1)

2:rigidbody.angularVelocity用来表示物体的旋转角速度,是一个Vector3类型的,而随机的起始速度则由Random类中的insideUnitSphere来表示,它是一个半径为1的球,方向是-1到1,并且这个方法只能放在Start函数中只能调用一次,自己在做的时候,放在了Update()函数中,导致这个产生的行星都像发了疯似的旋转,因为每一帧都会更新它的角速度,而这个角速度又是随机产生的。

四 爆炸 

代码如下

publicGameObjectzidanbaozha;

publicGameObjectfeichuanbaozha;

publicGameObjectzidanbaozhashengyin;

publicGameObjectfeichuanbaozhashengyin;

voidOnTriggerEnter(Colliderother){

//自己的写法

if(other.tag=="Boundary")//这里的if是不让边界盒子跟行星发生碰撞

{

return;//这里的return是返回一个空的值,表示什么也不做,也可以不写这个return

}

if(other.tag=="Bolt")

{

Destroy(other.gameObject);

Destroy(gameObject);

Instantiate(zidanbaozha,transform.position,Quaternion.identity);

Instantiate(zidanbaozhashengyin,transform.position,Quaternion.identity);

}

if(other.tag=="Player")

{

Destroy(other.gameObject);

Destroy(gameObject);

Instantiate(feichuanbaozha,transform.position,Quaternion.identity);

Instantiate(feichuanbaozhashengyin,transform.position,Quaternion.identity);

}

if(other.tag=="xingxing")

{

Destroy(other.gameObject);

Destroy(gameObject);

Instantiate(zidanbaozha,transform.position,Quaternion.identity);

}

//官方教程的写法

/*if(other.tag=="Boundary")//这里的if是不让边界盒子跟行星发生碰撞

{

return;//这里的return是返回一个空的值,表示什么也不做,也可以不写这个return

}

Instantiate(zidanbaozha,transform.position,Quaternion.identity);

if(other.tag=="Player")

{

Instantiate(feichuanbaozha,transform.position,Quaternion.identity);

}

Destroy(other.gameObject);

Destroy(gameObject);*/

1:这里的代码是分类子弹的collider碰撞到不同的物体的时候,会有不同的爆炸特效,判断的一句是gameobject的Tag。我的写法相比官方的想法可能会更死板,不简练,但毕竟不是写代码的老手,这样写代码方便自己理解,以后代码量上去了我相信自己还是能够养成良好的代码习惯。

2:这里的Quaternion.identity表示物体没有旋转。

五 生成波

代码如下

publicGameObjecthazard;

publicVector3spawnwaves;

publicinthazardcount;

publicfloatwaittime;

publicfloatspawntime;

publicfloatjiangeshijian;

voidStart(){

StartCoroutine(SpawnWaves());

}

IEnumeratorSpawnWaves(){

yieldreturnnewWaitForSeconds(waittime);

while(true)

{

for(inti=0;i

{

floata=Random.Range(-4.76f,4.76f);

hazard.transform.position=newVector3(a,0.0f,14.0f);

hazard.transform.rotation=Quaternion.identity;

Instantiate(hazard,hazard.transform.position,hazard.transform.rotation);

yieldreturnnewWaitForSeconds(spawntime);

}

yieldreturnnewWaitForSeconds(jiangeshijian);

}

}

1:这里的代码是让“敌人”也就是行星能够一波一波地产生,并且在每次游戏开始之前,会过1s后才开始,让玩家做好游戏准备,并且“敌人”会一波一个产生,每一波之间也会有间隔时间。

2:这里用到了SpawnWaves()方法,其实我觉得有没有这个方法也是一样的,因为能够产生一波一波效果的主要是协同方法中的WaitForSeconds类来控制的,而且SpawnWaves()方法必须要在void Star()方法中手动调用才行。

3:能够实现一波一波效果的,最关键是用到了协同函数,来实现延迟的效果也就是StartCoroutine(method name)方法,查看官方文档知道,StartCoroutine(方法名)就能启用Coroutine方法,并且返回值的类型不再是void,而是IEnumerator,并且用关键字yield return new WaitForSeconds (float value)来标识WaitForSeconds类,这里的value是标识延迟的时间。(http://docs.unity3d.com/ScriptReference/WaitForSeconds.html)这里插入官方文档关于如何调用Coroutine,并且使用WaitForSeconds来实现延迟效果有很好的解释

六 结束游戏

在这一块中,主要困惑点是在重新加载当前游戏的api,因为官方教程用的是4.2的版本,用Application.loadlevel(Application.loadlevel) 来加载当前场景,当时在5.3版本中完全不能用,在5.3版本中使用SceneManager.LoadScene(scene name or index of the scene),并且在用这个类的时候,还必须引用Scene Management,在官网文档中其实也没解释清楚,在网上搜了好久才知道这个的用法,最终,在5.3中重新加载当前场景,我的代码是

usingUnityEngine;

usingSystem.Collections;

usingUnityEngine.SceneManagement;

publicclassGameController:MonoBehaviour{

void Update()

{

 if (gameover)

{

     if(Input.GetKeyDown(Keycode.R))
   {

      SceneManager.LoadScene(0);

   }

}

}

}

这样就当按下R键的时候,就会重新加载当前的场景,重新开始游戏

上一篇 下一篇

猜你喜欢

热点阅读