本文是中山大学软件工程学院2020级3d游戏编程与设计的作业4
改进要求如下:
- 实现动作分离,用动作管理器控制移动游戏中的移动操作
- 实现裁判类,判断游戏的结束条件,游戏结束时通知场景控制器结束游戏
本次作业是基于上次作业的牧师与魔鬼进行进一步的改进,改进前的代码详见上一次博客。
在上一次作业之中,场记(控制器)管理的事情过多,需要处理用户交互事件,资源的加载,规则处理等,显得过于臃肿,同时在游戏对象等代码之中也存在类似的动作处理事件。为了解决这种情况,可以将动作从中剥离出来,作为一个独立的动作控制器。也就是动作分离。
动作分离是游戏设计的一大重要内容。动作控制器来管理控制所有的游戏对象移动,通过场景控制器将需要移动的游戏对象和位置等信息传递给动作控制器,动作控制器负责实现具体的移动。当动作很多或是需要做同样动作的游戏对象很多的时候,使用动作管理器可以让动作很容易管理,也提高了代码复用性。
实现裁判类
此步较为简单,只需要将之前代码中判断游戏状态的函数chenck()
独立出来即可。
动作分离ActionControllor
动作基类。代表了一个动作,记录了动作的相关信息。同时,动作基类只允许交互动作产生,不允许在代码中凭空产生。
public class SSAction : ScriptableObject
{
public bool lived = true;
public bool deleted = false;
public GameObject gameobject;
public Transform transform;
public ISSActionCallback callback;
// 不允许代码中生成该类
protected SSAction() { }
public virtual void Start()
{
throw new System.NotImplementedException();
}
public virtual void Update()
{
throw new System.NotImplementedException();
}
}
继承动作基类,用于实现物体的移动,即按照speed速度移动到target。
public class SSMoveToAction : SSAction
{
public Vector3 target;
public float speed;
private SSMoveToAction() { }
public static SSMoveToAction GetSSAction(Vector3 _target, float _speed)
{
SSMoveToAction action = ScriptableObject.CreateInstance();
action.target = _target;
action.speed = _speed;
return action;
}
public override void Start()
{
}
public override void Update()
{
this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);
if (this.transform.position == target)
{
this.deleted = true;
this.callback.SSActionEvent(this);
}
}
}
实现动作组合类,CCSequenceAction继承了ISSActionCallback和SSAction。组合动作是按照动作列表进行的,动作列表中每一个动作完成之后会通知组合动作类,并且所有动作完成后也会通知该这个类的上一层组合类。
public class CCSequenceAction : SSAction, ISSActionCallback
{
public List sequence;
public int times = -1;
public int now = 0;
public static CCSequenceAction GetSSAcition(int times, int now, List sequence)
{
CCSequenceAction action = ScriptableObject.CreateInstance();
action.sequence = sequence;
action.times = times;
action.now = now;
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 (now < sequence.Count)
{
sequence[now].Update();
}
}
public void SSActionEvent(
SSAction source, SSActionEventType events = SSActionEventType.competeted,
int intParam = 0, string strParam = null, Object objectParam = null
)
{
source.deleted = false;
this.now++;
if (this.now >= sequence.Count)
{
this.now = 0;
if (times > 0) times--;
if (times == 0)
{
this.deleted = true;
this.callback.SSActionEvent(this);
}
}
}
}
动作管理类。负责管理所有的动作以及动作组合,即CCSequenceAction和SSAction.
public class SSActionManager : MonoBehaviour, ISSActionCallback
{
private Dictionary actions = new Dictionary();
private List waitingAdd = new List();
private List waitingDelete = new List();
protected void Update()
{
foreach (SSAction ac in waitingAdd)
{
actions[ac.GetInstanceID()] = ac;
}
waitingAdd.Clear();
foreach (KeyValuePair kv in actions)
{
SSAction ac = kv.Value;
if (ac.deleted)
{
waitingDelete.Add(ac.GetInstanceID());
}
else if (ac.lived)
{
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)
{
}
}
本游戏的动作管理类。实现游戏中的两种动作,包括:
移动船:一个动作,从当前位置按照speed速度移动到target
移动该角色:两个动作,从当前位置按照speed速度移动到target1,再按照speed速度移动到target2(此种操作会使得移动角色的动画更加平滑)
public class CCActionManager : SSActionManager
{
public FirstController sceneController;
private CCSequenceAction boatMove;
private CCSequenceAction roleMove;
public void moveBoat(GameObject boat, Vector3 target, float speed)
{
SSAction action1 = SSMoveToAction.GetSSAction(target, speed);
boatMove = CCSequenceAction.GetSSAcition(0, 0, new List { action1 });
this.RunAction(boat, boatMove, this);
}
public void moveRole(GameObject role, Vector3 taeget1, Vector3 target2, float speed)
{
SSAction action1 = SSMoveToAction.GetSSAction(taeget1, speed);
SSAction action2 = SSMoveToAction.GetSSAction(target2, speed);
roleMove = CCSequenceAction.GetSSAcition(1, 0, new List { action1, action2 });
this.RunAction(role, roleMove, this);
}
}
动作事件接口。定义了动作的状态:开始或结束。同时定义了动作事件的处理接口,与运动有关的类都需要实现这个接口,来实现接收其子动作的消息以实现动作的调度管理。
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
);
}
裁判类。用于判断游戏的进行状态。
using UnityEngine;
namespace JudgeApplication
{
public class Judge : MonoBehaviour
{
public FirstController sceneController;
protected void Start()
{
sceneController = (FirstController)SSDirector.GetInstance().CurrentSceneController;
sceneController.gameStatusManager = this;
}
public int JudgeGame()
{
int[] boatRole = sceneController.boat.getRoleNum();
int[] startRole = sceneController.startLand.getRoleNum();
int[] endRole = sceneController.endLand.getRoleNum();
if (endRole[0] + endRole[1] == 6) return 1;
if (sceneController.boat.getBoatSign() == 1)
{
startRole[0] += boatRole[0];
startRole[1] += boatRole[1];
}
else
{
endRole[0] += boatRole[0];
endRole[1] += boatRole[1];
}
if ((endRole[0] > 0 && endRole[1] > endRole[0]) || (startRole[0] > 0 && startRole[1] > startRole[0]))
return -1;
return 0;
}
}
}
演示与上一次的演示无异,如下:
代码以及文档均已经上传至hw4 · XiaoChen04_3/3D_Computer_Game_Programming - gitee