? ? ? ?在寫學(xué)習(xí)筆記之前痕届,我先說(shuō)說(shuō)為什么會(huì)做這么一個(gè)游戲DEMO吧衡瓶,目前是在一家互聯(lián)網(wǎng)廣告公司任職3D設(shè)計(jì)組長(zhǎng)一職徘公,主要內(nèi)容是CG這塊,因?yàn)閷?duì)AR和VR很感興趣哮针,并且暫時(shí)選擇了Unity這款游戲引擎做為我的敲門磚关面,因?yàn)镃G是我的優(yōu)勢(shì),所以對(duì)Unity這款軟件不陌生十厢,上手很快等太,唯一比較陌生的是程序代碼這塊,但因?yàn)榇髮W(xué)的時(shí)候?qū)W過(guò)C和游戲編程蛮放,所以倒不止于看到程序就放棄澈驼,只是工作一兩年完全沒(méi)接觸程序了。因此我決定在上手了Unity軟件后筛武,準(zhǔn)備啃下Unity中編程這塊硬骨頭。我選擇了C#這門語(yǔ)言來(lái)學(xué)習(xí)挎塌,因?yàn)槭忻嫔洗蠖鄶?shù)AR和VR教程都是用C#來(lái)寫的徘六,這樣學(xué)習(xí)起來(lái)方便一些。
? ? ? ?我先在在網(wǎng)上學(xué)習(xí)了一些Unity的C#基礎(chǔ)知識(shí)榴都,比如GameObject待锈,Transform,枚舉嘴高,方法等竿音,對(duì)Unity中編程的基礎(chǔ)知識(shí)有了一些了解,接著我覺得還得跟著一些教程做練習(xí)拴驮,理解消化春瞬,跟著官方教程是一個(gè)很不錯(cuò)的選擇,接下來(lái)就進(jìn)入我的Spaceshooter筆記套啤。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
一:教程的前5個(gè)課時(shí)講述的是3D知識(shí)宽气,比如相機(jī)燈光背景和特效等,表示毫無(wú)壓力跳過(guò)
二:角色控制潜沦、發(fā)射子彈
代碼如下:
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)
? ? ? ? ? ? ?);
? ? }
}
這段代碼主要實(shí)現(xiàn)了以下功能
1:這段代碼首先沒(méi)有寫在Unity默認(rèn)的Update函數(shù)中萄涯,而是寫在了FixedUpdate()函數(shù)中,在網(wǎng)上搜了一下Update和FixedUpdate的區(qū)別唆鸡,Update()和FixedUpdate()在游戲中都會(huì)在更新的時(shí)候自動(dòng)循環(huán)調(diào)用涝影。但是Update是在每次渲染新的一幀的時(shí)候才會(huì)調(diào)用,也就是說(shuō)争占,這個(gè)函數(shù)的更新頻率和設(shè)備的性能有關(guān)以及被渲染的物體(可以認(rèn)為是三角形的數(shù)量)燃逻。在性能好的機(jī)器上可能fps 30序目,差的可能小些。這會(huì)導(dǎo)致同一個(gè)游戲在不同的機(jī)器上效果不一致唆樊,有的快有的慢宛琅。因?yàn)閁pdate的執(zhí)行間隔不一樣了。而FixedUpdate逗旁,是在固定的時(shí)間間隔執(zhí)行嘿辟,不受游戲幀率的影響。所以處理Rigidbody的時(shí)候最好用FixedUpdate片效。
PS:FixedUpdate的時(shí)間間隔可以在項(xiàng)目設(shè)置中更改红伦,Edit->Project Setting->time找到Fixed timestep。就可以修改了淀衣。
2:關(guān)于Rigidbody調(diào)用的問(wèn)題昙读,官方案例是4.6版本,可以直接調(diào)用rigidbody膨桥,但5.0以后的版本中蛮浑,Unity將C#規(guī)范了,要調(diào)用rigidbody必須要定義只嚣,比如Rigidbody r=GetComponent<Rigidebody>()來(lái)定義沮稚。
3:通過(guò)鍵盤上的"W" "A" "S" "D"或者方向鍵來(lái)控制飛船的前后左右的移動(dòng),左右是通過(guò)Input.GetAxis ("Horizontal")和Input.GetAxis ("Vertical")來(lái)實(shí)現(xiàn)册舞,它們都是Vector3類型并且值范圍都是在-1到1之間 蕴掏,此時(shí)定義一個(gè)訪問(wèn)修飾符為public且float類型的speed來(lái)控制飛船的速度;
4:用Math.Clamp方法來(lái)控制飛船移動(dòng)邊界,通過(guò)查閱官方文檔知道调鲸,Math.Clamp返回的是一個(gè)float類型value的值盛杰,寫法是Math.Clamp(float value,float min藐石,float max)
5:設(shè)定飛船的旋轉(zhuǎn)邊界值即供,rotation是一個(gè)Quaternion類型的值,用Quaternion.Euler來(lái)表示物體的旋轉(zhuǎn)于微,物體在x和y方向上的旋轉(zhuǎn)為0 募狂,在z方向上的旋轉(zhuǎn)跟物體的速度正負(fù)值掛鉤,所以用rigidbody.velovity.x來(lái)表示角雷,這樣祸穷,按下方向鍵,向左或者向右時(shí)勺三,飛船就能夠發(fā)生“側(cè)翻”效果.
6:Instantiat方法的使用雷滚,參照官方文檔,Instancitate是用來(lái)實(shí)例化物體的吗坚,使用方法是Instantiate (Object Original祈远,Vector3 position呆万,Quaternion rotation),Object是要實(shí)例化的對(duì)象车份,Vector是要復(fù)制到的位置谋减,Quaternion是設(shè)置物體的旋轉(zhuǎn)。
7:Time.time方法用來(lái)獲取游戲運(yùn)行的時(shí)間扫沼,在本例中用來(lái)設(shè)置按下鼠標(biāo)左鍵后間隔多少時(shí)間發(fā)射下一發(fā)子彈出爹。
8:Input.GetButton("FIre1")代碼塊中,F(xiàn)ire1表示鼠標(biāo)左鍵缎除,F(xiàn)ire0表示鼠標(biāo)中間严就,F(xiàn)ire2表示鼠標(biāo)右鍵,而且Input.GetButton表示持續(xù)按下Fire1器罐,同類型的還有Input.GetButtonDown和Input.GetButtonUp也表示按下按鍵梢为,但是只能計(jì)算一次按下或者彈起。
二:邊界
voidOnTriggerExit(Colliderother){
Destroy(other.gameObject);
}
調(diào)用了Trigger中的一個(gè)方法轰坊,即離開這個(gè)邊界的物體都會(huì)被“殺死”铸董,條件就是只要觸碰到了邊界物體的Collider就會(huì)調(diào)用這個(gè)方法,Destroy中的參數(shù)是other.gameobjet,表明只會(huì)殺死其他的物體肴沫,而不會(huì)把自身給銷毀掉粟害,跟后面要用到的碰撞方法有區(qū)別。
三 創(chuàng)建激光
void Start()?
{
Rigidbodyrigidbody=GetComponent();
rigidbody.velocity=transform.forward*speed;
rigidbody.angularVelocity=Random.insideUnitSphere*tumble;
}
1:這個(gè)代碼是用來(lái)表示行星的產(chǎn)生方向和起始隨機(jī)的旋轉(zhuǎn)速度樊零,transform.forward用來(lái)表示z軸方向上的速度,為(0孽文,0驻襟,1)
2:rigidbody.angularVelocity用來(lái)表示物體的旋轉(zhuǎn)角速度,是一個(gè)Vector3類型的芋哭,而隨機(jī)的起始速度則由Random類中的insideUnitSphere來(lái)表示沉衣,它是一個(gè)半徑為1的球,方向是-1到1减牺,并且這個(gè)方法只能放在Start函數(shù)中只能調(diào)用一次豌习,自己在做的時(shí)候,放在了Update()函數(shù)中拔疚,導(dǎo)致這個(gè)產(chǎn)生的行星都像發(fā)了瘋似的旋轉(zhuǎn)肥隆,因?yàn)槊恳粠紩?huì)更新它的角速度,而這個(gè)角速度又是隨機(jī)產(chǎn)生的稚失。
四 爆炸?
代碼如下
publicGameObjectzidanbaozha;
publicGameObjectfeichuanbaozha;
publicGameObjectzidanbaozhashengyin;
publicGameObjectfeichuanbaozhashengyin;
voidOnTriggerEnter(Colliderother){
//自己的寫法
if(other.tag=="Boundary")//這里的if是不讓邊界盒子跟行星發(fā)生碰撞
{
return;//這里的return是返回一個(gè)空的值栋艳,表示什么也不做,也可以不寫這個(gè)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是不讓邊界盒子跟行星發(fā)生碰撞
{
return;//這里的return是返回一個(gè)空的值句各,表示什么也不做吸占,也可以不寫這個(gè)return
}
Instantiate(zidanbaozha,transform.position,Quaternion.identity);
if(other.tag=="Player")
{
Instantiate(feichuanbaozha,transform.position,Quaternion.identity);
}
Destroy(other.gameObject);
Destroy(gameObject);*/
1:這里的代碼是分類子彈的collider碰撞到不同的物體的時(shí)候晴叨,會(huì)有不同的爆炸特效,判斷的一句是gameobject的Tag矾屯。我的寫法相比官方的想法可能會(huì)更死板兼蕊,不簡(jiǎn)練,但畢竟不是寫代碼的老手件蚕,這樣寫代碼方便自己理解孙技,以后代碼量上去了我相信自己還是能夠養(yǎng)成良好的代碼習(xí)慣。
2:這里的Quaternion.identity表示物體沒(méi)有旋轉(zhuǎn)骤坐。
五 生成波
代碼如下
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:這里的代碼是讓“敵人”也就是行星能夠一波一波地產(chǎn)生绪杏,并且在每次游戲開始之前,會(huì)過(guò)1s后才開始纽绍,讓玩家做好游戲準(zhǔn)備蕾久,并且“敵人”會(huì)一波一個(gè)產(chǎn)生,每一波之間也會(huì)有間隔時(shí)間拌夏。
2:這里用到了SpawnWaves()方法僧著,其實(shí)我覺得有沒(méi)有這個(gè)方法也是一樣的,因?yàn)槟軌虍a(chǎn)生一波一波效果的主要是協(xié)同方法中的WaitForSeconds類來(lái)控制的障簿,而且SpawnWaves()方法必須要在void Star()方法中手動(dòng)調(diào)用才行盹愚。
3:能夠?qū)崿F(xiàn)一波一波效果的,最關(guān)鍵是用到了協(xié)同函數(shù)站故,來(lái)實(shí)現(xiàn)延遲的效果也就是StartCoroutine(method name)方法皆怕,查看官方文檔知道,StartCoroutine(方法名)就能啟用Coroutine方法西篓,并且返回值的類型不再是void愈腾,而是IEnumerator,并且用關(guān)鍵字yield return new WaitForSeconds (float value)來(lái)標(biāo)識(shí)WaitForSeconds類岂津,這里的value是標(biāo)識(shí)延遲的時(shí)間虱黄。(http://docs.unity3d.com/ScriptReference/WaitForSeconds.html)這里插入官方文檔關(guān)于如何調(diào)用Coroutine,并且使用WaitForSeconds來(lái)實(shí)現(xiàn)延遲效果有很好的解釋
六 結(jié)束游戲
在這一塊中吮成,主要困惑點(diǎn)是在重新加載當(dāng)前游戲的api橱乱,因?yàn)楣俜浇坛逃玫氖?.2的版本,用Application.loadlevel(Application.loadlevel) 來(lái)加載當(dāng)前場(chǎng)景粱甫,當(dāng)時(shí)在5.3版本中完全不能用泳叠,在5.3版本中使用SceneManager.LoadScene(scene name or index of the scene),并且在用這個(gè)類的時(shí)候,還必須引用Scene Management茶宵,在官網(wǎng)文檔中其實(shí)也沒(méi)解釋清楚析二,在網(wǎng)上搜了好久才知道這個(gè)的用法,最終,在5.3中重新加載當(dāng)前場(chǎng)景叶摄,我的代碼是
usingUnityEngine;
usingSystem.Collections;
usingUnityEngine.SceneManagement;
publicclassGameController:MonoBehaviour{
void Update()
{
?if (gameover)
{
? ? ?if(Input.GetKeyDown(Keycode.R))
? ?{
? ? ? SceneManager.LoadScene(0);
? ?}
}
}
}
這樣就當(dāng)按下R鍵的時(shí)候属韧,就會(huì)重新加載當(dāng)前的場(chǎng)景,重新開始游戲