using System.Collections.Generic; public class BPMGroupModel { /* * 与设计书略微出入,我发现它还与数据类耦合,由于整个数据类就三个数值,完全可以改写为下列更有功能性的数据结构 * 只需要使用两个值就可以实现数据项的查找与编辑 * 为了保证其独立性,这里的游戏基础数据依赖外界手动更新,预计会为更新接口提供多个不同的重载,以满足注册事件的需要 * */ /// /// 依据序号查找,在目标排序容器中,key为开始时间,value为目标BPM值 /// private SortedDictionary> BPMData; /// /// 这是提供给不同游戏对象的引用对象列表 /// private SortedDictionary BPMGroupList; private float noteStream; private float baseSpeed; private float baseBPM; private float trackLength; public BPMGroupModel(float noteStream, float baseSpeed, float baseBPM, float trackLength) { //初始化函数,需要传入游戏基本参数 this.noteStream = noteStream; this.baseSpeed = baseSpeed; this.baseBPM = baseBPM; this.trackLength = trackLength; //初始化时,会创建数据字典和对象字典 BPMData = new SortedDictionary>(); BPMGroupList = new SortedDictionary(); } /// /// 返回一个BPM组数据对象,符合Chart的规范,它可以直接嵌入现有数据中 /// public List GetDataObject() { var list = new List(); foreach (var item in BPMData) { var valuePairs = item.Value; foreach (var pair in valuePairs) { var bpmItem = new BPMListItem(pair.Key, pair.Value, item.Key); list.Add(bpmItem); } } return list; } /// /// 读取现有符合Chart规范的数据对象,填充这整个数据类 /// public void ReadDataObject(List list) { var testList = new List(); foreach (var item in list) { CreateNewBPMGroup(item.number, item.startTime, item.bpm); } } /// /// 在一个编号序列中创建新的数据项 /// public void CreateNewBPMGroup(int groupNum, float startTime, float currentBPM) { if (BPMData.TryGetValue(groupNum, out var valuePairs)) { //这里检查项目是否已经创建过,如果已经创建过了就调用编辑方法,因为每次初始化项目时该对象都会默认创建默认对象,这点需要注意 if (valuePairs.ContainsKey(startTime)) { EditBPMGroup(groupNum, startTime, currentBPM); return;// 编辑后直接返回 } valuePairs.Add(startTime, currentBPM); } else { BPMData[groupNum] = new SortedList { { startTime, currentBPM } }; //这里还需要塞一个在BPMGroupList中实例化新BPMGroup对象的子程序 var group = new BPMGroup( currentBPM, groupNum, noteStream, baseSpeed, baseBPM, trackLength); BPMGroupList.Add(groupNum, group); } } /// /// 在一个编号序列中删除指定的数据项 /// public void DeleteBPMGroup(int groupNum, float startTime) { if (BPMData.TryGetValue(groupNum, out var valuePairs)) { if (!valuePairs.ContainsKey(startTime)) { //这里需要补充输出错误的语法 return; } valuePairs.Remove(startTime); //这里对这一整个序列进行检查,如果发现该列表已经空了,直接移除这一个序号 if (valuePairs.Count == 0) { DeleteNewBPMGroupIndex(groupNum); } //这里补充输出删除成功的语法 } } /// /// 删除已有的序号,在删除不存在的编号时将输出错误报告 /// public void DeleteNewBPMGroupIndex(int groupNum) { if (!BPMData.ContainsKey(groupNum)) { //输出错误报告 return; } BPMData.Remove(groupNum); BPMGroupList.Remove(groupNum); } /// /// 输入新的数据,在一个编号序列中对已有的数据项进行编辑 /// public void EditBPMGroup(int groupNum, float startTime, float newBPM) { //在预期的设计中,参数列表中的数值传递是被隐藏在编辑面板操作之下的,该函数只能由系统进行调用 if (BPMData.TryGetValue(groupNum, out var valuePairs)) { if (!valuePairs.ContainsKey(startTime)) { //这里需要补充输出错误的语法 return; } valuePairs[startTime] = newBPM; } else { //输出错误,非法查询 } } /// /// 查询目标BPMGroup /// public BPMGroup QueryBPMGroup(int groupNum) { //检查字典是否为空 if (BPMGroupList.TryGetValue(groupNum, out var value)) { return value; } else { return null; } } /// /// 用于查验目标数据项是否存在 /// public bool QueryBPMGroupDataItem(int groupNum, float startTime, float newBPM) { if (BPMData.TryGetValue(groupNum, out var valuePairs)) { if (!valuePairs.ContainsKey(startTime)) { return false; } } else { return false; } return true; } /// /// 每次时间变化时将会调用这个方法,将所有的BPMGroup对象调整为最新的状态 /// public void UpdateBPMGroup(float currentTime) { //遍历字典,使用当前时间去查找所有列表小于当前时间的最大值 foreach (var item in BPMData) { var value = FindMin(item.Value, currentTime); BPMGroupList[item.Key].CurrentBPM = value; } } private float FindMin(SortedList valuePairs, float currentTime) { //这里先使用顺序查找实现,一般情况下时间组都不超过10个左右,哪怕是顺序查找也是可以接受的,之后优化阶段再考虑上二分 //从尾部向前搜索 float min = 114514; for (int i = valuePairs.Count - 1; i >= 0; i--) { if (valuePairs.Keys[i] < currentTime) { min = valuePairs[valuePairs.Keys[i]]; break; } } return min; } public List<(int GroupNum, float startTime, float currentBPM)> GetAllBPMDataItem() { var list =new List<(int GroupNum,float startTime,float currentBPM)>(); foreach (var BPMDataList in BPMData) { foreach (var pair in BPMDataList.Value) { list.Add((BPMDataList.Key, pair.Key, pair.Value)); } } return list; } /// /// 在编辑器中会遇到各种直接修改游戏初始数值的情况,这个方法会遍历BPMGroupList,更新其中有关游戏设置的数值 /// public void UpdatePlayerSetting(float noteStream, float baseSpeed, float baseBPM, float trackLength) { //修改内部数值 this.noteStream = noteStream; this.baseSpeed = baseSpeed; this.baseBPM = baseBPM; this.trackLength = trackLength; foreach (var item in BPMGroupList.Values) { item.UpdatePlayerSetting(noteStream, baseSpeed, baseBPM, trackLength); } } }