我们知道,一个人物其实会有很多动作,虽然我之前做的牧师过河可以把动作和对象放在一起而且思路还能够保持清晰,但那也仅仅是因为这个游戏只有基础的移动动作。试想,一个大型动作游戏,游戏人物有奔跑,跳跃,开枪,开车等等复杂的动作,全部加到对象文件内部,一是代码容量太大,可读性差,理解性差,还有一个问题就是,如果我们以后想对这个游戏人物在进行动作更新呢?又要写进去一大堆代码?所以,仔细想想,将对象与动作分离有很棒的用处,这种开发思维有助于我们更好地构建游戏,同时,游戏人物的扩展性也得到了极大地增强。这次,我就针对牧师过河进行了改进。
我的理解还很浅,在这里记录下我的进度,以便接下来的学习
基础类
Base.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Com.Mygame;
namespace Com.Mygame {
public class Director : System.Object {
private static Director _instance;
public SceneController currentSceneController { get; set; }
public static Director getInstance() {
if (_instance == null) {
_instance = new Director ();
}
return _instance;
}
}
public interface SceneController {
void loadResources ();
}
public interface UserAction {
void moveBoat();
void characterIsClicked(MyCharacterController characterCtrl);
void restart();
}
/*-----------------------------------MyCharacterController------------------------------------------*/
public class MyCharacterController {
readonly GameObject character;
//readonly Moveable moveableScript;
readonly ClickGUI clickGUI;
readonly int characterType; // 0->牧师, 1->魔鬼
public readonly float movingSpeed = 20;
// change frequently
bool IsInBoat;
CoastController coastController;
public MyCharacterController(string which_character) {
if (which_character == "牧师") {
character = Object.Instantiate (Resources.Load ("Perfabs/牧师", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
characterType = 0;
} else {
character = Object.Instantiate (Resources.Load ("Perfabs/魔鬼", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
characterType = 1;
}
//moveableScript = character.AddComponent (typeof(Moveable)) as Moveable;
clickGUI = character.AddComponent (typeof(ClickGUI)) as ClickGUI;
clickGUI.setController (this);
}
public void setName(string name) {
character.name = name;
}
public void setPosition(Vector3 pos) {
character.transform.position = pos;
}
/*
public void moveToPosition(Vector3 destination) {
moveableScript.setDestination(destination);
}
*/
public int getType() { // 0->牧师, 1->魔鬼
return characterType;
}
public string getName() {
return character.name;
}
public void getOnBoat(BoatController boatCtrl) {
coastController = null;
character.transform.parent = boatCtrl.getGameobj().transform;
IsInBoat = true;
}
public void getOnCoast(CoastController coastCtrl) {
coastController = coastCtrl;
character.transform.parent = null;
IsInBoat = false;
}
public bool isOnBoat() {
return IsInBoat;
}
public CoastController getCoastController() {
return coastController;
}
public void Reset() {
// moveableScript.Reset ();
coastController = (Director.getInstance ().currentSceneController as FirstController).fromCoast;
getOnCoast (coastController);
setPosition (coastController.getEmptyPosition ());
coastController.getOnCoast (this);
}
public Vector3 getPos() {
return this.character.transform.position;
}
public GameObject getGameobj() {
return this.character;
}
}
/*-----------------------------------CoastController------------------------------------------*/
public class CoastController {
readonly GameObject coast;
readonly Vector3 from_pos = new Vector3(13.2f, 3.47f, 0.0f);
readonly Vector3 to_pos = new Vector3(-18.1f, 3.47f, 0.0f);
readonly Vector3[] positions;
readonly int to_or_from; // to->-1, from->1
// change frequently
MyCharacterController[] passengerPlaner;
public CoastController(string _to_or_from) {
positions = new Vector3[] {new Vector3(4, 6.0f, 0), new Vector3(6, 6.0f, 0), new Vector3(8, 6.0f, 0),
new Vector3(10, 6.0f, 0), new Vector3(12, 6.0f, 0), new Vector3(14, 6.0f, 0)};
passengerPlaner = new MyCharacterController[6];
if (_to_or_from == "from") {
coast = Object.Instantiate (Resources.Load ("Perfabs/岸", typeof(GameObject)), from_pos, Quaternion.identity, null) as GameObject;
coast.name = "from";
to_or_from = 1;
} else {
coast = Object.Instantiate (Resources.Load ("Perfabs/岸", typeof(GameObject)), to_pos, Quaternion.identity, null) as GameObject;
coast.name = "to";
to_or_from = -1;
}
}
public int getEmptyIndex() {
for (int i = 0; i < passengerPlaner.Length; i++) {
if (passengerPlaner [i] == null) {
return i;
}
}
return -1;
}
public Vector3 getEmptyPosition() {
Vector3 pos = positions [getEmptyIndex ()];
pos.x *= to_or_from;
return pos;
}
public void getOnCoast(MyCharacterController characterCtrl) {
int index = getEmptyIndex ();
passengerPlaner [index] = characterCtrl;
}
public MyCharacterController getOffCoast(string passenger_name) { // 0->牧师, 1->魔鬼
for (int i = 0; i < passengerPlaner.Length; i++) {
if (passengerPlaner [i] != null && passengerPlaner [i].getName () == passenger_name) {
MyCharacterController charactorCtrl = passengerPlaner [i];
passengerPlaner [i] = null;
return charactorCtrl;
}
}
Debug.Log ("cant find passenger on coast: " + passenger_name);
return null;
}
public int get_to_or_from() {
return to_or_from;
}
public int[] getCharacterNum() {
int[] count = {0, 0};
for (int i = 0; i < passengerPlaner.Length; i++) {
if (passengerPlaner [i] == null)
continue;
if (passengerPlaner [i].getType () == 0) { // 0->牧师, 1->魔鬼
count[0]++;
} else {
count[1]++;
}
}
return count;
}
public void Reset() {
passengerPlaner = new MyCharacterController[6];
}
}
/*-----------------------------------BoatController------------------------------------------*/
public class BoatController {
readonly GameObject boat;
//readonly Moveable moveableScript;
readonly Vector3 fromPosition = new Vector3(3, 5, 0);
readonly Vector3 toPosition = new Vector3(-8.0f, 5.0f, 0.0f);
readonly Vector3[] from_positions;
readonly Vector3[] to_positions;
public readonly float movingSpeed = 20;
GameObject cameraObj;
GameObject lightObj;
// change frequently
int to_or_from; // to->-1; from->1
MyCharacterController[] passenger = new MyCharacterController[2];
public BoatController() {
to_or_from = 1;
from_positions = new Vector3[] { new Vector3(2, 6.0f, 0.0f), new Vector3(6, 6.0f, 0.0f)};
to_positions = new Vector3[] { new Vector3(-9, 6.0f, 0.0f), new Vector3(-7, 6.0f, 0.0f) };
boat = Object.Instantiate (Resources.Load ("Perfabs/船", typeof(GameObject)), fromPosition, Quaternion.identity, null) as GameObject;
boat.name = "boat";
//moveableScript = boat.AddComponent (typeof(Moveable)) as Moveable;
boat.AddComponent (typeof(ClickGUI));
attachCamera(); // add a camera moving with this boat
attachLight();
}
private void attachCamera() {
cameraObj = new GameObject("Camera_follow"); // 新建一个空对象叫做Camera_follow
cameraObj.transform.parent = boat.transform; // 先让这个对象成为boat的子对象
// 调整一下Camera_follow与boat的相对方位
cameraObj.transform.localPosition = new Vector3(0, 7, -8);
cameraObj.transform.localRotation = Quaternion.Euler(10, 0, 0);
cameraObj.AddComponent<Camera>(); // 添加Camera组件,让这个空对象成为一个摄像机
Camera cameraComp = cameraObj.GetComponent<Camera>(); // 获取摄像机组件
cameraComp.fieldOfView = 40;
// 将Resources/skybox添加为这个摄像机的天空盒
cameraObj.AddComponent<Skybox>().material = Resources.Load("skybox") as Material;
}
public Vector3 getDestination() {
if (to_or_from == -1) {
return fromPosition;
} else {
return toPosition;
}
}
public void move() {
if (to_or_from == -1) {
to_or_from = 1;
} else {
to_or_from = -1;
}
}
public int getEmptyIndex() {
for (int i = 0; i < passenger.Length; i++) {
if (passenger [i] == null) {
return i;
}
}
return -1;
}
public bool isEmpty() {
for (int i = 0; i < passenger.Length; i++) {
if (passenger [i] != null) {
return false;
}
}
return true;
}
public Vector3 getEmptyPosition() {
Vector3 pos;
int emptyIndex = getEmptyIndex ();
if (to_or_from == -1) {
pos = to_positions[emptyIndex];
} else {
pos = from_positions[emptyIndex];
}
return pos;
}
public void GetOnBoat(MyCharacterController characterCtrl) {
int index = getEmptyIndex ();
passenger [index] = characterCtrl;
}
public MyCharacterController GetOffBoat(string passenger_name) {
for (int i = 0; i < passenger.Length; i++) {
if (passenger [i] != null && passenger [i].getName () == passenger_name) {
MyCharacterController charactorCtrl = passenger [i];
passenger [i] = null;
return charactorCtrl;
}
}
Debug.Log ("Cant find passenger in boat: " + passenger_name);
return null;
}
public GameObject getGameobj() {
return boat;
}
public int get_to_or_from() { // to->-1; from->1
return to_or_from;
}
public int[] getCharacterNum() {
int[] count = {0, 0};
for (int i = 0; i < passenger.Length; i++) {
if (passenger [i] == null)
continue;
if (passenger [i].getType () == 0) { // 0->牧师, 1->魔鬼
count[0]++;
} else {
count[1]++;
}
}
return count;
}
public void Reset() {
//moveableScript.Reset ();
if (to_or_from == -1) {
move ();
}
boat.transform.position = fromPosition;
passenger = new MyCharacterController[2];
}
}
}
CGUI.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Com.Mygame;
public class CGUI : MonoBehaviour {
UserAction action;
MyCharacterController characterController;
public void setController(MyCharacterController characterCtrl) {
characterController = characterCtrl;
}
void Start() {
action = Director.getInstance ().currentSceneController as UserAction;
}
void OnMouseDown() {
if (gameObject.name == "boat") {
action.moveBoat ();
} else {
action.characterIsClicked (characterController);
}
}
}
UGUI.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Com.Mygame;
public class UGUI : MonoBehaviour {
private UserAction action;
public int status = 0;
GUIStyle style;
GUIStyle buttonStyle;
void Start() {
action = Director.getInstance ().currentSceneController as UserAction;
style = new GUIStyle();
style.fontSize = 40;
style.alignment = TextAnchor.MiddleCenter;
buttonStyle = new GUIStyle("button");
buttonStyle.fontSize = 30;
}
void OnGUI() {
if (status == 1) {
GUI.Label(new Rect(450, 50, 200, 200), "You loose!");
if (GUI.Button(new Rect(450, 400, 100, 50), "Reset"))
status = 0;
action.restart ();
}
} else if(status == 2) {
GUI.Label(new Rect(450, 50, 200, 200), "You win!");
if (GUI.Button(new Rect(450, 400, 100, 50), "Reset")) {
status = 0;
action.restart ();
}
}
}
}
FirstController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FirstController : MonoBehaviour, SceneController, UserAction {
UserGUI userGUI;
public CoastController From;
public CoastController To;
public BoatController boat;
private MyCharacterController[] 角色;
private FirstManager actionManager;
void Awake() {
Director director = Director.getInstance ();
director.currentSceneController = this;
userGUI = gameObject.AddComponent <UserGUI>() as UserGUI;
角色 = new MyCharacterController[6];
loadResources ();
}
void Start() {
actionManager = GetComponent<FirstSceneActionManager>();
}
public void loadResources() {
//GameObject water = Instantiate (Resources.Load ("Perfabs/Water", typeof(GameObject)), water_pos, Quaternion.identity, null) as GameObject;
//water.name = "water";
From = new CoastController ("from");
toCoast = new CoastController ("to");
boat = new BoatController ();
loadCharacter ();
}
private void loadCharacter() {
for (int i = 0; i < 3; i++) {
MyCharacterController cha = new MyCharacterController ("牧师");
cha.setName("牧师" + i);
cha.setPosition (From.getEmptyPosition ());
cha.getOnCoast (From);
From.getOnCoast (cha);
角色 [i] = cha;
}
for (int i = 0; i < 3; i++) {
MyCharacterController cha = new MyCharacterController ("魔鬼");
cha.setName("魔鬼" + i);
cha.setPosition (From.getEmptyPosition ());
cha.getOnCoast (From);
From.getOnCoast (cha);
角色 [i+3] = cha;
}
}
public void moveBoat() {
if (boat.isEmpty ())
return;
/* old way to move boat
boat.Move ();
*/
actionManager.moveBoat(boat);
boat.move();
userGUI.status = check_game_over ();
}
public void characterIsClicked(MyCharacterController characterCtrl) {
if (characterCtrl.isOnBoat ()) {
CoastController whichCoast;
if (boat.get_to_or_from () == -1) { // to->-1; from->1
whichCoast = toCoast;
} else {
whichCoast = From;
}
boat.GetOffBoat (characterCtrl.getName());
//characterCtrl.moveToPosition (whichCoast.getEmptyPosition ());
actionManager.moveCharacter(characterCtrl, whichCoast.getEmptyPosition ());
characterCtrl.getOnCoast (whichCoast);
whichCoast.getOnCoast (characterCtrl);
} else { // character on coast
CoastController whichCoast = characterCtrl.getCoastController ();
if (boat.getEmptyIndex () == -1) { // boat is full
return;
}
if (whichCoast.get_to_or_from () != boat.get_to_or_from ()) // boat is not on the side of character
return;
whichCoast.getOffCoast(characterCtrl.getName());
//characterCtrl.moveToPosition (boat.getEmptyPosition());
actionManager.moveCharacter(characterCtrl, boat.getEmptyPosition());
characterCtrl.getOnBoat (boat);
boat.GetOnBoat (characterCtrl);
}
userGUI.status = check_game_over ();
}
int check_game_over() { // 0->not finish, 1->lose, 2->win
int from_priest = 0;
int from_devil = 0;
int to_priest = 0;
int to_devil = 0;
int[] fromCount = From.getCharacterNum ();
from_priest += fromCount[0];
from_devil += fromCount[1];
int[] toCount = toCoast.getCharacterNum ();
to_priest += toCount[0];
to_devil += toCount[1];
if (to_priest + to_devil == 6) // win
return 2;
int[] boatCount = boat.getCharacterNum ();
if (boat.get_to_or_from () == -1) { // boat at To
to_priest += boatCount[0];
to_devil += boatCount[1];
} else { // boat at From
from_priest += boatCount[0];
from_devil += boatCount[1];
}
if (from_priest < from_devil && from_priest > 0) { // lose
return 1;
}
if (to_priest < to_devil && to_priest > 0) {
return 1;
}
return 0; // not finish
}
public void restart() {
boat.reset ();
From.reset ();
To.reset ();
for (int i = 0; i < 角色.Length; i++) {
角色 [i].reset ();
}
}
}
Action
SSAction.cs
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 () {
Debug.Log("SSAction");
throw new System.NotImplementedException();
}
}
CCMoveToAction.cs
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;
}
// Use this for initialization
public override void Start () {
}
// Update is called once per frame
public override void Update () {
Debug.Log("CCMoveToAction");
this.transform.position = Vector3.MoveTowards(this.transform.position, target, 1 * Time.deltaTime);
if (this.transform.position == target)
{
this.destory = true;
this.callback.SSActionEvent(this);
}
}
}
ISSActionCallback.cs
这是一个接口,用于传递信息,通讯作用
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);
}
CCSequenceAction.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCSequenceAction : SSAction, ISSActionCallback {
public List<SSAction> sequence;
public int repeat = -1;
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;
}
// 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();
}
}
// Update is called once per frame
public override void Update () {
Debug.Log("CCSequenceAction");
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);
}
}
}
private void OnDestroy()
{
//destory
}
}
SSActionManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSActionManager : MonoBehaviour {
public Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
private List<SSAction> waitingAdd = new List<SSAction>();
private List<int> waitingDelete = new List<int>();
// Use this for initialization
protected void Start () {
}
// Update is called once per frame
protected void Update () {
Debug.Log("SSActionManager");
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());
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();
}
}
CCActionManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCActionManager : SSActionManager, ISSActionCallback {
public void moveBoat(BoatController boat) {
MoveToAction action = MoveToAction.getAction(boat.getDestination(), boat.movingSpeed);
this.addAction(boat.getGameobj(), action, this);
}
public void moveCharacter(MyCharacterController characterCtrl, Vector3 destination) {
Vector3 currentPos = characterCtrl.getPos();
SSAction action1 = CCMoveToAction.GetSSAction(middlePos, characterCtrl.movingSpeed);
SSAction seqAction = CCSequenceAction.GetSSAction(1, 0, new List<ObjAction>{action});
this.addAction(characterCtrl.getGameobj(), seqAction, this);
}
}
}