在这一节,我们通过一些纸上设计,来说明框架的最佳实践方法。

《点点点》功能规划

尝试把《点点点》这个游戏项目用纸上设计的方式给完善掉。

目前《点点点》我们预留了几个数据,代码如下

GameModel脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
namespace FrameWorkDesign.Example
{
public interface IGameModel : IModel
{
BindableProperty<int> KillCount { get; }
BindableProperty<int> Gold { get; }
BindableProperty<int> Score { get; }
BindableProperty<int> BestScore { get; }
}
public class GameModel : AbstractModel , IGameModel
{
public BindableProperty<int> KillCount { get; } = new BindableProperty<int>() { Value = 0 };
public BindableProperty<int> Gold { get; } = new BindableProperty<int>() { Value = 0 };
public BindableProperty<int> Score { get; } = new BindableProperty<int>() { Value = 0 };
public BindableProperty<int> BestScore { get; } = new BindableProperty<int>() { Value = 0 };

protected override void OnInit()
{

}
}
}

我们增加几个功能

功能 规则
最佳分数 存储并记录最佳分数
倒计时 游戏倒计时10秒
计分 点错一次扣5分,点对一次得10分,结束时倒计时每剩余1秒得10分
金币 每次点击正确的方块,有一定概率获得1~3金币
商店 游戏开始前可以在商店购买,能够抵消点击错误的次数
成就 百分成就、手残成就(分数为负数)、零失误成就、
重开 游戏结束后回到首页再来一次

比起UML类图,用铅笔和橡皮在纸上涂改才是最高效的纸上设计

最佳分数纸上设计

我们的《点点点》目前还没有完全引进各个层级,所以我们先完善层级绘图

《点点点》层级设计

由于有倒计时系统计分规则,所以游戏的最终分数只能在游戏结束时计算,我们需要在游戏结束时记录并比较最佳分数。

目前游戏结束的事件,是我们在KillEnemyCommand中发送的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace FrameWorkDesign.Example
{
public class KillEnemyCommand : AbstractCommand
{
protected override void OnExecute()
{
var gameModel = this.GetModel<IGameModel>();
gameModel.KillCount.Value++;
if (gameModel.KillCount.Value == 9)
{
this.SendEvent<GameEndEvent>();
}
}
}
}

我们可以监听GameEndEvent,每次它发生时比较并记录最高分。

GameEndEvent截图中是GamePassEvent

在后续的功能中,会有一些分数的计算规则,这些规则我们写在IScoreSystem一个系统中,

IScoreSystem

最佳分数实现

在FrameworkDesign——Example——Scripts里面新建Utility文件夹,在其中新建IStorage脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using UnityEngine;

namespace FrameWorkDesign.Example
{
public interface IStorage : IUtility
{
void SaveInt(string key, int value);
int LoadInt(string key, int defaultValue = 0);
}
public class PlayerPrefStorage : IStorage
{
public int LoadInt(string key, int defaultValue = 0)
{
return PlayerPrefs.GetInt(key,defaultValue);
}

public void SaveInt(string key, int value)
{
PlayerPrefs.SetInt(key, value);
}
}
}

修改PointGame

1
2
3
4
5
6
7
8
9
10
11
namespace FrameWorkDesign.Example
{
public class PointGame : Architecture<PointGame>
{
protected override void Init()
{
RegisterModel<IGameModel>(new GameModel());//
RegisterUtility<IStorage>(new PlayerPrefStorage());//
}
}
}

我们在GameModel里面的OnInit方法中获取并调用IStorage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace FrameWorkDesign.Example
{
public interface IGameModel : IModel
{
//...
}
public class GameModel : AbstractModel , IGameModel
{
//...
protected override void OnInit()
{
var storage = this.GetUtility<IStorage>();
BestScore.Value = storage.LoadInt(nameof(BestScore));
BestScore.OnValueChanged += score => storage.SaveInt(nameof(BestScore), score);
}
}
}

实现IScoreSystem

在在FrameworkDesign——Example——Scripts里面新建System文件夹,在其中新建IScoreSystem脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using UnityEngine;

namespace FrameWorkDesign.Example
{
public interface IScoreSystem : ISystem
{

}
public class ScoreSystem : AbstractSystem, IScoreSystem
{
protected override void OnInit()
{
var gameModel = this.GetModel<IGameModel>();

this.RegisterEvent<GameEndEvent>(e =>
{
gameModel.Score.Value = Random.Range(1, 50);//还没有实现计分逻辑,这里用随机数代替
Debug.Log("Score: "+gameModel.Score.Value);
Debug.Log("BestScore: "+gameModel.BestScore.Value);

if (gameModel.Score.Value > gameModel.BestScore.Value)
{
gameModel.BestScore.Value = gameModel.Score.Value;
Debug.Log("新纪录");
}
});
}
}
}

别忘记在PointGame内注册一下IScoreSystem

1
2
3
4
5
6
7
8
9
10
11
12
namespace FrameWorkDesign.Example
{
public class PointGame : Architecture<PointGame>
{
protected override void Init()
{
RegisterSystem<IScoreSystem>(new ScoreSystem());
RegisterModel<IGameModel>(new GameModel());
RegisterUtility<IStorage>(new PlayerPrefStorage());
}
}
}

运行后,点击直至结束游戏时,刷新结果

最高分

总结

我们通过纸上分析设计,然后再去实现功能。

在纸上分析设计的时候,可以充分利用现有的代码,而不是不断去写新的轮子。

当项目特别复杂的时候,纸上分析设计是必须做的。

作为项目技术负责人的时候,纸上分析设计也是必须的。

当需要沟通实现思路的时候,拿一张纸,画上四个层级,然后画出交互方式和关键对象,就可以很清晰地传达想法,大大减少沟通成本。