项目地址
程序设计框架:
为了用一组简单的动作组合成复杂的动作,我们采用 cocos2d 的方案,建立与 CCAtion 类似的类。
最终,程序员可以方便的定义动作并实现动作的自由组合,做到:
Actions.cs
public class SSAction : ScriptableObject //动作
{
public bool enable = true; //是否正在进行此动作
public bool destroy = false; //是否需要被销毁
public GameObject gameobject{get;set;} //动作对象
public Transform transform{get;set;} //动作对象的transform
public ISSActionCallback callback{get;set;} //回调函数
protected SSAction() { } //保证SSAction不会被new
public virtual void Start() //初始化
{
throw new System.NotImplementedException();
}
public virtual void Update() //每帧调用一次更新
{
throw new System.NotImplementedException();
}
}
public class CCMoveToAction : SSAction //移动
{
public Vector3 target; //移动到的目的地
public float speed; //移动的速度
public static CCMoveToAction GetSSAction(Vector3 target, float speed)
{
CCMoveToAction action = ScriptableObject.CreateInstance();//让unity自己创建一个CCMoveToAction实例,并自己回收
action.target = target;
action.speed = speed;
return action;
}
public override void Update()
{
this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);
if (this.transform.position == target)
{
this.destroy = true;
this.callback.SSActionEvent(this); //告诉动作管理或动作组合这个动作已完成
}
}
public override void Start()
{
//移动动作建立时候不做任何事情
}
}
public class CCSequenceAction : SSAction, ISSActionCallback
{
public List sequence; //动作的列表
public int repeat = -1; //-1就是无限循环做组合中的动作
public int start = 0; //当前做的动作的索引
public static CCSequenceAction GetSSAcition(int repeat, int start, List sequence)
{
CCSequenceAction action = ScriptableObject.CreateInstance();//让unity自己创建一个CCSequenceAction实例
action.repeat = repeat;
action.sequence = sequence;
action.start = start;
return action;
}
public override void Update()
{
if (sequence.Count == 0) return;
if (start < sequence.Count)
{
sequence[start].Update(); //一个组合中的一个动作执行完后会调用接口,所以这里看似没有start++实则是在回调接口函数中实现
}
}
public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
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); //告诉组合动作的管理对象组合做完了
}
}
}
public override void Start()
{
foreach (SSAction action in sequence)
{
action.gameobject = this.gameobject;
action.transform = this.transform;
action.callback = this; //组合动作的每个小的动作的回调是这个组合动作
action.Start();
}
}
void OnDestroy()
{
//如果组合动作做完第一个动作突然不要它继续做了,那么后面的具体的动作需要被释放
}
}
public enum SSActionEventType : int { Started, Competeted }
public interface ISSActionCallback
{
void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, string strParam = null, Object objectParam = null);
}
public class SSActionManager : MonoBehaviour //action管理器
{
private Dictionary actions = new Dictionary(); //将执行的动作的字典集合,int为key,SSAction为value
private List waitingAdd = new List(); //等待去执行的动作列表
private List waitingDelete = new List(); //等待删除的动作的key
//每帧调用更新
protected void Update()
{
foreach (SSAction ac in waitingAdd)
{
actions[ac.GetInstanceID()] = ac; //获取动作实例的ID作为key
}
waitingAdd.Clear();
foreach (KeyValuePair 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);
DestroyObject(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();
}
//初始化
protected void Start(){
}
}
public class CCActionManager : SSActionManager,ISSActionCallback //本游戏管理器
{
private CCMoveToAction moveBoatToEndOrStart; //移动船到结束岸,移动船到开始岸
private CCSequenceAction moveRoleToLandorBoat; //移动角色到陆地,移动角色到船上
public Controllor sceneController;
public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, string strParam = null, Object objectParam = null)
{
//牧师与魔鬼的游戏对象移动完成后就没有下一个要做的动作了,所以回调函数为空
}
protected new void Start()
{
sceneController = (Controllor)SSDirector.GetInstance().CurrentScenceController;
sceneController.actionManager = this;
}
public void moveBoat(GameObject boat, Vector3 target, float speed)
{
moveBoatToEndOrStart = CCMoveToAction.GetSSAction(target, speed);
this.RunAction(boat, moveBoatToEndOrStart, this);
}
public void moveRole(GameObject role, Vector3 middle_pos, Vector3 end_pos, float speed)
{
SSAction action1 = CCMoveToAction.GetSSAction(middle_pos, speed);
SSAction action2 = CCMoveToAction.GetSSAction(end_pos, speed);
moveRoleToLandorBoat = CCSequenceAction.GetSSAcition(1, 0, new List { action1, action2 });
this.RunAction(role, moveRoleToLandorBoat, this);
}
}
Controllor.cs
Umpire umpire; //裁判
public CCActionManager actionManager; //动作管理器
void Start ()
{
SSDirector director = SSDirector.GetInstance();
director.CurrentScenceController = this;
user_gui = gameObject.AddComponent() as UserGUI;
transform.position = transform.rotation * (new Vector3(0, 1, -14));//设置Main Camera位置
LoadResources();
umpire=new Umpire(from_land,to_land,boat);
actionManager = gameObject.AddComponent() as CCActionManager;
}
//在船上有人且游戏正在进行中的状态下移动船,并检查游戏是否结束
public void MoveBoat()
{
if (boat.IsEmpty() || user_gui.state != 0) return;
//通知动作管理器执行开船动作
actionManager.moveBoat(boat.getGameObject(),boat.BoatMoveToPosition(),boat.move_speed);
user_gui.state = Check();
}
//移动角色,并检查游戏是否结束
public void MoveRole(RoleModel role)
{
if (user_gui.state != 0) return;
if (role.IsOnBoat())//如果在船上就上岸
{
LandModel land;
if (boat.GetBoatState() == 0)
land = to_land;
else
land = from_land;
boat.DeleteRole(role.GetName());
//通知动作管理器执行角色上岸动作,先竖直移动,后水平移动
Vector3 end_pos = land.GetEmptyPosition();
Vector3 middle_pos = new Vector3(role.getGameObject().transform.position.x, end_pos.y, end_pos.z);
actionManager.moveRole(role.getGameObject(), middle_pos, end_pos, role.move_speed);
role.GoLand(land);
land.AddRole(role);
}
else//如果不在船上就上船
{
LandModel land = role.GetLandModel();
if (boat.GetEmptyIndex() == -1 || land.GetLandType() != boat.GetBoatState()) return; //船没有空位或此角色不在船停靠的那侧陆地,就不上船
land.DeleteRole(role.GetName());
//通知动作管理器执行角色上船动作,先水平移动,后竖直移动
Vector3 end_pos = boat.GetEmptyPosition();
Vector3 middle_pos = new Vector3(end_pos.x, role.getGameObject().transform.position.y, end_pos.z);
actionManager.moveRole(role.getGameObject(), middle_pos, end_pos, role.move_speed);
role.GoBoat(boat);
boat.AddRole(role);
}
user_gui.state = Check();
}
public int Check()
{
return umpire.Check();
}
Model.cs
删除了辅助类Move,对BoatModel类和RoleModel类做出修改
public float move_speed = 250;
public GameObject getGameObject() { return boat; }
public Vector3 BoatMoveToPosition()
{
if (boat_state == 0)
{
boat_state = 1;
return new Vector3(-5.5F, 0, 0);
}
else
{
boat_state = 0;
return new Vector3(5.5F, 0, 0);
}
}
public float move_speed = 250;
public GameObject getGameObject() { return role; }
Umpire.cs
public LandModel from_land; //左侧起点陆地
public LandModel to_land; //右侧终点陆地
public BoatModel boat; //船
public Umpire(LandModel FromLand,LandModel ToLand,BoatModel Boat)
{
from_land=FromLand;
to_land=ToLand;
boat=Boat;
}
public int Check()
{
int from_priests = (from_land.GetRoleType())[0];
int from_devils = (from_land.GetRoleType())[1];
int to_priests = (to_land.GetRoleType())[0];
int to_devils = (to_land.GetRoleType())[1];
//全部角色均到达终点则获胜
if (to_priests + to_devils == 6)
return 2;
//一侧魔鬼多于牧师则失败
int[] boat_role_type = boat.GetRoleType();
if (boat.GetBoatState() == 1) //船在起点岸边
{
from_priests += boat_role_type[0];
from_devils += boat_role_type[1];
}
else //船在终点岸边
{
to_priests += boat_role_type[0];
to_devils += boat_role_type[1];
}
if ((from_priests > 0 && from_priests < from_devils)||(to_priests > 0 && to_priests < to_devils)) //失败
{
return 1;
}
//游戏继续
return 0;
}
UserGUI.cs
未做修改,与上一版本牧师与魔鬼相同,不再赘述。
牧师和魔鬼是一款益智游戏,你将帮助牧师和魔鬼在限定时间内过河。河的一边有三个牧师和三个魔鬼。他们都想去这条河的对岸,但是只有一艘船,这艘船每次只能载两个人。必须有一个人把船从一边开到另一边。在flash游戏中,你可以点击它们移动它们,然后点击船将船移动到另一个方向。如果在河的两边,牧师的人数超过了魔鬼,他们就会被杀,游戏就结束了。你可以用很多方法来尝试。让所有牧师活着!祝你好运!
启动游戏,初始界面如下图:
点击左上角Rule按钮可以查看游戏规则
游戏进行中效果图:点击最左侧的牧师和最右侧的魔鬼,然后点击船
游戏失败界面,点击Restart按钮可以重新开始游戏
游戏获胜界面,点击Restart按钮可以重新开始游戏