一、基本操作演练
1.下载Fantasy Skybox FREE, 构建自己的游戏场景
答:
首先,我们打开unity,然后打开Asset Store界面,这里一定是要登录的,我们可以提前在unity hub中登录。
然后,我们再在Asset Store中搜索Fantasy Skybox FREE,选择相应天空盒,下载并导入。
然后,我们制作天空盒,在Assets中右击->Create->Material,将shader改为Skybox/6 sided并放入我们导入的图片。
接下来制作地形,在对象栏右击->3D Object->Terrain,新建一个地图对象。
绘制地图如下:
初步构造场景如下,这里没有进一步美化地形:
2.写一个简单的总结,总结游戏对象的使用
游戏对象是Unity场景里面所有实体的基类。我们可以通过预制来构建对象,即GameObject可以生成保存为assets,也可以使用已经创建好的assets来生成GameObject。
游戏对象的属性:
activeInHierarchy——定义游戏对象在场景中是否活跃。
activeSelf——游戏对象本地激活状态(只读)。
isStatic——仅编辑器的API,用于指定游戏对象是否为静态。
layer——游戏对象所在的层次。
scene——游戏对象所属的场景。
tag——游戏对象的标签。
transform——附加到此游戏对象的Transform组件。
游戏对象的构造函数:
GameObject——创建一个名为name的新游戏对象。
游戏对象的Public方法:
AddComponent——将名为className的组件类添加到游戏对象。
BroadcastMessage——在此游戏对象或其任何子对象中的每个MonoBehaviour上调用名为methodName的方法。
CompareTag——返回这个游戏对象是否带有标签的布尔值。
GetComponent——如果游戏对象附加了一个组件,则返回组件类型type;如果没有,则返回null。
SendMessage——在此游戏对象中的每个MonoBehaviour上调用名为methodName的方法。
SendMessageUpwards——在此游戏对象中的每个MonoBehaviour以及该行为的每个祖先上调用名为methodName的方法。
SetActive——根据给定的true或false值激活/停用GameObject。
游戏对象的静态方法
CreatePrimitive——使用基本的网格渲染器和适当的碰撞器创建游戏对象。
Find——通过名称查找GameObject并返回这个对象。
FindGameObjectsWithTag——返回带有标签的活跃的游戏对象的列表,如果没有找到游戏对象返回空数组。
FindWithTag——返回一个带有标签的活跃的游戏对象,如果没有找到游戏对象返回null。
二、编程实践
牧师与魔鬼 动作分离版
【2019新要求】:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束
我们先来看动作分离管理器的基本架构如下:
首先,我们需要明确这次的游戏与上次的区别在什么地方。这次的游戏我们是要将每个需要移动的游戏对象的移动方法提取出来,建立一个动作管理器来管理不同的移动方法。具体来说,就是我们将原来每个对象的Move脚本整合到了一个动作管理器中,并由该动作管理器控制所有对象的移动。通过场景控制器(在我的游戏中是Controllor)把需要移动的游戏对象传递给动作管理器,让动作管理器去移动游戏对象。
显然,这样做,我们运用了设计模式中的组合模式。降低了代码的耦合,提高了内聚,并降低了重构代码的难度。
接下来我们看具体实现。
SSActionCallback(动作事件接口)
这个接口定义了定义了事件处理接口,所有事件管理者都必须实现这个接口来实现事件调度。当动作完成的时候,动作会调用这个接口,发送消息告诉动作管理者对象,进而管理者会对下一个动作进行处理。
public interface SSActionCallback{
void SSActionEvent(SSAction source);
}
SSAction类
这个类是所有类的基类。SSAction继承了ScriptableObject代表SSAction不需要绑定GameObject对象,且受Unity引擎场景管理。其子类有CCSequenceAction和CCMoveToAction,根据门面模式将其功能抽象为SSActionManager
public class SSAction : ScriptableObject {
public bool enable = true; //动作正在进行
public bool destory = false;//动作是否终止
public GameObject gameObject;
public Transform transform;
public ISSActionCallback callback; //动作处理接口
protected SSAction(){}
public virtual void Start () {
throw new System.NotImplementedException();
}
// Update is called once per frame
public virtual void Update () {
throw new System.NotImplementedException();
}
}
SSActionManager类
这个类是动作管理的基类。向动作管理器传入游戏对象并指定动作或组合动作,就可以管理动作之间的切换。
public class SSActionManger:MonoBehaviour{
private Dictionary<int,SSAction> actions = new Dictionary<int, SSAction>();
private List<SSAction> waitingToAdd = new List<SSAction>();
private List<int> waitingToDelete = 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.destory){
waitingToDelete.Add(ac.GetInstanceID());
}
else if (ac.enable){
ac.Update();
}
}
foreach(int key in waitingToDelete){
SSAction ac = actions[key];
actions.Remove(key);
DestroyObject(ac);
}
waitingToDelete.Clear();
}
CCMoveToAction类
这个类表示最基本的将物体移动到目标位置的动作的实现,具体如下:
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(){//update,并返回动作完成信息
this.transform.position = Vector3.MoveTowards(transform.position,target,speed*Time.deltaTime);
if(transform.position == target){
this.destory = true;
this.callback.SSActionEvent(this);
}
}
public override void Start(){
//do nothing
}
}
CCSequenceAction类
这个类继承了SSActionCallback类,由于组合动作是每一个动作的顺序完成,它管理这一连串动作中的每一个小的动作,所以当小的动作完成的时候,也要发消息告诉它,然后它得到消息后去处理下一个动作。它也继承了SSAction,因为成个组合动作也需要游戏对象,也需要标识是否摧毁,也会有一个组合动作的管理者的接口,组成动作也是动作的子类,只不过是让具体的动作组合起来做,需要加入一个动作释放的函数,动作完成时将动作组合释放,代码如下:
public class CCSequenceAction:SSAction,SSActionCallback{
public List<SSAction> sequence;
public int repeat = 1;
public int index = 0;
public static CCSequenceAction getAction(int repeat,int index,List<SSAction>sequence){
CCSequenceAction action = ScriptableObject.CreateInstance<CCSequenceAction>();
action.sequence = sequence;
action.repeat = repeat;
action.index = index;
return action;
}
public void SSActionEvent(SSAction source){
source.destory = false;
this.index ++;
if(this.index >= sequence.Count){
this.index = 0;
if(repeat>0)
repeat --;
if(repeat == 0){
this.destory = true;
this.callback.SSActionEvent(this);
}
}
}
public override void Update(){
if (sequence.Count == 0) return;
if (index < sequence.Count){
sequence[index].Update();
}
}
public override void Start(){
foreach (SSAction action in sequence){
action.gameObject = this.gameObject;
action.transform = this.transform;
action.callback = this;
action.Start();
}
}
void OnDestory(){
foreach(SSAction action in sequence){
DestroyObject(action);//动作释放
}
}
}
SceneActionManager类
对于这个类,我们需要实现的是在当前场景下的动作控制管理。我们这里将船的移动与角色移动的动作进行组合,并使得场景类调用这个类的方法,从而实现动作的切换,如下:
public class SceneActionManager : SSActionManager
{
private SSMoveToAction moveBoatToEndOrStart;
private SequenceAction moveRoleToLandorBoat;
public Controller sceneController;
protected void Start()//
{
sceneController = (Controller)SSDirector.getInstance().CurrentSceneController;
sceneController.actionManager = this;
}
public void moveBoat(GameObject boat, Vector3 target, float speed)
{
moveBoatToEndOrStart = SSMoveToAction.GetSSAction(target, speed);SSMoveToAction
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);
}
}
然后,我们还要实现对FirstController类,从而实现对象之间的动作同步,管理船和角色的移动,这里就不贴出代码赘述。
最后就是新增的裁判类,首先我们定义一个裁判事件接口,该接口是动作控制器与裁判的交互接口。有裁判判定结果,并将结果返回给事件控制器。
public interface IJudgeCallback
{
void JudgeEvent(JudgeEventType events);
}
接下来是具体的裁判类Judge,该类实现的主要是FirstController对状态获取的方法,该类在Update() 中实时检测当前状况,并调用回调函数通知控制器。
public class Judge : MonoBehaviour
{
landModel startLand;
landModel endLand;
boatModel boat;
public IJudgeCallback callback;
public Controller sceneController;
void Start ()
{
sceneController = (Controller)SSDirector.getInstance().CurrentSceneController;
startLand = sceneController.startLand;
endLand = sceneController.endLand;
boat = sceneController.boat;
callback = sceneController;
sceneController.judge = this;
}
protected void Update ()
{
int startpriest = (startLand.getRoleNumber())[0];
int startevil = (startLand.getRoleNumber())[1];
int endpriest = (endLand.getRoleNumber())[0];
int enddevil = (endLand.getRoleNumber())[1];
if(endpriest == 3 && enddevil == 3)
return 2;
int [] boatroles = boat.getRoleNumber();
if(boat.getBoatPos() == 0){
startpriest += boat_roles[0];
startdevil += boat_roles[1];
}
else{
endpriest += boat_roles[0];
enddevil += boat_roles[1];
}
if((startpriest > 0 && startpriest < startdevil) || (endpriest>0&&end_priest<enddevil))
return 1;
return 0;
}
}
游戏界面如下:
三、材料与渲染联系
1.从 Unity 5 开始,使用新的 Standard Shader 作为自然场景的渲染。
阅读官方 Standard Shader 手册 。选择合适内容,如 Albedo Color and Transparency,寻找合适素材,用博客展示相关效果的呈现
答:我们打开调色板,如下图:
我们可以看到,调色板中除了RGB三色值外,还有一个A值,这个值就是控制材料的透明度的值。
在官方的Standard Shader 手册中,有这样一段对该值的描述:
The alpha value of the Albedo colour controls the transparency level for the material.
我们现在看一个例子,我们创建五个对象,并将五个对象设置为不同的透明度如下:
我们创建三个不同的material,并再创建三个不同的sphere,将每个material都设置为static,并将rendering mode都设为transparent,如下:
然后,我们设置不同的A值给每个material,可以看到,最后每个sphere的透明度变得不同: