游戏对象与图形基础——牧师与魔鬼(动作分离版)

    科技2026-04-19  6

    文章目录

    游戏对象与图形基础——牧师与魔鬼(动作分离版)一、基本操作演练1.1 下载 Fantasy Skybox FREE, 构建自己的游戏场景1.2 总结: 二、牧师与魔鬼(动作分离版)2.1 UML图2.2 程序实现:FirstSceneActionManagerSS新增动作事件接口SSACTION动作管理类Judge类控制器GUI 2.3 效果展示2.4 仓库地址 三、材料与渲染联系3.1 Standard Shader 自然场景渲染器相关效果的实现3.2 用博客给出游戏中利用 Reverb Zones 呈现车辆穿过隧道的声效的案例

    游戏对象与图形基础——牧师与魔鬼(动作分离版)

    一、基本操作演练

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

    首先在Asset Store中,下载Fantasy Skybox FREE素材包,之后再unity中导入。 之后,我们可以看到素材包中包含了天空盒、terrain等材料的模型,我们这里直接使用模板的素材来构建游戏场景首先为camera添加天空盒: 之后将素材包中的SimpleTerrain地形加入场景,我们再用工具对地形进行一定的修改(挖点洞,更改一下地皮,设置地形平缓,种点花花草草),这样我们就完成了游戏场景的设置:‘

    离近点可以看到我们种得草:

    1.2 总结:

    首先我们最常见的游戏对象就是角色、地形等的实例游戏对象,就像我们牧师与魔鬼中的船、牧师、魔鬼等实例游戏对象;对这类游戏对象,我们的常见操作有对象的移动、实例化预设加载游戏对象、挂载脚本、设置对象形状属性、添加删改组件等,这是我们游戏过程中最直接操作和接触的游戏对象;游戏场景中的光照和摄像头是一种辅助对象,用于玩家在实际游戏过程中,对游戏场景进行观察。空GameObject常常用于挂载脚本,进行资源加载等操作;Terrain等地图编辑对象,可用于游戏地形等的设定。

    二、牧师与魔鬼(动作分离版)

    2.1 UML图

    程序设计的流程图根据老师课件中给出的UML图架构:

    2.2 程序实现:

    我们此次实在上一次作业的基础上,实现动作与游戏场景的分离,所以我们要在源代码的基础上,分离一个动作管理类(SSAction)出来,在动作管理类中,管理所有动作。动作管理类的实现,均按照课间代码方式,所以下面主要po出来具体实现代码和与上一次不同代码;

    这里给出上一次作业的博客和gitee仓库地址:空间与运动——模拟太阳系、牧师与魔鬼游戏实现详解、仓库地址

    FirstSceneActionManagerSS

    FirstSceneActionManagerSS是动作控制器,它继承了动作管理基类和SSActionCallback接口,实现人物和船的移动的调用

    public class FirstSceneActionManager : SSActionManager, SSActionCallback { public SSActionEventType Complete = SSActionEventType.Completed; public void BoatMove(BoatController Boat) { Complete = SSActionEventType.Started; CCMoveToAction action = CCMoveToAction.getAction(Boat.GetDestination(), Boat.GetMoveSpeed()); addAction(Boat.GetGameObject(), action, this); Boat.ChangeState(); } public void CharacterMove(Character GameObject, Vector3 Destination) { Complete = SSActionEventType.Started; Vector3 CurrentPos = GameObject.GetPosition(); Vector3 MiddlePos = CurrentPos; if (Destination.y > CurrentPos.y) { MiddlePos.y = Destination.y; } else { MiddlePos.x = Destination.x; } SSAction action1 = CCMoveToAction.getAction(MiddlePos, GameObject.GetMoveSpeed()); SSAction action2 = CCMoveToAction.getAction(Destination, GameObject.GetMoveSpeed()); SSAction seqAction = CCSequenceAction.getAction(1, 0, new List<SSAction> { action1, action2 }); this.addAction(GameObject.GetGameobject(), seqAction, this); } public void SSActionCallback(SSAction source) { Complete = SSActionEventType.Completed; } }

    新增动作事件接口

    public enum SSActionEventType : int { Started, Completed } public interface SSActionCallback { void SSActionCallback(SSAction source); }

    SSACTION动作管理类

    包含了动作管理基类 – SSActionManager、 顺序动作组合类实现CCSequenceAction、 简单动作实现CCMoveToAction、动作基类(SSAction),代码均使用课件上所给代码

    动作管理基类 SSActionManager:使用AddAction将游戏对象和动作绑定,并实现动作的自动回收 public class SSActionManager : MonoBehaviour { private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>(); private List<SSAction> waitingToAdd = new List<SSAction>(); private List<int> watingToDelete = new List<int>(); protected void Update() { foreach (SSAction ac in waitingToAdd) { actions[ac.GetInstanceID()] = ac; } waitingToAdd.Clear(); foreach (KeyValuePair<int, SSAction> kv in actions) { SSAction ac = kv.Value; if (ac.destroy) { watingToDelete.Add(ac.GetInstanceID()); } else if (ac.enable) { ac.Update(); } } foreach (int key in watingToDelete) { SSAction ac = actions[key]; actions.Remove(key); Object.Destroy(ac); } watingToDelete.Clear(); } public void addAction(GameObject gameObject, SSAction action, SSActionCallback ICallBack) { action.gameObject = gameObject; action.transform = gameObject.transform; action.CallBack = ICallBack; waitingToAdd.Add(action); action.Start(); } } 动作基类(SSAction):创建了动作的基本元素,继承ScriptableObject类,不需要绑定具体的游戏对象的可编程类; public class SSAction : ScriptableObject { public bool enable = true; public bool destroy = false; public GameObject gameObject; public Transform transform; public SSActionCallback CallBack; public virtual void Start() { throw new System.NotImplementedException(); } public virtual void Update() { throw new System.NotImplementedException(); } } 简单动作实现CCMoveToAction:实现物体的运动,就像非动作分离版本中的move类功能相似 public class CCMoveToAction : SSAction { public Vector3 target; public float speed; private CCMoveToAction() { } public static CCMoveToAction getAction(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(transform.position, target, speed * Time.deltaTime); if (transform.position == target) { destroy = true; CallBack.SSActionCallback(this); } } public override void Start() { } } 顺序动作组合类实现CCSequenceAction:实现动作的顺序实现,也是原本版本中的Move类中的函数的分离实现 public class CCSequenceAction : SSAction, SSActionCallback { public List<SSAction> sequence; public int repeat = 1; // 1->only do it for once, -1->repeat forever public int currentActionIndex = 0; public static CCSequenceAction getAction(int repeat, int currentActionIndex, List<SSAction> sequence) { CCSequenceAction action = ScriptableObject.CreateInstance<CCSequenceAction>(); action.sequence = sequence; action.repeat = repeat; action.currentActionIndex = currentActionIndex; return action; } public override void Update() { if (sequence.Count == 0) return; if (currentActionIndex < sequence.Count) { sequence[currentActionIndex].Update(); } } public void SSActionCallback(SSAction source) { source.destroy = false; this.currentActionIndex++; if (this.currentActionIndex >= sequence.Count) { this.currentActionIndex = 0; if (repeat > 0) repeat--; if (repeat == 0) { this.destroy = true; this.CallBack.SSActionCallback(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() { foreach (SSAction action in sequence) { Object.Destroy(action); } } }

    Judge类

    Judge类其实就是上个版本作业中的checkGameover函数的分离出来,单独作为一个脚本

    public class Judge : MonoBehaviour { CoastController fromCoast; CoastController toCoast; public BoatController boat; public Judge(CoastController c1,CoastController c2, BoatController b) { fromCoast = c1; toCoast = c2; boat = b; } public int Check() { // 0->not finish, 1->lose, 2->win int from_priest = 0; int from_devil = 0; int to_priest = 0; int to_devil = 0; int[] fromCount = fromCoast.GetobjectsNumber(); from_priest += fromCount[0]; from_devil += fromCount[1]; int[] toCount = toCoast.GetobjectsNumber(); to_priest += toCount[0]; to_devil += toCount[1]; if (to_priest + to_devil == 6) // win return 2; int[] boatCount = boat.GetobjectsNumber(); if (boat.get_State() == -1) { // boat at toCoast to_priest += boatCount[0]; to_devil += boatCount[1]; } else { // boat at fromCoast from_priest += boatCount[0]; from_devil += boatCount[1]; } if (from_priest < from_devil && from_priest > 0) { // lose return 1; } if (to_priest < to_devil && to_priest > 0) { return 1; } return 0; // not finish } }

    控制器

    将原本在Director类中的CharacterController控制器,BoatController控制器和LandController控制器拆分成单独的文件,并且删除Move控制器,因为被分离后的动作管理基类替代。

    GUI

    与原先版本基本相同,还是实现Label显示和Button的Restart功能和计时器功能

    2.3 效果展示

    添加了天空盒

    2.4 仓库地址

    gitee仓库

    三、材料与渲染联系

    3.1 Standard Shader 自然场景渲染器相关效果的实现

    首先我们进行金属关泽属性的渲染工作,我们首先新建5个material,均使用RGB(200,80,80)创建,之后使用不同的程度的渲染,查看它们之间的区别

    未进行渲染图:

    首先尝试使用不同的金属光泽来更改着色器,通过Shader中的Metallic来实现: 从左到右金属光泽程度分别为:0、0.25、0.5、0.75、1.0

    我们可以看到,随着金属光泽程度的加深,物体的反光性更高,并且颜色加深,类似于在原材质上方涂了一层包浆。

    尝试使用不同的平滑程度来渲染小球: 从左到右平滑程度为:0、0.25、0.5、0.75、1.0

    我们可以看到,随着平滑度的增加,材质从类似于磨砂的不反光材质,变成了全反光的类似与玻璃球的材质

    最后尝试一下镜面模式,首先要更换材质的类型为镜面模式,而非金属模式 我们选择几个不同的颜色镜面,最右侧两个选择相同颜色,光滑度不同的镜面

    我们可以看到,镜面类似于在物体上添加了一个对应颜色的反光镜,最后的呈现颜色是材质颜色和镜面颜色的混合。

    3.2 用博客给出游戏中利用 Reverb Zones 呈现车辆穿过隧道的声效的案例

    我们首先加入Audio Reverb Zone和Audio Source。 我们设定相机位置(0,0,0),所以设置音源位置为(-5,0,-12),这样我们的距离音源为13,我们的ReverbZone的最小距离为10,最大距离15,所以13位于梯度混响区; 我们这里将Reverb Preset设置为Cave,并为Audio Source加入一段音源(Asset Store中下载),之后这只音源为Loop,即可开始播放(这里没有找到开车音源,用了一段警笛音源代替)

    Processed: 0.014, SQL: 9