游戏规则:
游戏有 n 个 round,每个 round 都包括10 次 trial;
每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。它们由该 round 的 ruler 控制;
每个 trial 的飞碟有随机性,总体难度随 round 上升;
鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定。
基本代码实现:
SceneController
SceneController也是一个接口,但这一次的游戏并不需要预先加载角色(上次要加载牧师、恶魔、船之类的对象,但这次没有),所以loadResources在FirstController的实现为空。
public interface ISceneControl
{
void loadResources();
}
UserAction
UserAction是门面模式,与用户交互相关的,与玩家动作有关:
public enum GameState { ROUND_START, ROUND_FINISH, RUNNING, PAUSE, START, FUNISH }
public interface IUserAction
{
GameState getGameState();//获取游戏状态
void setGameState(GameState gameState);//设置游戏状态
int getScore();//获取当前积分
void hit(Vector3 pos);//
bool getActionMode();//获取模式状态(运动学还是动力学)
void setActionMode(bool mode);//设置模式
}
FristSceneControl
FristSceneControl就要实现上面(SceneController、UserAction)两个类的函数。
public ActionManagerAdapter actionManager { set; get; }//适配器
public ScoreRecorder scoreRecorder { set; get; } //成绩记录器
public Queue<GameObject> diskQueue = new Queue<GameObject>();//飞碟对象队列
private int diskNumber = 0;//飞碟的数量,主要用于设定每一轮抛出的飞碟数量
private int currentRound = -1;//当前进行到第几局
private float time = 0;//时间控制,用来做抛飞碟的时候用到
private GameState gameState = GameState.START;//游戏状态
UserGUI userGUI;//挂载GUI
private bool isPhysical = false;//用于判断是否使用物理引擎
void Awake()
{
Director director = Director.getInstance();//获取导演
director.current = this;
diskNumber = 10;//初始化每轮抛出的飞碟数
this.gameObject.AddComponent<ScoreRecorder>();//挂载ScoreRecorder
this.gameObject.AddComponent<DiskFactory>();//挂载DiskFactory
scoreRecorder = Singleton<ScoreRecorder>.Instance;
userGUI = gameObject.AddComponent <UserGUI>() as UserGUI;//挂载UserGUI
director.current.loadResources();
}
public void loadResources()
{
}
public void hit(Vector3 pos)
{
RaycastHit[] hits = Physics.RaycastAll(Camera.main.ScreenPointToRay(pos));
for (int i = 0; i < hits.Length; i++)
{
RaycastHit hit = hits[i];
if (hit.collider.gameObject.GetComponent<DiskData>() != null)
{
scoreRecorder.record(hit.collider.gameObject);
hit.collider.gameObject.transform.position = new Vector3(0, -5, 0);
}
}
}
private void Update()
{
//因为需要依赖actionManager的功能
// 注意actionManager是适配器,根据isPhysical的值采用不同的actionManager
if (actionManager == null)
{
return;
}
if (actionManager.getDiskNumber() == 0 && gameState == GameState.RUNNING)//飞碟已经出完,并且游戏进行中,就表示round结束
{
gameState = GameState.ROUND_FINISH;
if (currentRound == 2)//如果round = 2,表示已经完成了三局游戏,游戏结束
{
gameState = GameState.FUNISH;
return;
}
}
if (actionManager.getDiskNumber() == 0 && gameState == GameState.ROUND_START)//飞碟数为空,并且是round开始状态,则准备开始下一round游戏
{
currentRound++;//回合数增加
nextRound();//下一回合的准备
actionManager.setDiskNumber(10);//重置飞碟数目
gameState = GameState.RUNNING;//改变游戏状态
}
if (time > 1 && gameState != GameState.PAUSE)//其他时候执行抛飞碟的动作
{
throwDisk();
time = 0;
}
else
{
time += Time.deltaTime;//time主要用来控制抛飞碟的时间间隔
}
}
private void nextRound()
{
DiskFactory diskFactory = Singleton<DiskFactory>.Instance;//获取飞碟工厂单例
for (int i = 0; i < diskNumber; i++)
{
diskQueue.Enqueue(diskFactory.getDisk(currentRound,isPhysical));//根据当前round数和是否使用物理引擎(启动刚体)来取出飞碟,并且让它加入本局抛的飞碟队列
}
actionManager.startThrow(diskQueue);//开始抛飞碟(执行动作)
}
void throwDisk()
{
if (diskQueue.Count != 0)
{
GameObject disk = diskQueue.Dequeue();//从队列取出一个飞碟
Vector3 pos = new Vector3(-disk.GetComponent<DiskData>().getDirection().x * 10, Random.Range(0f, 4f), 0);
disk.transform.position = pos;//设置目标位置
disk.SetActive(true);
}
}
public interface ActionManagerAdapter
{
void setDiskNumber(int dn);//设置飞碟数目
int getDiskNumber();//给继承的类获得飞碟数目
SSAction getSSAction();//设置动作方式
void freeSSAction(SSAction action);//释放某个动作
void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Completed, int intPram = 0, string strParm = null, Object objParm = null);
//callback的函数,用于告诉类某个动作已经完成
void startThrow(Queue<GameObject> diskQueue);//开始抛飞碟(执行动作)
}
protected void Start()
{
sceneControl = (FirstSceneControl)Director.getInstance().current;
sceneControl.actionManager = this;
flys.Add(CCFlyAction.getCCFlyAction());
base.flag = true;//这个flag如果是true表示使用运动学,false表示使用物理学
}
private new void Update()
{
if (sceneControl.getGameState() == GameState.RUNNING)
base.Update();
}
protected void Start()
{
sceneControl = (FirstSceneControl)Director.getInstance().current;
sceneControl.actionManager = this;
flys.Add(CCFlyAction.getCCFlyAction());
base.flag = false;//使用物理学
}
private new void Update()
{
if (sceneControl.getGameState() == GameState.RUNNING)
{
base.Update();
base.startRigidbodyAction();//启动刚体
}
else
{
base.stopRigidbodyAction();//停止刚体
}
}
SSActionManager
ActionManager跟上次作业的基本相似,多了要判断哪一种运动方式用不同的update:
protected void Update()
{
//......
foreach (KeyValuePair<int, SSAction> i in actions)
{
SSAction value = i.Value;
if (value.destroy)
{
waitingDelete.Add(value.GetInstanceID());
}
else if (value.enable && flag)//flag就是对应上面说使用哪种运动方式的变量
{
value.Update();
}
else if(value.enable && !flag)
{
value.FixedUpdate();
}
}
//......
}
public void stopRigidbodyAction()
{
foreach (SSAction action in actions.Values)
{
action.rigidbodyStopAction();
}
}
public void startRigidbodyAction()
{
foreach (SSAction action in actions.Values)
{
action.rigidbodyStartAction();
}
}
CCFlyAction
CCFlyAction是负责实现飞碟飞行动作的类,它负责设置了大部分的变量参数。
float acceleration;//加速度
float horizontalSpeed;//水平速度
Vector3 direction;
float time;
bool flag=false;//记录游戏是否经过暂停状态
Vector3 temp;//记录游戏暂停时刚体游戏对象的速度矢量
Rigidbody rigidbody; //物理运动,添加刚体
//变量的初始化
public override void Start()
{
enable = true;
acceleration = 9.8f;
time = 0;
horizontalSpeed = gameObject.GetComponent<DiskData>().getSpeed();
direction = gameObject.GetComponent<DiskData>().getDirection();
//执行行为的对象如果有刚体性质,则需要设置刚体的速度属性
rigidbody = gameObject.GetComponent<Rigidbody>();
if (rigidbody)
{
rigidbody.velocity = horizontalSpeed * direction;
temp = rigidbody.velocity;
}
}
public override void Update()
{
if (gameObject.activeSelf)
{
time += Time.deltaTime;
transform.Translate(Vector3.down * acceleration * time * Time.deltaTime);
transform.Translate(direction * horizontalSpeed * Time.deltaTime);
//运用运动学的方式,需要我们自己手动使用transform.Translate函数并且给出计算公式
if (this.transform.position.y < -4)
{
this.destroy = true;
this.enable = false;
this.callback.SSActionEvent(this);
}
}
}
public override void FixedUpdate()//物理学方式的运动
{
if (gameObject.activeSelf)
{
//对比运动学方式就没有了要自行使用transform.Translate的步骤
if (this.transform.position.y < -4)
{
this.destroy = true;
this.enable = false;
this.callback.SSActionEvent(this);
}
}
}
DiskFactory
对于一个飞碟,有以下的一些参数:
private Vector3 size;//大小
private Color color;//颜色
private float speed;//运动速度
private Vector3 direction;//位置
public List<DiskData> used = new List<DiskData>();
public List<DiskData> free = new List<DiskData>();
private void Awake()
{
diskPrefab = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/Disk"), Vector3.zero, Quaternion.identity);
diskPrefab.SetActive(false);
}
ScoreRecorder
ScoreRecorder就是拿来记录不同颜色的飞碟对应的分数,以及当玩家击中飞碟时计算总得分。
因此它的初始化是设置总分为0、设置各种颜色飞碟对应多少分:
void Start() /* 初始化各种颜色的分数值 */
{
score = 0;
scoreTable.Add(Color.white, 1);
scoreTable.Add(Color.gray, 2);
scoreTable.Add(Color.black, 4);
}
public void record(GameObject disk)
{
score += scoreTable[disk.GetComponent<DiskData>().getColor()];
}