- 宠物动画延迟加载, 大幅提高启动性能
This commit is contained in:
Hakoyu 2024-04-18 21:55:17 +08:00
parent a3c15260b8
commit 7a2fa5c261
5 changed files with 159 additions and 84 deletions

View File

@ -2,6 +2,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using HKW.HKWUtils.Extensions;
using HKW.HKWUtils.Observable; using HKW.HKWUtils.Observable;
using VPet_Simulator.Core; using VPet_Simulator.Core;
@ -22,10 +23,9 @@ public class AnimeModel : ObservableObjectX, ICloneable<AnimeModel>
var info = Path.GetFileNameWithoutExtension(file).Split(NativeUtils.Separator); var info = Path.GetFileNameWithoutExtension(file).Split(NativeUtils.Separator);
ID = info[0]; ID = info[0];
var duration = info.Last(); var duration = info.Last();
var imageModel = new ImageModel( if (int.TryParse(duration, out var result) is false)
NativeUtils.LoadImageToMemoryStream(file), result = 100;
int.Parse(duration) var imageModel = new ImageModel(file, result);
);
Images.Add(imageModel); Images.Add(imageModel);
} }
} }
@ -63,6 +63,14 @@ public class AnimeModel : ObservableObjectX, ICloneable<AnimeModel>
/// </summary> /// </summary>
public ObservableList<ImageModel> Images { get; } = new(); public ObservableList<ImageModel> Images { get; } = new();
public void LoadAnime()
{
if (Images.HasValue() is false || Images.First().Image is not null)
return;
foreach (var image in Images)
image.LoadImage();
}
/// <summary> /// <summary>
/// 复制 /// 复制
/// </summary> /// </summary>

View File

@ -180,6 +180,18 @@ public class AnimeTypeModel : ObservableObjectX
} }
} }
public void LoadTypeAnime()
{
foreach (var anime in HappyAnimes)
anime.LoadAnime();
foreach (var anime in NomalAnimes)
anime.LoadAnime();
foreach (var anime in PoorConditionAnimes)
anime.LoadAnime();
foreach (var anime in IllAnimes)
anime.LoadAnime();
}
public void Close() public void Close()
{ {
foreach (var anime in HappyAnimes) foreach (var anime in HappyAnimes)
@ -194,6 +206,7 @@ public class AnimeTypeModel : ObservableObjectX
public void Clear() public void Clear()
{ {
Close();
HappyAnimes.Clear(); HappyAnimes.Clear();
NomalAnimes.Clear(); NomalAnimes.Clear();
PoorConditionAnimes.Clear(); PoorConditionAnimes.Clear();
@ -435,14 +448,14 @@ public class AnimeTypeModel : ObservableObjectX
or GraphInfo.GraphType.Shutdown or GraphInfo.GraphType.Shutdown
or GraphInfo.GraphType.StartUP or GraphInfo.GraphType.StartUP
) )
SaveDefault(path, this); SaveDefault(path);
else if ( else if (
GraphType GraphType
is GraphInfo.GraphType.Touch_Head is GraphInfo.GraphType.Touch_Head
or GraphInfo.GraphType.Touch_Body or GraphInfo.GraphType.Touch_Body
or GraphInfo.GraphType.Sleep or GraphInfo.GraphType.Sleep
) )
SaveMultiType(path, this); SaveMultiType(path);
else if ( else if (
GraphType GraphType
is GraphInfo.GraphType.Switch_Up is GraphInfo.GraphType.Switch_Up
@ -450,55 +463,52 @@ public class AnimeTypeModel : ObservableObjectX
or GraphInfo.GraphType.Switch_Thirsty or GraphInfo.GraphType.Switch_Thirsty
or GraphInfo.GraphType.Switch_Hunger or GraphInfo.GraphType.Switch_Hunger
) )
SaveSwitch(path, this); SaveSwitch(path);
else if ( else if (
GraphType is GraphInfo.GraphType.Raised_Dynamic or GraphInfo.GraphType.Raised_Static GraphType is GraphInfo.GraphType.Raised_Dynamic or GraphInfo.GraphType.Raised_Static
) )
SaveRaised(path, this); SaveRaised(path);
else if (GraphType is GraphInfo.GraphType.StateONE or GraphInfo.GraphType.StateTWO) else if (GraphType is GraphInfo.GraphType.StateONE or GraphInfo.GraphType.StateTWO)
SaveState(path, this); SaveState(path);
else if (GraphType is GraphInfo.GraphType.Common) else if (GraphType is GraphInfo.GraphType.Common)
SaveCommon(path, this); SaveCommon(path);
else if (GraphType.IsHasNameAnime()) else if (GraphType.IsHasNameAnime())
SaveHasNameAnime(path, this); SaveHasNameAnime(path);
} }
/// <summary> /// <summary>
/// 保存为带有名称的动画样式 /// 保存为带有名称的动画样式
/// </summary> /// </summary>
/// <param name="path">路径</param> /// <param name="path">路径</param>
/// <param name="animeTypeModel">动画模型</param> void SaveHasNameAnime(string path)
void SaveHasNameAnime(string path, AnimeTypeModel animeTypeModel)
{ {
var animeTypePath = Path.Combine(path, animeTypeModel.GraphType.ToString()); var animeTypePath = Path.Combine(path, GraphType.ToString());
Directory.CreateDirectory(animeTypePath); Directory.CreateDirectory(animeTypePath);
var animePath = Path.Combine(animeTypePath, animeTypeModel.Name); var animePath = Path.Combine(animeTypePath, Name);
Directory.CreateDirectory(animePath); Directory.CreateDirectory(animePath);
SaveWithModeType(animePath, animeTypeModel); SaveWithModeType(animePath);
} }
/// <summary> /// <summary>
/// 保存为通用样式 /// 保存为通用样式
/// </summary> /// </summary>
/// <param name="path">路径</param> /// <param name="path">路径</param>
/// <param name="animeTypeModel">模型</param> void SaveCommon(string path)
void SaveCommon(string path, AnimeTypeModel animeTypeModel)
{ {
var animePath = Path.Combine(path, animeTypeModel.Name); var animePath = Path.Combine(path, Name);
Directory.CreateDirectory(animePath); Directory.CreateDirectory(animePath);
SaveWithModeType(animePath, animeTypeModel); SaveWithModeType(animePath);
} }
/// <summary> /// <summary>
/// 保存为 <see cref="GraphInfo.GraphType.StateONE"/> 或 <see cref="GraphInfo.GraphType.StateTWO"/> 样式 /// 保存为 <see cref="GraphInfo.GraphType.StateONE"/> 或 <see cref="GraphInfo.GraphType.StateTWO"/> 样式
/// </summary> /// </summary>
/// <param name="path">路径</param> /// <param name="path">路径</param>
/// <param name="animeTypeModel">模型</param> void SaveState(string path)
void SaveState(string path, AnimeTypeModel animeTypeModel)
{ {
var animePath = Path.Combine(path, "State"); var animePath = Path.Combine(path, "State");
Directory.CreateDirectory(animePath); Directory.CreateDirectory(animePath);
SaveMultiType(animePath, animeTypeModel); SaveMultiType(animePath);
} }
/// <summary> /// <summary>
@ -506,14 +516,14 @@ public class AnimeTypeModel : ObservableObjectX
/// </summary> /// </summary>
/// <param name="path">路径</param> /// <param name="path">路径</param>
/// <param name="animeTypeModel">模型</param> /// <param name="animeTypeModel">模型</param>
void SaveRaised(string path, AnimeTypeModel animeTypeModel) void SaveRaised(string path)
{ {
var animePath = Path.Combine(path, "Raise"); var animePath = Path.Combine(path, "Raise");
Directory.CreateDirectory(animePath); Directory.CreateDirectory(animePath);
if (animeTypeModel.GraphType is GraphInfo.GraphType.Raised_Dynamic) if (GraphType is GraphInfo.GraphType.Raised_Dynamic)
SaveDefault(animePath, animeTypeModel); SaveDefault(animePath);
else if (animeTypeModel.GraphType is GraphInfo.GraphType.Raised_Static) else if (GraphType is GraphInfo.GraphType.Raised_Static)
SaveMultiType(animePath, animeTypeModel); SaveMultiType(animePath);
} }
/// <summary> /// <summary>
@ -521,12 +531,12 @@ public class AnimeTypeModel : ObservableObjectX
/// </summary> /// </summary>
/// <param name="path">路径</param> /// <param name="path">路径</param>
/// <param name="animeTypeModel">模型</param> /// <param name="animeTypeModel">模型</param>
void SaveSwitch(string path, AnimeTypeModel animeTypeModel) void SaveSwitch(string path)
{ {
var animePath = Path.Combine(path, "Switch"); var animePath = Path.Combine(path, "Switch");
Directory.CreateDirectory(animePath); Directory.CreateDirectory(animePath);
var switchName = animeTypeModel.GraphType.ToString().Split(NativeUtils.Separator).Last(); var switchName = GraphType.ToString().Split(NativeUtils.Separator).Last();
SaveWithAnimeType(Path.Combine(animePath, switchName), animeTypeModel); SaveWithAnimeType(Path.Combine(animePath, switchName));
} }
/// <summary> /// <summary>
@ -534,11 +544,11 @@ public class AnimeTypeModel : ObservableObjectX
/// </summary> /// </summary>
/// <param name="path"></param> /// <param name="path"></param>
/// <param name="animeTypeModel"></param> /// <param name="animeTypeModel"></param>
static void SaveDefault(string path, AnimeTypeModel animeTypeModel) void SaveDefault(string path)
{ {
var animePath = Path.Combine(path, animeTypeModel.GraphType.ToString()); var animePath = Path.Combine(path, GraphType.ToString());
Directory.CreateDirectory(animePath); Directory.CreateDirectory(animePath);
SaveWithAnimeType(animePath, animeTypeModel); SaveWithAnimeType(animePath);
} }
/// <summary> /// <summary>
@ -546,11 +556,11 @@ public class AnimeTypeModel : ObservableObjectX
/// </summary> /// </summary>
/// <param name="path"></param> /// <param name="path"></param>
/// <param name="animeTypeModel"></param> /// <param name="animeTypeModel"></param>
static void SaveMultiType(string path, AnimeTypeModel animeTypeModel) void SaveMultiType(string path)
{ {
var animePath = Path.Combine(path, animeTypeModel.GraphType.ToString()); var animePath = Path.Combine(path, GraphType.ToString());
Directory.CreateDirectory(animePath); Directory.CreateDirectory(animePath);
SaveWithModeType(animePath, animeTypeModel); SaveWithModeType(animePath);
} }
/// <summary> /// <summary>
@ -558,27 +568,27 @@ public class AnimeTypeModel : ObservableObjectX
/// </summary> /// </summary>
/// <param name="path"></param> /// <param name="path"></param>
/// <param name="animeTypeModel"></param> /// <param name="animeTypeModel"></param>
static void SaveWithModeType(string path, AnimeTypeModel animeTypeModel) void SaveWithModeType(string path)
{ {
if (animeTypeModel.HappyAnimes.Count > 0) if (HappyAnimes.Count > 0)
{ {
var modePath = Path.Combine(path, nameof(ModeType.Happy)); var modePath = Path.Combine(path, nameof(ModeType.Happy));
SaveAnimes(modePath, animeTypeModel.HappyAnimes); SaveAnimes(modePath, HappyAnimes);
} }
if (animeTypeModel.NomalAnimes.Count > 0) if (NomalAnimes.Count > 0)
{ {
var modePath = Path.Combine(path, nameof(ModeType.Nomal)); var modePath = Path.Combine(path, nameof(ModeType.Nomal));
SaveAnimes(modePath, animeTypeModel.NomalAnimes); SaveAnimes(modePath, NomalAnimes);
} }
if (animeTypeModel.PoorConditionAnimes.Count > 0) if (PoorConditionAnimes.Count > 0)
{ {
var modePath = Path.Combine(path, nameof(ModeType.PoorCondition)); var modePath = Path.Combine(path, nameof(ModeType.PoorCondition));
SaveAnimes(modePath, animeTypeModel.PoorConditionAnimes); SaveAnimes(modePath, PoorConditionAnimes);
} }
if (animeTypeModel.IllAnimes.Count > 0) if (IllAnimes.Count > 0)
{ {
var modePath = Path.Combine(path, nameof(ModeType.Ill)); var modePath = Path.Combine(path, nameof(ModeType.Ill));
SaveAnimes(modePath, animeTypeModel.IllAnimes); SaveAnimes(modePath, IllAnimes);
} }
static void SaveAnimes(string animePath, ObservableList<AnimeModel> animes) static void SaveAnimes(string animePath, ObservableList<AnimeModel> animes)
@ -619,27 +629,27 @@ public class AnimeTypeModel : ObservableObjectX
/// </summary> /// </summary>
/// <param name="animePath"></param> /// <param name="animePath"></param>
/// <param name="animeType"></param> /// <param name="animeType"></param>
static void SaveWithAnimeType(string animePath, AnimeTypeModel animeType) void SaveWithAnimeType(string animePath)
{ {
if (animeType.HappyAnimes.Count > 0) if (HappyAnimes.Count > 0)
{ {
var modePath = Path.Combine(animePath, nameof(ModeType.Happy)); var modePath = Path.Combine(animePath, nameof(ModeType.Happy));
SaveAnimes(modePath, animeType.HappyAnimes); SaveAnimes(modePath, HappyAnimes);
} }
if (animeType.NomalAnimes.Count > 0) if (NomalAnimes.Count > 0)
{ {
var modePath = Path.Combine(animePath, nameof(ModeType.Nomal)); var modePath = Path.Combine(animePath, nameof(ModeType.Nomal));
SaveAnimes(modePath, animeType.NomalAnimes); SaveAnimes(modePath, NomalAnimes);
} }
if (animeType.PoorConditionAnimes.Count > 0) if (PoorConditionAnimes.Count > 0)
{ {
var modePath = Path.Combine(animePath, nameof(ModeType.PoorCondition)); var modePath = Path.Combine(animePath, nameof(ModeType.PoorCondition));
SaveAnimes(modePath, animeType.PoorConditionAnimes); SaveAnimes(modePath, PoorConditionAnimes);
} }
if (animeType.IllAnimes.Count > 0) if (IllAnimes.Count > 0)
{ {
var modePath = Path.Combine(animePath, nameof(ModeType.Ill)); var modePath = Path.Combine(animePath, nameof(ModeType.Ill));
SaveAnimes(modePath, animeType.IllAnimes); SaveAnimes(modePath, IllAnimes);
} }
static void SaveAnimes(string animePath, ObservableList<AnimeModel> animes) static void SaveAnimes(string animePath, ObservableList<AnimeModel> animes)
{ {
@ -659,9 +669,15 @@ public class AnimeTypeModel : ObservableObjectX
Directory.CreateDirectory(imagesPath); Directory.CreateDirectory(imagesPath);
foreach ((var index, var image) in model.Images.EnumerateIndex()) foreach ((var index, var image) in model.Images.EnumerateIndex())
{ {
image.Image.SaveToPng( var path = Path.Combine(imagesPath, $"{model.ID}_{index:000}_{image.Duration}.png");
Path.Combine(imagesPath, $"{model.ID}_{index:000}_{image.Duration}.png") if (image.Image is not null)
); {
image.Image.SaveToPng(path);
}
else if (Path.Exists(image.ImageFile))
{
File.Copy(image.ImageFile, path, true);
}
} }
} }
#endregion #endregion

View File

@ -6,6 +6,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using HKW.HKWUtils.Extensions;
using HKW.HKWUtils.Observable; using HKW.HKWUtils.Observable;
using LinePutScript; using LinePutScript;
using VPet_Simulator.Core; using VPet_Simulator.Core;
@ -22,7 +23,6 @@ public class FoodAnimeModel : ObservableObjectX, ICloneable<FoodAnimeModel>
{ {
foreach (var item in line.Where(i => i.Name.StartsWith('a'))) foreach (var item in line.Where(i => i.Name.StartsWith('a')))
{ {
//var index = int.Parse(item.Name.Substring(1));
var infos = item.Info.Split(','); var infos = item.Info.Split(',');
var foodLocationInfo = new FoodAnimeLocationModel(); var foodLocationInfo = new FoodAnimeLocationModel();
foodLocationInfo.Duration = int.Parse(infos[0]); foodLocationInfo.Duration = int.Parse(infos[0]);
@ -71,6 +71,20 @@ public class FoodAnimeModel : ObservableObjectX, ICloneable<FoodAnimeModel>
/// </summary> /// </summary>
public ObservableList<FoodAnimeLocationModel> FoodLocations { get; } = new(); public ObservableList<FoodAnimeLocationModel> FoodLocations { get; } = new();
public void LoadAnime()
{
if (BackImages.FirstOrDefault()?.Image is null)
{
foreach (var image in BackImages)
image.LoadImage();
}
if (FrontImages.FirstOrDefault()?.Image is null)
{
foreach (var image in FrontImages)
image.LoadImage();
}
}
/// <summary> /// <summary>
/// 复制 /// 复制
/// </summary> /// </summary>

View File

@ -43,7 +43,7 @@ public class FoodAnimeTypeModel : ObservableObjectX
#region ID #region ID
[DebuggerBrowsable(DebuggerBrowsableState.Never)] [DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string _id; private string _id = string.Empty;
public string ID public string ID
{ {
@ -54,7 +54,7 @@ public class FoodAnimeTypeModel : ObservableObjectX
#region Name #region Name
[DebuggerBrowsable(DebuggerBrowsableState.Never)] [DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string _name; private string _name = string.Empty;
/// <summary> /// <summary>
/// 名称 /// 名称
@ -91,6 +91,18 @@ public class FoodAnimeTypeModel : ObservableObjectX
/// </summary> /// </summary>
public ObservableList<FoodAnimeModel> IllAnimes { get; } = new(); public ObservableList<FoodAnimeModel> IllAnimes { get; } = new();
public void LoadTypeAnime()
{
foreach (var anime in HappyAnimes)
anime.LoadAnime();
foreach (var anime in NomalAnimes)
anime.LoadAnime();
foreach (var anime in PoorConditionAnimes)
anime.LoadAnime();
foreach (var anime in IllAnimes)
anime.LoadAnime();
}
public void Close() public void Close()
{ {
foreach (var anime in HappyAnimes) foreach (var anime in HappyAnimes)
@ -255,9 +267,9 @@ public class FoodAnimeTypeModel : ObservableObjectX
return new( return new(
Directory Directory
.EnumerateFiles(Path.Combine(path, pngAnimeInfo.Path)) .EnumerateFiles(Path.Combine(path, pngAnimeInfo.Path))
.Select(i => new ImageModel( .Select(f => new ImageModel(
NativeUtils.LoadImageToMemoryStream(i), f,
int.Parse(Path.GetFileNameWithoutExtension(i).Split('_')[2]) int.Parse(Path.GetFileNameWithoutExtension(f).Split('_')[2])
)) ))
); );
} }
@ -329,17 +341,29 @@ public class FoodAnimeTypeModel : ObservableObjectX
var backLayPath = Path.Combine(indexPath, BackLayName); var backLayPath = Path.Combine(indexPath, BackLayName);
Directory.CreateDirectory(frontLayPath); Directory.CreateDirectory(frontLayPath);
Directory.CreateDirectory(backLayPath); Directory.CreateDirectory(backLayPath);
foreach ((var index, var frontImage) in anime.FrontImages.EnumerateIndex()) foreach ((var index, var image) in anime.FrontImages.EnumerateIndex())
{ {
frontImage.Image.SaveToPng( var path = Path.Combine(frontLayPath, $"{anime.ID}_{index:000}_{image.Duration}.png");
Path.Combine(frontLayPath, $"{anime.ID}_{index:000}_{frontImage.Duration}.png") if (image.Image is not null)
); {
image.Image.SaveToPng(path);
} }
foreach ((var index, var backImage) in anime.BackImages.EnumerateIndex()) else if (Path.Exists(image.ImageFile))
{ {
backImage.Image.SaveToPng( File.Copy(image.ImageFile, path, true);
Path.Combine(backLayPath, $"{anime.ID}_{backImage:000}_{backImage.Duration}.png") }
); }
foreach ((var index, var image) in anime.BackImages.EnumerateIndex())
{
var path = Path.Combine(backLayPath, $"{anime.ID}_{index:000}_{image.Duration}.png");
if (image.Image is not null)
{
image.Image.SaveToPng(path);
}
else if (Path.Exists(image.ImageFile))
{
File.Copy(image.ImageFile, path, true);
}
} }
} }
@ -384,16 +408,9 @@ public class FoodAnimeTypeModel : ObservableObjectX
} }
} }
public class PNGAnimeInfo public class PNGAnimeInfo(string name, string path, IGameSave.ModeType mode)
{ {
public string Name { get; } public string Name { get; } = name;
public string Path { get; } public string Path { get; } = path;
public ModeType Mode { get; } public ModeType Mode { get; } = mode;
public PNGAnimeInfo(string name, string path, ModeType mode)
{
Name = name;
Path = path;
Mode = mode;
}
} }

View File

@ -15,12 +15,23 @@ namespace VPet.ModMaker.Models.ModModel;
/// </summary> /// </summary>
public class ImageModel : ObservableObjectX, ICloneable<ImageModel> public class ImageModel : ObservableObjectX, ICloneable<ImageModel>
{ {
public ImageModel(string imageFile, int duration = 100)
{
ImageFile = imageFile;
Duration = duration;
}
public ImageModel(BitmapImage image, int duration = 100) public ImageModel(BitmapImage image, int duration = 100)
{ {
Image = image; Image = image;
Duration = duration; Duration = duration;
} }
/// <summary>
/// 图片路径
/// </summary>
public string ImageFile { get; set; } = string.Empty;
#region Image #region Image
[DebuggerBrowsable(DebuggerBrowsableState.Never)] [DebuggerBrowsable(DebuggerBrowsableState.Never)]
private BitmapImage _image = null!; private BitmapImage _image = null!;
@ -48,9 +59,18 @@ public class ImageModel : ObservableObjectX, ICloneable<ImageModel>
set => SetProperty(ref _duration, value); set => SetProperty(ref _duration, value);
} }
#endregion #endregion
public void LoadImage()
{
Image = NativeUtils.LoadImageToMemoryStream(ImageFile);
}
public ImageModel Clone() public ImageModel Clone()
{ {
var model = new ImageModel(Image.CloneStream(), Duration); var model = new ImageModel(
Image?.CloneStream() ?? NativeUtils.LoadImageToMemoryStream(ImageFile),
Duration
);
return model; return model;
} }