3D游戏编程实践——Priests and Devils

2023-11-20

编程实践——Priests and Devils

github链接:https://github.com/ctlchild/SYSU-unity3d-learning/tree/master/hw2

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!

  • 游戏中提及的事物有:河岸Coast(包括起始河岸和目的河岸),河River,3个牧师Priests,3个魔鬼Devils,船。

  • 玩家动作表:

    事物
    牧师/魔鬼 从河岸上船
    牧师/魔鬼 从船上河岸
    从河岸行驶到另一河岸
  • 预制游戏中的对象:
    在这里插入图片描述

  • 在 GenGameObjects 中创建 长方形、正方形、球 及其色彩代表游戏中的对象:

    我用金色的方块代表牧师,红色的球代表恶魔,蓝色的细长长方体代表河流,带有纹理的方块代表河岸,方块代表船,游戏效果图如下:

在这里插入图片描述

  • 使用MVC结构实现程序

首先是Model部分,

首先建立一个Director类,它使用单例模式,控制场景控制器。

public class Director : System.Object {
    private static Director _instance; 
    public static Director getInstance() { //使用单例模式
        if (_instance == null) _instance = new Director ();
        return _instance;
    }
    public SceneController currentSceneController { get; set; } //控制场景控制器
}

建立场景控制器接口

并在Controller中实现,它需要加载各种资源以及人物(牧师/魔鬼)

public interface SceneController {
    void loadResources();
    void loadCharacter();
}

建立用户操作接口

并在Controller中实现,它需要实现点击船让船运动,点击人物让人物运动,游戏结束时点击restart按钮使游戏重新开始

public interface UserAction {
    void ClickBoat();
    void ClickCha(ChaCon characterCtrl);
    void restart();
}

建立运动类

运动类是船或者人物的组件,实现事物的运动。因为人物运动时跨度比较大,比如说从船到河岸,需要先到船边,然后到河岸的对应位置,所以需要设置运动类的状态为3种,分别为没有运动,从开始运动到中途,从中途运动到终点。我在update函数中进行transform位置的移动。并设置了setDest函数,当事物调用了这个函数时,update函数会自动地使事物进行移动。

void Update() {
    if (status == 1) {
        transform.position = Vector3.MoveTowards (transform.position, mid, speed * Time.deltaTime);
        if (transform.position == mid) status = 2;
    } 
    if (status == 2) {
        transform.position = Vector3.MoveTowards (transform.position, dest, speed * Time.deltaTime);
        if (transform.position == dest) status = 0;
    }
}

public void setDest(Vector3 _dest) {
    dest = _dest;
    mid = _dest;
    if (_dest.y == transform.position.y) status = 2; //boat
    else {
        if (_dest.y < transform.position.y) mid.y = transform.position.y;	// from coast to boat
        else mid.x = transform.position.x;	// from boat to coast
        status = 1;
    }
}

建立人物(牧师/魔鬼)控制器

需要有人物的GameObject,人物的类型(用0表示牧师,1表示魔鬼),运动类的组件,可供点击的ClickGUI,是否在船上的布尔变量,当前在哪个河岸的河岸控制器(如果不在河岸就为null)。

构造函数实例化GameObject,设置了位置向量,增加了运动类组件和可供点击的ClickGUI.

public ChaCon(string name) {
    if (name == "priest") {
        character = Object.Instantiate (Resources.Load ("Perfabs/Priest", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
        characterType = 0;
    } else {
        character = Object.Instantiate (Resources.Load ("Perfabs/Devil", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
        characterType = 1;
    }
    move = character.AddComponent (typeof(Move)) as Move;
    clickGUI = character.AddComponent (typeof(ClickGUI)) as ClickGUI;
    clickGUI.setController (this);
}

上船或是上岸时对所属河岸控制器和是否在船上的布尔变量进行修改。

public void getOnBoat(BoatCon boat) {
    CoastCon = null;
    character.transform.parent = boat.getGameobj().transform;
    isonboat = true;
}

public void getOnCoast(CoastCon coast) {
    CoastCon = coast;
    character.transform.parent = null;
    isonboat = false;
}

建立河岸控制器

需要有河岸的GameObject,状态量表示是起始河岸或是终点河岸,位置数组以及人物控制器数组表示在该河岸上的位置和人物。

构造函数设置了位置数组,对GameObject进行实例化。

public CoastCon(string _status) {
    positions = new Vector3[] {new Vector3(6.5F,2.25F,0), new Vector3(7.5F,2.25F,0), new Vector3(8.5F,2.25F,0), 
                               new Vector3(9.5F,2.25F,0), new Vector3(10.5F,2.25F,0), new Vector3(11.5F,2.25F,0)};

    passenger = new ChaCon[6];

    if (_status == "from") {
        coast = Object.Instantiate (Resources.Load ("Perfabs/Coast", typeof(GameObject)), from_pos, Quaternion.identity, null) as GameObject;
        coast.name = "from";
        status = 1;
    } else {
        coast = Object.Instantiate (Resources.Load ("Perfabs/Coast", typeof(GameObject)), to_pos, Quaternion.identity, null) as GameObject;
        coast.name = "to";
        status = -1;
    }
}

当人物需要上岸时,需要找到空闲位置从而得到人物运动的目的地,并修改人物数组。当人物下岸时遍历人物数组进行修改。

public void getOnCoast(ChaCon cha) {
    int index = getEmptyIndex();
    passenger[index] = cha;
}

public ChaCon getOffCoast(string name) {	// 0->priest, 1->devil
    for (int i = 0; i < passenger.Length; i++) {
        if (passenger[i] != null && passenger[i].getName() == name) {
            ChaCon cha = passenger [i];
            passenger [i] = null;
            return cha;
        }
    }
    return null;
}

建立船控制器

需要船的GameObejct,运动类,船运动方向的状态(1为从起始河岸到终点河岸,-1为从终点河岸到起始河岸),人物数组,位置数组。

在构造函数中进行GameObject的实例化,增加运动类组件,ClickGUI组件。

public BoatCon() {
    status = 1;
    from_positions = new Vector3[] { new Vector3 (4.5F, 1.5F, 0), new Vector3 (5.5F, 1.5F, 0) };
    to_positions = new Vector3[] { new Vector3 (-5.5F, 1.5F, 0), new Vector3 (-4.5F, 1.5F, 0) };

    boat = Object.Instantiate (Resources.Load ("Perfabs/Boat", typeof(GameObject)), fromPosition, Quaternion.identity, null) as GameObject;
    boat.name = "boat";

    move = boat.AddComponent (typeof(Move)) as Move;
    boat.AddComponent (typeof(ClickGUI));
}

运动时设置运动类的终点,并将运动方向状态取反。

public void Move() {
    if (status == -1) move.setDest(fromPosition);
    else move.setDest(toPosition);
    status=-status;
}

与河岸控制器类似,在上船和下船时需要寻找空闲位置,修改人物数组。

接下来是Controller部分

它需要实现在Model中的SceneControllerUserAction接口。

Awake函数中,获得Director类的实例,加载资源和人物,实现一开始场景的加载。

void Awake() {
    Director director = Director.getInstance ();
    director.currentSceneController = this;
    userGUI = gameObject.AddComponent <UserGUI>() as UserGUI;
    characters = new ChaCon[6];
    loadResources();
    loadCharacter();
}

实现SceneController接口,实现加载资源和人物函数。

加载资源和人物就是调用Model部分中的对应类的构造函数进行实例化。同时,需要进行一些初始化操作:人物上到起始河岸,记录起始河岸承载的人物。

public void loadResources() {
    Vector3 river_pos = new Vector3(0,0.5F,0);
    GameObject river = Instantiate (Resources.Load ("Perfabs/River", typeof(GameObject)), river_pos, Quaternion.identity, null) as GameObject;
    river.name = "river";
    fromCoast = new CoastCon ("from");
    toCoast = new CoastCon ("to");
    boat = new BoatCon ();
}

public void loadCharacter() {
    for (int i = 0; i < 3; i++) {
        ChaCon cha = new ChaCon ("priest");
        cha.setName("priest" + i);
        cha.setPosition (fromCoast.getEmptyPosition ());
        cha.getOnCoast (fromCoast);
        fromCoast.getOnCoast (cha);
        characters [i] = cha;
    }
    for (int i = 0; i < 3; i++) {
        ChaCon cha = new ChaCon ("devil");
        cha.setName("devil" + i);
        cha.setPosition (fromCoast.getEmptyPosition ());
        cha.getOnCoast (fromCoast);
        fromCoast.getOnCoast (cha);
        characters [i+3] = cha;
    }
}

实现UserAction接口,需要实现对船和人物的点击函数。

对船的点击函数,首先需要判断船是否为空,如果为空则不能运动,船运动后根据规则判定游戏结果。

public void ClickBoat() {
    if (boat.isEmpty ()) return;
    boat.Move ();
    userGUI.status = check();
}

对人物的点击函数需要求出目的地的空位,调用人物类中的moveToPosition函数进行运动,并同时对人物所属的河岸控制器进行修改,调用船或河岸的函数实现上/下的逻辑。最后根据规则判定游戏结果。

public void ClickCha(ChaCon cha) {
    if (cha.isOnBoat ()) {
        CoastCon nowCoast;
        if (boat.getStatus () == -1) nowCoast = toCoast; // to->-1; from->1	
        else nowCoast = fromCoast;
        boat.getOffBoat (cha.getName());
        cha.moveToPosition (nowCoast.getEmptyPosition ());
        cha.getOnCoast (nowCoast);
        nowCoast.getOnCoast (cha);
    } 
    else {									// character on coast
        CoastCon nowCoast = cha.getCoastCon ();
        if (boat.getEmptyIndex () == -1) return; // boat is full
        if (nowCoast.getStatus () != boat.getStatus ())	return; // boat is not on the side of character
        nowCoast.getOffCoast(cha.getName());
        cha.moveToPosition (boat.getEmptyPosition());
        cha.getOnBoat (boat);
        boat.getOnBoat (cha);
    }
    userGUI.status = check();
}

check函数实现了对游戏结果的判定:如果终点河岸人数为6则胜利,如果有一边河岸和靠岸的船(如果存在)中魔鬼人数大于牧师人数且牧师人数不为0则失败,否则游戏继续进行。

int check() {	// 0->not finish, 1->lose, 2->win
    int fromP = 0, fromD = 0, toP = 0, toD = 0;
    int[] fromCount = fromCoast.getCharacterNum();
    fromP += fromCount[0];
    fromD += fromCount[1];

    int[] toCount = toCoast.getCharacterNum ();
    toP += toCount[0];
    toD += toCount[1];

    if (toP + toD == 6) return 2; //win
    int[] boatCount = boat.getCharacterNum ();
    if (boat.getStatus () == -1) {
        toP += boatCount[0]; toD += boatCount[1];	// boat at toCoast
    }
    else {
        fromP += boatCount[0]; fromD += boatCount[1];	// boat at fromCoast	
    }
    if (fromP < fromD && fromP > 0) return 1; //lose		
    if (toP < toD && toP > 0) return 1; //lose
    return 0;			// not finish
}

最后是View部分,在UserGUI类中绘制胜利或失败时的label以及button。

void OnGUI() {
    if (status == 1) {
        GUI.Label(new Rect(Screen.width/2-50, Screen.height/2-85, 100, 50), "Gameover!", style);
        if (GUI.Button(new Rect(Screen.width/2-70, Screen.height/2, 140, 70), "Restart", buttonStyle)) {
            status = 0;
            action.restart ();
        }
    } else if(status == 2) {
        GUI.Label(new Rect(Screen.width/2-50, Screen.height/2-85, 100, 50), "You win!", style);
        if (GUI.Button(new Rect(Screen.width/2-70, Screen.height/2, 140, 70), "Restart", buttonStyle)) {
            status = 0;
            action.restart ();
        }
    }
}

游戏效果演示:

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

3D游戏编程实践——Priests and Devils 的相关文章

  • angular2 Http请求

    提供HTTP服务 HttpModule并不是Angular的核心模块 它是Angular用来进行Web访问的一种可选方式 并位于一个名叫 angular http的独立附属模块中 编辑app module ts import HttpMod
  • Fortran基础1——声明及数据类型

    声明及数据类型 一 声明的意义 告诉编译器要预留一些存放数据的内存空间 二 基本数据类型 数据类型 描述 整数 integer a 浮点数 real a 字符 character a 逻辑变量 logical a 复数 complex a
  • 程序员的自我修养——链接、装载与库

    1 温故而知新 操作系统概念 北桥 连接高速芯片 系统调用接口 以软件中断的方式提供 如Linux使用0x80号中断作为系统调用接口 多任务系统 进程隔离 设备驱动 直接使用物理内存的弊端 地址空间不隔离 内存使用效率低 程序运行的地址不确
  • httpclient 聚合

    文章目录 依赖 DefaultHttpClient 废弃 设置代理 传文件 HttpClientUtil version 4 3 6 依赖
  • html+js实现输入用户名和密码点击登录跳转页面

    html js实现 输入用户名和密码点击登录跳转其他页面 这里主页是index html 跳转的页面名字是随机点名 html 1 index html 用户名
  • OpenCV-Python调整图像对比度和带文字白纸照片背景漂白方法

    一 引言 在前面我们介绍了直方图均衡可以调整图像的对比度 那么还有没有其他方式调整对比度呢 答案是肯定的 今天就来招硬核的 二 对比度调整的硬核方法 这招硬核方法就是参考灰度图像的阈值处理 我们知道灰度图像的阈值处理的基本思想是 将图像中灰
  • SVN使用手册【简洁明了】

    这里写自定义目录标题 欢迎观看我的文档 废话不多说直接上方法 适合新手小白 SVN的工作原理 SVN的主要操作 1 SVN检出 SVN Checkout 2 SVN提交 上传SVN Commit SVN更新 下载 SVN Update 4
  • Kafka性能保证和延时队列实现原理

    数据不丢不漏 不重不错 一 不丢 生产写入消息不丢失 数据组织形式 首先 从数据组织形式来说 kafka有三层形式 kafka有多个主题 topic 每个主题有多个分区 分区分为主分区和副本分区 每个分区又有多条消息 而每个分区可以分布到不
  • epoll文件服务器,基于epoll实现的一个简单的web服务器

    详解1 EPOLLIN 文件描述符可以读 EPOLLOUT 文件描述符可以写 EPOLLPRI 文件描述符有紧急的数据可写 EPOLLERR 文件描述符发生错误 EPOLLHUP 文件描述符被挂起 EPOLLET 文件描述符有事件发生 所用
  • 服务器2012怎么安装无线网卡驱动,如何安装usb无线网卡驱动,教您如何安装电脑usb驱动...

    现在很多人都喜欢用无线上网 其原因就是摆脱了有限的束缚 使得电脑可以随时随地的上网 如果想要使用无线网卡的话 首先需要安装无线网卡驱动 那么无线网卡驱动怎么安装 这里 小编就来给大家分享无线网卡驱动安装的图文教程 对于没有内置无线网卡的台式
  • MacOS全套详细介绍如何下载Nginx

    前言 最近心血来潮试着用Mac下载Nginx 下载过程中也出现了一些下载问题 为了方便诸位更加舒心使用Nginx 避免走一些弯路和阅读大量文档 减少大量不必要的时间 这里就集中从0 1做个详细介绍 1 查看是否有HomeBrew Homeb
  • 理解 Linux 配置文件 [转载]

    理解 Linux 配置文件 转载 http www 900 ibm com developerWorks cn linux management configuration index shtml 分类和使用 Subodh Soni sub
  • echarts坐标轴

    坐标轴 直角坐标系中的 x y 轴 x 轴 y 轴 x 轴和 y 轴都由轴线 刻度 刻度标签 轴标题四个部分组成 部分图表中还会有网格线来帮助查看和计算数据 普通的二维数据坐标系都有 x 轴和 y 轴 通常情况下 x 轴显示在图表的底部 y
  • 服务器处理数据和信息吗,1.1 NG搭建服务器 与 动态数据处理介绍

    fastCGI 动态请求数据处理 1 fastcgi安装步骤 1 configure 生成makefile 2 在 make运行makefile sudo make instll 安装即可 出现错误如下 image png 解决方案如下 找
  • 微服务配置中心springcloud从nacos动态获取最新配置信息

    1 添加nacos配置中心依赖
  • springBoot+MongoTemplate,往多个数据库中插入数据

    一 准备 1 建立一个springBoot的maven项目 使用spring initailizr 或者建立一个maven后自己添加相关springBoot配置
  • 使用Pritunl OpenVPN远程连接:实现安全高效的远程访问

    文章目录 前言 1 环境安装 2 开始安装 3 访问测试 4 创建连接 5 局域网测试连接 6 安装cpolar 7 配置固定公网访问地址 8 远程连接测试 前言 Pritunl是一款免费开源的 VPN 平台软件 但使用的不是标准的开源许可
  • 使用PyCharm Professional Edition开发Flask Web程序

    PyCharm官方页面 https www jetbrains com pycharm 注意 只有专业版支持以下全部操作 社区版和教育版均不支持 一 使用PyCharm创建VirtualEnv File Default Settings D
  • 组合测试方法PK正交分析方法

    测试过程中 我们经常遇到需要覆盖多个变化参数的测试场景 如我们测试BS配置控制客户端组织资源远程配置一个设备时 进行一个设备通道视频参数设置的各种组合测试 如下图 多数情况下 类似于这种多组合测试时 老员工则是依靠经验去进行有针对性的测试

随机推荐

  • C++ 实现获取系统名称

    项目中需要用到操作系统名及版本信息 下面是用两种方法实现的 一种是通过查询注册表 include stdafx h include
  • Qt QGraphicsItem及派生类设置是否可选中,是否可移动

    设置可选中 可移动 setFlags ItemIsSelectable ItemIsMovable 设置不可选中 setFlags flags ItemIsSelectable
  • 在计算机上搭建云服务

    首先是安装VirtualBox虚拟机 这里由于之前已经安装完成 所以不再一一演示 对于虚拟机储存位置的设置 VirtualBox菜单 管理 gt 全局设定 常规页面 选择 默认虚拟电脑位置 即可 创建虚拟机内部虚拟网络 VirtualBox
  • 堆栈01--用两个栈实现队列

    堆栈01 用两个栈实现队列 jz05 题目概述 解析 参考答案 注意事项 说明 题目概述 算法说明 用两个栈来实现一个队列 完成队列的Push和Pop操作 队列中的元素为int类型 测试用例 队列先进先出 输入 1 2 输出 1 2 解析
  • U盘装系统教程,一键安装和U盘安装的区别

    装系统教程U盘安装篇 一键安装和U盘安装的区别 一键重装系统工具 优点 1 集成线上重装和本地备份还原功能 让用户多重选择 2 无人值守 无需电脑知识的小白用户也可以使用 3 用户可选择工具自带系统镜像安装 不需要单独下载 缺点 1 线上重
  • Elo评分算法原理与实现

    社交网络 里的Mark Zackburg被女朋友甩后 在舍友的启发下 充分发挥了技术宅男自娱自乐的恶搞天分 做出了Facemash网站 对学校女生的相貌进行排名打分 结果网站访问流量过大 直接把学校网络搞瘫痪了 Facemask大受欢迎的关
  • 创建vue项目,完整步骤

    1 安装nodejs node官网 https nodejs org en 检测是否安装成功 node v npm v 电脑上有多版本node时 需要用 nvm 管理工具 2 全局安装脚手架 vue cli npm install vue
  • Qt的信号和槽机制

    在C 中 对象与对象之间通信 要通过调用成员函数的方式来完成 而在Qt中提供了一种对象间的通信方式 信号与槽的机制 Qt通过QObject来提供这个通信机制 它的工作方式也很简单 通过QObject对象提供的connect连接函数将信号与处
  • SAP-ABAP-普通OOALV,OOALV分屏展示,发送邮件excel附件合并单元格,附件带框线,附件居中。

    功能展示 1 三个可拖动变换大小的屏幕 2 普通OOALV 3 带格式的邮件附件 三个表格 合并居中 单元格带框线 指定列宽 代码如下 复制可直接激活 没有include 创建程序后还有一些其他步骤 详情见后文 Report ZLQT OO
  • 工程和界面—Webstorm入门指南

    转载自http www 36ria com 5698 工程和界面 Webstorm入门指南 Webstorm中的工程 1 新建工程 Quick Start 界面新建工程 也可以点击顶部菜单栏 File gt New Project 弹出如下
  • MACD底背离选股公式——通达信、同花顺

    底背离 通达信版 同花顺版 DIFF EMA CLOSE 12 EMA CLOSE 26 DEA EMA DIFF 9 MACD 2 DIFF DEA QZQ BARSLAST REF MACD 1 lt 0 AND MACD gt 0 Q
  • c语言统计出现个数,C语言统计数字出现的个数

    程序功能 统计数字出现的个数 例如 输入1 2 3 1 2 4 2 3 1 输出 1 3 2 3 3 2 4 1 能看懂吗 就是1出现3次 2出现3次 3出现2次 4出现1次 define M 50 main int a M c 5 i n
  • SQLite、MySQL和PostgreSQL 三种关系数据库比较

    关系型数据库的使用已经有相当长的时间了 它们变得流行起来托了管理系统的福 关系模型被实现得相当的好 并且被证明是操作数据的好方法 特别是事务性强的应用 在这篇DigitalOcean文章中 我们将尝试理解一些最常用 最流行的关系型数据库管理
  • 正确打开visual studio2015

    找到visual studio2015的安装目录 点击其中的devenv exe打开
  • 02-RabbitMQ之Docker安装Rabbit单机与集群

    一 docker安装单机rabbit 1 查找rabbitmq镜像或者在docker仓库查看rabbitmq镜像 docker search rabbitmq 2 拉取最新的rabbitmq docker pull rabbitmq 3 运
  • KEIL经常出现 Encountered an improper argument 弹窗

    关于 keil5 使用在线调试时 经常出现 Encountered an improper argument 弹窗 经实测 可有如下方法 方法1 下载UV4 exe 替换本机电脑 Keil UV4目录下的UV4 exe 更换后 如果不能编译
  • 7-14 解一元一次方程 (17 分)

    请编写程序 解一元一次方程 ax b 0 一元一次方程求解公式为 x ab 求解要求 a 0 方程有唯一解 输出解 a 0 b 0 方程无解 输出no solution a 0 b 0 则方程有无穷多解 输出Infinitely solut
  • The absolute uri: http://java.sun.com/jsp/jstl/fmt cannot be resolved in either web.xml or the jar

    错误提示 org apache jasper JasperException file H netbeans workspace netbeans 6 9 ShoppingSystemOnline build web system fron
  • [LDUoj 倍增] 题解

    星星之火 可以燎原 细节的地方慢慢补充 欢迎提出问题 私聊 留言均可 A 跳跳棋 较难 B 聚会 板子题 C 祖孙询问 板子题 D Dis 板子题 E 次小生成树 严格次小生成树 难 F 异象石 难度适中 G 暗的连锁 难度适中 H 点的距
  • 3D游戏编程实践——Priests and Devils

    编程实践 Priests and Devils github链接 https github com ctlchild SYSU unity3d learning tree master hw2 Priests and Devils is a