一.游戏内容要求
改进飞碟(Hit UFO)游戏:
- 按 adapter模式设计图修改飞碟游戏
- 使它同时支持物理运动与运动学(变换)运动
二.适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
适配器模式主要的用途在于将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。以飞碟游戏为例,我们希望将不能用在控制器类中的物理学运动类通过动作管理器适配器使控制类中可以使用物理学运动和运动学运动的类,从而支持两种运动的操作。
适配器模式简单UML图:
三.改进实现
首先是适配器模式的实现:
适配器的实现中我们首先需要一个场景控制器与适配器之间的接口,这样我们可通过场景控制器通知适配器类调用运动学或物理学的飞碟运动。
public interface IActionManager
{
void UFOFly(GameObject disk, float angle, float power,bool flag);
}
其中前三项代表UFO运动所需要的参数,最后一项flag代表适配器选择物理学或运动学进行UFO运动,true代表物理学,false代表运动学。
接着是适配器类的实现,这个类继承了IActionManager这个接口,同时可以控制物理动作管理器和运动学动作管理器,当场景控制器通知接口需要用什么运动方式进行运动时,通过flag进行控制模式的选择。
public class ActionManagerAdapter : MonoBehaviour,IActionManager
{
public FlyActionManager action_manager;
public PhysicsFlyActionManager phy_action_manager;
public void UFOFly(GameObject disk, float angle, float power,bool flag) {
if(flag) phy_action_manager.UFOfly(disk, angle, power);
else action_manager.UFOfly(disk, angle, power);
}
void Start ()
{
action_manager = gameObject.AddComponent<FlyActionManager>() as FlyActionManager;
phy_action_manager = gameObject.AddComponent<PhysicsFlyActionManager>() as PhysicsFlyActionManager;
}
}
然后是物理学运动管理类以及实现类。物理学管理类实现与运动学管理类类似。在实现类中,通过控制飞碟Rigidbody组件的重力以及速度参数给飞碟添加重力和速度向量,实现飞碟运动的物理学模式。
public class PhysicsFlyActionManager : SSActionManager
{
public PhysicsUFOFlyAction fly;
protected void Start(){}
public void UFOfly(GameObject disk, float angle, float power)
{
fly = PhysicsUFOFlyAction.GetSSAction(disk.GetComponent<DiskData>().direction, angle, power);
this.RunAction(disk, fly, this);
}
}
public class PhysicsUFOFlyAction : SSAction
{
private Vector3 start_vector;
public float power;
private PhysicsUFOFlyAction() { }
public static PhysicsUFOFlyAction GetSSAction(Vector3 direction, float angle, float power) {
PhysicsUFOFlyAction action = CreateInstance<PhysicsUFOFlyAction>();
if (direction.x == -1) action.start_vector = Quaternion.Euler(new Vector3(0, 0, -angle)) * Vector3.left * power;
else action.start_vector = Quaternion.Euler(new Vector3(0, 0, angle)) * Vector3.right * power;
action.power = power;
return action;
}
public override void FixedUpdate() {
if (this.transform.position.y < -10) {
this.destroy = true;
this.callback.SSActionEvent(this);
}
}
public override void Update() { }
public override void Start() {
gameobject.GetComponent<Rigidbody>().velocity = power / 40 * start_vector;
gameobject.GetComponent<Rigidbody>().useGravity = true;
}
}
然后时场景控制器中的更改,我们加入布尔变量mode控制飞碟是通过物理学还是运动学的方式进行飞行。最后调用接口中的UFOFly即可控制适配器返回飞碟的运动函数。
public bool mode = false;
void Start(){
SSDirector director = SSDirector.GetInstance();
director.CurrentSceneControllor = this;
df = Singleton<DiskFactory>.Instance;
sr = gameObject.AddComponent<ScoreRecorder> () as ScoreRecorder;
iam = gameObject.AddComponent<ActionManagerAdapter>() as IActionManager;
ug = gameObject.AddComponent<UserGUI>() as UserGUI;
rc = gameObject.AddComponent<RoundControllor> () as RoundControllor;
explosion = Instantiate (Resources.Load<GameObject> ("Prefabs/ParticleSystem1"), new Vector3(0, -100, 0), Quaternion.identity);
t = speed;
}
private void SendDisk()
{
float position_x = 16;
if (dq.Count != 0)
{
GameObject disk = dq.Dequeue();
dfree.Add(disk);
disk.SetActive(true);
float ran_y = Random.Range(1f, 4f);
float ran_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;
disk.GetComponent<DiskData>().direction = new Vector3(ran_x, ran_y, 0);
Vector3 position = new Vector3(-disk.GetComponent<DiskData>().direction.x * position_x, ran_y, 0);
disk.transform.position = position;
float power = Random.Range(20f, 30f);
float angle = Random.Range(5f, 13f);
iam.UFOFly(disk,angle,power,mode);
}
最后调整UserGUI,使得玩家可以选择通过物理学还是运动学方式进行游戏。
void OnGUI ()
{
text_style.normal.textColor = new Color(1,1,1, 1);
text_style.fontSize = 16;
text_style2.normal.textColor = new Color(1,1,1, 1);
text_style2.fontSize = 100;
if (action.getModeSetting()) {
GUI.Label(new Rect(Screen.width / 2 - 40, Screen.width / 2 - 300, 50, 50), "模式选择", text_style);
if (GUI.Button(new Rect(Screen.width / 2 - 55, Screen.width / 2 - 180, 100, 50), "物理学模式"))
{
action.modeSet (true);
action.gameBegin ();
return;
}
if (GUI.Button(new Rect(Screen.width / 2 - 55, Screen.width / 2 - 250, 100, 50), "运动学模式"))
{
action.modeSet (false);
action.gameBegin ();
return;
}
} else {
if (action.isCounting ()) {
GUI.Label(new Rect(Screen.width / 2 - 40, Screen.width / 2 - 300, 50, 50), action.getEmitTime().ToString(), text_style2);
} else {
if (Input.GetButtonDown("Fire1"))
{
Vector3 pos = Input.mousePosition;
action.Hit(pos);
}
GUI.Label(new Rect(10, 5, 200, 50), "score:", text_style);
GUI.Label(new Rect(55, 5, 200, 50), action.GetScore().ToString(), text_style);
GUI.Label(new Rect(10, 30, 50, 50), "hp:", text_style);
for (int i = 0; i < life; i++)
{
GUI.Label(new Rect(40 + 10 * i, 30, 50, 50), "X", text_style);
}
if (life == 0)
{
GUI.Label(new Rect(Screen.width / 2 - 50, Screen.width / 2 - 300, 100, 100), "GameOver!", text_style);
if (GUI.Button(new Rect(Screen.width / 2 - 60, Screen.width / 2 - 250, 100, 50), "Restart"))
{
action.Restart();
return;
}
action.GameOver();
}
}
}
}
在prefab中需要在飞碟中加入rigidbody组件,然后将重力设置为0.
游戏效果:
只展示更新的UI部分,其余部分实际和上次飞碟游戏相同。
Github地址:UFO适配器模式版