240 lines
7.3 KiB
C#
240 lines
7.3 KiB
C#
using System.Collections.Generic;
|
|
|
|
public class BPMGroupModel
|
|
{
|
|
/*
|
|
* 与设计书略微出入,我发现它还与数据类耦合,由于整个数据类就三个数值,完全可以改写为下列更有功能性的数据结构
|
|
* 只需要使用两个值就可以实现数据项的查找与编辑
|
|
* 为了保证其独立性,这里的游戏基础数据依赖外界手动更新,预计会为更新接口提供多个不同的重载,以满足注册事件的需要
|
|
*
|
|
*/
|
|
/// <summary>
|
|
/// 依据序号查找,在目标排序容器中,key为开始时间,value为目标BPM值
|
|
/// </summary>
|
|
private SortedDictionary<int, SortedList<float , float>> BPMData;
|
|
/// <summary>
|
|
/// 这是提供给不同游戏对象的引用对象列表
|
|
/// </summary>
|
|
private SortedDictionary<int, BPMGroup> 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<int, SortedList<float, float>>();
|
|
BPMGroupList = new SortedDictionary<int, BPMGroup>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 返回一个BPM组数据对象,符合Chart的规范,它可以直接嵌入现有数据中
|
|
/// </summary>
|
|
public List<BPMListItem> GetDataObject()
|
|
{
|
|
var list = new List<BPMListItem>();
|
|
|
|
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;
|
|
}
|
|
/// <summary>
|
|
/// 读取现有符合Chart规范的数据对象,填充这整个数据类
|
|
/// </summary>
|
|
public void ReadDataObject(List<BPMListItem> list)
|
|
{
|
|
var testList = new List<BPMListItem>();
|
|
|
|
foreach (var item in list)
|
|
{
|
|
CreateNewBPMGroup(item.number, item.startTime, item.bpm);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 在一个编号序列中创建新的数据项
|
|
/// </summary>
|
|
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<float, float>
|
|
{
|
|
{ startTime, currentBPM }
|
|
};
|
|
//这里还需要塞一个在BPMGroupList中实例化新BPMGroup对象的子程序
|
|
var group = new BPMGroup( currentBPM, groupNum, noteStream, baseSpeed, baseBPM, trackLength);
|
|
BPMGroupList.Add(groupNum, group);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 在一个编号序列中删除指定的数据项
|
|
/// </summary>
|
|
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);
|
|
}
|
|
//这里补充输出删除成功的语法
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 删除已有的序号,在删除不存在的编号时将输出错误报告
|
|
/// </summary>
|
|
public void DeleteNewBPMGroupIndex(int groupNum)
|
|
{
|
|
if (!BPMData.ContainsKey(groupNum))
|
|
{
|
|
//输出错误报告
|
|
return;
|
|
}
|
|
BPMData.Remove(groupNum);
|
|
BPMGroupList.Remove(groupNum);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 输入新的数据,在一个编号序列中对已有的数据项进行编辑
|
|
/// </summary>
|
|
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
|
|
{
|
|
//输出错误,非法查询
|
|
}
|
|
|
|
}
|
|
/// <summary>
|
|
/// 查询目标BPMGroup
|
|
/// </summary>
|
|
public BPMGroup QueryBPMGroup(int groupNum)
|
|
{
|
|
//检查字典是否为空
|
|
if (BPMGroupList.TryGetValue(groupNum, out var value))
|
|
{
|
|
return value;
|
|
}
|
|
else { return null; }
|
|
}
|
|
/// <summary>
|
|
/// 用于查验目标数据项是否存在
|
|
/// </summary>
|
|
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;
|
|
}
|
|
/// <summary>
|
|
/// 每次时间变化时将会调用这个方法,将所有的BPMGroup对象调整为最新的状态
|
|
/// </summary>
|
|
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<float, float> 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;
|
|
}
|
|
/// <summary>
|
|
/// 在编辑器中会遇到各种直接修改游戏初始数值的情况,这个方法会遍历BPMGroupList,更新其中有关游戏设置的数值
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
}
|