unity实现牧师与魔鬼2.0(动作分离版)& 基本操作演练 & 材料与渲染联系

基本操作演练

下载 Fantasy Skybox FREE, 构建自己的游戏场景

在Window->Asset Store 中下载Fantasy Skybox FREE,并将其全部import进项目中。然后在场景栏中右键3D Object->Terrain创建地形,点击地形,在Inspector栏中选择Paint Terrain,点击Paint Texture,然后选择Edit Terrain Layers,点击Add Layer。将相应的地形贴图添加。
然后选择 Raise or Lower Terrain对地形进行改变,Smooth Height可以对地形进行光滑处理。

地形处理好后,接下来要给地面上种一些草。选择Paint Details中的Edit Details,选择合适的草。


添加完成后,就可以往地面上种草了。

为了让场景更加生动,还需要添加天空盒,首先选中摄像头,然后在上方的菜单栏中选择Component->Rendering->Skybox。于是在该摄像头上就会看到一个Skybox组件,选择相应的天空盒贴图即可。
最终场景完成如下:

写一个简单的总结,总结游戏对象的使用

游戏对象是游戏中的基本单位,不仅包括2D对象和3D对象,还包括了摄像头和光源。每一个游戏对象都有自己的属性,有自己的位置。可以通过C#脚本来控制游戏对象的移动、创建和销毁。

编程实践

牧师与魔鬼 动作分离版

在上一版牧师与魔鬼的基础上,将游戏对象中的move组件分离出来,通过一个专门的动作管理器管理所有动作。区分与每个游戏对象都拥有一个move脚本,该版本的牧师与魔鬼通过场景控制器来专门控制游戏对象的移动。另外根据要求还需要设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束。
UML类图如下:

将所有动作管理类合并成一个C#文件,所有文件如下:

代码分析
  • SSAction(动作基类)
public class SSAction : ScriptableObject
{public bool enable = true;public bool destroy = false;public GameObject gameobject {get;set;}public Transform transform {get;set;}public ISSActionCallback callback {get;set;}protected SSAction() {}public virtual void Start(){throw new System.NotImplementedException();}public virtual void Update(){throw new System.NotImplementedException();}
}

动作基类,其它所有的动作都继承于该类。其继承于ScriptableObject类,代表不需要绑定 GameObject 对象的可编程基类,这些对象受 Unity 引擎场景管理。

  • CCMoveToAction(管理移动动作的实现)
public class CCMoveToAction : SSAction
{public Vector3 target;public float speed;public static CCMoveToAction GetSSAction(Vector3 target, float speed){CCMoveToAction action = ScriptableObject.CreateInstance<CCMoveToAction>();action.target = target;action.speed = speed;return action;}public override void Update(){this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);if (this.transform.position == target){//waiting for destroythis.destroy = true;this.callback.SSActionEvent(this);}}public override void Start(){}
}

通过设置速度和目的地,使得游戏对象可以以一定的速度向目的地移动

  • CCSequenceAction(顺序动作组合类实现)
public class CCSequenceAction : SSAction, ISSActionCallback
{public List<SSAction> sequence;public int repeat = -1;public int start = 0;public static CCSequenceAction GetSSAcition(int repeat, int start, List<SSAction> sequence){CCSequenceAction action = ScriptableObject.CreateInstance<CCSequenceAction>();action.repeat = repeat;action.sequence = sequence;action.start = start;return action;}public override void Update(){if (sequence.Count == 0) return;if (start < sequence.Count){sequence[start].Update();}}public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,int intParam = 0, string strParam = null, Object objectParam = null){source.destroy = false;this.start++;if (this.start >= sequence.Count){this.start = 0;if (repeat > 0) repeat--;if (repeat == 0){this.destroy = true;this.callback.SSActionEvent(this);}}}public override void Start(){foreach (SSAction action in sequence){action.gameobject = this.gameobject;action.transform = this.transform;action.callback = this;action.Start();}}void OnDestroy(){//TODO: something}
}

实现一个动作组合序列,顺序播放动作,通过继承SSAction类和ISSActionCallback类,使得游戏对象完成一个动作时可以去处理下一个动作。通过设置SSActionEvent()函数,则当前动作执行完成时,推下一个动作,如果完成一次循环,减次数。当次数减为0时,通知动作管理器动作已完成。

  • ISSActionCallback(动作事件接口)
public interface ISSActionCallback
{void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,int intParam = 0, string strParam = null, Object objectParam = null);
}

通过该接口,当动作完成时,动作会调用该接口使得动作管理器去完成下一个动作。

  • SSActionManager(动作管理基类)
public class SSActionManager : MonoBehaviour, ISSActionCallback                      //action管理器
{private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();    //将执行的动作的字典集合,int为key,SSAction为valueprivate List<SSAction> waitingAdd = new List<SSAction>();                       //等待去执行的动作列表private List<int> waitingDelete = new List<int>();                              //等待删除的动作的key                public int Moving = 0;protected void Update(){foreach (SSAction ac in waitingAdd){actions[ac.GetInstanceID()] = ac;                                      //获取动作实例的ID作为key}waitingAdd.Clear();foreach (KeyValuePair<int, SSAction> kv in actions){SSAction ac = kv.Value;if (ac.destroy){waitingDelete.Add(ac.GetInstanceID());}else if (ac.enable){ac.Update();}}foreach (int key in waitingDelete){SSAction ac = actions[key];actions.Remove(key);DestroyObject(ac);}waitingDelete.Clear();}public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager){action.gameobject = gameobject;action.transform = gameobject.transform;action.callback = manager;waitingAdd.Add(action);action.Start();Moving++;}public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,int intParam = 0, string strParam = null, Object objectParam = null){Moving--;}
}

管理所有基本动作,由于其继承了ISSActionCallback接口,使得一个动作完成时,可以告知动作管理器去处理下一个动作。

public class MySceneActionManager : SSActionManager
{private CCMoveToAction moveBoatToEndOrStart;private CCSequenceAction moveRoleToBank;private CCSequenceAction moveRoleToBoat;public Controll sceneController;protected new void Start(){sceneController = (Controll)SSDirector.GetInstance().CurrentScenceController;sceneController.actionManager = this;}public void moveBoat(GameObject boat, Vector3 target, float speed){moveBoatToEndOrStart = CCMoveToAction.GetSSAction(target, speed);this.RunAction(boat, moveBoatToEndOrStart, this);}public void move_boat(Boat boat, Vector3 pos){moveBoatToEndOrStart = CCMoveToAction.GetSSAction(pos, 3);this.RunAction(boat.getGameObject(), moveBoatToEndOrStart, this);for(int i=0;i<2;++i){if(boat.roles[i] != null){Vector3 p = boat.roles[i].GetRolePos();move_role_to_bank(boat.roles[i].getGameObject(),new Vector3(-p.x + 2*i-1, p.y, p.z));}}}public void move_role_to_boat(GameObject role,Vector3 pos){Vector3 pos2 = new Vector3(role.transform.position.x, role.transform.position.y + 0.25f, role.transform.position.z);Vector3 pos1 = new Vector3(pos.x, pos2.y, role.transform.position.z);SSAction action1 = CCMoveToAction.GetSSAction(pos2, 3);SSAction action2 = CCMoveToAction.GetSSAction(pos1, 3);moveRoleToBoat = CCSequenceAction.GetSSAcition(1,0,new List<SSAction> {action1,action2});this.RunAction(role, moveRoleToBoat, this);}public void move_role_to_bank(GameObject role,Vector3 pos){SSAction action1 = CCMoveToAction.GetSSAction(pos, 3);moveRoleToBank = CCSequenceAction.GetSSAcition(1,0,new List<SSAction> {action1});this.RunAction(role, moveRoleToBank, this);}
}

管理角色和船的移动,其中角色的移动分为从岸到船和从船到岸。前者需要两个SSMoveToAction动作组合,而后者和船的移动一样只需要一个SSMoveToAction。

  • Controll
    修改过后,由于游戏对象不再通过自身的move脚本来移动,而是统一通过动作管理器来控制移动,所以部分修改代码如下:
    public MySceneActionManager actionManager;public Judge judge;void Start(){SSDirector director = SSDirector.GetInstance();director.CurrentScenceController = this;User = gameObject.AddComponent<UserGUI>() as UserGUI;LoadResources();actionManager = gameObject.AddComponent<MySceneActionManager>() as MySceneActionManager;judge = gameObject.AddComponent<Judge>() as Judge;}public void MoveBoat(){if(boat.IsEmpty())return;if(actionManager.Moving > 0)return;actionManager.move_boat(boat,boat.BoatMoveToPosition());User.check = judge.check();}public void MoveRole(Role role){if(role.GetOnBank() == -boat.Get_boat_bank()) return;if(actionManager.Moving > 0)return;Vector3 pos;if(role.GetOnBank() == 1 || role.GetOnBank() == -1){ //move role to boatint temp = 0;temp = boat.SetRoles(role);       //返回一个空位置,若没有则返回-1if(temp == -1)return;             //boat is fullpos = boat.GetBoatPosition();if(temp == 1 && role.GetOnBank() == 1) pos.x += 1;if(temp == 0 && role.GetOnBank() == -1) pos.x -= 1;//role.RoleMove(pos);actionManager.move_role_to_boat(role.getGameObject(),pos);  //动作分离版本if(role.GetOnBank() == 1) right_bank.DeleteRole(role);else left_bank.DeleteRole(role);role.SetOnBank(2);}else if(role.GetOnBank() == 2){                      //move role to bankif(boat.Get_boat_bank() == 1){pos = right_bank.GetPos();role.SetOnBank(1);right_bank.AddRole(role);boat.DeleteRole(role);//role.RoleMove2(pos);actionManager.move_role_to_bank(role.getGameObject(),pos);//动作分离版本}else {pos = left_bank.GetPos();role.SetOnBank(-1);left_bank.AddRole(role);boat.DeleteRole(role);//role.RoleMove2(pos);actionManager.move_role_to_bank(role.getGameObject(),pos);//动作分离版本}User.check = judge.check();}}
裁判类的实现

由于需要设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束。所以将原先控制器中的check()函数用一个专门的裁判类替换,代替控制器中的check()函数。

public class Judge : MonoBehaviour{public Controll mainController;public Boat boat;public Bank left_bank;public Bank right_bank;public MySceneActionManager actionManager;void Start(){mainController = (Controll)SSDirector.GetInstance().CurrentScenceController;this.boat = mainController.boat;this.left_bank = mainController.left_bank;this.right_bank = mainController.right_bank;this.actionManager = mainController.actionManager;}public int check(){if(left_bank.GetPriestSum() == 3 && left_bank.GetDevilSum() == 3)return 1;if(boat.Get_boat_bank() == -1){if((left_bank.GetDevilSum() + boat.GetDevilSum() > left_bank.GetPriestSum() + boat.GetPriestSum()) && (left_bank.GetPriestSum() + boat.GetPriestSum()) != 0 && actionManager.Moving > 0)return 2;else if((left_bank.GetDevilSum() + boat.GetDevilSum() > left_bank.GetPriestSum() + boat.GetPriestSum()) && (left_bank.GetPriestSum() + boat.GetPriestSum()) != 0 && actionManager.Moving == 0)return 3;if(right_bank.GetDevilSum() > right_bank.GetPriestSum() && right_bank.GetPriestSum() != 0 && actionManager.Moving > 0) return 2;else if(right_bank.GetDevilSum() > right_bank.GetPriestSum() && right_bank.GetPriestSum() != 0 && actionManager.Moving == 0) return 3;}else if(boat.Get_boat_bank() == 1){if((right_bank.GetDevilSum() + boat.GetDevilSum() > right_bank.GetPriestSum() + boat.GetPriestSum()) && (right_bank.GetPriestSum() + boat.GetPriestSum()) != 0 && actionManager.Moving > 0)return 2;else if((right_bank.GetDevilSum() + boat.GetDevilSum() > right_bank.GetPriestSum() + boat.GetPriestSum()) && (right_bank.GetPriestSum() + boat.GetPriestSum()) != 0 && actionManager.Moving == 0)return 3;if(left_bank.GetDevilSum() > left_bank.GetPriestSum() && left_bank.GetPriestSum() != 0 && actionManager.Moving > 0) return 2;else if(left_bank.GetDevilSum() > left_bank.GetPriestSum() && left_bank.GetPriestSum() != 0 && actionManager.Moving == 0) return 3;}return 0;}}

裁判类继承了MonoBehaviour。MonoBehaviour是每个脚本的基类,又继承于Behaviours。只有继承了MonoBehaviour的类才可以作为组件挂载到游戏对象上,在unity 中所有继承MonoBehaviour的类是不可以实例化的,因为unity都会自动为其创建实例,所以我们需要调用该类的时候不使用new,而是使用AddComponent来调用。

因此,通过在需要用到check函数的类里面声明一个裁判类并将其添加到游戏对象中就可以调用里面的check方法。实现对游戏结果的判断。
项目文件和代码

材料与渲染联系【可选】

  • Standard Shader 自然场景渲染器
    阅读官方 Standard Shader 手册 。
    选择合适内容,如 Albedo Color and Transparency,寻找合适素材,用博客展示相关效果的呈现
    官方Standard Shader手册
    新创建一个球,一个材料。设置材料的Rendering Mode为Transparent

    通过调色板可以调整材料的颜色和透明度

    通过调节材料的Metallic和Smoothness属性,可以分别改变物体的金属参数和平滑度。

    最后,将材料拖到小球上。
  • 声音
    阅读官方 Audio 手册
    用博客给出游戏中利用 Reverb Zones 呈现车辆穿过隧道的声效的案例

创建一个空对象,往空对象中增加两个组件,分别是Audio source和Audio Reverb Zone。然后将Audio Reverb Zone中的Reverb Preset改成cave。再往Audio source中添加相应的音频即可。

unity实现牧师与魔鬼2.0(动作分离版) 基本操作演练相关推荐

  1. 【3D游戏编程与设计】四 游戏对象与图形基础 : 构建游戏场景+牧师与魔鬼 动作分离版

    [3D游戏编程与设计]四 游戏对象与图形基础 : 构建游戏场景+牧师与魔鬼 动作分离版 基本操作演练 下载 Fantasy Skybox FREE, 构建自己的游戏场景 下载 Fantasy Skyb ...

  2. Unity实战之牧师与魔鬼(动作分离版)

    Unity实战之牧师与魔鬼(动作分离版) 项目链接 整体描述 本次项目在第一版牧师与魔鬼的基础上,将动作从场记中分离出来,并设计一个裁判类实时监测游戏进行的情况.这样改进的优点有很多: 降低了不同功能 ...

  3. Unity牧师与魔鬼小游戏(动作分离版)

    Unity牧师与魔鬼小游戏(动作分离版) 前言 这是中大计算机学院3D游戏编程课的一次作业,在这里分享一下设计思路. 主要代码上传到了gitee上,请按照后文的操作运行. 项目地址:https://g ...

  4. Unity实现牧师与魔鬼动作分离版

    牧师与魔鬼动作分离版 项目地址 动作管理器的设计 程序设计框架: 为了用一组简单的动作组合成复杂的动作,我们采用 cocos2d 的方案,建立与 CCAtion 类似的类. 通过门面模式(控制器模式) ...

  5. unity编程实践-牧师与魔鬼动作分离版

    作业要求 牧师与魔鬼 动作分离版 [2019开始的新要求]:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束 目标:建立动作管理器,使动作抽象出来,可以应用到任何游戏对象上,以此提高代码复 ...

  6. 基于Unity开发的牧师与魔鬼动作分离版游戏设计

    1 作业要求 牧师与魔鬼 动作分离版 设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束 2 实现细节 在原来代码的基础上,修改如下: 将UserGUI的sign成员变量和Controlle ...

  7. unity实现牧师与魔鬼问题回答

    unity实现牧师与魔鬼&问题回答 简答题 游戏对象运动的本质是什么? 游戏运动本质就是使用矩阵变换(平移.旋转.缩放)改变游戏对象的空间属性. 请用三种方法以上方法,实现物体的抛物线运动.( ...

  8. Unity3D项目四:牧师与魔鬼(动作分离版)

    Unity3D项目四:牧师与魔鬼(动作分离版) 基本介绍 动作管理是游戏的重要内容,全部都放在游戏对象里显得十分笨重,所以本次项目需要将动作从对象中提取出来写成单独的动作控制器.动作控制器来管理控制所 ...

  9. Unity实战——牧师与魔鬼

    Unity实战--牧师与魔鬼 项目源码 最终效果 牧师与魔鬼游戏效果 背景描述 牧师与魔鬼是一款益智游戏,您将帮助牧师与魔鬼在规定时间内过河.河边有三个牧师和三个魔鬼.他们都想去这条河的对岸,但只有一 ...

最新文章

  1. window.open html打开一个新页面
  2. python小项目案例-python_flask小项目实例-编一个小网站
  3. boost::date_time模块测试时间分辨率特征
  4. c++运算符优先级总结
  5. 新手上路必学的Python函数基础知识,全在这里了(多段代码举例)
  6. v4l2 框架下如何设置分辨率_【微学习】低压计量电表如何设置?(下)
  7. es6 Generator函数概述
  8. java8 lambda this_java8里lambda里的 this 为什么会指向 lamdba 所在的外部类
  9. maven打的包带exec包比不带的大_spring boot maven打包可执行jar包缺少依赖包的问题...
  10. 一次领光天猫双 11 所有优惠卷
  11. Visio连接线相关问题
  12. AMD不要靠近卡巴斯基,会变得不幸(卡巴斯基导致的蓝屏问题)
  13. python爬取豆瓣Top250完整代码
  14. android 浏览器 该网站的安全证书有问题
  15. marshmallow——快速入门
  16. 2020CCF BDCI 企业非法集资风险预测-线上0.848(水哥的baseline),在此基础已做到线上0.848,排名前1%(参赛队伍3000+))。
  17. 我的世界手机版虚拟人生服务器,我的世界大型虚拟人生整合包
  18. 电动推杆复位程序c语言,程序中怎样区别是热复位还是冷复位?
  19. mysql中local方法,LOCAL
  20. 简单回顾下过去这一年的工作

热门文章

  1. 气象数据的各种插值问题 | 小骏不抬杠
  2. POJ 3616 DP
  3. 闪客工具:postman接口测试工具
  4. python语言字符串定义_Python语言基础1-字符串
  5. dumpsys使用详解
  6. 2020宝塔面板和旗鱼云梯linux面板对比
  7. java的invoke_java 中 invoke()的作用是什么?
  8. 我用YOLOv5做情感识别!
  9. 云计算与大数据概论题库
  10. Oracle公司的历史发展