一、游戏介绍与要求
河的一侧有三个牧师与三个魔鬼,你需要帮助他们过河。但是只有一条小船,小船每次最多只能载两个人,且至少需要一个人来开船。注意同一侧的魔鬼数量不能大于牧师数量,否则牧师将会被魔鬼杀死。
- 将游戏中对象做成预制
- 在场景控制器 LoadResources 方法中加载并初始化长方形、正方形、球及其色彩代表游戏中的对象。
- 使用 C# 集合类型有效组织对象
- 整个游戏仅主摄像机和一个 Empty 对象,其他对象必须代码动态生成!整个游戏不许出现 Find 游戏对象,SendMessage 这类突破程序结构的通讯耦合语句。
- 使用MVC架构
二、准备工作
游戏中的物体
GameObject | 表达方式 |
---|---|
牧师 | 白色球 |
魔鬼 | 红色球 |
船 | 棕色长方体 |
河岸 | 灰色长方体 |
玩家动作表
动作 | 事件 |
---|---|
牧师上船 | 船所在一侧的牧师上船 |
牧师下船 | 船上的牧师到船所在一侧的河岸 |
恶魔上船 | 船所在一侧的恶魔上船 |
恶魔下船 | 船上的恶魔到船所在一侧的河岸 |
开船 | 船及船上的物体移动到另一侧 |
预制对象
MVC架构设计
- Model
定义Boat模型,包含船所在一侧、船上物体数量、船上物体及位置等信息。
(因为在FirstController中使用Stack组织牧师与恶魔对象,且该对象无需返回对象信息,故不定义其Model) - View
SSDirector:获取当前场景、控制场景转换等。(该游戏只有一个场景)
UserGUI:绘制用户交互界面,实现用户交互。 - Controller
FirstController:控制当前场景下各个Model的行为。
三、游戏实现代码
Model
- Boat
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Boat
{
public GameObject role;
public bool boat_state;
public int boat_num;
public GameObject[] object_on_boat = new GameObject[2];
public Vector3 boat_object_position = new Vector3(-4f, 0.05f, 0);
public Boat(){
boat_state = false;
boat_num = 0;
}
}
View
- SSDirector
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSDirector : System.Object {
// singlton instance
private static SSDirector _instance;
public ISceneController currentSceneController { get; set;}
// get instance anytime anywhare!
public static SSDirector getInstance() {
if (_instance == null) {
_instance = new SSDirector ();
}
return _instance;
}
public int getFPS() {
return Application.targetFrameRate;
}
public void setFPS(int fps) {
Application.targetFrameRate = fps;
}
public void NextScene(){
Debug.Log ("Waiting next Scene now...");
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
//UnityEditor.EditorApplication.Exit(0);
#else
Application.Quit();
#endif
}
}
- UserGUI
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UserGUI : MonoBehaviour {
private IUserAction action;
void Start () {
action = SSDirector.getInstance ().currentSceneController as IUserAction;
}
void OnGUI() {
if(!action.isMoving()){
float width = Screen.width / 5;
float height = Screen.height / 12;
if (GUI.Button(new Rect(0, 0, width, height), "牧师上船")) {
action.priest_on();
}
if (GUI.Button(new Rect(width, 0, width, height), "牧师下船")) {
action.priest_off();
}
if (GUI.Button(new Rect(width * 2, 0, width, height), "开船")) {
action.move_boat();
}
if (GUI.Button(new Rect(width * 3, 0, width, height), "恶魔下船")) {
action.devil_off();
}
if (GUI.Button(new Rect(width * 4, 0, width, height), "恶魔上船")) {
action.devil_on();
}
if (action.getResult() != "Continue") {
GUI.Box(new Rect(width, height, width * 3, height * 3), action.getResult());
if (GUI.Button(new Rect(width, height * 3, width * 3, height), "结束游戏")) {
action.GameOver();
}
}
}
}
}
Controller
- FirstController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// Scene Controller
/// Usage: host on a gameobject in the scene
/// responsiablities:
/// acted as a scene manager for scheduling actors.log something ...
/// interact with the director and players
public class FirstController : MonoBehaviour, ISceneController, IUserAction {
Boat boat = new Boat();
bool moving = false;
string result = "Continue";
Stack<GameObject> priest_left = new Stack<GameObject>();
Stack<GameObject> priest_right = new Stack<GameObject>();
Stack<GameObject> devil_left = new Stack<GameObject>();
Stack<GameObject> devil_right = new Stack<GameObject>();
static Vector3 priest_left_position = new Vector3(-5.5f, 1.5f, 0);
static Vector3 priest_right_position = new Vector3(5.5f, 1.5f, 0);
static Vector3 devil_left_position = new Vector3(-10f, 1.5f, 0);
static Vector3 devil_right_position = new Vector3(10f, 1.5f, 0);
static Vector3 boat_left_position = new Vector3(-3, -0.7f, 0);
static Vector3 boat_right_position = new Vector3(3, -0.7f, 0);
Vector3[] relative_position = new Vector3[2];
//船移动相关
static private float moveTime = 0.25f;
private float startTime;
static private float length = Vector3.Distance(boat_left_position, boat_right_position);
private float speed = length / moveTime;
// the first scripts
void Awake () {
SSDirector director = SSDirector.getInstance ();
director.setFPS (60);
director.currentSceneController = this;
director.currentSceneController.LoadResources ();
}
// loading resources for the first scence
public void LoadResources () {
Debug.Log ("load resource ...\n");
Instantiate(Resources.Load("Prefabs/shore"), new Vector3(-10, 0, 0), Quaternion.identity);
Instantiate(Resources.Load("Prefabs/shore"), new Vector3(10, 0, 0), Quaternion.identity);
boat.role = Instantiate(Resources.Load("Prefabs/boat"), boat_left_position, Quaternion.identity) as GameObject;
for(int i = 0; i < 3; i++){
GameObject p = Instantiate(Resources.Load("Prefabs/priest"), priest_left_position, Quaternion.identity) as GameObject;
priest_left_position.x -= 1.5f;
p.name = "priest";
priest_left.Push(p);
GameObject d = Instantiate(Resources.Load("Prefabs/devil"), devil_left_position, Quaternion.identity) as GameObject;
devil_left_position.x -= 1.5f;
d.name = "devil";
devil_left.Push(d);
}
}
string check(){
int priest_num;
int devil_num;
if(boat.boat_state){
priest_num = 3 - priest_left.Count;
devil_num = 3 - devil_left.Count;
if(priest_num == 3 && devil_num == 3) return "Win";
if((devil_num > priest_num && priest_num != 0) || (devil_left.Count > priest_left.Count && priest_left.Count != 0)) return "Lose";
return "Continue";
}
else{
priest_num = 3 - priest_right.Count;
devil_num = 3 - devil_right.Count;
if((devil_num > priest_num && priest_num != 0)|| (devil_right.Count > priest_right.Count && priest_right.Count != 0)) return "Lose";
return "Continue";
}
}
#region IUserAction implementation
public void priest_on(){
if(boat.boat_num < 2){
if(boat.boat_state){
if(priest_right.Count == 0) return;
boat.object_on_boat[boat.boat_num] = priest_right.Pop();
priest_right_position.x += 1.5f;
}
else{
if(priest_left.Count == 0) return;
boat.object_on_boat[boat.boat_num] = priest_left.Pop();
priest_left_position.x += 1.5f;
}
boat.object_on_boat[boat.boat_num].transform.position = boat.boat_object_position;
boat.boat_object_position.x += 2f;
boat.boat_num++;
}
}
public void priest_off(){
for(int i = boat.boat_num - 1; i >= 0; i--){
if(boat.object_on_boat[i].name == "priest"){
boat.boat_num--;
boat.boat_object_position.x -= 2f;
if(boat.boat_state){
priest_right.Push(boat.object_on_boat[i]);
boat.object_on_boat[i].transform.position = priest_right_position;
priest_right_position.x += 1.5f;
}
else{
priest_left.Push(boat.object_on_boat[i]);
boat.object_on_boat[i].transform.position = priest_left_position;
priest_left_position.x -= 1.5f;
}
return;
}
if(boat.boat_num == 2 && boat.object_on_boat[0].name == "priest"){
boat.boat_num--;
boat.boat_object_position.x -= 2f;
if(boat.boat_state){
priest_right.Push(boat.object_on_boat[0]);
boat.object_on_boat[0].transform.position = priest_right_position;
priest_right_position.x += 1.5f;
}
else{
priest_left.Push(boat.object_on_boat[0]);
boat.object_on_boat[0].transform.position = priest_left_position;
priest_left_position.x -= 1.5f;
}
boat.object_on_boat[1].transform.position = new Vector3(boat.object_on_boat[1].transform.position.x - 2f, boat.object_on_boat[1].transform.position.y, boat.object_on_boat[1].transform.position.z);
boat.object_on_boat[0] = boat.object_on_boat[1];
return;
}
}
}
public void devil_on(){
if(boat.boat_num < 2){
if(boat.boat_state){
if(devil_right.Count == 0) return;
boat.object_on_boat[boat.boat_num] = devil_right.Pop();
devil_right_position.x -= 1.5f;
}
else{
if(devil_left.Count == 0) return;
boat.object_on_boat[boat.boat_num] = devil_left.Pop();
devil_left_position.x += 1.5f;
}
boat.object_on_boat[boat.boat_num].transform.position = boat.boat_object_position;
boat.boat_object_position.x += 2f;
boat.boat_num++;
}
}
public void devil_off(){
for(int i = boat.boat_num - 1; i >= 0; i--){
if(boat.object_on_boat[i].name == "devil"){
boat.boat_num--;
boat.boat_object_position.x -= 2f;
if(boat.boat_state){
devil_right.Push(boat.object_on_boat[i]);
boat.object_on_boat[i].transform.position = devil_right_position;
devil_right_position.x += 1.5f;
}
else{
devil_left.Push(boat.object_on_boat[i]);
boat.object_on_boat[i].transform.position = devil_left_position;
devil_left_position.x -= 1.5f;
}
return;
}
if(boat.boat_num == 2 && boat.object_on_boat[0].name == "devil"){
boat.boat_num--;
boat.boat_object_position.x -= 2f;
if(boat.boat_state){
devil_right.Push(boat.object_on_boat[0]);
boat.object_on_boat[0].transform.position = devil_right_position;
devil_right_position.x += 1.5f;
}
else{
devil_left.Push(boat.object_on_boat[0]);
boat.object_on_boat[0].transform.position = devil_left_position;
devil_left_position.x -= 1.5f;
}
boat.object_on_boat[1].transform.position = new Vector3(boat.object_on_boat[1].transform.position.x - 2f, boat.object_on_boat[1].transform.position.y, boat.object_on_boat[1].transform.position.z);
boat.object_on_boat[0] = boat.object_on_boat[1];
return;
}
}
}
public void move_boat(){
if(boat.boat_num != 0){
for(int i = 0; i < boat.boat_num; i++){
relative_position[i] = boat.object_on_boat[i].transform.position - boat.role.transform.position;
}
if(boat.boat_state){
boat.boat_object_position.x -= 6.0f;
}
else{
boat.boat_object_position.x += 6.0f;
}
startTime = Time.time;
moving = true;
}
}
public bool isMoving(){
return moving;
}
public string getResult(){
return result;
}
public void GameOver ()
{
SSDirector.getInstance ().NextScene ();
}
#endregion
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
//船移动
if(moving){
float dist = (Time.time - startTime) * speed;
float fraction = dist / length;
Vector3 end_position;
if(boat.boat_state){
boat.role.transform.position = Vector3.MoveTowards(boat_right_position, boat_left_position, fraction);
end_position = boat_left_position;
}
else{
boat.role.transform.position = Vector3.MoveTowards(boat_left_position, boat_right_position, fraction);
end_position = boat_right_position;
}
for(int i = 0; i < boat.boat_num; i++){
boat.object_on_boat[i].transform.position = boat.role.transform.position + relative_position[i];
}
if(boat.role.transform.position == end_position){
boat.boat_state = !boat.boat_state;
moving = false;
result = check();
}
}
}
}
Interface
- IUserAction
using System;
public interface IUserAction
{
void priest_on();
void priest_off();
void devil_on();
void devil_off();
void move_boat();
bool isMoving();
string getResult();
void GameOver();
}
- ISceneController
using System;
public interface ISceneController
{
void LoadResources();
}
四、游戏视频
牧师与魔鬼游戏视频