// Unity學(xué)習(xí)筆記(零基礎(chǔ)入坑)清笨,自用的同時(shí)也許也能幫助別人
// 初學(xué)者聋袋,沒太多編程基礎(chǔ)妖滔,所以難免可能會有寫錯(cuò)的地方,請見諒
//詳細(xì)教程視頻都有氢惋,筆記只記重點(diǎn)
傳送門: ?Unity學(xué)習(xí)筆記 - Roguelike Tutorial(上)
五硅确、讓游戲元素動起來
1. 創(chuàng)建了一個(gè)基類
為玩家和敵人提供運(yùn)動功能,玩家和敵人的腳本會繼承這個(gè)基類明肮。
public abstract class MovingObject : MonoBehaviour {}
關(guān)于abstract:
The Abstract keyword enables us?to create classes and class members?that are incomplete and must be implemented?in the derived class.
https://msdn.microsoft.com/en-gb/library/sf985hc5.aspx
2.變量
public float moveTime = 0.1f;? ? //移動 1 point 的時(shí)間
public LayerMask blockingLayer;? //檢測碰撞的層
private BoxCollider2D boxCollider;
private Rigidbody2D rb2D;
private float inverseMoveTime;
3. 函數(shù)1 - Start函數(shù)
獲取到會用到的變量
4. 函數(shù)2 - 平滑運(yùn)動函數(shù) SmoothMovement
使玩家和敵人呈現(xiàn)連續(xù)、平滑的運(yùn)動缭付。
輸入目的地的Vector3即可實(shí)現(xiàn)當(dāng)前物體向目的地的連續(xù)柿估、平滑的直線運(yùn)動
邏輯:和終點(diǎn)有距離——>向終點(diǎn)方向移動一“步”(inverseMoveTime * Time.deltaTime)并刷新距離——>停一幀——> 和終點(diǎn)有距離 ……
Magnitude是向量的長度,sqrMagnitude是長度的平方:
一個(gè)向量V的長度計(jì)算方法是Mathf.Sqrt(Vector3.Dot(v, v))陷猫。然而秫舌,sqrt計(jì)算是相當(dāng)復(fù)雜的的妖,需要更長的時(shí)間比正常執(zhí)行算術(shù)操作。計(jì)算長度平方來代替直接計(jì)算長度速度更快足陨,除掉開根號計(jì)算是基本相同的嫂粟。如果你只是使用大小比較的距離,那么你同樣可以比較平方長度大小來比較距離墨缘,得到相同的結(jié)果星虹。
http://wiki.ceeger.com/script:unityengine:classes:vector3:vector3.sqrmagnitude
public static?Vector3?MoveTowards(Vector3?current,?Vector3?target, ?float maxDistanceDelta);
MoveToward的作用是將當(dāng)前值current移向目標(biāo)target。(對Vector3是沿兩點(diǎn)間直線)
maxDistanceDelta就是每次移動的最大長度镊讼。
返回值是當(dāng)current值加上maxDistanceDelta的值宽涌,如果這個(gè)值超過了target,返回的就是target的值蝶棋。
文/fan2b(簡書作者)
5. 函數(shù)3 - Move
輸入移動方向卸亮,返回是否有障礙及障礙物體
沒有障礙則觸發(fā)“平滑運(yùn)動函數(shù)” SmoothMovement (有障礙的問題會在后兩個(gè)函數(shù)中處理)
“out”在這里被用來讓函數(shù)返回多個(gè)值,即不止返回一個(gè)bool值玩裙,還會返回一個(gè)raycastHit2D類型的值(hit)兼贸。
The out keyword causes arguments to be parsed by reference. In this case we're using it to return more than one value from our Move function.
boxCollider.enabled = false;
We're going to disable the attached box collider2D to make sure that when we are casting our ray that we're not going to hit our own collider.
hit = Physics2D.Linecast(start, end, blockingLayer);
Physics2D.Linecast會返回一個(gè)RaycastHit2D,即射線檢測到的第一個(gè)物體吃溅。
6. 函數(shù)4 - AttemptMove 獲取障礙物
接收Move函數(shù)返回的障礙物溶诞,
獲取到障礙物的component并作為參數(shù)傳入函數(shù)5 OnCantMove,并運(yùn)行OnCantMove罕偎。
泛類型 <T>
public T GenericMethod <T>?(T param)
在函數(shù)被實(shí)際調(diào)用時(shí)很澄,T會被具體的類型替代。
http://unity3d.com/cn/learn/tutorials/topics/scripting/generics
*之所以使用泛類型是因?yàn)檠占埃诶^承類player甩苛、和繼承類enemy中障礙的類型是不同的(墻、player)俏站。
泛型類型約束——關(guān)鍵字 where
protected virtual void AttemptMove(intxDir,intyDir) ? ?where T:Component
{ ... }
限定泛類型的范圍
四種限制類型:
Class, Struct, new (), interfaces
https://msdn.microsoft.com/zh-cn/library/bb384067.aspx
7. 函數(shù)5 - OnCantMove
abstract函數(shù)讯蒲,在基類中未定義具體功能,在繼承類中會被重寫肄扎,以實(shí)現(xiàn)在不同繼承類中的不同功能墨林。
2016.08.10 這篇腳本?對一個(gè)零編程基礎(chǔ)的“玩家”真心夠復(fù)雜,而我的技巧就是適度地?不 求 甚 解犯祠!
六旭等、墻的邏輯
C# Wall,撞一下會碎衡载,撞多了會消失搔耕。
墻被撞的時(shí)候,調(diào)用函數(shù)DamageWall就可以了痰娱。
返回unity弃榨,要把這個(gè)腳本掛在8塊墻上菩收,每塊墻的dmgSprite變量處要放入對應(yīng)的被撞碎的墻的圖片。
七鲸睛、Player動畫控制器設(shè)置
打開Player的Animator Controller娜饵。
1. 參數(shù)列表里,新建兩個(gè)trigger官辈,一個(gè)用來觸發(fā)攻擊箱舞,一個(gè)用來觸發(fā)受傷。
2. 在PlayerIdle和PlayerAttack之間建立雙向Transitions
PlayerIdle --> PlayerAttack: Has Exit Time不勾選钧萍,即觸發(fā)后立即切換動畫
PlayerIdle <-- PlayerAttack: Has Exit Time勾選褐缠,即當(dāng)前動畫結(jié)束后切換動畫,Exit Time =1风瘦,即完全播完后切換
Transition Duration都改為0队魏,因?yàn)?D的基于sprite的動畫切換無法實(shí)現(xiàn)過渡,3D才需要万搔。
3. 觸發(fā)條件設(shè)定:PlayerIdle --> PlayerAttack的Condition選上建好的Trigger
4. PlayerIdle和PlayerDamage同理
動畫轉(zhuǎn)換測試:
點(diǎn)擊左下參數(shù)列表里的Trigger即可觸發(fā)相應(yīng)動畫:
2016.08.11?
八胡桨、Player的腳本 —— 控制player的移動
先在GameManager中補(bǔ)充了一些要用到的東西:
新建腳本Player:
1. 函數(shù) - Start
重寫基類函數(shù):
protected override void Function ()
? ? { ? ?重寫內(nèi)容……
? ? ?base.Function();
? ? 重寫內(nèi)容……?}
2. 函數(shù) - CheckIfGameOver
3. 函數(shù) - 重寫 AttemptMove
4. 自啟動函數(shù) - Update
##兩次栽在Input.GetAxis的這個(gè)“Horizontal”上了,是“Horizontal”不是“Horizonal”八脖ⅰ昧谊!
調(diào)用AttemptMove,同時(shí)設(shè)定泛類型T-->Wall酗捌,并傳入獲取到的輸入?yún)?shù)呢诬。
*泛類型在函數(shù)被執(zhí)行時(shí),需將泛類型具體化胖缤。
AttemptMove運(yùn)行時(shí)會調(diào)用基類中的Move函數(shù)和OnCantMove函數(shù)尚镰,而Move函數(shù)又會調(diào)用SmoothMovement函數(shù)。
Input.GetAxisRaw:
Since input is not smoothed, keyboard input will always be either -1, 0 or 1. This is useful if you want to do all smoothing of keyboard input processing yourself.
5. 函數(shù) - 重寫OnCantMove
6. 函數(shù) - Restart
7. 自啟動函數(shù) - OnTriggerEnter2D
8. 自啟動函數(shù) - OnDisable
當(dāng)前物體disable的時(shí)候哪廓,將分?jǐn)?shù)值傳給GameManager
9. 函數(shù) - LoseFood
當(dāng)前腳本中未被調(diào)用狗唉,留在enemy腳本中產(chǎn)生攻擊時(shí)調(diào)用。
最后涡真,Player腳本掛到player上分俯,Blocking Layer選好。
2016.08.12 ?Fighting
九哆料、Enemy的腳本 —— enemy的移動和攻擊
1. 繼承MovingObject缸剪,重寫Start函數(shù)
獲取自身動畫和player的transform。
2. 重寫AttemptMove函數(shù)
增加是否移動的判斷东亦,enemy動一幀停一幀杏节,不會連續(xù)動。
3. 新增函數(shù) MoveEnemy —— 智能判斷趨向player的移動方向
這個(gè)函數(shù)的作用是判斷往哪個(gè)方向移動,先選X or Y拢锹,再選 正 or 負(fù)。
MoveEnemy是這個(gè)腳本的核心萄喳,它調(diào)用AttemptMove卒稳,而AttemptMove則調(diào)用其他基類中的函數(shù)。但enemy腳本中并不包含Update他巨,MoveEnemy會被GameManager調(diào)用充坑。這大概是因?yàn)楫?dāng)前是否讓enemy移動的判斷要寫在GameManager里,并且場景中會有多個(gè)enemy染突。
public void MoveEnemy()
This is going to be called by the GameManager when it issues the order to move to each of our enemies in our Enemies list.
Mathf.Abs()是取絕對值捻爷。
這里也凸顯了float.Epsilon作為一個(gè)無限接近于0的數(shù)值的作用。
條件表達(dá)式:
基本格式 ? ? ? ?表達(dá)式1? 表達(dá)式2:表達(dá)式3
1為真則返回2的值份企,1為假則返回3的值也榄。
http://blog.csdn.net/liyzh_inspur/article/details/3080769
4. 重寫OnCantMove
最后要返回編輯器把腳本掛在兩個(gè)enemy上,把playerDamage參數(shù)設(shè)定上司志,還有blockingLayer甜紫。
2016.08.13?
十、Enemy動畫控制和行動觸發(fā)
1. enemy動畫控制器
跟player動畫控制器的操作基本相同
Enemy1Idle ---> Enemy1Attack :No Has Exit Time骂远,Condition +Trigger "enemyAttack"
Enemy1Idle <--- Enemy1Attack :Has Exit Time, Exit Time = 1
Transition Duration都改為0
2. GameManager中調(diào)度enemy的移動
變量:
Awake新加List
每次開始新level囚霸,GameManager重新加載,Awake運(yùn)行激才,清空上一關(guān)的敵人:
加敵人到列表的函數(shù)拓型,在enemy腳本中調(diào)用:
協(xié)程,調(diào)用enemy腳本中的移動函數(shù):
*為什么如果沒有敵人瘸恼,要多停一下劣挫,player再動?不知道钞脂。
Update 調(diào)用協(xié)程MoveEnemies揣云,MoveEnemies調(diào)用enemy上的MoveEnemy完成移動:
2016.08.14
PS: 今天測試的時(shí)候發(fā)現(xiàn),動畫Trigger名字的大小寫又搞錯(cuò)了冰啃,然后OutWall沒有加Collider
2016.08.15