一、介绍
帮助三个牧师与三个魔鬼过河。过程中,河的两岸分别要满足:牧师数量不能少于魔鬼的数量。
二、实现
1、框架
Model:
(1)包含船、陆地、角色等游戏中涉及到的模型(物体、物件),
它们的基础类型都是GameObject,区别它们的是各自使用的预制件、初始化的位置等等
这些模型仅仅定义了其本身的属性(位置等),并不提供运动等操作函数。
(2)包含click、move两个“特殊”的模型:
click的点击事件(函数)触发的是Contorller中的IObjectController定义的点击函数;
同时船、陆地、角色等模型在定义时被赋予“click”(可点击?)
move:用于实现模型的移动(判断是否在移动、Update函数更新位置)
同样,这两个模型并没有自发的功能实现,更类似于可供其他类使用的“工具”。
View:
用户(玩家)操作的按钮等界面
Controller:
(1)Controllers文件夹包含各种控制器。FirstContorller是“主脑”,由它生成并管理其他控制器的实例(如船、陆地、角色等的控制器)
(2)船、陆地、角色等控制器定义了各种数据(位置、座位数等辅助数据)以及各种功能函数(创建物体、如何入座、点击事件等)
(3)这些物件都有一些共同的动作或功能函数,于是创建一些接口来统一定义:
IObjectController定义了物体的点击、生成、重置等函数
IUserAction定义了物体动作、场景状态等函数(物体动作、场景状态(重新开始等)都是由玩家来操控,所以叫IUserAction)
(4)ISceneController定义了场景初始化函数
(3)(4)这些controller只是对函数的“声明”(类似于基类,由firstcontroller继承?),具体的函数实现由firstcontroller完成。
只有当FirstController创建了船等其他控制器、调用其他控制器的“创建物件”函数,游戏实体才被创建出来。同样,只有当FirstController调用了其他控制器的功能函数,游戏实体才能实现运动。
玩家行为:
玩家动作 | 效果 | 备注 |
点击牧师或魔鬼 | 牧师或魔鬼:离岸上船、下船上岸 | 需要点中对象 |
点击船体 | 船从一岸移动到另一岸 | 1<=船上人数<=2 |
UML图:
2、实现:
Scripts脚本文件夹中分成三部分:Controllers、Models、Views,分别实现上述对应的代码。
在这里我参照了这位博主的博客https://www.cnblogs.com/LC32/p/15420714.html(或者说,我只是在他的代码、框架上做小修改,在此特别感谢)
3、游戏逻辑介绍
角色分为牧师与魔鬼
船上只可乘坐一个或两个角色(对应的,在boatcontroller定义座位数),无人时不可移动。
boatController(部分):
......
public int[] seat; // 座位数组,存储船上的乘客座位号
public Boat boatModel; // 船的模型对象
public int seatNum=2; //船的座位 //添加这个是为了方便控制船的位置数
//在下面的初始化、循环中统一用seatNum
public BoatController()
{
userAction = SSDirector.GetInstance().CurrentSceneController as IUserAction; // 获取当前场景的用户操作接口
seat = new int[seatNum]; // 初始化座位数组
Reset(); // 重置船的状态
}
......
点击岸上、船上的角色,对应角色上船、下船。点击船体,船左右移动。船体或角色移动过程中,不响应其他点击事件。
角色上岸(回到左岸或上到右岸),则回到岸上一个空位置上(对应的,landconcroller定义位置等参数)
角色在左岸、右岸、船上转移,对应的rolecontroller定义角色位置等参数
最后,在游戏的适当时机(船载着角色移动时、角色上到右岸时),执行“check”,检查游戏状态:是否成功或者失败(所有角色到达右岸则为成功;存在某一岸上的魔鬼数量>牧师数量,则游戏失败)
firstController:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FirstController : MonoBehaviour, ISceneController, IUserAction
{
public static int LEFTLAND = 0;
public static int RIGHTLAND = 1;
public static int BOAT = 2;
public static int PRIEST = 0;
public static int DEVIL = 1;
public static int PLAYING = 0;
public static int WIN = 1;
public static int FAILED = 2;
BoatController BoatCtrl;
RoleController[] RoleCtrl = new RoleController[6];
LandController[] LandCtrl = new LandController[2];
MoveController MoveCtrl;
int[] rolesID = new int[6]{0,1,2,3,4,5};
int gameState;
void Awake(){
SSDirector director = SSDirector.GetInstance();
director.CurrentSceneController = this;
director.CurrentSceneController.Initialize();
}
public void Initialize(){
//如果有,则释放原有的GameObject
for(int i = 0; i < 6; i++){
if(RoleCtrl[i] != null){
Destroy(RoleCtrl[i].GetModelGameObject());
}
}
for(int i = 0; i < 2; i++){
if(LandCtrl[i] != null){
Destroy(LandCtrl[i].GetModelGameObject());
}
}
if(BoatCtrl != null){
Destroy(BoatCtrl.GetModelGameObject());
}
// 加载控制器和模型
BoatCtrl = new BoatController();
BoatCtrl.CreateModel();
for(int i = 0; i < 6; i++){
int roleType = (i < 3) ? PRIEST : DEVIL;
RoleCtrl[i] = new RoleController(roleType, rolesID[i]);
RoleCtrl[i].CreateModel();
}
LandCtrl[0] = new LandController(LEFTLAND, rolesID);
LandCtrl[1] = new LandController(RIGHTLAND, rolesID);
LandCtrl[0].CreateModel();
LandCtrl[1].CreateModel();
MoveCtrl = new MoveController();
//开始游戏
gameState = PLAYING;
}
//将角色的ID转换成数组的下标
int IDToNumber(int ID){
for(int i = 0; i < 6; i++){
if(rolesID[i] == ID){
return i;
}
}
return -1;
}
//点击船时执行
public void MoveBoat(){
if(gameState != PLAYING || MoveCtrl.IsMoving() || BoatCtrl.isEmpty()) return;
CheckAndSetGameState();
if(BoatCtrl.onLeftside){
MoveCtrl.SetMove(BoatCtrl.GetModelGameObject(), Position.boatRightPos);
for(int i = 0; i < BoatCtrl.seatNum; i++){
if(BoatCtrl.seat[i] != -1){
RoleController r = RoleCtrl[IDToNumber(BoatCtrl.seat[i])];
MoveCtrl.SetMove(r.GetModelGameObject(), Position.seatRightPos[i]);
}
}
}
else{
MoveCtrl.SetMove(BoatCtrl.GetModelGameObject(), Position.boatLeftPos);
for(int i = 0; i < BoatCtrl.seatNum; i++){
if(BoatCtrl.seat[i] != -1){
RoleController r = RoleCtrl[IDToNumber(BoatCtrl.seat[i])];
MoveCtrl.SetMove(r.GetModelGameObject(), Position.seatLeftPos[i]);
}
}
}
BoatCtrl.onLeftside = !BoatCtrl.onLeftside;
}
//点击角色时执行
public void MoveRole(int id){
int num = IDToNumber(id);
if(gameState != PLAYING || MoveCtrl.IsMoving()) return;
int seat;
switch(RoleCtrl[num].roleState){
case 0: // LEFTLAND
if(!BoatCtrl.onLeftside || (BoatCtrl.getEmptySeat() == -1)) return;//增加判断getEmptySeat :
LandCtrl[0].LeaveLand(id); //如果上不了船,就直接返回,而
seat = BoatCtrl.embark(id); //不用改动role、land、boat的位置参数
RoleCtrl[num].GoTo(BOAT);
if(seat == -1) return; //增加判断getEmptySeat后,这句应该可以略去
MoveCtrl.SetMove(RoleCtrl[num].GetModelGameObject(), Position.seatLeftPos[seat]);
break;
case 1: // RIGHTLAND
if(BoatCtrl.onLeftside || (BoatCtrl.getEmptySeat() == -1)) return;//同上
LandCtrl[1].LeaveLand(id);
seat = BoatCtrl.embark(id);
RoleCtrl[num].GoTo(BOAT);
if(seat == -1) return;
MoveCtrl.SetMove(RoleCtrl[num].GetModelGameObject(), Position.seatRightPos[seat]);
break;
case 2: //BOAT
if(BoatCtrl.onLeftside){
seat = LandCtrl[0].getEmptySeat();
BoatCtrl.disembark(id);
LandCtrl[0].GoOnLand(id);
RoleCtrl[num].GoTo(LEFTLAND);
MoveCtrl.SetMove(RoleCtrl[num].GetModelGameObject(), Position.roleLeftPos[seat]);
}
else{
UnityEngine.Debug.Log("mid_d");
seat = LandCtrl[1].getEmptySeat();
BoatCtrl.disembark(id);
LandCtrl[1].GoOnLand(id);
RoleCtrl[num].GoTo(RIGHTLAND);
CheckAndSetGameState();//新添加一个在这里,在最后一个角色上右岸之后判定成功
MoveCtrl.SetMove(RoleCtrl[num].GetModelGameObject(), Position.roleRightPos[seat]);
}
break;
default: break;
}
}
//判断游戏状态
public void CheckAndSetGameState(){
if(gameState != PLAYING) return;
//判断是否失败
int[,] rolePos = new int[2, 3]{{0, 0, 0}, {0, 0, 0}};
foreach(RoleController r in RoleCtrl){
rolePos[r.roleType, r.roleState]++; //roletype: 0天使、1魔鬼
} //rolestate:0左岸、1右岸、2船上
if((rolePos[0,0]>0 && rolePos[0,0]<rolePos[1,0]) || //左岸存在天使 && 左岸的天使数量少于左岸魔鬼的数量
(rolePos[0,1]>0 && rolePos[0,1]<rolePos[1,1]) || //右岸存在天使 && 右岸的天使数量少于右岸魔鬼的数量
(rolePos[0,2]>0 && rolePos[0,2] < rolePos[1,2])){ //船上最多只能承载两个角色时,这行可以删去
gameState = FAILED;
return;
}
//判断是否成功
foreach(RoleController r in RoleCtrl){
if(r.roleState != RIGHTLAND){//存在角色未到达右岸,则直接返回 //r.roleType == 0 &&
UnityEngine.Debug.Log("这里");
return;
}
}
UnityEngine.Debug.Log("其实是这里");
gameState = WIN; //上面的情况都不符合,则说明所有角色都安全到达右岸,win
return;
}
//Reset按钮执行的功能
public void Restart(){
Initialize();
gameState = PLAYING;
}
//获取游戏当前状态
public int GetGameState(){
return gameState;
}
}