这里写目录标题
牧师与魔鬼项目动作初版传送门:https://github.com/lurui7/3D-Game/tree/master/Priests-and-Devils
牧师与魔鬼项目动作分离版传送门:https://github.com/lurui7/3D-Game/tree/master/Priests-and-Devils-v2
基本操作演练【建议做】
1. 下载 Fantasy Skybox FREE, 构建自己的游戏场景
这是非常有意思的设计环节,我们可以通过各种素材的组合,设计各种风格的游戏场景(就是比较耗费时间),不管是Skybox,还是绘制地图都是比较有意思的内容。这里简要展示我制作的Skybox及制作过程。
- 1.首先创建名为mySky的Material,接下来就在 Inspector 视图中选择 Shader -> Skybox -> 6Sided,一一添加素材如下:
- 2.将Skybox加入到Camera 对象中的部件 Rendering ,然后Camera 就能看到此时的效果。
2.写一个简单的总结,总结游戏对象的使用
- 首先是Camera对象,这是每一个游戏中都不可缺少的一部分,是“玩家”观察游戏世界的一个“窗口”。利用这一特性,我们可以在上面挂载一些脚本,例如人物视角的移动,或者某个基类等等。
- Light对象,这是游戏中的光源,可以设置点光源、平行光、聚光顶,以及多个光源的组合等等,来实现游戏中的光影变化。
- 各种实体对象(包括Cube,Sphere等),可以通过这些对象来构建游戏场景,例如各种抽象小游戏的场景(上次实现的牧师与魔鬼),同时,还可以给这些对象挂载脚本,可以模拟各种场景(例如模拟抛物线,模拟爆炸)。
- 空对象。这是一个比较重要的内容,通常我们将它作为一个载体来使用,因为挂载在实体对象上的脚本可能会因为对象的删除而失效,这时候就需要一个永远存在的空对象来挂载脚本,渲染游戏场景。
编程实践 牧师与魔鬼动作分离版
要求
- 动作分离,把上一版本中的ModelCon中的move方法移除,由Action来管理。即不用为每个对象都一一加上move方法,通过Action来统一管理调用。
- 【2019开始的新要求】:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束
实现思路
-
首先是裁判类的实现
这个部分相较Action类来说要更简单一点,只需要把在接口把Check方法移除,以及将Controller中的Check方法实现转到Judge类中,把Check的调用改为Judge调用就可以解决。
实现如下:using System.Collections; using System.Collections.Generic; using UnityEngine; using modelcon; public class Judge { LandModel start_land; LandModel end_land; BoatModel boat; public Judge(LandModel start_,LandModel end_,BoatModel boat_) { start_land = start_; end_land = end_; boat = boat_; } 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 2; int[] boat_role_num = boat.GetRoleNumber(); 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) { return 1; } if (end_priest > 0 && end_priest < end_devil) { return 1; } return 0; } }
-
Action部分实现解析:
ISSActionCallback接口
该接口实现了不同函数之间信息回调,将一个函数运行结果传给另一个函数的功能。
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);
}
SSAction子类
实现动作类的基类。其中gameObject为动作作用的实体对象。callback是回调接口,当动作类需要向别的类传递信息时,就通过ISSActionCallback接口来实现。
public class SSAction : ScriptableObject
{
public bool enable = true;
public bool destroy = false;
public GameObject gameobject;
public Transform transform;
public ISSActionCallback callback;
protected SSAction() { }
public virtual void Start()
{
throw new System.NotImplementedException();
}
public virtual void Update()
{
throw new System.NotImplementedException();
}
}
SSActionManager子类
动作管理类的基类,作为动作生成、运行与销毁的管理者。其中:
actions保存了当前正在运行的动作
waitingAdd是等待运行的动作
waitingDelete中是运行结束,等待删除的动作。
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>();
protected void Update()
{
foreach (SSAction ac in waitingAdd)
{
actions[ac.GetInstanceID()] = ac;
}
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();
}
public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, string strParam = null, Object objectParam = null)
{
}
}
SSMoveToAction子类
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()
{
}
}
SequenceAction子类
SequenceAction为组合动作类,即把一个动作序列按照合适的顺序执行:
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()
{
}
}
MySceneActionManager子类
管理对象的动作,包括船的移动,牧师与魔鬼的上下船等
public class MySceneActionManager : SSActionManager
{
private SSMoveToAction moveBoatToEndOrStart;
private SequenceAction moveRoleToLandorBoat;
public Controller sceneController;
protected void Start()
{
sceneController = (Controller)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);
}
}
至此,完成了Action部分的实现,接下来只需要把Modelcon中的动作调用修改为Action调用,方法实现移除,就完成了动作分离的目标。
其余的部分代码相较上次项目并没有上面改变,就不再展示出来了,可以去往Priests-and-Devils查看。
运行效果展示
材料与渲染联系【可选】
要求
实现
阅读官方手册Albedo Color and Transparency部分,发现这种方法相较与直接调整Material颜色来说,更简便地实现了对象的材质效果。
以下为演示效果:
创建五个Cube和五个Material(0,255,0),并逐渐增大A属性,然后可以看到如下效果: