1)下载 Fantasy Skybox FREE:在Unity的Asset Store中找到Fantasy Skybox FREE的材料包,然后下载并且导入自己的项目中。
导入成功后多了很多材料:
2) 构建自己的游戏场景:
在Camera中添加Component,然后添加Skybox,再将相应的Skybox图案添加上去,就能够完成天空盒的创建了,得到自己喜欢的背景了。
然后创建地形,需要添加Terrain,然后可以在Terrain中创造一些自己的场景,具体方法查看下图:
游戏对象主要包括:空对象,摄像机,光线,3D物体,声音,UI基于事件的new UI系统和粒子系统与特效,预制的材料
1)本次作业需要在上次作业的基础上,将游戏场景的动作分离出来:
即依然是采用MVC结构实现,与上一次无动作分离版的区别在于,之前对于动作的管理是实现了一个动作类,当鼠标点击船或是人物时,相当于是控制器让船或人物的实例调用动作类来实现运动。这次实践中将船或人物与动作分离开来,单独实现了一个动作管理器,鼠标点击船或是人物时,相当于是控制器发送请求给动作管理器,动作管理器来实现船或人物的运动。
2)主要思路:按照下图将上次的代码进行分解:
3)接下来是动作基类的实现(课程中有):
设计要点:
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)顺序动作组合类实现:
代码重点:
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)视频链接