IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity牧师与魔鬼小游戏 -> 正文阅读

[游戏开发]Unity牧师与魔鬼小游戏

Unity牧师与魔鬼小游戏

前言

这是中大计算机学院3D游戏编程课的一次作业,在这里分享一下设计思路。
主要代码上传到了gitee上,请按照后文的操作运行。
项目地址:https://gitee.com/cuizx19308024/unity-games/tree/master/hw2
成果视频:https://www.bilibili.com/video/BV16g411F7yF?spm_id_from=333.999.0.0

游戏说明

游戏规则

Priests and Devils is a puzzle game in which you will help the Priests and Devils to cross the river within the time limit. There are 3 priests and 3 devils at one side of the river. They all want to get to the other side of this river, but there is only one boat and this boat can only carry two persons each time. And there must be one person steering the boat from one side to the other side. In the flash game, you can click on them to move them and click the go button to move the boat to the other direction. If the priests are out numbered by the devils on either side of the river, they get killed and the game is over. You can try it in many > ways. Keep all priests alive! Good luck!

游戏中提及的事物

牧师、魔鬼、船、河、两边的陆地

用表格列出玩家的动作表

玩家动作执行条件执行结果
点击牧师/魔鬼游戏进行中,且可以上岸/上船上岸/上船
点击船游戏进行中且船上有1或2个人开船到另一边

项目组成和运行环境

由于文件过大,本项目仅上传了Assets文件夹。可以直接新建一个Unity项目,然后将Assets文件夹替换。
其中包括了Resources文件夹,包括Prefab对象预置、Material材料预制和Script脚本文件。使用时可以将Scripts文件夹中的Controller.cs拖动到对象ScriptFactory上。

设计与实现

MVC基本框架

本次将MVC三层分别放在了Models.csController.csUserGUI.cs三个文件下。结构上与老师给过的结构基本相同。
在这里插入图片描述

  1. 接口、导演。包括ISceneControllerIUserAction两个接口,分别定义场景操作和用户操作。导演类SSDirector采用单例模式,进行切换片场和类之间通信的操作。
  2. 模型。包括RoleModelBoatModelLandModel三个类,分别定义了其中可执行的操作和属性。
  3. 辅助脚本。包括MovableClickable类,定义了移动的方式和在屏幕上的显示效果,并设置了点击模型的事件处理器,将点击信息发送给Controller来处理。
  4. 控制器。本次只有一个控制器,可以很方便地控制各model之间的操作和游戏逻辑判定。
  5. UI界面。UI界面可以接受游戏外的一些操作,如重新开始。

接口、导演

分别定义场景操作和用户操作:

//加载场景接口
public interface ISceneController
{
    void LoadResources();
}

//用户操作接口
public interface IUserAction
{
    void MoveBoat(); //移动船
    void Restart(); //重新开始
    void MoveRole(RoleModel role); //移动角色
    int GameJudge(); //检测游戏结束
}

//导演类
public class SSDirector : System.Object
{
    private static SSDirector _instance;
    public ISceneController CurrentScenceController { get; set; }

    public static SSDirector GetInstance()
    {
        if (_instance == null)
        {
            _instance = new SSDirector();
        }
        return _instance;
    }
}

模型

由于代码过长,这里只说几个关键点:

  1. 角色模型RoleModel
    GameObject role;
    int role_sign;  //0为牧师,1为魔鬼
    bool on_boat;   //是否在船上
    LandModel land = (SSDirector.GetInstance().CurrentScenceController as Controller).src_land;//所在的陆地
    Clickable click;//给model赋予属性,可点击、可移动,相当于添加了脚本。
    Movable move;
    
    这里定义了游戏对象,并定义了所在的大陆和是否在船上,方便与其他两个模型关联。
    public RoleModel(int id, Vector3 pos)
    {
        if (id == 0)
        {
            role_sign = 0;
            role = Object.Instantiate(Resources.Load("Prefabs/Priest", typeof(GameObject)), pos, Quaternion.identity) as GameObject;
        }
        else
        {
            role_sign = 1;
            role = Object.Instantiate(Resources.Load("Prefabs/Ghost", typeof(GameObject)), pos, Quaternion.identity) as GameObject;
        }
        move = role.AddComponent(typeof(Movable)) as Movable;       //添加属性
        click = role.AddComponent(typeof(Clickable)) as Clickable;
        click.SetRole(this);
    }
    
    初始化对象,根据输入的pos确定游戏对象位置,并加载资源。对于move和click,相当于添加两个脚本,让它们可移动、可点击。
    public void ToLand(LandModel land)//上岸的移动
    {
        Vector3 pos = land.GetEmptyPosition();
        move.MoveTo(pos);
        this.land = land;
        on_boat = false;
    }
    
    public void ToBoat(BoatModel boat)//上船的移动
    {
        Vector3 pos = boat.GetEmptyPosition();
        move.MoveTo(pos);
        this.land = null;
        on_boat = true;
    }
    
    注意上船和上岸之后,要对本地的boat和land关联属性惊醒修改,并用MoveTo()函数通知UI界面调整位置。
  2. 陆地模型LandModel
    public int land_mark;//src为1,des为-1。
    
    这里用1和-1来标记大陆,可以直接用land_mark来计算对称的位置,比较方便。
    此外,由于陆地不可以移动和点击,因此不需要处理相关的移动函数,只需要在逻辑层统计好陆地上现有的角色即可,并用AddRole()RemoveRole()函数管理即可。
  3. 船模型BoatModel
    首先,需要一个Total()函数统计船上的人数,之后需要研究Move()函数,注意注释:
    public void Move()//移动船只的同时,调用角色的函数Move,同时将角色移到指定位置。
    {
        if (boat_mark == -1)
        {
            move.MoveTo(new Vector3(4.5F, 0.5F, 0));
            for (int i = 0; i < 2; i++)
            {
                if (roles[i] != null)
                {
                    roles[i].Move(src_empty_pos[i]);
                }
            }
            boat_mark = 1;
        }
        else
        {
            move.MoveTo(new Vector3(-4.5F, 0.5F, 0));
            for (int i = 0; i < 2; i++)
            {
                if (roles[i] != null)
                {
                    roles[i].Move(des_empty_pos[i]);
                }
            }
            boat_mark = -1;
        }
    }
    
    尤其要注意船在两边的时候船上空位的位置是不同的。

辅助脚本

  1. Movable类:
    public void MoveTo(Vector3 pos);//定义了移动到指定位置的UI动画,如何平移。
    void Update();//自带的函数,可以逐帧决定如何移动。
    
  2. Clickable类:
    public class Clickable : MonoBehaviour
    {
        IUserAction action;
        RoleModel role = null;
    
        public void SetRole(RoleModel role)
        {
            this.role = role;
        }
        private void Start()
        {
            action = SSDirector.GetInstance().CurrentScenceController as IUserAction;//获取控制器
        }
        private void OnMouseDown()//点击时调用控制器中的相关函数,用gameObject的name来区分对象。
        {
            if (gameObject.name == "boat")
            {
                action.MoveBoat();
            }
            else
            {
                action.MoveRole(role);
            }
        }
    }
    

控制器

这里只说明关键点:

  1. start()函数:
    // Start is called before the first frame update
    void Start()
    {
        SSDirector director = SSDirector.GetInstance();
        director.CurrentScenceController = this;      //脚本在此处运行,故现让Controller指向自己。
        user_gui = gameObject.AddComponent<UserGUI>() as UserGUI;//添加GUI属性
        LoadResources();
    }
    
  2. LoadResources()函数中,分别调用了类的构造函数进行初始化,尤其注意初始化模型之间关联的属性,要保持一致,可以参考注释。
  3. 移动船和移动角色:
    public void MoveBoat()
    {
        if (boat.Total() == 0 || user_gui.status != 0)
        {//空船不可以移动,且必须为游戏进行状态
            return;
        }
        boat.Move();
        user_gui.status = GameJudge();//判断游戏结束
    }
    
    public void MoveRole(RoleModel role)
    {
        if (user_gui.status != 0)
        {
            return;
        }
        if (role.IsOnBoat())//人在船上,上岸
        {
            LandModel land;//确定上哪边的岸
            if (boat.GetBoatMark() == -1)
            {
                land = des_land;
            }
            else
            {
                land = src_land;
            }
            boat.RemoveRole(role.GetName());//船操作
            role.ToLand(land);//角色操作
            land.AddRole(role);//陆地操作
        }
        else
        {
            LandModel land = role.GetLandModel();
            if (boat.Total() == 2 || land.GetLandMark() != boat.GetBoatMark())
            {//船已满或船不在此岸边
                return;
            }
            land.RemoveRole(role.GetName());
            role.ToBoat(boat);
            boat.AddRole(role);
        }
    
        user_gui.status = GameJudge();
    }
    
  4. 判断游戏结束。注意,游戏规则是开船之后,判断岸边魔鬼和牧师的数量。但实际上,在船靠一便停的时候,这一边的魔鬼和牧师数量多少是无所谓的,因此要等船开走后才能判断这边的数量关系:
    if (boat.GetBoatMark() == 1)//由于在这一边船还没开,因此只需检测另一边的数量即可。
    {
        if (des_priest < des_ghost && des_priest > 0)
        {//失败
            return -1;
        }
    }
    else
    {
        if (src_priest < src_ghost && src_priest > 0)
        {//失败
            return -1;
        }
    }
    

UI界面

注意这里的重新开始按钮,需要关联所对应的Controller,而这个Controller会从导演类获得。
action = SSDirector.GetInstance().CurrentScenceController as IUserAction;

运行结果

请参考展示视频:传送门

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-10-08 12:05:58  更:2021-10-08 12:06:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/28 2:39:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码