1、基本操作演練
1.1 下載 Fantasy Skybox FREE, 構建自己的遊戲場景
1)下載 Fantasy Skybox FREE:在Unity的Asset Store中找到Fantasy Skybox FREE的材料包,然後下載並且導入自己的項目中。
導入成功後多了很多材料:
2) 構建自己的遊戲場景:
在Camera中添加Component,然後添加Skybox,再將相應的Skybox圖案添加上去,就能夠完成天空盒的創建了,得到自己喜歡的背景了。
然後創建地形,需要添加Terrain,然後可以在Terrain中創造一些自己的場景,具體方法查看下圖:
1.2 寫一個簡單的總結,總結遊戲對象的使用
遊戲對象主要包括:空對象,攝像機,光線,3D物體,聲音,UI基於事件的new UI系統和粒子系統與特效,預製的材料
- 空對象(Empty):不顯示卻是最常用的對象之一
- 攝像機(Camara):觀察遊戲世界的窗口
- 光線(Light):遊戲世界的光源,讓遊戲世界富有魅力
- 3D物體 :遊戲中的重要組成部分,可以改變其網格和材質,也是很多複雜對象的初始材料
- 聲音(Audio):遊戲中的音樂或者聲音來源
- 預製材料:方便複雜對象的重複使用
2、編程實踐:牧師與魔鬼動作分離版
1)本次作業需要在上次作業的基礎上,將遊戲場景的動作分離出來:
即依然是採用MVC結構實現,與上一次無動作分離版的區別在於,之前對於動作的管理是實現了一個動作類,當鼠標點擊船或是人物時,相當於是控制器讓船或人物的實例調用動作類來實現運動。這次實踐中將船或人物與動作分離開來,單獨實現了一個動作管理器,鼠標點擊船或是人物時,相當於是控制器發送請求給動作管理器,動作管理器來實現船或人物的運動。
2)主要思路:按照下圖將上次的代碼進行分解:
3)接下來是動作基類的實現(課程中有):
設計要點:
- ScriptableObject是不需要綁定GameObject對象的可編程基類。這些對象受Unity引擎場景管理
- protected SSAction()是防止用戶自己new對象
- 使用virtual申明虛方法,通過重寫實現多態。這樣繼承者就能明確使用Start和Update編程遊戲對象行爲
- 利用接口實現消息通知,避免與動作管理者直接依賴
public class SSAction : ScriptableObject
{
public bool enable = true;
public bool destroy = false;
public GameObject gameobject { get; set; }
public Transform transform { get; set; }
public ISSActionCallback callback { get; set; }
protected SSAction() {}
public virtual void Start() {
throw new System.NotImplementedException();
}
public virtual void Update() {
throw new System.NotImplementedException();
}
}
4)簡單動作實現:
設計目的:遊戲中移動的動作,通過傳入遊戲對象的位置和設置好的動作,就能夠使遊戲對象移動起來
public class CCMoveToAction : SSAction {
public Vector3 target;
public float speed;
public static CCMoveToAction GetSSAction(Vector3 target, float speed) {
CCMoveToAction action = ScriptableObject.CreateInstance<CCMoveToAction>();
action.target = target;
action.speed = speed;
return action;
}
public override void Update () {
this.transform.position = Vector3.MoveTowards(this.transform.position,target,speed);
if(this.transform.position == target) {
this.destroy = true;
this.callback.SSActionEvent(this);
}
}
public override void Start() {}
}
5)順序動作組合類實現:
代碼重點:
- repeat的值爲-1表示動作無限循環,而start則表示動作開始
- Update的重寫則是表示執行當前的動作
- SSActionEvent則是一個回調通知的動作,當收到當前動作執行完成後,則推下一個動作,如果完成一次循環,則減少它的次數。如果當所有動作完成,就通知動作的管理者,將其銷燬。
- Start的重寫則是表示,在執行動作前,爲每個動作注入當前動作的遊戲對象,並將自己作爲動作事件的接收者。
public class CCSequenceAction : SSAction, ISSActionCallback {
public List<SSAction> sequence;
public int repeat = -1; //repeat forever
public int start = 0;
public static CCSequenceAction GetSSAction(int repeat, int start, List<SSAction> sequence) {
CCSequenceAction action = ScriptableObject.CreateInstance<CCSequenceAction>();
action.repeat = repeat;
action.sequence = sequence;
action.start = start;
return action;
}
public override void Start() {
foreach (SSAction action in sequence) {
action.gameobject = this.gameobject;
action.transform = this.transform;
action.callback = this;
action.Start();
}
}
public override void Update() {
if (sequence.Count == 0) return;
if (start < sequence.Count)
sequence[start].Update();
}
public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Completed,
int intParam = 0, string strParam = null, Object objectParam = null) {
source.destroy = false;
this.start++;
if (this.start >= sequence.Count) {
this.start = 0;
if (repeat > 0) repeat--;
if (repeat == 0) {
this.destroy = true;
this.callback.SSActionEvent(this);
}
else {
sequence[start].Start();
}
}
}
private void OnDestroy() {
//destory
}
}
6)動作事件接口定義:在定義了時間處理接口以後,所有的事件管理者都必須實現這個接口來實現事件調度。所以,組合事件需要實現它,事件管理器也必須實現它。
public enum SSActionEventType : int { Started, Completed }
public interface ISSActionCallback
{
void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Completed,
int intParam = 0, string strParam = null, Object objectParam = null);
}
7)動作管理基類 – SSActionManager:實現了所有動作的基本管理
public class SSActionManager : MonoBehaviour, ISSActionCallback { //action管理器
private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>(); //將執行的動作的字典集合,int爲key,SSAction爲value
private List<SSAction> waitingAdd = new List<SSAction>(); //等待去執行的動作列表
private List<int> waitingDelete = new List<int>(); //等待刪除的動作的key
protected void Update(){
foreach (SSAction ac in waitingAdd){
actions[ac.GetInstanceID()] = ac; //獲取動作實例的ID作爲key
}
waitingAdd.Clear();
foreach (KeyValuePair<int, SSAction> kv in actions){
SSAction ac = kv.Value;
if (ac.destroy) waitingDelete.Add(ac.GetInstanceID());
else if (ac.enable) ac.Update();
}
foreach (int key in waitingDelete){
SSAction ac = actions[key];
actions.Remove(key);
Object.Destroy(ac);
}
waitingDelete.Clear();
}
public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager){
action.gameobject = gameobject;
action.transform = gameobject.transform;
action.callback = manager;
waitingAdd.Add(action);
action.Start();
}
public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, string strParam = null, Object objectParam = null){
//牧師與魔鬼的遊戲對象移動完成後就沒有下一個要做的動作了,所以回調函數爲空
}
}
8)裁判類的實現:對當前局面勝負關係的判斷
public class Judger : System.Object {
private static Judger _instance;
public static Judger getInstance() { //使用單例模式
if (_instance == null) _instance = new Judger ();
return _instance;
}
public int check(CoastCon fromCoast,CoastCon toCoast,BoatCon boat) { // 0->not finish, 1->lose, 2->win
int fromP = 0, fromD = 0, toP = 0, toD = 0;
int[] fromCount = fromCoast.getCharacterNum();
fromP += fromCount[0];
fromD += fromCount[1];
int[] toCount = toCoast.getCharacterNum ();
toP += toCount[0];
toD += toCount[1];
if (toP + toD == 6) return 2; //win
int[] boatCount = boat.getCharacterNum ();
if (boat.getStatus () == -1) {
toP += boatCount[0]; toD += boatCount[1]; // boat at toCoast
}
else {
fromP += boatCount[0]; fromD += boatCount[1]; // boat at fromCoast
}
if (fromP < fromD && fromP > 0) return 1; //lose
if (toP < toD && toP > 0) return 1; //lose
return 0; // not finish
}
}
9)視頻鏈接