牧师与魔鬼(动作分离版)

游戏初版

牧师与魔鬼-MVC架构

在初版的《牧师与魔鬼》游戏中使用了MVC架构将模型、视图、控制相互分离,但是游戏对象的运动以及游戏输赢的判定集中到了场景控制器中,使稍显臃肿。

为了减少场景控制器的复杂程度,尝试将游戏对象的运动以及游戏输赢的判定分离出来。

动作分离

动作管理器设计

在这里插入图片描述

  • 动作类
    创建SSAction类作为动作基类,并派生出两个子类CCMoveToAction类和CCsequenceAction类。其中CCMoveToAction实现了基本的对象移动动作,而CCSequenceAction则是将基本动作组合成一个复杂动作。
  • 动作控制类
    创建SSActionManager类作为基类,并在Update()函数中调用添加进动作管理器类的动作的Update()函数,当动作完成后通过ISSActionCallback接口像动作管理器传递消息。CCActionManager类继承SSActionManager类,可以根据当前场景自定义接收到动作完成消息后的逻辑操作。

在该游戏例子中,在FirstController中声明一个CCActionManager动作管理器对象,需要移动对象时只需要声明一个CCMoveToAction对象或者CCSequenceAction对象并添加到动作管理器对象即可实现动作从场景控制器中分离。

代码实现

  • SSAction类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SSAction : ScriptableObject {

	public bool enable = true;
	public bool destory = false;

	public GameObject gameobject { get; set; }
	public Transform transform { get; set; }
	public ISSActionCallback callback { get; set; }

	protected SSAction () {}

	// Use this for initialization
	public virtual void Start () {
		throw new System.NotImplementedException ();
	}
	
	// Update is called once per frame
	public virtual void Update () {
		throw new System.NotImplementedException ();
	}
		
}
  • CCMoveToAction类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

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 * Time.deltaTime);
		if (this.transform.position == target) {
			//waiting for destroy
			this.destory = true;  
			this.callback.SSActionEvent (this);
		}
	}

	public override void Start () {
	}
}
  • CCSequenceAction类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

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;
	}
		
	// Update is called once per frame
	public override void Update ()
	{
		if (sequence.Count == 0) return;  
		if (start < sequence.Count) {
			sequence [start].Update ();
		}
	}

	public void SSActionEvent (SSAction source, SSActionEventType events = SSActionEventType.Competeted, int intParam = 0, string strParam = null, Object objectParam = null)
	{
		source.destory = false;
		this.start++;
		if (this.start >= sequence.Count) {
			this.start = 0;
			if (repeat > 0) repeat--;
			if (repeat == 0) { this.destory = true; this.callback.SSActionEvent (this); }
		}
	}

	// Use this for initialization
	public override void Start () {
		foreach (SSAction action in sequence) {
			action.gameobject = this.gameobject;
			action.transform = this.transform;
			action.callback = this;
			action.Start ();
		}
	}

	void OnDestory() {
		
	}
}
  • SSActionManager类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SSActionManager : MonoBehaviour {

	protected Dictionary <int, SSAction> actions = new Dictionary <int, SSAction> ();
	private List <SSAction> waitingAdd = new List<SSAction> (); 
	private List<int> waitingDelete = new List<int> ();

	// Update is called once per frame
	protected void Update () {
		foreach (SSAction ac in waitingAdd) actions [ac.GetInstanceID ()] = ac;
		waitingAdd.Clear ();

		foreach (KeyValuePair <int, SSAction> kv in actions) {
			SSAction ac = kv.Value;
			if (ac.destory) { 
				waitingDelete.Add(ac.GetInstanceID()); // release action
			} else if (ac.enable) { 
				ac.Update (); // update action
			}
		}

		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 ();
	}


	// Use this for initialization
	protected void Start () {
	}
}
  • CCActionManager类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CCActionManager : SSActionManager, ISSActionCallback {
	
	private FirstController sceneController;

	protected new void Start() {
		sceneController = (FirstController)SSDirector.getInstance ().currentSceneController;
		sceneController.actionManager = this;
	}

	// Update is called once per frame
	protected new void Update ()
	{
		base.Update ();
	}

    public bool is_all_destory(){
        foreach (KeyValuePair <int, SSAction> kv in this.actions) {
            SSAction ac = kv.Value;
            if(!ac.destory) return false;
        }
        return true;
    }
		
	#region ISSActionCallback implementation
	public void SSActionEvent (SSAction source, SSActionEventType events = SSActionEventType.Competeted, int intParam = 0, string strParam = null, Object objectParam = null)
	{
		if(is_all_destory()){
            sceneController.set_state();
            sceneController.set_moving(false);
            sceneController.check();
        }
	}
	#endregion
}
  • ISSActionCallback接口
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

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);
}
  • FirstController类改动
    声明一个动作管理器actionManager。
public CCActionManager actionManager { get; set; }

为船以及船上的对象分别声明一个CCMoveToAction对象,并添加进动作管理器。

public void move_boat(){
		if(boat.boat_num != 0){
			moving = true;
			if(boat.boat_state){
				CCMoveToAction move1 = CCMoveToAction.GetSSAction(boat_left_position, 3);
				actionManager.RunAction(boat.role, move1, actionManager);
				for(int i = 0; i < boat.boat_num; i++){
					Vector3 target_position = boat.object_on_boat[i].transform.position - new Vector3(6.0f, 0, 0);
					CCMoveToAction object_move = CCMoveToAction.GetSSAction(target_position, 3);
					actionManager.RunAction(boat.object_on_boat[i], object_move, actionManager);
				}
				boat.boat_object_position.x -= 6.0f;
			}
			else{
				CCMoveToAction move1 = CCMoveToAction.GetSSAction(boat_right_position, 3);
				actionManager.RunAction(boat.role, move1, actionManager);
				for(int i = 0; i < boat.boat_num; i++){
					Vector3 target_position = boat.object_on_boat[i].transform.position + new Vector3(6.0f, 0, 0);
					CCMoveToAction object_move = CCMoveToAction.GetSSAction(target_position, 3);
					actionManager.RunAction(boat.object_on_boat[i], object_move, actionManager);
				}
				boat.boat_object_position.x += 6.0f;
			}
		}
	}

裁判类

创建一个裁判类Judge,在判定游戏输赢前传入当前游戏状况,根据当前游戏状况判定输赢并将结果返回给场景控制器。

  • Judge类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Judge
{
    bool state;
    int left_priest;
    int right_priest;
    int left_devil;
    int right_devil;

    public Judge(){
        state = false;
        left_priest = 3;
        right_priest = 0;
        left_devil = 3;
        right_devil = 0;
    }

    public void set_Judge(bool flag, int lp, int rp, int ld, int rd) {
        state = flag;
        left_priest = lp;
        right_priest = rp;
        left_devil = ld;
        right_devil = rd;
    }

    public string judge_res() {
        int priest_num;
		int devil_num;
		if(state){
			priest_num = 3 - left_priest;
			devil_num = 3 - left_devil;
			if(priest_num == 3 && devil_num == 3) return "Win";
			else if((devil_num > priest_num && priest_num != 0) || (left_devil > left_priest && left_priest != 0)) return "Lose";
			else return "Continue";
		}
		else{
			priest_num = 3 - right_priest;
			devil_num = 3 - right_devil;
			if((devil_num > priest_num && priest_num != 0)|| (right_devil > right_priest && right_priest != 0)) return "Lose";
			else return "Continue";
		}
    }

}
  • FirstController类改动
    将原本的check()函数内容转移至裁判类Judge中。
    声明一个裁判对象judge,并在check()函数中通过裁判对象获取游戏结果。
Judge judge = new Judge();

public void check(){
		judge.set_Judge(boat.boat_state, priest_left.Count, priest_right.Count, devil_left.Count, devil_right.Count);
		result = judge.judge_res();
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是C#语言实现牧师与野人问题求解的代码和注释: ```csharp using System; using System.Collections.Generic; namespace MissionariesAndCannibals { class Program { static void Main(string[] args) { // 初始化状态 State initialState = new State(3, 3, true, 0, 0); // 初始化搜索树 SearchTree searchTree = new SearchTree(initialState); // 进行搜索 searchTree.BFS(); } } // 表示状态的类 class State { public int MissionariesLeft { get; set; } // 左岸传教士数量 public int CannibalsLeft { get; set; } // 左岸野人数量 public bool BoatPosition { get; set; } // 船的位置,true表示在左岸,false表示在右岸 public int MissionariesRight { get; set; } // 右岸传教士数量 public int CannibalsRight { get; set; } // 右岸野人数量 // 构造函数 public State(int missionariesLeft, int cannibalsLeft, bool boatPosition, int missionariesRight, int cannibalsRight) { MissionariesLeft = missionariesLeft; CannibalsLeft = cannibalsLeft; BoatPosition = boatPosition; MissionariesRight = missionariesRight; CannibalsRight = cannibalsRight; } // 判断状态是否合法 public bool IsValid() { if (MissionariesLeft < 0 || CannibalsLeft < 0 || MissionariesRight < 0 || CannibalsRight < 0) { return false; } if (MissionariesLeft > 3 || CannibalsLeft > 3 || MissionariesRight > 3 || CannibalsRight > 3) { return false; } if (CannibalsLeft > MissionariesLeft && MissionariesLeft > 0) { return false; } if (CannibalsRight > MissionariesRight && MissionariesRight > 0) { return false; } return true; } // 判断状态是否为目标状态 public bool IsGoal() { return MissionariesLeft == 0 && CannibalsLeft == 0; } // 判断两个状态是否相等 public override bool Equals(object obj) { State state = obj as State; if (state == null) { return false; } return MissionariesLeft == state.MissionariesLeft && CannibalsLeft == state.CannibalsLeft && BoatPosition == state.BoatPosition && MissionariesRight == state.MissionariesRight && CannibalsRight == state.CannibalsRight; } // 获取状态的哈希值 public override int GetHashCode() { return MissionariesLeft * 10000 + CannibalsLeft * 1000 + (BoatPosition ? 100 : 0) + MissionariesRight * 10 + CannibalsRight; } // 获取状态的字符串表示 public override string ToString() { return "MissionariesLeft: " + MissionariesLeft + ", CannibalsLeft: " + CannibalsLeft + ", BoatPosition: " + (BoatPosition ? "Left" : "Right") + ", MissionariesRight: " + MissionariesRight + ", CannibalsRight: " + CannibalsRight; } } // 表示搜索树的类 class SearchTree { private State _initialState; // 初始状态 private Queue<Node> _frontier; // 存放待扩展的节点的队列 private HashSet<State> _exploredSet; // 存放已扩展过的状态的集合 // 构造函数 public SearchTree(State initialState) { _initialState = initialState; _frontier = new Queue<Node>(); _frontier.Enqueue(new Node(_initialState, null)); _exploredSet = new HashSet<State>(); } // 广度优先搜索 public void BFS() { while (_frontier.Count > 0) { Node node = _frontier.Dequeue(); State state = node.State; if (state.IsGoal()) { // 找到了目标状态,输出路径 List<State> path = new List<State>(); while (node != null) { path.Insert(0, node.State); node = node.Parent; } foreach (State s in path) { Console.WriteLine(s); } return; } _exploredSet.Add(state); List<State> successors = GetSuccessors(state); foreach (State successor in successors) { if (!_exploredSet.Contains(successor)) { _frontier.Enqueue(new Node(successor, node)); } } } Console.WriteLine("No solution found."); } // 获取一个状态的所有合法后继状态 private List<State> GetSuccessors(State state) { List<State> successors = new List<State>(); if (state.BoatPosition) { // 船在左岸 for (int i = 0; i <= 2; i++) { for (int j = 0; j <= 2; j++) { if (i + j >= 1 && i + j <= 2) { State successor = new State(state.MissionariesLeft - i, state.CannibalsLeft - j, false, state.MissionariesRight + i, state.CannibalsRight + j); if (successor.IsValid()) { successors.Add(successor); } } } } } else { // 船在右岸 for (int i = 0; i <= 2; i++) { for (int j = 0; j <= 2; j++) { if (i + j >= 1 && i + j <= 2) { State successor = new State(state.MissionariesLeft + i, state.CannibalsLeft + j, true, state.MissionariesRight - i, state.CannibalsRight - j); if (successor.IsValid()) { successors.Add(successor); } } } } } return successors; } } // 表示搜索树中的节点的类 class Node { public State State { get; set; } // 节点对应的状态 public Node Parent { get; set; } // 父节点 // 构造函数 public Node(State state, Node parent) { State = state; Parent = parent; } } } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值