前言
中山大学数据科学与计算机学院3D游戏课程学习记录博客。
游戏代码: gitee
游戏视频: bilibili
参考师兄的博客: 师兄博客
游戏内容
- 游戏有 n 个 round,每个 round 都包括10 次 trial;
- 每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。它们由该 round 的 ruler 控制;
- 每个 trial 的飞碟有随机性,总体难度随 round 上升;
- 鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定。
游戏要求
- 使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类
- 近可能使用前面 MVC 结构实现人机交互与游戏模型分离
游戏分析
在本次的设计中:
- 动作管理方面可以使用之前游戏四的动作管理器。但是需要增加与打飞碟相关的类。
- 需要修改游戏界面,改进UI相关的类。
- 需要修改FirstController类,改变游戏的进程。
- 要使用单例模式和工厂模式。
单例模式:
单例模式的目的是为了实现只能有一个实例的类。好处是可以节省资源和防止错误。
工厂模式:
工厂模式是用一个类作为工厂,生产创建不同的产品,也就是其他对象。好处是方便代码的扩展以及减少代码编写。
游戏实现
- 动作管理相关实现:
飞碟的动作管理除了之前的代码外,用了新的两个类来实现飞碟的运动,分别是用来管理飞碟动作的FlyActionManager类和实现飞碟动作的类DiskFlyAction。
- FlyActionManager类:
FlyActionManager类需要关联到FirstController类和DiskFlyAction类;
一方面方便调用DiskFlyAction类的功能,一方面方便被FirstController类调用;
FlyActionManager类需要实现初始化函数和管理飞行动作的函数。
public class FlyActionManager : SSActionManager {
public DiskFlyAction fly;
public FirstController sceneController;
//初始化函数
protected void Start() {
sceneController = (FirstController)SSDirector.GetInstance().CurrentScenceController;
sceneController.action_manager = this;
}
//管理飞行动作
public void DiskFly(GameObject disk, float angle, float power) {
//根据参数创建DiskFlyAction对象
//调用DiskFlyAction对象的功能
}
}
- DiskFlyAction类:
DiskFlyAction类需要实现飞碟的动作,所以需要一系列和移动相关的参数;
DiskFlyAction类需要重载父类的部分函数。
public class DiskFlyAction : SSAction {
public float gravity = -5; //向下加速度
private Vector3 start_vector; //初速度
private Vector3 gravity_vector = Vector3.zero; //加速度
private Vector3 current_angle = Vector3.zero; //欧拉角
private float time; //时间
//禁用默认构造函数
private DiskFlyAction() { }
//提供使用三个参数创建DiskFlyAction实例的方法
public static DiskFlyAction GetSSAction(int lor, float angle, float power) {
//根据参数初始化相关的变量
//创建DiskFlyAction对象并返回
}
public override void Update() {
//根据变量改变物体的位置
}
//重载Start函数
public override void Start() { }
}
-
UI界面改进实现:
UI界面使用标签和按钮进行构造;
未开始游戏时需要有游戏开始的按钮;
开始游戏后需要显示游戏Round和Trial以及分数;
UI界面详细代码见gitee仓库。 -
工厂模式设计:
工厂模式是用来生成飞碟的,所以具体包括Disk类和DiskFactory类。
1.Disk类:
Disk类只包含三个变量,用来创建飞碟。
public class Disk : MonoBehaviour {
public int type = 1;
public int score = 1;
public Color color = Color.white;
}
2.DiskFactory类:
DiskFactory类需要用容器存储飞碟,因此需要链表作为成员变量;
DiskFactory类需要实现生成飞碟和释放飞碟的函数。
public class DiskFactory : MonoBehaviour {
private List<Disk> used = new List<Disk>(); //将使用的
private List<Disk> free = new List<Disk>(); //使用过的
public GameObject GetDisk(int type) {
//寻找需要被释放的飞碟
//根据type由预设生成飞碟
//把产生的飞碟加入容器
//返回产生的飞碟
}
public void FreeDisk() {
//释放所有飞碟
}
public void Reset() {
FreeDisk();
}
}
- 单例模式设计:
DiskFactory类和ScoreReconder类只需要一个实例;
DiskFactory类用来生成飞碟;
ScoreRecorder类用来记录分数;
使用模板类Singleton的代码即可。
1.Singleton类代码:
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour {
protected static T instance;
public static T Instance {
get {
if (instance == null) {
instance = (T)FindObjectOfType(typeof(T));
if (instance == null) {
Debug.LogError("An instance of " + typeof(T)
+ " is needed in the scene, but there is none.");
}
}
return instance;
}
}
}
2.ScoreRecorder类代码:
//记录分数
public class ScoreRecorder : MonoBehaviour {
private float score;
void Start () {
score = 0;
}
public void Record(GameObject disk) {
score += disk.GetComponent<Disk>().score;
}
public float GetScore() {
return score;
}
public void Reset() {
score = 0;
}
}
- FirstController类的实现:
FirstController类需要一些成员变量存储正常游戏的数据信息;
FirstController类需要实现游戏对象创建的函数;
FirstController类需要实现点击飞盘的函数;
FirstController类需要实现初始化函数;
FirstController类需要其他相关函数。
public class FirstController : MonoBehaviour, ISceneController, IUserAction {
public FlyActionManager action_manager;
public DiskFactory disk_factory;
public UserGUI user_gui;
public ScooreRecorder score_recorder;
private int round = 1;
private int trial = 0;
private float speed = 1f;
private bool running = false;
public int count = 0;
void Start () {
//下列两个成员用单例模式创建
disk_factory = Singleton<DiskFactory>.Instance;
score_recorder = Singleton<ScoreRecorder>.Instance;
//其他成员正常创建
}
void Update () {
//游戏运行才进入
if(running) {
count++;
//检测鼠标点击
if (Input.GetButtonDown("Fire1")) {
Vector3 pos = Input.mousePosition;
Hit(pos);
}
//根据round的不同创建不同的飞碟
//产生不同的难度
//使用SendDisk函数产生飞碟
}
}
//资源加载
public void LoadResources() {
disk_factory.GetDiskoound);
disk_factory.FreeDisk();
}
//产生飞碟
private void SendDisk(int type) {
//从工厂中拿一个飞碟
//通过随机的方式确定飞碟位置和角度
//给游戏增加不确定性
}
public void Hit(Vector3 pos) {
//撞击发生事件
}
public float GetScore() {
return score_recorder.GetScore();
}
public int GetRound() {
return round;
}
public int GetTrial() {
return trial;
}
public void ReStart() {
running = true;
score_recorder.Reset();
disk_factory.Reset();
round = 1;
trial = 1;
speed = 2f;
}
//游戏结束
public void GameOver() {
running = false;
}
}
至此,游戏设计完成。
详细代码见gitee仓库。
简答题
- 编写一个简单的自定义 Component 。
可以通过脚本来实现,编写一个DiskData.cs的脚本:
public class DiskData : MonoBehaviour{
public int score;
public Vector3 direction;
public Vector3 scale = new Vector3(1,1,1);
}
这样把它拉到预设上会出现这样的Component :
练习完成了!