Unity3d 打飞碟-物理运动版
题目要求
改进飞碟(Hit UFO)游戏:
- 按 adapter模式 设计图修改飞碟游戏;
- 使它同时支持物理运动与运动学(变换)运动。
Adapter 模式
运动学和动力学的物体运动存在区别。在 unity3d 中两种方式会产生不同的效果。使用 adapter 模式,使得游戏能支持两种方式的运动。
uml 图变化如下,增加了一个接口和方法,场景控制器通过接口调用该方法,而具体的不同的动作控制类将实现该接口,方便场控的调用。

实现
效果如下
- 和上一个版本的主要区别是添加了刚体后,飞碟碰撞会反弹。
主要写一下修改的地方。
接口
- 根据模式变化,需要修改接口。新加一个
IActionManager接口。另外,在userAction中添加了getMode(),switchMode()方法,方便 GUI 获取当前模式和在动力学 (Dynamics) 模式,即物理运动,和运动学 (Kinematics) 模式之间进行切换。
namespace Interfaces {
public interface ISceneController {}
public interface UserAction {
void Hit(Vector3 pos);
int getScore();
int getRound();
bool getMode();
void stopGame();
void Restart();
void switchMode();
}
public enum SSActionEventType: int { Started, Completed}
public interface ISSActionCallback {
void SSActionCallback(SSAction source);
}
public interface IActionManager {
void MoveDisk(Disk disk);
}
}CCPhysisAction 的添加
- 和
CCMoveToAction一个道理,CCPhysisAction也是一个继承了SSAction的具体的运动类。CCMoveToAction是基于运动学的动作变换,是通过计算物体中心的运动方程,轨迹,位移,速度,旋转来直接变换游戏对象;CCPhysisAction考虑了物体实际的重力和本身的形状,外部力对物体的影响,使游戏对象的运动更加逼真。 - 通过物理引擎更新的
CCPhysisAction类需要使用FixedUpdate(尽管这里只需要在start()中赋一次初始速度,无需在 FixedUpdate 中一直添加速度,但为了区别,我还是写了一个FixedUpdate),所以在其继承的SSAction中添加了一个FixedUpdate虚函数,然后在CCPhysicAction中实现。而需要重写的Update函数空着即可。
public class CCPhysisAction : SSAction {
public float speedx, speedy, speedz;
public float gravity = 0.0f;
private CCPhysisAction() { }
public static CCPhysisAction GetAction(Vector3 speed) {
CCPhysisAction action = ScriptableObject.CreateInstance<CCPhysisAction>();
action.speedx = speed.x;
action.speedy = speed.y;
action.speedz = speed.z;
return action;
}
public override void Update() {}
public override void FixedUpdate() {
if (transform.position.y < -20) {
Destroy(this.gameObject.GetComponent<Rigidbody>());
destroy = true;
callback.SSActionCallback(this);
}
}
public override void Start() {
if(!this.gameObject.GetComponent<Rigidbody>()) {
this.gameObject.AddComponent<Rigidbody>();
}
this.gameObject.GetComponent<Rigidbody>().AddForce(new Vector3(speedx, speedy, speedz), ForceMode.VelocityChange);
}
}SSAction 的修改
- 添加了一个
FixedUpdate虚函数,就本游戏而言,在继承了SSAction的CCMoveToAction中重写FixedUpdate时,空着即可。
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();
}
public virtual void FixedUpdate() {
throw new System.NotImplementedException();
}
}CCPhysisActionManager 的添加
- 和
CCActionManager相似,CCPhysisActionManager是一个具体的动作管理器,只不过,在实现Adapter接口IActionManager的MoveDisk()方法时,调用CCPhysisAction.GetAction(),而非CCActionManager中的CCMoveToAction.GetAction()。
public class CCPhysisActionManager : SSActionManager, IActionManager, ISSActionCallback {
public SSActionEventType Complete = SSActionEventType.Completed;
int count = 0;
public void MoveDisk(Disk disk) {
++count;
Complete = SSActionEventType.Started;
CCPhysisAction diskAction = CCPhysisAction.GetAction(disk.Speed);
this.addAction(disk.gameObject, diskAction, this);
}
public void SSActionCallback(SSAction source) {
--count;
Complete = SSActionEventType.Completed;
source.gameObject.SetActive(false);
}
}
SSActionManager 的修改
- 因为
SSActionManager通过SSAction使用具体的动作,而SSAction中添加了一个FixedUpdate函数,所以只需要在SSActionManager中的update函数中,增加对action的FixedUpdate()的调用,使得CCPhysisAction的FixedUpdate()生效。
public class SSActionManager : MonoBehaviour {
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 action in waitingAdd) {
actions[action.GetInstanceID()] = action;
}
waitingAdd.Clear();
foreach (KeyValuePair<int, SSAction> kv in actions) {
SSAction action = kv.Value;
if (action.destroy) { // delete this action
waitingDelete.Add(action.GetInstanceID());
}
else if (action.enable) { // run this action
action.Update();
action.FixedUpdate();
}
}
foreach (int key in waitingDelete) {
SSAction action = actions[key];
actions.Remove(key);
Destroy(action);
}
waitingDelete.Clear();
}
/* add action to a gameobject */
public void addAction(GameObject gameObject, SSAction action, ISSActionCallback manager) {
action.gameObject = gameObject;
action.transform = gameObject.transform;
action.callback = manager;
waitingAdd.Add(action);
action.Start();
}
}FirstController 的修改
FirstController实现了UserAction接口,因为为了方便 GUI 与用户的交互,添加了switchMode(),getMode()方法,在此实现。同时,更换模式时,需要改变调用的具体的动作管理器,所以添加了一个reset()方法进行变换。
public void switchMode() {
usePhysic = !usePhysic;
reset();
}
public bool getMode() {
return usePhysic;
}
public void reset() {
if (usePhysic) {
actionManager = gameObject.AddComponent<CCPhysisActionManager>() as CCPhysisActionManager;
}
else {
actionManager = gameObject.AddComponent<CCActionManager>() as CCActionManager;
}
}版权声明:本文为ShiYiloo_原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。