先挂一个github地址
https://github.com/ddghost/unity3d/tree/master/unity%20hw2
本次游戏的设计采用MVC模式,游戏中的所有GameObject就是Model,View包括toSolveClick和GuiCtrl,Controller包括FirstController以及FirstController里的各个controller(boat,environment,people)。
首先先把预设都做出来,黑色方块代表恶魔,白色方块代表牧师(黑色是邪恶,白色是正义,很不错的设计)。
导演的代码主要参考老师的,当然别的代码也借鉴了不少师兄博客上的代码。但我个人感觉UesrAction应该是每个场景通用的动作,如暂停,开始,重置等,虽然本次只有一个场景,但个人感觉逻辑上要有一个类(firstScenceUserAction)代表第一个场记继承的动作,因为如果要做几个场景的话,其他场记与第一个场记应该有相同的,也有不同的,因此继承的类应该另外再写,下一关继承的就不是firstScenceUserAction。
namespace baseCode{
public class Director : System.Object {
private static Director _instance;
public sceneController currentSceneController { get; set; }
public sceneController CurrentScenceController{ get; set; }
public static Director getInstance() {
if (_instance == null) {
_instance = new Director ();
}
return _instance;
}
}
public interface sceneController{
void loadResources();
}
}
public interface UserAction{
void reset();
}
public interface firstScenceUserAction: UserAction{
void boatMove ();
void getBoatOrGetShore (string name);
void reset();
string getStatus();
}
本次作业中遇到一个比较棘手的问题就是如何在玩家点击了游戏对象(船,人)后作出反应,解决的办法是给船和人都挂上一个脚本,当鼠标点击后,通过导演找到场记,达到通知场记的目的。
public class firstScenceSolveClick : MonoBehaviour {
firstScenceUserAction action ;
string characterName ;
// Use this for initialization
void Start () {
action = Director.getInstance().currentSceneController as firstScenceUserAction;
}
public void setName(string name){
characterName = name;
}
void Update(){
}
// Update is called once per frame
void OnMouseDown(){
if (action.getStatus() != "playing") {
return;
}
else{
if (characterName == "boat") {
action.boatMove ();
}
else {
action.getBoatOrGetShore (name);
}
}
}
}
其次就是场记判断人或者船可以动了,但是如何让他们动起来的问题,我的解决办法是给他们都挂上一个运动脚本,也即他们一直运动,运动到目的地就不会运动了,那么场记的任务就只要给这个运动脚本设定目标即可。下面是该运动脚本的代码,只写了船的运动,没有写人的运动。(因为感觉人的运动有点奇怪,按照我的场景布置,人要下船不能直接在岸上走到船上,因为那样就和前面的人撞在一起了,感觉很奇怪,当然也可以让他跳起来,跳到船上,但是就不要在意这些细节了,会写船的运动就好了,因此点击人是直接将人放到要放的地方。)
public class boatMoveBeahave: MonoBehaviour{
Vector3 aim = new Vector3 (4f, 0.7f, 0f);
float speed = 20.0f;
string status = "waiting" ;
void Update(){
if (this.transform.position == aim) {
status = "waiting";
}
else {
this.transform.position = Vector3.MoveTowards (this.transform.position, aim , speed * Time.deltaTime);
}
}
public void setAim(Vector3 aim){
this.aim = aim;
status = "running";
}
public string getState(){
return status;
}
}
在写代码的时候还写了几个控制器类,毕竟一下子做出场记是很难的,分几个部分使得代码更有层次。也更有逻辑,例如人要上船,那么人的控制器就要去找船的控制器提供位置,人要上岸,然的控制器就找环境的控制器要位置,这样就可以减少场记中的代码,场记只要告诉人的控制器要上船,要上岸就可以了。
下面是船的控制器。
public class boatController{
GameObject boat;
readonly boatMoveBeahave updateBoatMove;
readonly firstScenceSolveClick toSolveClick;
Vector3 leftPos = new Vector3 (-4f, 0.7f, 0f);
Vector3 rightPos = new Vector3 (4f, 0.7f, 0f);
string []nameOfPeopleOnBoat = {"" , ""};
Vector3 []boatPos = { new Vector3( -0.25f , 1.5f , 0f ) , new Vector3( 0.25f , 1.5f , 0f ) };
public string size ;
private string defaultSize ;
public boatController(string size){
boat = Object.Instantiate (Resources.Load ("prefabs/boat", typeof(GameObject))
, rightPos , Quaternion.identity, null) as GameObject ;
boat.name = "boat";
toSolveClick = boat.AddComponent (typeof(firstScenceSolveClick)) as firstScenceSolveClick;
toSolveClick.setName (boat.name);
updateBoatMove = boat.AddComponent (typeof(boatMoveBeahave)) as boatMoveBeahave;
defaultSize = size;
this.size = defaultSize;
}
public bool ifEmpty(){
return nameOfPeopleOnBoat[0] == "" && nameOfPeopleOnBoat[1] == "";
}
public bool ifHaveSeat(){
return nameOfPeopleOnBoat[0] == "" || nameOfPeopleOnBoat[1] == "";
}
public void move(){
if (size == "right") {
updateBoatMove.setAim (leftPos);
size = "left";
}
else {
updateBoatMove.setAim (rightPos);
size = "right";
}
}
public string getRunningState(){
return updateBoatMove.getState ();
}
public string[] getPassengerName(){
return nameOfPeopleOnBoat;
}
public GameObject getBoat(){
return boat;
}
public void outBoat(string name){
if (nameOfPeopleOnBoat [0] == name) {
nameOfPeopleOnBoat [0] = "";
}
else if (nameOfPeopleOnBoat [1] == name) {
nameOfPeopleOnBoat [1] = "";
}
}
public Vector3 getBoatPos(string name ){
Vector3 result = Vector3.zero;
for (int loop = 0; loop < 2; loop++) {
if (nameOfPeopleOnBoat [loop].Length == 0) {
nameOfPeopleOnBoat [loop] = name;
result = boatPos [loop];
break;
}
}
return result;
}
public void reset(){
nameOfPeopleOnBoat [0] = nameOfPeopleOnBoat [1] = "";
size = defaultSize;
updateBoatMove.setAim (rightPos);
}
}
还有人的控制器
public class peopleController{
GameObject people;
private string status;
public string size ;
private string defaultSize ;
firstScenceSolveClick solveClick;
int number ;
public peopleController(string name , int number , Vector3 pos , string status , string size){
people = Object.Instantiate (Resources.Load ("prefabs/" + name, typeof(GameObject))
, pos, Quaternion.identity, null) as GameObject;
people.name = name + number.ToString() ;
solveClick = people.AddComponent (typeof(firstScenceSolveClick)) as firstScenceSolveClick;
solveClick.setName (people.name);
this.number = number;
this.status = status;
defaultSize = size;
this.size = size;
}
public string getName(){
return people.name;
}
public string getStatus(){
return status;
}
public void getOnBoat(boatController boatCtrl){
status = "boat";
people.transform.parent = boatCtrl.getBoat().transform ;
people.transform.localPosition = boatCtrl.getBoatPos( getName() ) ;
}
public void getOffBoat(environmentController envCtrl){
status = "shore";
people.transform.parent = null;
people.transform.position = envCtrl.getPosVec(size , number );
}
public void reset( environmentController envCtrl ){
status = "shore";
size = defaultSize;
people.transform.parent = null;
people.transform.position = envCtrl.getPosVec(size , number );
}
以及环境的控制器
public class environmentController{
GameObject environment;
Vector3 environmentPos = Vector3.zero ;
Vector3 leftShorePos = new Vector3 (-6f, 2f, 0f);
Vector3 rightShorePos = new Vector3 (6f, 2f, 0f);
public environmentController(){
environment = Object.Instantiate (Resources.Load ("prefabs/environment", typeof(GameObject))
, environmentPos, Quaternion.identity, null) as GameObject;
}
public Vector3 getPosVec(string size , int number){
Vector3 result = new Vector3(0 , 0 , 0);
if (size == "right") {
result = rightShorePos + number * Vector3.right;
}
else {
result = leftShorePos + number * Vector3.left;
}
return result;
}
}
主要的场记的代码
public class firstSceneController : MonoBehaviour , sceneController , firstScenceUserAction{
environmentController environment;
boatController myBoat ;
const int numOfPirestOrDevil = 3;
peopleController[] peopleCtrl = new peopleController[numOfPirestOrDevil * 2];
string oriSize = "right";
FirstSceneGuiCtrl guiCtrl;
Vector3 environmentPos = Vector3.zero;
Vector3 leftShorePos = new Vector3 (-6f, 2f, 0f);
Vector3 rightShorePos = new Vector3 (6f, 2f, 0f);
string gameStatus = "playing";
void Awake(){
Director.getInstance ().currentSceneController = this;
guiCtrl = gameObject.AddComponent <FirstSceneGuiCtrl>() as FirstSceneGuiCtrl;
loadResources();
}
// Use this for initialization
void Start () {
}
//check Win Or Lost
void Update(){
if (myBoat.getRunningState () == "running") {
return;
}
int leftDevil = 0, leftPriest = 0, rightDevil = 0, rightPriest = 0 , leftShorePeople = 0;
for (int loop = 0; loop < numOfPirestOrDevil * 2; loop++) {
if (peopleCtrl [loop].getStatus() == "shore" && peopleCtrl [loop].size == "left") {
leftShorePeople++;
}
if (peopleCtrl [loop].getName()[0] == 'd' && peopleCtrl [loop].size == "left") {
leftDevil++;
} else if (peopleCtrl [loop].getName()[0] == 'd' && peopleCtrl [loop].size == "right") {
rightDevil++;
} else if (peopleCtrl [loop].getName()[0] == 'p' && peopleCtrl [loop].size == "left") {
leftPriest++;
} else {
rightPriest++;
}
}
if ((leftDevil > leftPriest && leftPriest != 0) || (rightPriest != 0 && rightDevil > rightPriest)) {
gameStatus = "lost";
}
else if (leftShorePeople == 6) {
gameStatus = "win";
}
}
当然,这样做其实也有点麻烦,就是不同的控制器是并列的,因此如果要调用别的控制器,那么就要场记把控制器当成参数传过去,但是在逻辑上比较好,不那么混乱。
最后还要写的就是一些用户按的按钮。
public class FirstSceneGuiCtrl : MonoBehaviour {
firstScenceUserAction action ;
bool ifShowHelp = true;
string helpText = "黑色方块为恶魔,白色方块为牧师,目标:让所有牧师和恶魔都到左岸,规则:点击牧师或者恶魔可以上岸或者上船,任意一边岸的恶魔" +
"数量若多于牧师(包括那一边船里的人物),那么游戏失败。";
// Use this for initialization
void Start () {
action = Director.getInstance().currentSceneController as firstScenceUserAction;
}
// Update is called once per frame
void OnGUI () {
firstScenceUserAction action = Director.getInstance().currentSceneController as firstScenceUserAction;
string status = action.getStatus ();
if (ifShowHelp == true) {
GUI.Box (new Rect (Screen.width / 2 - 100, Screen.height / 2 - 90, 200, 180), "" );
GUI.Label (new Rect (Screen.width / 2 - 100, Screen.height / 2 - 90, 200, 180), helpText);
if( GUI.Button (new Rect (Screen.width / 2 - 20, Screen.height / 2 + 60, 40, 30), "Ok") ){
ifShowHelp = false;
}
}
if (GUI.Button (new Rect(10 , 10 , 100, 50), "help") ) {
ifShowHelp = true;
}
if (status == "playing") {
if (GUI.Button (new Rect(130 , 10 , 100, 50), "restart")) {
action.reset ();
}
}
else {
string showMsg;
if (status == "lost") {
showMsg = "you lost!!";
}
else {
showMsg = "you win!!";
}
if (GUI.Button (new Rect(Screen.width/2-50, Screen.height/2-25, 100, 50), showMsg) ) {
action.reset ();
}
}
}
}