HartoukChartEditor/Assets/Script/Data/BPMGroupModel.cs

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);
}
}
}