牧师与魔鬼

目录

一、牧师与魔鬼游戏介绍

二、实现游戏前的准备

1、MVC架构 

2、游戏事物(Objects)

3、玩家动作表

4、预制对象(Prefabs)

三、编写实现游戏的代码

 1、SSDirector类

2、SceneController类

3、IUserAction接口

4、UserGUI类

5、GenGameObject类

四、游戏视频


一、牧师与魔鬼游戏介绍

为了完成牧师与魔鬼,首先需要了解牧师与魔鬼这一款游戏,它的游戏内容如下:

       牧师和魔鬼是一款益智游戏,您将在其中帮助牧师和魔鬼过河。河的一侧有3个祭司和3个魔鬼。他们都想去这条河的另一边,但只有一条船,这条船每次只能载两个人。而且必须有一个人将船从一侧驾驶到另一侧。您可以单击按钮来移动它们,然后单击移动按钮将船移动到另一个方向。如果靠岸的船上和同一侧岸上的牧师被岸上的魔鬼人数所淹没,他们就会被杀死,游戏就结束了。您可以通过多种方式尝试它。让所有的祭司活着!最后所有牧师和魔鬼都成功过河,则表示游戏胜利。

你还可以通过网站  ( http://www.flash-game.net/game/2535/priests-and-devils.html ) 来试玩一下这个游戏来更加了解这个游戏的规则,我将使用unity来实现这一游戏,在编写代码实现的过程中程序需要满足以下要求:

  • 请将游戏中对象做成预制
  • 在场景控制器 LoadResources 方法中加载并初始化 长方形、正方形、球 及其色彩代表游戏中的对象。
  • 使用 C# 集合类型 有效组织对象
  • 整个游戏仅 主摄像机 和 一个 Empty 对象, 其他对象必须代码动态生成!!! 。 整个游戏不许出现 Find 游戏对象, SendMessage 这类突破程序结构的 通讯耦合 语句。 
  • 请使用MVC架构图编程,不接受非 MVC 结构程序
  • 注意细节,例如:船未靠岸,牧师与魔鬼上下船运动中,均不能接受用户事件

二、实现游戏前的准备

1、MVC架构 

由于我要实现的是动作分离版的牧师与魔鬼,所以我们需要先了解一下游戏的总体框架MVC框架。

Model
其中Model负责的是各个游戏对象的属性和基本行为,包括人物角色(魔鬼、牧师),船,以及河的两岸。船和岸是作为一个容器,需要有容纳人物的位置,也需要记录每个位置对应的xyz坐标。而它们都需要有一定的方法去返回自身的信息,比如在左还是右、是第几个人物、返回对象类型的方法等等。

View
这个就是与用户交互的接口,其中需要接受来自用户的点击信息,并且根据点击的物体不同而传递不同的信息。还有就是反馈,游戏开始或结束需要反馈相应的信息给用户,也就是一个简单的界面。

Controller
控制器需要将M和V连接器来,达到控制全局的目的,不仅需要从model中获取相应的信息,并且利用这些信息判断他们的位置,还需要从View中获取相应的用户输入,进行相应的物体移动,在Model和View之间充当桥梁的作用。同时还需要判断游戏进行程度,也就是判断输赢,并且将输赢信息返回给View,从而达到反馈的目的。

针对本游戏实现代码的MVC框架如下:

2、游戏事物(Objects)
名称实现方式
牧师白色长方体
恶魔黑色球体
河岸绿色长方体
棕色长方体
3、玩家动作表
动作条件
船移动船上必须有一个人
牧师在左岸上船 左岸必须有牧师,船上有空位,船在左岸
牧师在右岸上船 右岸必须有牧师,船上有空位,船在右岸
牧师在左岸下船船上有牧师,船在左岸
牧师在右岸下船船上有牧师,船在右岸
恶魔在左岸上船左岸必须有恶魔,船上有空位,船在左岸
恶魔在右岸上船右岸必须有恶魔,船上有空位,船在右岸
恶魔在左岸下船 船上有恶魔,船在左岸
恶魔在右岸下船

船上有恶魔,船在右岸

4、预制对象(Prefabs)

通过创建GameObject并使用Metariel设置好对应的颜色和形状作为预制,最后完成的预制对象如下图:

三、编写实现游戏的代码

根据前面构建的MVC框架,先创建脚本scripts,然后具体实现过程如下:

 1、SSDirector类

该类在游戏中类似于导演,担任的职责如下:

  • 获取当前游戏的场景
  • 控制场景运行、切换、入栈与出栈
  • 暂停、恢复、退出
  • 管理游戏全局状态
  • 设定游戏的配置
  • 设定游戏全局视图

为了完成以上职责,需要先拥有一些全局属性,如 running,fps等,这样可以让任何地方的代码访问它。然后还需要创建控制器对象来访问不同的控制器。代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//Director.cs
public class SSDirector : System.Object
{

    private static SSDirector instance;
    public FirstController controller;
    //public ISceneController sceneController{get; set;}
    //public IUserActions action{get;set;}
    public bool running{get; set;}

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

2、SceneController类

在游戏中类似于场记,其职责包括:

  • 管理本次场景所有的游戏对象
  • 协调游戏对象(预制件级别)之间的通讯
  • 响应外部输入事件
  • 管理本场次的规则(裁判)
  • 各种杂务

为了完成以上职责,需要实现IUserAction接口以便于实现玩家与游戏之间的实现,函数的具体实现可以放在模型GenGameObject中,这里调用GenGameObject中的函数即可。在场景被加载(awake)时,它也会自动注入SSDirector,作为当前场景。

public class FirstController : MonoBehaviour, IUserActions
{   
    private GenGameObject model;
    // Start is called before the first frame update
    void Start()
    {
        
    }
    
    // Update is called once per frame
    void Update()
    {
        
    }
    
    void Awake()
    {
        SSDirector director = SSDirector.getInstance();
        director.setFPS(60);
        //director.controller = this;
        //director.action = this;
        director.controller = this;
        //model.Start();
        //director.controller.LoadResources();
        //director.sceneController.LoadResources();
    }

    public void LoadResources() //加载物体
    {
        model.LoadResources();
    }
    


    public void setModel(GenGameObject Model)
    {
        model = Model;
    }

    public void priestOn()
    {
        model.priestOn();
    }

    public void devilOn()
    {
        model.devilOn();
    }

    public void changeState()
    {
        model.change_state();
    }

    public void priestOnEnd()
    {
        model.priestOnEnd();
    }

    public void devilOnEnd()
    {
        model.devilOnEnd();
    }

    public int Check()
    {
        return model.Check();
    }

    public void priestOff()
    {
        model.priestOff();
    }

    public void devilOff()
    {
        model.devilOff();
    }

    public void Restart()
    {
        model.Restart();
    }
}
3、IUserAction接口

接口定义了一组操作,该接口实现了用户行为与游戏系统规则计算的分离。这个接口就是游戏逻辑与用户界面之间交互的门面(Fasàde),用户可以通过这个接口中的操作进行游戏,要实现的操作可以参考上述的玩家动作表。

public interface IUserActions
{
    void priestOn();
    void priestOnEnd();
    void devilOn();
    void devilOnEnd();
    void changeState();
    int Check();
    void priestOff();
    void devilOff();
    void Restart();
}
4、UserGUI类

UserGUI类用来设置按钮并调用接口,完成用户交互。

public class UserGUI : MonoBehaviour
{
    SSDirector instance;
    IUserActions action;

    void Start()
    {
        instance = SSDirector.getInstance();
        action = instance.controller as IUserActions;
    }

    private void OnGUI()
    {
        GUIStyle fontStyle = new GUIStyle();
        fontStyle.fontSize = 40;
        fontStyle.normal.textColor = new Color(255, 255, 255);

        if (action.Check() == 1)
        {
            GUI.Label(new Rect(440, 200, 100, 100), "Win", fontStyle);
            if (GUI.Button(new Rect(435, 360, 80, 50), "Restart"))
            {
                action.Restart();
            }
        }
        if (action.Check() == 2)
        {
            GUI.Label(new Rect(390, 200, 100, 100), "GameOver", fontStyle);
            if (GUI.Button(new Rect(435, 360, 80, 50), "Restart"))
            {
                action.Restart();
            }
        }
        if (GUI.Button(new Rect(130, 0, 80, 50), "恶魔上船"))
        {
            action.devilOn();
        }
        if (GUI.Button(new Rect(270, 0, 80, 50), "牧师上船"))
        {
            action.priestOn();
        }
        if (GUI.Button(new Rect(600, 0, 80, 50), "牧师上船"))
        {
            action.priestOnEnd();
        }
        if (GUI.Button(new Rect(750, 0, 80, 50), "恶魔上船"))
        {
            action.devilOnEnd();
        }
        if (GUI.Button(new Rect(450, 0, 80, 50), "移动"))
        {
            action.changeState();
        }
        if (GUI.Button(new Rect(450, 50, 80, 50), "牧师下船"))
        {
            action.priestOff();
        }
        if (GUI.Button(new Rect(450, 100, 80, 50), "恶魔下船"))
        {
            action.devilOff();
        }
    }
}
5、GenGameObject类

在这个类中完成玩家的交互函数的具体实现,用栈来分别存储左岸和右岸的牧师和魔鬼,以此来判断何时游戏失败,何时游戏成功

public class GenGameObject : MonoBehaviour
{
    private SSDirector instance;
    int onBoat;
    public int speed;
    int boat_state = 1;
    GameObject[] objectOnBoat = new GameObject[2];
    readonly Vector3 shore_begin = new Vector3(10, 0, 0);
    readonly Vector3 shore_end = new Vector3(-10, 0, 0);
    readonly Vector3 boat_begin = new Vector3(-3, 0, 0);
    readonly Vector3 boat_end = new Vector3(3, 0, 0);
    readonly Vector3 priests_begin = new Vector3(-9f, 2, 0);
    readonly Vector3 priests_last = new Vector3(9f, 2, 0);
    readonly Vector3 devils_begin = new Vector3(-15f, 2, 0);
    readonly Vector3 devils_last = new Vector3(15f, 2, 0);
    readonly Vector3 gap = new Vector3(-1.5f, 0, 0);
    Stack<GameObject> priests_start = new Stack<GameObject>();
    Stack<GameObject> devils_start = new Stack<GameObject>();
    Stack<GameObject> priests_end = new Stack<GameObject>();
    Stack<GameObject> devils_end = new Stack<GameObject>();
    GameObject boat;


    public void Start()
    {
        speed = 10;
        onBoat = 0;
        boat_state = 1;
        instance = SSDirector.getInstance();
        instance.controller.setModel(this);
        LoadResources();
    }
    public void LoadResources() //加载物体
    {
        UnityEngine.Debug.Log("load...\n");
        Instantiate(Resources.Load("Prefabs/Shore"), shore_begin, Quaternion.identity);
        Instantiate(Resources.Load("Prefabs/Shore"), shore_end, Quaternion.identity);
        boat = Instantiate(Resources.Load("Prefabs/Boat"), boat_begin, Quaternion.identity) as GameObject;
        boat.name = "boat";
        for (int i = 0; i < 3; i++)
        {
            GameObject priest = Instantiate(Resources.Load("Prefabs/Priest"), (priests_begin - gap * i), Quaternion.identity) as GameObject;
            priest.name = "Priest";
            priests_start.Push(priest);
            GameObject devil = Instantiate(Resources.Load("Prefabs/Devil"), (devils_begin - gap * i), Quaternion.identity) as GameObject;
            devil.name = "Devil";
            devils_start.Push(devil);
        }
    }
    public int Check()//返回当前状态
    {
        int devilleft, devilright, preleft, preright;
        devilleft = devilright = preleft = preright = 0;
        devilleft += devils_start.Count;
        devilright += devils_end.Count;
        preleft += priests_start.Count;
        preright += priests_end.Count;
        if(boat_state==1 && onBoat>0)
        {
            for(int i=0;i<2;i++)
            {
                if (objectOnBoat[i] != null && objectOnBoat[i].name == "Devil")
                    devilleft += 1;
                else if (objectOnBoat[i] != null && objectOnBoat[i].name == "Priest")
                    preleft += 1;
                
            }
        }
        else if(boat_state==0 && onBoat>0)
        {
            for (int i = 0; i < 2; i++)
            {
                if (objectOnBoat[i]!=null && objectOnBoat[i].name == "Devil")
                    devilright += 1;
                else if (objectOnBoat[i] != null && objectOnBoat[i].name == "Priest")
                    preright += 1;
            }
        }

        if (devils_end.Count == 3 && priests_end.Count == 3)
        {
            return 1;
        }
        if (devilleft>preleft && preleft>0)
        {
           // UnityEngine.Debug.Log(preleft);
            return 2;

        }
        else if(devilright >preright && preright>0)
        {
          //  UnityEngine.Debug.Log(preright);
            return 2;
        }

        return 3;
    }

    private void Update()
    {
        Check();
    }

    public void priestOn()//牧师上船
    {
        if (priests_start.Count == 0)
            return;
        if (onBoat < 2)
        {
            GameObject temp = priests_start.Pop();
            temp.transform.parent = boat.transform;
            if (objectOnBoat[0]==null)
            {
                temp.transform.localPosition = new Vector3(0.3f, 1.5f, 0);
                objectOnBoat[0] = temp;
            }
            else
            {
                temp.transform.localPosition = new Vector3(-0.3f, 1.5f, 0);
                objectOnBoat[1] = temp;
            }
            onBoat++;
        }
        
    }

    public void priestOff()//牧师下船
    {
        for (int i = 0; i < 2; i++)
        {
            if (objectOnBoat[i] != null && objectOnBoat[i].name == "Priest")
            {
                if (boat.transform.position != boat_begin && boat.transform.position != boat_end)
                    return;
                GameObject pri = objectOnBoat[i];
                pri.transform.parent = null;
                if (boat.transform.position == boat_end)
                {
                    pri.transform.position = priests_last + gap * priests_end.Count;
                    priests_end.Push(pri);
                }
                else if (boat.transform.position == boat_begin)
                {
                    pri.transform.position = priests_begin - gap * priests_start.Count;
                    priests_start.Push(pri);
                }
                objectOnBoat[i] = null;
                onBoat--;
                return;
            }
        }
    }

    public void devilOn()//恶魔上船
    {
        if (devils_start.Count == 0)
            return;
        if (onBoat < 2)
        {
            GameObject temp = devils_start.Pop();
            temp.transform.parent = boat.transform;
            if (objectOnBoat[0]==null)
            {
                temp.transform.localPosition = new Vector3(0.3f, 1.5f, 0);
                objectOnBoat[0] = temp;
            }
            else
            {
                temp.transform.localPosition = new Vector3(-0.3f, 1.5f, 0);
                objectOnBoat[1] = temp;
            }
            onBoat++;
           // UnityEngine.Debug.Log(onBoat);
        }
    }

    public void devilOff()//恶魔下船
    {
        
        for (int i = 0; i < 2; i++)
        {
            if (objectOnBoat[i] != null && objectOnBoat[i].name == "Devil")
            {
                
                if (boat.transform.position != boat_begin && boat.transform.position != boat_end)
                    return;
                GameObject pri = objectOnBoat[i];
                pri.transform.parent = null;
                if (boat_state==0)
                {
                    pri.transform.position = devils_last + gap * devils_end.Count;
                    devils_end.Push(pri);
            //        UnityEngine.Debug.Log(devils_end.Count);
                }
                else if (boat_state == 1)
                {
                    pri.transform.position = devils_begin - gap * devils_start.Count;
                    devils_start.Push(pri);
                }
                objectOnBoat[i] = null;
                onBoat--;
                return;
            }
        }
    }

    public void Restart()//游戏重新开始
    {
        int num = priests_end.Count;
        for (int i = 0; i < num; i++)
        {
            GameObject temp = priests_end.Pop();
            temp.transform.position = priests_begin - gap * priests_start.Count;
            priests_start.Push(temp);
        }
        num = devils_end.Count;
        for (int i = 0; i < num; i++)
        {
            GameObject temp = devils_end.Pop();
            temp.transform.position = devils_begin - gap * devils_start.Count;
            devils_start.Push(temp);
        }
        if(onBoat>0)
        for (int i = 0; i < 2; i++)
        {
            if (objectOnBoat[i] == null)
            {
                    continue;
            }
            GameObject temp = objectOnBoat[i];
            temp.transform.parent = null;
            if (temp.name == "Priest")
            {
                temp.transform.position = priests_begin - gap * priests_start.Count;
                priests_start.Push(temp);
            }
            else
            {
                temp.transform.position = devils_begin - gap * devils_start.Count;
                priests_start.Push(temp);
            }
            objectOnBoat[i] = null;
        }
        onBoat = 0;
        boat.transform.position = boat_begin;
        boat_state = 1;
    }

    public void priestOnEnd()//右岸牧师上船
    {
        if (priests_end.Count == 0)
            return;
        if (onBoat < 2)
        {
            GameObject temp = priests_end.Pop();
            temp.transform.parent = boat.transform;
            if (objectOnBoat[0]==null)
            {
                temp.transform.localPosition = new Vector3(0.3f, 1.5f, 0);
                objectOnBoat[0] = temp;
            }
            else
            {
                temp.transform.localPosition = new Vector3(-0.3f, 1.5f, 0);
                objectOnBoat[1] = temp;
            }
            onBoat++;
        }
    }

    public void devilOnEnd()//右岸恶魔上船
    {
        if (devils_end.Count == 0)
            return;
        if (onBoat < 2)
        {
            GameObject temp = devils_end.Pop();
            temp.transform.parent = boat.transform;
            if (objectOnBoat[0]==null)
            {
                temp.transform.localPosition = new Vector3(0.3f, 1.5f, 0);
                objectOnBoat[0] = temp;
            }
            else
            {
                temp.transform.localPosition = new Vector3(-0.3f, 1.5f, 0);
                objectOnBoat[1] = temp;
            }
            onBoat++;
        }
    }

    public void boatMove()//船移动
    {
        if (onBoat != 0)
        {
            if (boat_state == 0)
                boat.transform.position = boat_end;
            else
                boat.transform.position = boat_begin;

        }
       
    }

    public void change_state()
    {
        if (boat_state == 0)
            boat_state = 1;
        else
            boat_state = 0;
        boatMove();
    }

}

代码存放的位置:张菁/3D游戏编程与设计 (gitee.com)

四、游戏视频

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值