本次作业主要是要添加一个管理物理动作的动作管理器CCPhysicsActionManager,并且通过添加按钮来决定选择哪一种创造飞碟的方式。因此在游戏的初始阶段有2中选择,一种是飞碟的运动将以运动的方式形成,另外一种是飞碟会以物理的方式形成,采用适配器模式,因此需要新建一个公共接口IActionManager,用来确定游戏开始的时候选择哪一种动作管理器。
游戏的初始接口效果图如下所示:
适配器模式的UML图的设计如下所示:
接下来是各个相关类修改后的方法,及其代码,因为代码和之前的设计有很多相似的地方,所以这里只分析修改过和新添加的代码,不做重复,相关可以参考上一次的作业:Unity3D课程学习笔记(四)
下面是一个公共的接口IActionManager,是CCActionManager和CCPhysicsActionManager需要实现的,在场景控制器中通过定义IActionManager和选择相应的按钮,可以选择使用运动学方式还是物理学方式:
public interface IActionManager
{
int getDiskNumber();
void setDiskNumber(int _diskNumber);
void StartThrow(Queue diskQueue);
}
然后是CCActionManager的代码,基本上跟上次的没有什么改变,但是在这个类的代码里面添加了一个延迟执行Start的方法,因为我需要在开始的时候通过按钮来选择相应的模式,但是又不能在Update中时刻检测这个状态码的改变,所以只好采用这种方式,具体的代码如下所示:
public class CCActionManager : SSActionManager, ISSActionCallback, IActionManager
{
public FirstSceneController sceneController;
public List Fly;
private int DiskNumber = 0;
//private bool isFirstEnter = true;
private List Used = new List();
private List Free = new List();
public void setDiskNumber(int _diskNumber)
{
DiskNumber = _diskNumber;
}
public int getDiskNumber()
{
return DiskNumber;
}
//GetSSAction,首先从工厂里面找,如果没有的话就创造
SSAction GetSSAction()
{
SSAction action = null;
if (Free.Count > 0)
{
action = Free[0];
Free.Remove(Free[0]);
}
else
{
action = ScriptableObject.Instantiate(Fly[0]);
}
Used.Add(action);
return action;
}
//FreeSSAction
public void FreeSSAction(SSAction action)
{
SSAction temp = null;
foreach (SSAction disk in Used)
{
if (action.GetInstanceID() == disk.GetInstanceID())
{
temp = disk;
}
}
if (temp != null)
{
temp.reset();
Free.Add(temp);
Used.Remove(temp);
}
}
protected new void Start()
{
StartCoroutine(PlayerAttack());
}
public void SSActionEvent(SSAction source,
SSActionEventType events = SSActionEventType.Competeted,
int Param = 0,
string strParam = null,
UnityEngine.Object objectParam = null)
{
if (source is CCFlyAction)
{
DiskNumber--;
DiskFactory factory = Singleton.Instance;
factory.FreeDisk(source.gameobject);
FreeSSAction(source);
}
}
public void StartThrow(Queue diskQueue)
{
foreach (GameObject temp in diskQueue)
{
//if (GetSSAction() != null)
RunAction(temp, GetSSAction(), (ISSActionCallback)this);
}
}
IEnumerator PlayerAttack()
{
yield return new WaitForSeconds(3.0f);
Debug.Log("CCActionManager!");
sceneController = (FirstSceneController)Director.getInstance().currentSceneController;
if (sceneController.getActionState() == ActionState.MOVING)
{
Debug.Log("CCActionManager! Set actionManager!");
sceneController.actionManager = this;
}
Fly.Add(CCFlyAction.GetSSAction());
}
}
接着是CCPhysicsActionManager的代码,这个类跟CCActionManager的代码非常相似,只是我新建了一个CCPhysicsAction的基本动作来定义物理动作的生成,所以感觉有点冗余,但是这样做又是最方便的,所以就先采取这种方法。
public class CCPhysicsActionManager : SSActionManager, ISSActionCallback, IActionManager
{
public FirstSceneController sceneController;
public List Fly;
private int DiskNumber = 0;
private List Used = new List();
private List Free = new List();
public void setDiskNumber(int _diskNumber)
{
DiskNumber = _diskNumber;
}
public int getDiskNumber()
{
return DiskNumber;
}
//GetSSAction,首先从工厂里面找,如果没有的话就创造
SSAction GetSSAction()
{
SSAction action = null;
if (Free.Count > 0)
{
action = Free[0];
Free.Remove(Free[0]);
}
else
{
action = ScriptableObject.Instantiate(Fly[0]);
}
Used.Add(action);
return action;
}
//FreeSSAction
public void FreeSSAction(SSAction action)
{
SSAction temp = null;
foreach (SSAction disk in Used)
{
if (action.GetInstanceID() == disk.GetInstanceID())
{
temp = disk;
}
}
if (temp != null)
{
temp.reset();
Free.Add(temp);
Used.Remove(temp);
}
}
protected new void Start()
{
StartCoroutine(PlayerAttack());
}
public void SSActionEvent(SSAction source,
SSActionEventType events = SSActionEventType.Competeted,
int Param = 0,
string strParam = null,
UnityEngine.Object objectParam = null)
{
if (source is CCPhysicsFlyAction)
{
DiskNumber--;
DiskFactory factory = Singleton.Instance;
factory.FreeDisk(source.gameobject);
FreeSSAction(source);
}
}
public void StartThrow(Queue diskQueue)
{
foreach (GameObject temp in diskQueue)
{
//if (GetSSAction() != null)
RunAction(temp, GetSSAction(), (ISSActionCallback)this);
}
}
IEnumerator PlayerAttack()
{
//Start After 3 seconds.
yield return new WaitForSeconds(3.0f);
Debug.Log("CCActionManager!");
sceneController = (FirstSceneController)Director.getInstance().currentSceneController;
if (sceneController.getActionState() == ActionState.PHYSICS)
{
Debug.Log("PhysicsActionManager! Set actionManager!");
sceneController.actionManager = this;
}
Fly.Add(CCPhysicsFlyAction.GetSSAction());
}
}
在工厂中创造飞碟的时候,需要注意的是,如果选择的是物理状态,那么就需要给飞碟添加一个刚体属性,这样他的物理特性才能够体现出来,并且在CCPhysicsActionManager中应该要注意把相关的内容放在FixedUpdate中,一下是这两个类的代码
CCPhysicsActionManager
public class CCPhysicsFlyAction : SSAction
{
//动作基本参数:加速度,垂直速度,边界,飞行时间,飞行方向
public ScoreRecorder scoreRecorder { get; set; }
private float acceleration = 3.0f;
private float horizontalSpeed;
private float lowerBound = -4;
private float flyTime;
private Vector3 direction;
Rigidbody rigidbody;
public override void Start()
{
enable = true;
flyTime = 0;
horizontalSpeed = gameobject.GetComponent().getDiskSpeed();
direction = gameobject.GetComponent().getDiskDirection();
scoreRecorder = Singleton.Instance;
rigidbody = this.gameobject.GetComponent();
if (rigidbody)
{
Debug.Log("AddForce");
rigidbody.velocity = horizontalSpeed * direction * 3;
}
}
public override void Update()
{
if (gameobject.activeSelf)
{
if (checkWhetherShouldRecycle())
{
scoreRecorder.MinRecord();
}
}
}
public override void FixedUpdate()
{
Debug.Log("Physics!");
if (gameobject.activeSelf)
{
if (checkWhetherShouldRecycle())
{
scoreRecorder.MinRecord();
}
}
}
//如果飞碟的高度低于最低限度,则回收该飞碟
private bool checkWhetherShouldRecycle()
{
if (this.transform.position.y < lowerBound)
{
this.destroy = true;
this.enable = false;
this.callback.SSActionEvent(this);
return true;
}
return false;
}
public static CCPhysicsFlyAction GetSSAction()
{
CCPhysicsFlyAction action = ScriptableObject.CreateInstance();
return action;
}
}
DiskFactory类
public class DiskFactory : MonoBehaviour
{
public GameObject Disk_Product;
public FirstSceneController sceneController;
//Used用来保存被激活的飞碟,Free用来保存空闲的飞碟
private List Used = new List();
private List Free = new List();
//Awake
private void Awake()
{
sceneController = (FirstSceneController)Director.getInstance().currentSceneController;
Disk_Product = GameObject.Instantiate(Resources.Load("Prefabs/DiskModel"),
Vector3.zero, Quaternion.identity);
Disk_Product.SetActive(false);
}
//GetDisk
public GameObject GetDisk(int Game_Round)
{
GameObject NewDiskProduct = null;
if (Free.Count > 0)
{
NewDiskProduct = Free[0].gameObject;
Free.Remove(Free[0]);
}
else
{
NewDiskProduct = GameObject.Instantiate(Disk_Product, Vector3.zero,
Quaternion.identity);
NewDiskProduct.AddComponent();
}
//控制飞碟产生的频率
int From = 0;
if (Game_Round == 1)
{
From = 100;
}
if (Game_Round == 2)
{
From = 250;
}
int TheDiskColor = Random.Range(From, Game_Round * 499);
if (TheDiskColor > 500)
{
Game_Round = 2;
}
else if (TheDiskColor > 300)
{
Game_Round = 1;
}
else
{
Game_Round = 0;
}
//根据回合控制生成飞碟的属性
if (Game_Round == 0)
{
setDiskProp(NewDiskProduct, Color.yellow, 2.0f);
}
else if (Game_Round == 1)
{
setDiskProp(NewDiskProduct, Color.red, 3.0f);
}
else if (Game_Round == 2)
{
setDiskProp(NewDiskProduct, Color.black, 4.0f);
}
Debug.Log("Produce!");
if (sceneController.getActionState() == ActionState.PHYSICS)
{
Debug.Log("Check!");
NewDiskProduct.AddComponent();
}
Used.Add(NewDiskProduct.GetComponent());
NewDiskProduct.name = NewDiskProduct.GetInstanceID().ToString();
return NewDiskProduct;
}
//setDiskProp.
public void setDiskProp(GameObject Disk, Color _Color, float _Speed)
{
Disk.GetComponent().setDiskColor(_Color);
Disk.GetComponent().setDiskSpeed(_Speed);
float RanX = UnityEngine.Random.Range(-1.0f, 1.0f) < 0 ? -1 : 1;
Disk.GetComponent().setDiskDirection(new Vector3(RanX, 1, 0));
Disk.GetComponent().material.color = _Color;
}
//FreeDisk.
public void FreeDisk(GameObject Disk)
{
DiskData temp = null;
foreach (DiskData disk in Used)
{
if (Disk.GetInstanceID() == disk.gameObject.GetInstanceID())
{
temp = disk;
}
}
if (temp != null)
{
temp.gameObject.SetActive(false);
Free.Add(temp);
Used.Remove(temp);
}
}
}
至于其他的类,都和上次作业差不多,在这里就不再多叙述了,如果想查看相应的代码,可以到我的Github
最后附上一张效果图: