3D游戏:第4次作业
姓名:韩宝欣 学号:20331013
文章目录
1 基本操作演练
1.1 下载 Fantasy Skybox FREE, 构建自己的游戏场景
-
到 Asset Store) 下载 Fantasy Skybox FREE,然后从 Package Manager 中导入下载好的Fantasy Skybox FREE资源。
添加到我的资源 -> 在 Unity 中打开 -> Download
![](https://i-blog.csdnimg.cn/blog_migrate/cd0173cd202d503d360616922c610efd.png)
![](https://i-blog.csdnimg.cn/blog_migrate/15e0b2cc6f553658bb422bb894e9943b.png)
-
Assets 上下文菜单 -> create -> Material,命名为
mysky
。 -
mysky
的 Inspector 视图中选择 Shader -> Skybox -> 6Sided; -
在资源贴图中选择合适的图片,拖放到对应位置,如图:
-
在 Camera 对象中添加部件 Rendering -> Skybox, 将
skybox
拖放到 Skybox 即可。效果如下:
1.2 写一个简单的总结,总结游戏对象的使用
在实际游戏生产中,我们依赖的 model、prefab,是由最基础的游戏对象构成的:
- Empty:作用是创建一个新的对象空间,也可作为子对象的容器,不会在 play 的时候显示,是最常用对象之一;
- 3D object
- 基础 3D 物体(Primitive Object):立方体(Cube)、球体(Sphere)、胶囊体(Capsule)、圆柱体(Sylinder)、平面(Plane)、四边形(Quad);
- 构造 3D 物体:由三角形网格构建的物体:如地形、水等;
- Camera :观察游戏世界的窗口;
- Light:游戏世界的光源,分为平行光(类似太阳光)、聚光灯(spot)、点光源(point)、区域光(area);
- Audio:游戏世界的声音;
通过对基础的游戏对象的使用,可以构建出复杂、完整、各种各样的游戏。
2 牧师与魔鬼 - 动作分离版
要求:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束
背景:作业三 - 牧师与魔鬼
思路:在 作业三 的基础上进行修改和增加
2.1 代码修改与删除
-
在
FirstConstrollor
类中加入游戏状态管理器和动作管理类;public Referee GSM; // 游戏状态控制器,也就是裁判 public PADSceneActionManager actionManager; // 动作管理类
修改
MoveBoat
和MoveRole
,使用PADSceneActionManager
来实现,SceneContrller
只负责传递游戏对象和位置信息。 -
将
MoveCortrollor.cs
删除(因为已经有动作管理器ActionControllor
);- 将
BoatModel
和RoleModel
中和move
相关的 method 和对象删去,并添加各自的 speed;
- 将
-
删除
check()
的实现和使用check
的实现从FirstCortrollor.cs
中删除- 将
check()
的使用从Interfaces.cs
中删除
2.2 创建新的类
2.2.1 动作事件接口 Interfaces.cs
- 作用:当动作完成的时候会调用该接口通知管理者该动作完成。
public enum SSActionEventType : int { Started, Competeted }
public interface ISSActionCallback {
void SSActionEvent(
SSAction source, SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, string strParam = null, Object objectParam = null
);
}
动作组合类和动作管理类都需要继承这个接口,来实现接收其子动作的消息以实现动作的调度管理。
2.2.2 动作控制器 ActionControllor.cs
-
目的
通过场景控制器把需要移动的游戏对象和位置等信息传递给动作控制器,动作控制器再管理并实现所有游戏对象具体的移动。
-
代码实现
- 动作基类:定义一个动作,继承了
ScriptableObject
;
public class SSAction : ScriptableObject { public bool enable = true; // 是否进行 public bool destroy = false; // 是否销毁 public GameObject gameobject; // 游戏对象,作为动作对象 public Transform transform; // 动作对象的transform public ISSActionCallback callback; // 回调函数 /* 处理用户自己new对象的情况 */ protected SSAction() { } public virtual void Start() { throw new System.NotImplementedException(); } public virtual void Update() { throw new System.NotImplementedException(); } }
SSAction
的子类:实现具体移动到指定位置
public class SSMoveToAction : SSAction { public Vector3 target; // 目的地位置 public float speed; // 移动速度 private SSMoveToAction() { } public static SSMoveToAction GetSSAction(Vector3 _target, float _speed) { // 让 unity 创建一个实例,确保内存正确回收 SSMoveToAction action = ScriptableObject.CreateInstance<SSMoveToAction>(); action.target = _target; action.speed = _speed; return action; } // 重写父类虚函数 public override void Start() { } 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 class SequenceAction : SSAction, ISSActionCallback { public List<SSAction> sequence; // 动作的列表 public int repeat = -1; // -1:无限循环做组合中的动作 public int start = 0; // 当前做的动作 public static SequenceAction GetSSAcition(int repeat, int start, List<SSAction> sequence) { //让 unity 创建一个实例 SequenceAction action = ScriptableObject.CreateInstance<SequenceAction>(); action.sequence = sequence; action.repeat = repeat; action.start = start; return action; } public override void Start() { // 初始化每个动作 foreach (SSAction action in sequence) { action.gameobject = this.gameobject; action.transform = this.transform; action.callback = this; // 每一个动作的回调函数为该动作组合 action.Start(); } } public override void Update() { if (sequence.Count == 0) return; if (start < sequence.Count) { sequence[start].Update(); // start在回调函数中递增 } } // 实现接口 ISSActionCallback 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--; // repeat==0时,结束 if (repeat == 0) { // 动作组合结束 this.destroy = true; // 删除 this.callback.SSActionEvent(this); // 通知管理者 } } } void OnDestroy() { } }
- 动作管理基类:负责管理所有的动作以及动作组合的具体实现,并继承了
ISSActionCallback
接口,用于接受动作是否完成的消息。
public class SSActionManager : MonoBehaviour, ISSActionCallback { private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>(); //动作字典 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; } waitingAdd.Clear(); // 对于字典中每一对pair,看是执行还是删除 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); Object.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) { } }
- 动作管理类:和对于的场景控制器绑定,再实现船和角色具体的移动。
public class PADSceneActionManager : SSActionManager { public FirstControllor sceneController; private SequenceAction boatMove; private SequenceAction roleMove; protected void Start() { sceneController = (FirstControllor)SSDirector.GetInstance().CurrentScenceController; sceneController.actionManager = this; } protected new void Update() { base.Update(); } // 移动船只 public void moveBoat(GameObject boat, Vector3 endPos, float speed) { SSAction action1 = SSMoveToAction.GetSSAction(endPos, speed); boatMove = SequenceAction.GetSSAcition(0, 0, new List<SSAction> { action1 }); this.RunAction(boat, boatMove, this); } // 移动角色 public void moveRole(GameObject role, Vector3 middlePos, Vector3 endPos, float speed) { // 分为两次移动 SSAction action1 = SSMoveToAction.GetSSAction(middlePos, speed); SSAction action2 = SSMoveToAction.GetSSAction(endPos, speed); // repeat=1,start=0,两个动作 roleMove = SequenceAction.GetSSAcition(1, 0, new List<SSAction> { action1, action2 }); this.RunAction(role, roleMove, this); }
- 动作基类:定义一个动作,继承了
2.2.3 裁判类 Referee.cs
using UnityEngine;
namespace RefereeApplication {
public class Referee : MonoBehaviour {
public FirstControllor sceneController;
protected void Start() {
sceneController = (FirstControllor)SSDirector.GetInstance().CurrentScenceController;
sceneController.gameStatusManager = this;
}
// retuen: 0-游戏继续,1-失败,2-成功
public int JudgeGame() {
int startPriests = (sceneController.startLand.GetRoleNum())[0];
int startDevils = (sceneController.startLand.GetRoleNum())[1];
int endPriests = (sceneController.endLand.GetRoleNum())[0];
int endDevils = (sceneController.endLand.GetRoleNum())[1];
if (endPriests + endDevils == 6) return 2;
int[] boatNum = sceneController.boat.GetRoleNumber();
// 加上船上的人
if (sceneController.boat.GetBoatSign() == 1) {
startPriests += boatNum[0];
startDevils += boatNum[1];
}
else {
endPriests += boatNum[0];
endDevils += boatNum[1];
}
// 游戏失败
if ((endPriests > 0 && endPriests < endDevils) || (startPriests > 0 && startPriests < startDevils))
return 1;
// 其他情况:游戏继续
return 0;
}
}
}
2.3 源码与结果展示
源码:3d-game/ 作业四:牧师与恶魔 动作分离版
视频(和homework3一样,就不上传新的视频了):演示视频