3D游戏编程与设计 HW 3.5 牧师与恶魔

3D游戏编程与设计 HW 3.5 牧师与恶魔


完整游戏过程可见以下视频:
https://www.bilibili.com/video/BV1Vk4y117J2/

完整代码可见以下仓库:
https://gitee.com/beilineili/game3-d

1.游戏介绍

  • 牧师和恶魔是一款益智游戏
  • 你将帮助神父和恶魔在限定的时间内过河。河的一边有三个牧师和三个魔鬼。他们都想到河的另一边去,但是只有一条船,每次只能载两个人。必须有一个人把船从一边转向另一边。如果牧师们被河两岸的恶魔们打败(在岸上的牧师数量少于恶魔),他们就会被杀死,游戏也就结束了。
  • 在游戏中,你可以点击它们来移动它们,点击go按钮来移动船到另一个方向。你可以用很多的方法来尝试。让所有的牧师活下去! 祝你好运!

2.题目要求

在这里插入图片描述

3.MVC结构

  • MVC是模型(model)-视图(view)-控制器(controller)的缩写,它是一种软件设计模式,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
  • MVC基本结构图:

在这里插入图片描述

  • 在牧师与恶魔游戏设计中,可以分为
    ① SSDirector:整个结构的管理者,控制各个场景的切换协调;
    ② ISceneController(抽象):每个场景的管理者,是Director用来向场景控制器传递要求的接口
    ③ IUserAction(抽象):负责管理行为之间的交互,对于玩家的每个动作产生相应的响应结果
  • 通过这样基于职责的设计,实现了赋予不同对象特定的职责,使游戏程序的结构更加清晰、可复用性更高。

4.游戏对象表,玩家动作表,事件表

游戏对象对象类型
Priest 牧师Cube
Devil 魔鬼Sphere
Boat 船Cube
River 河流Cube
Coast 河岸Cube
动作触发事件
点击 Character 人物人物上船,下船
点击 Boat 船只船只过河
事件结果
其中一边恶魔数量大于牧师(包括船上和岸上的)玩家失败
所有牧师和恶魔都到了另一岸玩家成功

5.类设计

基于职责设计,使用面向对象技术设计游戏。
项目的程序代码采用MVC结构,根据功能将代码文件分别放入Controllers, Models, Views文件夹中。

(一)Models

① Boat
  • 有 location(船位置坐标)属性,有起点和终点,此外还需要维护船上的空位坐标以及角色信息
② Coast
  • 同 Boat,需要维护岸上的空位坐标和角色信息
③ Character
  • 有 name,location 和 isOnBoard 属性;其中 name 用于区分角色是牧师还是恶魔,IsOnBoat 用于区分是否在船上

(二)Views

① UserGUI
  • Start 函数,通过获得 Director 的单例,初始化获得当前场记 CurrentSecnController
        // 得到 GameController
	    void Start () {
            status = 0;
            action = Director.GetInstance().CurrentSecnController as IUserAction;
        }
  • OnGUI 函数,渲染游戏界面;
	    void OnGUI () {
            textStyle = new GUIStyle {
                fontSize = 40,
                alignment = TextAnchor.MiddleCenter
            };
            hintStyle = new GUIStyle {
                fontSize = 15,
                fontStyle = FontStyle.Normal
            };
            btnStyle = new GUIStyle("button") {
                fontSize = 30
            };
            // 打印标题和规则提示信息
            GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 195, 100, 50), 
                "Priest And Devil", textStyle);
            GUI.Label(new Rect(Screen.width / 2 - 400, Screen.height / 2 - 125, 100, 50), 
                "White Cube: Periest\n Red Sphere: Devil\n\n" + rule, hintStyle);
            
            // 打印游戏结果和重开游戏的按钮
            if (status == 1) {
                // Lose
                GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 85, 100, 50), "You Lose!", textStyle);
                if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 140, 70), "Restart", btnStyle)) {
                    status = 0;
                    action.Restart();
                }
            } else if (status == 2) {
                // Win
                GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 85, 100, 50), "You Win!", textStyle);
                if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 140, 70), "Restart", btnStyle)) {
                    status = 0;
                    action.Restart();
                }
            }
	    }
  • OnMouseDown 函数,监听捕捉用户的点击动作,转至 Controller 处理
        void OnMouseDown() {
            if (gameObject.name == "boat") {
                action.MoveBoat();
            } else {
                action.CharacterClicked(characterCtrl);
            }
        }

(三)Controllers

① Director
  • 获取当前游戏的场景
  • 管理游戏全局状态
    public class Director : System.Object
    {
        // Singlton instance.
        private static Director instance;
        public ISceneController CurrentSecnController { get; set; }

        // get instance anytime anywhere!
        public static Director GetInstance(){
            if (instance == null){
                instance = new Director();
            }
            return instance;
        }

        public int getFPS(){
            return Application.targetFrameRate;
        }
    
        public void setFPS(int fps){
            Application.targetFrameRate = fps;
        }
    }
② ISceneController
  • 场记 GameController 与导演 Director 交互的接口
public interface ISceneController
    {
        void LoadResources();
    }
③ IUserAction
  • 玩家与 GameController 互动的用户交互接口
    public interface IUserAction {
        void MoveBoat();
        void CharacterClicked(CharacterController characterCtrl);
        void Restart();
    }
④ Moveable
  • SetDestination 用于控制对应游戏对象的运动
        public void SetDestination(Vector3 pos) {
            destination = pos;
            middle = pos;
            if (pos.y == transform.position.y) {          // 移动船
                status = 2;
            } else if (pos.y < transform.position.y) {    // 将角色从岸上移到船上
                middle.y = transform.position.y;
            } else {                                      // 将角色从船上移到岸上
                middle.x = transform.position.x;
            }
            status = 1;
        }
⑤ GameController
  • CoastController
    GetEmptyIndex:岸上空位的下标
    GetEmptyPosition:岸上空位的坐标
    GetOnCoast:上岸
    GetOffCoast:离岸
    GetCharacterNum:计算岸上角色的数量
  • BoatController
    Move:移动船
    GetEmptyIndex:船上空位的下标
    IsEmpty:船是否有空位
    GetEmptyPosition:船上空位的坐标
    GetOnBoat:当玩家点击牧师或魔鬼,使之上船时,被调用。
    GetOffBoat:当玩家点击牧师或魔鬼,使之上岸时,被调用。
    GetCharacterNum:计算船上角色的数量
  • CharacterController
    配合 CoastController 和 BoatController
⑥ FirstController
  • 在场景被加载唤醒(awake)时,它会自动注入导演,设置当前场景
    void Awake() {
        Director director = Director.GetInstance();
        director.CurrentSecnController = this;
        userGUI = gameObject.AddComponent<UserGUI>() as UserGUI;
        characters = new MyNamespace.CharacterController[6];
        LoadResources();
    }
  • CheckGameOver 判断游戏胜负
    private int CheckGameOver() {
        int rightPriest = 0;
        int rightDevil = 0;
        int leftPriest = 0;
        int leftDevil = 0;
        int status = 0;

        rightPriest += rightCoastCtrl.GetCharacterNum()[0];
        rightDevil += rightCoastCtrl.GetCharacterNum()[1];
        leftPriest += leftCoastCtrl.GetCharacterNum()[0];
        leftDevil += leftCoastCtrl.GetCharacterNum()[1];

        // Win
        if (leftPriest + leftDevil == 6) {
            status = 2; 
        }
        
        if (boatCtrl.boat.Location == Location.right) {
            rightPriest += boatCtrl.GetCharacterNum()[0];
            rightDevil += boatCtrl.GetCharacterNum()[1];
        } else {
            leftPriest += boatCtrl.GetCharacterNum()[0];
            leftDevil += boatCtrl.GetCharacterNum()[1];
        }

        // Lose
        if ((rightPriest < rightDevil && rightPriest > 0) ||
            (leftPriest < leftDevil && leftPriest > 0)) {
            status = 1;
        }

        return status;
    }

6.游戏结果展示

完整游戏过程可见以下视频:
https://www.bilibili.com/video/BV1Vk4y117J2/

(一) 游戏界面

在这里插入图片描述

(二)游戏失败

在这里插入图片描述

(三)游戏胜利

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值