Mode and devil (action separate version)
1. Basic operation drill
Download Fantasy Skybox FREE and build your own game scene
Write a simple summary of the use of game objects
The game objects we use are written in resources. Because the downloaded resources are free and have not been studied in depth, the use of game objects is basically in the stage of realizing some basic actions, such as moving, running, jumping, etc, You can load and control game objects by writing simple scripts.
2. Programming practice
Priest and devil action separate version
In addition, a referee class is designed to notify the scene controller that the game is over when the game reaches the end condition.
previously, we have implemented the MVC architecture to write the game of priest and devil, which realizes the decoupling between programs to a certain extent. However, the controller we implemented before manages too many matters of the game, such as handling user interaction events, loading game objects, realizing game rules, realizing game object movement, etc. too much content is integrated into the controller, making the controller too bloated. With the expansion of our project, the amount of code continues to increase, The controller becomes unmanageable.
from the perspective of object-oriented and responsibility, we can use special objects to manage sports, special objects to manage game rules, and so on. Therefore, this improvement is to separate action control and rule control into a separate controller. Write an action manager to manage actions. Write a referee class to judge whether the game is over. When the game is over, notify the scene controller that the game is over.
UML class diagram of action manager
#####Concrete implementation of action separation
The action manager consists of the following six components:
- CCActionMannager
- CCMoveToAction
- CCSequenceAction
- SSAction
- SSActionCallback
- SSActionManager
The responsibilities and relationships of these six components are explained below:
SSAction
SSAction is the base class of all actions and contains properties that are used by all actions.
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() { } // Start is called before the first frame update public virtual void Start() { throw new System.NotImplementedException(); } // Update is called once per frame public virtual void Update() { throw new System.NotImplementedException(); } }
CCMoveToAction
Realize simple movement, that is, the movement used in the last experiment, move an object to the target position and notify completion.
public class CCMoveToAction : SSAction { //destination public Vector3 target; //speed public float speed; private CCMoveToAction() { } //Production function (factory mode) public static CCMoveToAction GetSSAction(Vector3 target, float speed) { CCMoveToAction action = ScriptableObject.CreateInstance<CCMoveToAction>(); action.target = target; action.speed = speed; return action; } // Start is called before the first frame update public override void Start() { } // Update is called once per frame public override void Update() { //Judge whether it meets the moving conditions if (this.gameObject == null || this.transform.localPosition == target) { this.destroy = true; this.callback.SSActionEvent(this); return; } //move this.transform.localPosition = Vector3.MoveTowards(this.transform.localPosition, target, speed * Time.deltaTime); } }
SSActionCallback
public interface ISSActionCallback { //Callback function void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competed, int intParam = 0, string strParam = null, Object objectParam = null); }
SSactionCallback is a platform for action implementation to communicate with action manager.
CCSequenceAction
Realize an action and play the action in sequence.
This is a composite class, which implements the interface of callback function. After each action in the action list is completed, the composite action class will be notified, and the composite class of the upper layer of this class will also be notified after all actions are completed. The combined object and the combined object belong to the same type, and all subsequent actions are realized through composite actions.
public class CCSequenceAction : SSAction, ISSActionCallback { //Action sequence public List<SSAction> sequence; //Number of repetitions public int repeat = -1; //Action start pointer public int start = 0; //Production function (factory mode) public static CCSequenceAction GetSSAction(int repeat, int start, List<SSAction> sequence) { CCSequenceAction action = ScriptableObject.CreateInstance<CCSequenceAction>(); action.repeat = repeat; action.start = start; action.sequence = sequence; return action; } //Initializes the actions in the sequence public override void Start() { foreach (SSAction action in sequence) { action.gameObject = this.gameObject; action.transform = this.transform; action.callback = this; action.Start(); } } //Actions in the run sequence public override void Update() { if (sequence.Count == 0) return; if (start < sequence.Count) { sequence[start].Update(); } } //Callback processing, triggered when an action is completed public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competed, int Param = 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); } } } void OnDestroy() { } }
SSActionManager
It is responsible for managing all actions and action combinations, binding game objects to them and controlling the execution or deletion of actions after completion. It also implements the ISSActionCallback interface to receive information about whether actions are completed or not.
public class SSActionManager : MonoBehaviour { //Action set, in dictionary form private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>(); //Waiting to be added to the action queue (the action is about to start) private List<SSAction> waitingAdd = new List<SSAction>(); //Action queue waiting to be deleted (action completed) private List<int> waitingDelete = new List<int>(); protected void Update() { //Save the actions in waitingAdd foreach (SSAction ac in waitingAdd) actions[ac.GetInstanceID()] = ac; waitingAdd.Clear(); //Run saved events foreach (KeyValuePair<int, SSAction> kv in actions) { SSAction ac = kv.Value; if (ac.destroy) { waitingDelete.Add(ac.GetInstanceID()); }else if (ac.enable) { ac.Update(); } } //Destroy actions in waitingDelete foreach (int key in waitingDelete) { SSAction ac = actions[key]; actions.Remove(key); Destroy(ac); } waitingDelete.Clear(); } //Prepare to run an action, initialize the action and add it to waitingAdd public void RunAction(GameObject gameObject, SSAction action, ISSActionCallback manager) { action.gameObject = gameObject; action.transform = gameObject.transform; action.callback = manager; waitingAdd.Add(action); action.Start(); } // Start is called before the first frame update protected void Start() { } }
See project warehouse for specific code
Referee class implementation
In fact, the code of the referee class has been implemented in the last operation. Now we only need to separate the last implemented check function and write it into a class separately.
using UnityEngine; namespace CheckApplication { public class Check : MonoBehaviour { public FirstControllor sceneController; protected void Start() { sceneController = (FirstControllor)SSDirector.GetInstance().CurrentScenceController; sceneController.gameStatusManager = this; } public int CheckGame() { //0 - the game continues, 1 - fails, 2 - succeeds int startPriests = (sceneController.startLand.GetRoleNum())[0]; int startDevils = (sceneController.startLand.GetRoleNum())[1]; int endPriests = (sceneController.endLand.GetRoleNum())[0]; int endDevils = (sceneController.endLand.GetRoleNum())[1]; if (endPriests + endDevils == 6) return 2; int[] boatNum = sceneController.boat.GetRoleNumber(); //Plus the people on board if (sceneController.boat.GetBoatSign() == 1) { startPriests += boatNum[0]; startDevils += boatNum[1]; } else { endPriests += boatNum[0]; endDevils += boatNum[1]; } if ((endPriests > 0 && endPriests < endDevils) || (startPriests > 0 && startPriests < startDevils)) { return 1; } return 0; } } }