这里写目录标题
游戏开发要求
- 使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类
- 近可能使用前面 MVC 结构实现人机交互与游戏模型分离
- 必须使用对象池管理飞碟对象。
- 建议使用 ScriptableObject 配置不同的飞碟(方便配置修改)
- 建议使用物理引擎管理飞碟飞行路径。
使用 UML 图、伪代码辅助表达对象池的原理,对象配置方法,以及你的设计
游戏规则
- 游戏有 n 个 round,每个 round 都包括10 次 trial。
- 每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。它们由该 round 的 ruler 控制。
- 每个 trial 的飞碟有随机性,总体难度随 round 上升。
- 鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定。
disk | score |
---|---|
红 | 2 |
黄 | 4 |
蓝 | 6 |
绿 | 8 |
工厂与对象池理解
工厂方法,即类一个方法能够得到一个对象实例,使用者不需要知道该实例如何构建、初始化等细节。
好处:
- 游戏对象的创建与销毁高成本,必须减少销毁次数。
- 屏蔽创建与销毁的业务逻辑,使程序易于扩展
UML图
代码介绍
代码分为下面几个模块
遵循MVC架构和cocos2d的设计思想
action
action不多做讲解,可以参考之前博客的牧师与魔鬼动作分离版的讲解
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum SSActionEventType : int { Started, Competeted }
public interface ISSActionCallback
{
/// <summary>
/// when an action has been excuted, call the callback function
/// </summary>
/// <param name="source">action has been excuted</param>
/// <param name="events"></param>
/// <param name="intParam"></param>
/// <param name="strParam"></param>
/// <param name="objectParam"></param>
public void SSActionEvent(SSAction source,
SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0,
string strParam = null,
GameObject objectParam = null);
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSAction : ScriptableObject
{
/// <summary>
/// if enable, then will call update per frame
/// </summary>
public bool enable = true;
/// <summary>
/// if destory, will be removed after a frame
/// </summary>
public bool destroy = false;
public GameObject gameobject { get; set; }
public Transform transform { get; set; }
public ISSActionCallback callback { get; set; }
protected SSAction() { }
// Start is called before the first frame update
public virtual void Start()
{
throw new System.NotImplementedException();
}
// Update is called once per frame
public virtual void Update()
{
throw new System.NotImplementedException();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
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>();
// Start is called before the first frame update
protected void Start()
{
}
// Update is called once per frame
protected void Update()
{
// add to actions
foreach(SSAction ac in waitingAdd){
actions[ac.GetInstanceID()] = ac;
}
waitingAdd.Clear();
//run actions
foreach(KeyValuePair<int, SSAction> kv in actions){
SSAction ac = kv.Value;
if(ac.destroy){
waitingDelete.Add(ac.GetInstanceID());
}
else if(ac.enable){
ac.Update();//call action update manually
}
}
//delete actions
foreach(int key in waitingDelete){
SSAction ac = actions[key];
actions.Remove(key);
Destroy(ac);
}
waitingDelete.Clear();
}
public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager){
action.gameobject = gameobject;//add action to gameobject
action.transform = gameobject.transform;//ser action's transform
action.callback = manager;//set action's callback
waitingAdd.Add(action);
action.Start();//not monobehavior thus call start mannually
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NPhysicalDiskAction : SSAction
{
float gravity;
float time;
float speed;
Vector3 direction;
// builder, get specific actions success SSAction
public static NPhysicalDiskAction GetSSAction(Vector3 direction, float speed)
{
NPhysicalDiskAction action = ScriptableObject.CreateInstance<NPhysicalDiskAction>();
action.gravity = 9.8f;
action.time = 0;
action.speed = speed;
action.direction = direction;
return action;
}
// Start is called before the first frame update
public override void Start()
{
gameobject.GetComponent<Rigidbody>().isKinematic = true;
}
// Update is called once per frame
public override void Update()
{
// disk move 运动合成,transform is the one of scriptable object,
// which will be set equal to gameobject which excecutes this action.
transform.Translate(Vector3.down * gravity * time * Time.deltaTime);
transform.Translate(direction * speed * Time.deltaTime);
time += Time.deltaTime;
//Debug.Log("postion of the disk"+gameobject.name+" is "+gameobject.transform.position);
// 飞碟到达屏幕底部回调
Camera mainCamera = Camera.main; // 获取主摄像机
float cameraHeight = mainCamera.orthographicSize * 2f; // 获取相机的视图高度
// 计算相机视图的上下边界坐标
float upperBoundary = mainCamera.transform.position.y + cameraHeight / 2f;
float lowerBoundary = mainCamera.transform.position.y - cameraHeight / 2f;
//Debug.Log("Upper Boundary: " + upperBoundary);
//Debug.Log("Lower Boundary: " + lowerBoundary);
if (this.transform.position.y < lowerBoundary-100 || this.transform.position.y > upperBoundary+100)
{
// action destory
this.destroy = true;
this.enable = false;
// action callback
this.callback.SSActionEvent(this);//source as this
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PhysicalDiskAction : SSAction
{
private float speed;
private Vector3 direction;
// builder
public static PhysicalDiskAction GetSSAction(Vector3 direction, float speed)
{
PhysicalDiskAction ac = ScriptableObject.CreateInstance<PhysicalDiskAction>();
ac.speed = speed;
ac.direction = direction;
return ac;
}
// Start is called before the first frame update
public override void Start()
{
// add clash pysical effect
gameobject.GetComponent<Rigidbody>().isKinematic = false;
// add init speed
gameobject.GetComponent<Rigidbody>().velocity = speed * direction;
}
// Update is called once per frame
public override void Update()
{
//Debug.Log("postion of the disk"+gameobject.name+" is "+gameobject.transform.position);
// 飞碟到达屏幕底部回调
Camera mainCamera = Camera.main; // 获取主摄像机
float cameraHeight = mainCamera.orthographicSize * 2f; // 获取相机的视图高度
// 计算相机视图的上下边界坐标
float upperBoundary = mainCamera.transform.position.y + cameraHeight / 2f;
float lowerBoundary = mainCamera.transform.position.y - cameraHeight / 2f;
//Debug.Log("Upper Boundary: " + upperBoundary);
//Debug.Log("Lower Boundary: " + lowerBoundary);
if (this.transform.position.y < lowerBoundary - 100 || this.transform.position.y > upperBoundary + 100)
{
// action destory
this.destroy = true;
this.enable = false;
// action callback
this.callback.SSActionEvent(this);//source as this
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class DiskActionManager : SSActionManager, ISSActionCallback
{
public FirstController firstController;
public void ShootDisk(GameObject disk, float speed, Vector3 direction,PhysiclaType type)
{
SSAction ac;
// run action
if (type== PhysiclaType.noPhysical)
{
ac = NPhysicalDiskAction.GetSSAction(direction, speed);
}
else if (type == PhysiclaType.pysical)
{
ac = PhysicalDiskAction.GetSSAction(direction, speed);
}
else
{
throw new NotImplementedException();
}
RunAction(disk, ac, this);
}
protected new void Start()
{
// get fisrt controller
firstController = (FirstController)Director.GetInstance().CurrentSceneController;
// set action manager as this
firstController.actionManager = this;
}
// specific manager do callback
public void SSActionEvent(SSAction source,
SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0,
string strParam = null,
GameObject objectParam = null)
{
//回收飞碟
//source is the class success the SSAction, in this case is obj of NphysicalDiskAction Class
Singleton<RoundController>.Instance.FreeDisk(source.gameobject);
}
}
config
using System;
using UnityEngine;
[CreateAssetMenu(menuName ="create ruler config")]
public class Ruler : ScriptableObject
{
// 对局属性
public int trailIndex;
public int roundIndex;
public int SumRoundNum;
public int SumTrailNum;
public int[] diskNumPerRound;//avg disk num per round
public float[] shootDeltaTimePerRound;//发射间隔
// 飞碟属性
public DiskFeature diskFeature;
[NonSerialized]
public Vector3 startPos;
[NonSerialized]
public Vector3 startDir;
}
controller
Director 导演类,管理场记
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Director : System.Object
{
static Director _instance;
// 关联场记实例
public ISceneController CurrentSceneController { get; set; }
// 获取当前的场记
public static Director GetInstance()
{
if (_instance == null)
{
_instance = new Director();
}
return _instance;
}
public static void ReloadCurrentScene()
{
int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneManager.LoadScene(currentSceneIndex);
}
}
ISceneController场记类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface ISceneController
{
void LoadResources();
}
IUserAction定义了用户的动作
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 抽象接口,定义用户的行为
public interface IUserAction
{
void Restart();
}
FirstController 第一个场记,继承ISceneController
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/* 游戏状态,0为准备进行,1为正在进行游戏,2为结束 */
public enum GameState
{
Ready = 0, Playing = 1, GameOver = 2
};
public class FirstController : MonoBehaviour, ISceneController
{
public DiskActionManager actionManager;
public RoundController roundController;
public UserGUI userGUI;
public GameState gameState;
// ruler config
public Ruler rulerConfig;
public void LoadResources()
{
Director.GetInstance().CurrentSceneController = this;
roundController = gameObject.AddComponent<RoundController>();
Debug.Log("add roundController Component");
userGUI = gameObject.AddComponent<UserGUI>();
Debug.Log("add UserGUI");
actionManager = gameObject.AddComponent<DiskActionManager>();
Debug.Log("add DiskActionManager");
gameState = (int)GameState.Ready;
}
public void Restart()
{
Director.ReloadCurrentScene();
}
public void JudgeResultCallBack(string result)
{
}
void Awake()
{
LoadResources();
}
void Start()
{
}
void Update()
{
}
public void GameOver()
{
gameState = GameState.GameOver;
userGUI.SetGameState(GameState.GameOver);
}
public void SetGameMode(PhysiclaType type)
{
roundController.phyType= type;
}
}
RoundController 控制飞碟对象的运动类型为运动学运动或物理运动,并根据一定规则请求飞碟工厂生成相应的飞碟对象,控制游戏飞碟对象的发射
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public enum PhysiclaType
{
pysical,
noPhysical
}
public class RoundController : MonoBehaviour
{
DiskActionManager actionManager;
ScoreRecorder scoreRecorder;
FirstController firstController;
Ruler ruler;
float time = 0;
public PhysiclaType phyType;
public void LaunchDisk()
{
// 使飞碟飞入位置尽可能分开,从不同位置飞入使用的数组
int[] beginPosY = new int[10];
float beginPosX;
// shoot the disks for each trail
int diskNum = ruler.diskNumPerRound[ruler.roundIndex] / ruler.SumTrailNum;
diskNum += Random.Range(0, 5);// random num per trail
for (int i = 0; i < diskNum; ++i)
{
// 获取随机数
int randomNum = Random.Range(1, 4);
// 飞碟速度随回合数增加而变快
ruler.diskFeature.speed = randomNum * (ruler.roundIndex + 2);
// 根据随机数选择飞碟颜色
randomNum = Random.Range(1, 4);
if (randomNum == 1)
{
ruler.diskFeature.color = DiskFeature.colors.red;
}
else if (randomNum == 2)
{
ruler.diskFeature.color = DiskFeature.colors.green;
}
else
{
ruler.diskFeature.color = DiskFeature.colors.blue;
}
// 重新选取随机数,并根据随机数选择飞碟的大小
ruler.diskFeature.size = Random.Range(1, 4);
// 重新选取随机数,并根据随机数选择飞碟飞入的方向
randomNum = Random.Range(0, 2);
Camera mainCamera = Camera.main;
float cameraW = mainCamera.orthographicSize * 2f * Camera.main.aspect;
if (randomNum == 1)
{
ruler.startDir = new Vector3(3, 1, 0);
beginPosX= mainCamera.transform.position.x - cameraW / 2f;
}
else
{
ruler.startDir = new Vector3(-3, 1, 0);
beginPosX = mainCamera.transform.position.x + cameraW / 2f;
}
// 重新选取随机数,并使不同飞碟的飞入位置尽可能分开
int iterNum = 0;
do
{
// case if infinite iter
iterNum++;
if (iterNum>=100)
{
int ii;
for (ii = 0; ii < beginPosY.Length; ii++)
{
beginPosY.SetValue(0, ii);
}
}
randomNum = Random.Range(0, 10);
} while (beginPosY[randomNum] != 0);
beginPosY[randomNum] = 1;
ruler.startPos = new Vector3(beginPosX, -0.2f * randomNum, 0);
// 根据ruler从工厂中生成一个飞碟
GameObject disk = Singleton<DiskFactory>.Instance.GetDisk(ruler);
// 设置飞碟的飞行动作
actionManager.ShootDisk(disk, ruler.diskFeature.speed, ruler.startDir,phyType);
}
}
/// <summary>
/// free the gameobject that loaded the action
/// </summary>
/// <param name="obj">obj is the one load the action</param>
public void FreeDisk(GameObject disk)
{
// call factory to free the disk
Singleton<DiskFactory>.Instance.FreeDisk(disk);
}
// 用户点击碰撞
public void Hit(Vector3 position)
{
Camera camera = Camera.main;
Ray ray = camera.ScreenPointToRay(position);
RaycastHit[] hits;
hits = Physics.RaycastAll(ray);
for (int i = 0; i < hits.Length; i++)
{
RaycastHit hit = hits[i];
if (hit.collider.gameObject != null)
{
// 把击中的飞碟移出屏幕,触发回调释放
hit.collider.gameObject.transform.position = new Vector3(0, -6, 0);
// 记录飞碟得分
scoreRecorder.Record(hit.collider.gameObject.GetComponent<Disk>());
// 显示当前得分
UserGUI userGUI = Singleton<UserGUI>.Instance;
userGUI.SetScore(scoreRecorder.GetScore());
}
}
}
// Start is called before the first frame update
void Start()
{
Debug.Log("gameobject of roundcontroller is:" + this.gameObject);
actionManager = gameObject.AddComponent<DiskActionManager>();
scoreRecorder = gameObject.AddComponent<ScoreRecorder>();
firstController = Director.GetInstance().CurrentSceneController as FirstController;
ruler = firstController.rulerConfig;
gameObject.AddComponent<DiskFactory>();
ruler.roundIndex= 0;
ruler.trailIndex= 0;
Debug.Log("ruler config:");
Debug.Log(JsonUtility.ToJson(ruler));
phyType = PhysiclaType.noPhysical;
Debug.Log("round controller's phytype is:"+phyType);
}
// Update is called once per frame
void Update()
{
// when playing
if (firstController.gameState == GameState.Playing)
{
//update usergui round index
UpdateRoundInfo();
//Debug.Log("round cont is playing update--");
//Debug.Log("roundIndex" + ruler.roundIndex + "trailIndex" + ruler.trailIndex);
time += Time.deltaTime;
float shootDeltaTime = ruler.shootDeltaTimePerRound[ruler.roundIndex];
// 发射一次飞碟(trial)
if (time > shootDeltaTime)
{
// reset time
time = 0;
if (ruler.trailIndex<ruler.SumTrailNum && ruler.roundIndex<ruler.SumRoundNum)
{
// 发射飞碟
Debug.LogFormat("call LaunchDisk at round controller--at round {0} trail {1}"
, ruler.roundIndex, ruler.trailIndex);
LaunchDisk();
ruler.trailIndex++;
// 回合加一,重新生成飞碟数组
if (ruler.trailIndex == ruler.SumTrailNum)
{
ruler.trailIndex = 0;
ruler.roundIndex++;
}
}
// 否则游戏结束,提示重新进行游戏
else
{
firstController.GameOver();
}
}
}
}
private void UpdateRoundInfo()
{
Singleton<UserGUI>.Instance.SetIndex(ruler.roundIndex, ruler.trailIndex);
Singleton<UserGUI>.Instance.SetRTNum(ruler.SumRoundNum, ruler.SumTrailNum);
Singleton<UserGUI>.Instance.SetPhyMode(phyType);
}
}
记分类根据记分规则,对被点击中的飞碟得分进行记录
using UnityEngine;
public class ScoreRecorder : MonoBehaviour
{
private int score;
public ScoreRecorder()
{ score = 0; }
public ScoreRecorder(int score)
{ this.score = score; }
public int GetScore()
{
return score;
}
// record the score
public void Record(Disk disk)
{
// 飞碟大小为1得3分,大小为2得2分,大小为3得1分
int diskSize = disk.diskFeature.size;
switch (diskSize)
{
case 1:
score += 3;
break;
case 2:
score += 2;
break;
case 3:
score += 1;
break;
default: break;
}
// 速度越快分就越高
score += disk.diskFeature.speed;
// 颜色为红色得1分,颜色为黄色得2分,颜色为蓝色得3分
DiskFeature.colors diskColor = disk.diskFeature.color;
if (diskColor == DiskFeature.colors.red)
{
score += 1;
}
else if (diskColor == DiskFeature.colors.yellow)
{
score += 2;
}
else if (diskColor == DiskFeature.colors.blue)
{
score += 3;
}
}
/* 重置分数,设为0 */
public void Reset()
{
score = 0;
}
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
factory
Singleton单实例类,用来获取单实例的类的对象
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
protected static T instance;
public static T Instance
{
get
{ if (instance == null)
{
instance = FindObjectOfType(typeof(T)) as T;
if (instance == null)
{
Debug.LogError("no instance of type " + typeof(T));
}
}
return instance;
}
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
飞碟工厂类负责飞碟对象的创建和销毁,在场景中是单实例的,且使用了对象池,实现了缓存功能。
当一个飞碟对象被创建时,会首先在对象池中寻找没有被使用的空闲飞碟对象,有的话就根据规则设置飞碟对象相应属性后直接使用,没有再创建。
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using Unity.VisualScripting.Antlr3.Runtime;
using UnityEngine;
using UnityEngine.Experimental.GlobalIllumination;
public class DiskFactory : MonoBehaviour
{
public GameObject diskPrefab;
private List<GameObject> used;
private List<GameObject> free;
private int diskIndex;
public GameObject GetDisk(Ruler ruler)
{
GameObject disk;
// 如果free没有空闲就创建
if (free.Count==0)
{
disk = GameObject.Instantiate(diskPrefab, Vector3.zero, Quaternion.identity);
disk.name = "UFO" + diskIndex;
Debug.LogFormat("factory create a disk, index is {0}",diskIndex);
disk.AddComponent(typeof(Disk));
diskIndex++;
}
else
{
int freeNum = free.Count;
disk = free[freeNum-1];
free.Remove(free[freeNum-1]);
}
// cache
used.Add(disk);
// 初始化disk feature
disk.GetComponent<Disk>().diskFeature = ruler.diskFeature;
// initial disk color
Renderer render = disk.GetComponent<Renderer>();
if (ruler.diskFeature.color == DiskFeature.colors.white)
{
render.material.color = Color.red;
}
else if (ruler.diskFeature.color == DiskFeature.colors.blue)
{
render.material.color = Color.blue;
}
else if (ruler.diskFeature.color == DiskFeature.colors.black)
{
render.material.color = Color.black;
}
else if (ruler.diskFeature.color == DiskFeature.colors.yellow)
{
render.material.color = Color.yellow;
}
else if (ruler.diskFeature.color == DiskFeature.colors.green)
{
render.material.color= Color.green;
}
// set disk position and scale
disk.transform.localScale = new Vector3(1f, (float)ruler.diskFeature.size, 1f);
disk.transform.position = ruler.startPos;
disk.SetActive(true);
return disk;
}
public void FreeDisk(GameObject dd)
{
foreach (GameObject d in used)
{
if (d.GetInstanceID()==dd.GetInstanceID())
{
Debug.LogFormat("factory free a disk, name is {0}", d.name);
d.SetActive(false);
used.Remove(d);
free.Add(d);
break;
}
}
}
// Start is called before the first frame update
void Start()
{
diskPrefab = GameObject.Instantiate(Resources.Load<GameObject>("Prefabs/UFO"));
diskPrefab.SetActive(false);
Debug.Log("DiskFactory initial the diskPrefab");
used = new List<GameObject>();
free = new List<GameObject>();
diskIndex = 0;
}
// Update is called once per frame
void Update()
{
}
}
model
飞碟的model类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Disk : MonoBehaviour
{
public DiskFeature diskFeature;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
飞碟的特征类,有大小,颜色,速度等属性
public struct DiskFeature
{
public enum colors
{
green,
blue,
red,
purple,
black,
white,
yellow,
}
public int size;
public colors color;
public int speed;
}
view
视图类,游戏有三种状态:就绪,游戏中,游戏结束
根据游戏状态不同显示不同的gui界面
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
struct Status
{
public int score;
public string tip;
public int roundNum;
public int trialNum;
public int roundIndex;
public int trialIndex;
public PhysiclaType type;
public GameState gameState;
}
public class UserGUI : MonoBehaviour
{
private Status status;
private ISceneController currentSceneController;
private GUIStyle playInfoStyle;
// Start is called before the first frame update
void Start()
{
Init();
currentSceneController = Director.GetInstance().CurrentSceneController;
// set style
playInfoStyle = new GUIStyle();
playInfoStyle.normal.textColor = Color.black;
playInfoStyle.fontSize= 25;
}
private void Init()
{
status.score= 0;
status.tip = "";
status.roundNum = 0;
status.trialNum = 0;
status.gameState = GameState.Ready;
}
// Update is called once per frame
void Update()
{
}
private void OnGUI()
{
// title
GUIStyle titleStyle = new GUIStyle();
titleStyle.normal.textColor = Color.red;
titleStyle.fontSize = 50;
GUI.Label(new Rect(Screen.width / 2 - 80, 10, 60, 100), "Hit UFO", titleStyle);
// show user page
ShowPage();
}
public void SetIndex(int roundIndex, int trailIndex)
{
status.roundIndex = roundIndex;
status.trialIndex = trailIndex;
}
public void SetRTNum(int roundNum, int trailNum)
{
status.roundNum = roundNum;
status.trialNum= trailNum;
}
public void SetGameState(GameState gameState)
{
status.gameState = gameState;
}
/*
set property of status
*/
public void SetScore(int score)
{
status.score = score;
}
/*
base on game status to show different user view
*/
private void ShowPage()
{
switch (status.gameState)
{
case GameState.Ready:
ShowHomePage();
break;
case GameState.Playing:
ShowPlayingPage();
break;
case GameState.GameOver:
ShowGameoverPage();
break;
}
}
private void ShowGameoverPage()
{
GUI.Label(new Rect(Screen.width / 2 - 40, 60, 60, 100), "游戏结束!", playInfoStyle);
if (GUI.Button(new Rect(420, 200, 100, 60), "重新开始"))
{
FirstController f = (FirstController)currentSceneController;
// set game status
f.Restart();
}
}
private void ShowPlayingPage()
{
GUI.Label(new Rect(10, 10, 60, 100), "正在游戏",playInfoStyle);
GUI.Label(new Rect(Screen.width - 200, 10, 60, 100),
"round:" +(status.roundIndex+1)+" trail:"+ (status.trialIndex+1), playInfoStyle);
GUI.Label(new Rect(10, 40, 60, 100), "得分:"+status.score, playInfoStyle);
GUI.Label(new Rect(Screen.width - 200, 35, 60, 100),
"总轮数:" + status.roundNum + "\n每轮射击数:" + status.trialNum, playInfoStyle);
GUI.Label(new Rect(10, 70, 60, 100),
"当前模式:"+(status.type==PhysiclaType.noPhysical?"运动学模式":"物理模式"), playInfoStyle);
if (Input.GetButtonDown("Fire1"))
{
Singleton<RoundController>.Instance.Hit(Input.mousePosition);
}
// chose mode
if (GUI.Button(new Rect(50, 450, 80, 50), "运动学模式"))
{
FirstController f = (FirstController)currentSceneController;
f.SetGameMode(PhysiclaType.noPhysical);
}
if (GUI.Button(new Rect(150, 450, 80, 50), "物理学模式"))
{
FirstController f = (FirstController)currentSceneController;
f.SetGameMode(PhysiclaType.pysical);
}
}
private void ShowHomePage()
{
// add game mode
if (GUI.Button(new Rect(Screen.width/2-50, 100, 100, 60), "开始游戏式\n(默认为运动学模式)"))
{
FirstController f = (FirstController)currentSceneController;
// set game status
f.gameState = GameState.Playing;
status.gameState = GameState.Playing;
}
}
internal void SetPhyMode(PhysiclaType phyType)
{
status.type= phyType;
}
}
创建预制&&配置ruler&&配置天空盒&&挂载脚本
飞碟的预制,需要提前制作好,并加上刚体组件Rigidbody,将Use Gravity项勾选上
配置游戏对局数据,这样就不用要修改都在代码里修改了
先创建一个config,之后配置我们的对局数据
再给camera配上一个天空盒
最后挂载firstcontroller和ruler,config脚本到一个空gameobject上
游戏就可以运行了
视频演示
unity 打飞碟小游戏