HW4 游戏对象与图形基础
1、基本操作演练【建议做】
-
下载 Fantasy Skybox FREE, 构建自己的游戏场景
-
在 Asset Store 中下载 Fantasy Skybox FREE ,下载完成后导入
-
创建一个Terrian对象,并使用如下的 Brushes 工具绘制山
绘制效果如下:
-
在这里插入图片描述
-
在 Asset Store 中下载 Mobile Tree Package ,下载完成后导入
-
在地形的Inspector界面,点击Edit Trees→Add Tree,将导入的预制树添加进去,然后用Brush添加即可
添加完效果如下:
-
点击Edit Details→Add Grass Texture,选择预制好的草,然后用Brush添加即可
但此时遇到一个小问题,用Brush添加后看不到,百度以后才知道是因为草太小了,在Scene界面放大以后可以才可以看到(????
- 最终效果:
(草因为太小了,看不到...)
-
写一个简单的总结,总结游戏对象的使用
-
Camera:
通过Camera来观察游戏世界。 -
Light:
光源,可以用来照明也可用于添加阴影 -
Empty空对象
空对象多被用于当做载体,例如挂载游戏脚本、成为其他对象的父对象等。 -
Cube等3D Object:
搭建游戏世界的组成元素,通过设置其Transform等属性来变换它们的位置、形态等。 -
Terrain等:
即是组成元素,又是编辑工具,例如Terrain本身是地图,然后又附带了绘制地图的各项工具(造山、造草等)。 -
除了直接在Unity3d中控制和改变对象的属性,我们还需要编写脚本来控制各种对象。
-
2、牧师与魔鬼 动作分离版
【2019开始的新要求】:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束
-
该版本改进的目的:
-
把每个需要移动的游戏对象的移动方法单独拿出来,建立一个动作管理器来管理不同的移动方法。
-
当动作很多或是需要做同样动作的游戏对象很多的时候,使用动作管理器可以让动作很容易管理,也提高了代码复用性。
-
上一个版本中,每一个可移动的游戏对象的组件都有一个Move脚本,当游戏对象需要移动时候,游戏对象自己调用Move脚本中的方法让自己移动。
而动作分离版中剥夺了游戏对象自己调用动作的能力,而是建立一个动作管理器,通过场景控制器(Controllor)把需要移动的游戏对象传递给动作管理器,让动作管理器去移动游戏对象。
-
-
具体实现如下:
Actions.cs:
-
SSAction是所有动作的基类,代码如下:
public class SSAction : ScriptableObject // 所有动作的父类 { public bool enable = true; public bool destroy = false; public GameObject gameobject; public Transform transform; public ISSActionCallback callback; // 回调函数 protected SSAction() { } // 使SSAction不会被new // 子类重写这两个函数 public virtual void Start() { throw new System.NotImplementedException(); } public virtual void Update() { throw new System.NotImplementedException(); } }
-
SSMoveToAction使对象以一定的速度向目的地移动,代码如下:
public class SSMoveToAction : SSAction // 移动类 { public Vector3 target; // 目的地 public float speed; // 移动速度 private SSMoveToAction() { } public static SSMoveToAction GetSSAction(Vector3 target, float speed) { SSMoveToAction action = ScriptableObject.CreateInstance<SSMoveToAction>(); 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) { this.destroy = true; this.callback.SSActionEvent(this); //告诉动作管理或动作组合该动作已完成 } } public override void Start() { } }
-
ISSActionCallback是动作管理者和动作的回调接口。当动作完成的时候,动作会调用这个接口,告诉动作管理者,当前动作已完成,然后动作管理者对下一个动作进行处理。动作管理者继承并实现接口。代码如下:
public interface ISSActionCallback { void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted, int intParam = 0, string strParam = null, Object objectParam = null); }
-
SequenceAction继承了SSAction基类和ISSActionCallback,实现角色移动的组合动作,代码如下:
public class SequenceAction : SSAction, ISSActionCallback { public List<SSAction> sequence; public int repeat = -1; // 无限循环做组合中的动作 public int start = 0; public static SequenceAction GetSSAcition(int repeat, int start, List<SSAction> sequence) { SequenceAction action = ScriptableObject.CreateInstance<SequenceAction>(); 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() { } }
-
SSActionManager是动作管理基类,管理SequenceAction和SSAction,给它们传递游戏对象,从而使对象做动作,同时还控制动作的切换,代码如下:
public class SSActionManager : MonoBehaviour, ISSActionCallback // action管理器 { private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>(); // 将执行的动作的字典集合,int为key,SSAction为value private List<SSAction> waitingAdd = new List<SSAction>(); // 等待去执行的动作列表 private List<int> waitingDelete = new List<int>(); // 等待删除的动作的key 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); Destroy(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(); } public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted, int intParam = 0, string strParam = null, Object objectParam = null) { // 移动完成后无下一个动作,所以为空 } }
-
MySceneActionManager是游戏的动作管理器,设置当前场景控制器的动作管理者为MySceneActionManager,从而调用动作管理器的方法实现不同游戏对象(船和角色)的移动,代码如下:
public class MySceneActionManager : SSActionManager // 本游戏管理器 { private SSMoveToAction moveBoatToEndOrStart; // 移动船到结束岸,移动船到开始岸 private SequenceAction moveRoleToLandorBoat; // 移动角色到陆地,移动角色到船上 public Controllor sceneController; protected void Start() { sceneController = (Controllor)SSDirector.GetInstance().CurrentScenceController; sceneController.actionManager = this; } public void moveBoat(GameObject boat, Vector3 target, float speed) { moveBoatToEndOrStart = SSMoveToAction.GetSSAction(target, speed); this.RunAction(boat, moveBoatToEndOrStart, this); } public void moveRole(GameObject role, Vector3 middle_pos, Vector3 end_pos, float speed) { SSAction action1 = SSMoveToAction.GetSSAction(middle_pos, speed); SSAction action2 = SSMoveToAction.GetSSAction(end_pos, speed); moveRoleToLandorBoat = SequenceAction.GetSSAcition(1, 0, new List<SSAction> { action1, action2 }); this.RunAction(role, moveRoleToLandorBoat, this); } }
Model.cs:
-
为实现**“设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束”**的目的,将
Controllor.cs
中的Check函数改为Model.cs
中的裁判类,并在Controllor
中实例化,代码如下:public class Judger { LandModel start_land; LandModel end_land; BoatModel boat; public Judger(LandModel bl, LandModel el, BoatModel b) { start_land = bl; end_land = el; boat = b; } public int Check() { int start_priest = (start_land.GetRoleNum())[0]; int start_devil = (start_land.GetRoleNum())[1]; int end_priest = (end_land.GetRoleNum())[0]; int end_devil = (end_land.GetRoleNum())[1]; if (end_priest + end_devil == 6) // 获胜 return 3; int[] boat_role_num = boat.GetRoleNum(); if (boat.GetBoatSign() == 1) { // 在开始岸和船上的角色 start_priest += boat_role_num[0]; start_devil += boat_role_num[1]; } else { // 在结束岸和船上的角色 end_priest += boat_role_num[0]; end_devil += boat_role_num[1]; } if ((start_priest > 0 && start_priest < start_devil) || (end_priest > 0 && end_priest < end_devil)) { //失败 return 2; } return 1; //未完成 } }
-
3、材料与渲染联系【可选】
-
Standard Shader 自然场景渲染器。
- 阅读官方 Standard Shader 手册 。
- 选择合适内容,如 Albedo Color and Transparency,寻找合适素材,用博客展示相关效果的呈现
调节Albedo参数控制曲面的基础颜色:
颜色改变过程如图所示:
改变对比度,变化过程如图所示:
-
声音
- 阅读官方 Audio 手册
- 用博客给出游戏中利用 Reverb Zones 呈现车辆穿过隧道的声效的案例
-
从Asset Store中下载汽车音效
-
新建一个空的游戏对象,添加Audio Source和Audio Reverb Zone组件
-
把下载导入的汽车音效挂到刚创建的空对象上,勾选Audio Source中的Loop、将Audio Reverb Zone中的Reverb Preset改为Cave,,即完成了车辆穿过隧道的声效
上一篇: 3D游戏——AR技术
下一篇: HW5 与游戏世界交互