前言
本人的上一篇文章实现游戏数据文件读取和写入中最后提到,要做一个可视化工具来避免每次新增游戏数据只能从xml文件中手动添加。话不多说,直接开始~
准备
开发环境:VisualStudio2019;项目类型:WPF应用程序。
界面预览
如上,就是做出来的可视化工具的技能数据页签,技能效果与Boss数据页签与此类似,直接上图。
在这里界面的布局代码就不贴出来了,读者在自己的项目里按照图中所示自行布局即可。本人项目的代码,在文章最后会提供一个资源链接。想看这个项目源码的人可以去下载,但是本人还是建议自己去编写程序。
代码开始
首先,创建需要用到的全局变量
private GameData gameData;
private List<SkillData> lstSkillData = new List<SkillData>();
private List<SkillEffectData> lstSkillEffectData = new List<SkillEffectData>();
private List<BossData> lstBossData = new List<BossData>();
private CheckBox[] chbBossSkills = null;
其中,gameData是全局的游戏数据对象,关于GameData类,笔者的上一篇博客已经写过,读者可自行去查看。lstSkillData、lstSkillEffectData及lstBossData分别用来存技能、技能效果和Boss相关的一些数据。chbBossSkills是用来做Boss可选技能的复选框的。
然后,编写一个LoadData函数,界面初始化后调用。
private void LoadData()
{
string path = Utils.SavePath;
try
{
gameData = Utils.DeserializeFile<GameData>(path);
}
catch (Exception)
{
MessageBox.Show("读取错误,请检查GameData.cs文件!", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
Close();
return;
}
txt_Version.Text = gameData.strVersion.ToString();
chbBossSkills = new CheckBox[gameData.lstSkill.Count];
Thickness th = new Thickness();
th.Bottom = 1;
th.Left = 5;
th.Right = 5;
th.Top = 1;
lb_SkillDetail.Items.Clear();
lstSkillData.Clear();
wp_BossSkills.Children.Clear();
for (int i = 0; i < gameData.lstSkill.Count; i++)
{
lb_SkillDetail.Items.Add(gameData.lstSkill[i].strName);
lstSkillData.Add(gameData.lstSkill[i]);
chbBossSkills[i] = new CheckBox();
chbBossSkills[i].Margin = th;
chbBossSkills[i].Content = gameData.lstSkill[i].strName;
wp_BossSkills.Children.Add(chbBossSkills[i]);
}
lb_SkillEffectDetail.Items.Clear();
lstSkillEffectData.Clear();
cb_SkillEffect.Items.Clear();
for (int i = 0; i < gameData.lstSkillEffect.Count; i++)
{
lb_SkillEffectDetail.Items.Add(gameData.lstSkillEffect[i].strName);
lstSkillEffectData.Add(gameData.lstSkillEffect[i]);
cb_SkillEffect.Items.Add(gameData.lstSkillEffect[i].strName);
}
lb_BossDetail.Items.Clear();
lstBossData.Clear();
for (int i = 0; i < gameData.lstBoss.Count; i++)
{
lb_BossDetail.Items.Add(gameData.lstBoss[i].strName);
lstBossData.Add(gameData.lstBoss[i]);
}
}
其中,Utils是一个静态类,里面有一些全局需要使用的变量和方法。其余代码笔者不做过多解释,读者应该能自行理解。
实现技能数据的增删功能
private void btn_AddSkill_Click(object sender, RoutedEventArgs e)
{
NewData newData = new NewData("New Skill", "请输入技能名");
newData.WindowStartupLocation = WindowStartupLocation.Manual;
newData.Left = 200;
newData.Top = 200;
newData.Topmost = true;
if (newData.ShowDialog() == true)
{
lb_SkillDetail.Items.Add(Utils.Message);
SkillData skillData = new SkillData();
skillData.strName = Utils.Message;
skillData.strImage = "";
lstSkillData.Add(skillData);
lb_SkillDetail.SelectedIndex = lb_SkillDetail.Items.Count - 1;
}
}
private void btn_DeleteSkill_Click(object sender, RoutedEventArgs e)
{
if (lb_SkillDetail.SelectedItem != null)
{
for (int i = 0; i < lstSkillData.Count; i++)
{
if (lb_SkillDetail.SelectedItem.ToString() == lstSkillData[i].strName)
{
lstSkillData.Remove(lstSkillData[i]);
continue;
}
}
lb_SkillDetail.Items.Remove(lb_SkillDetail.SelectedItem);
lb_SkillDetail.SelectedIndex = 0;
}
}
其中,NewData是项目里做的另外一个WPF窗口,当点击新增技能按钮时会弹出一个新窗口,并提示用户输入技能名以创建。NewData接收的两个参数分别为新窗口的标题和提示信息,这样做是为了通用,技能效果和Boss的新增窗口也可以调用此。
保存技能
新增技能后,可以修改技能相关数据保存,也可以选择已有技能修改相关数据保存。
private void btn_SaveSkill_Click(object sender, RoutedEventArgs e)
{
if (lb_SkillDetail.SelectedItem == null)
{
MessageBox.Show("请选中要保存的项!", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
for (int i = 0; i < lstSkillData.Count; i++)
{
if (lb_SkillDetail.SelectedItem.ToString() == lstSkillData[i].strName.ToString())
{
lstSkillData[i].strName = txt_SkillName.Text.ToString();
lstSkillData[i].skillType = GetSkillType(((ComboBoxItem)cb_SkillType.SelectedItem).Tag.ToString());
lstSkillData[i].dDamage = Convert.ToDouble(txt_SkillDamage.Text.ToString());
lstSkillData[i].skillEffect = GetSkillEffect((cb_SkillEffect.SelectedItem).ToString());
lstSkillData[i].iBlueLoss = Convert.ToInt32(txt_SkillBlueLoss.Text.ToString());
lstSkillData[i].attackRange = GetSkillAttackRange(((ComboBoxItem)cb_SkillAttackRange.SelectedItem).Tag.ToString());
lstSkillData[i].dPreCastTime = Convert.ToDouble(txt_SkillPreCastTime.Text.ToString());
lstSkillData[i].dAfterCastTime = Convert.ToDouble(txt_SkillAfterCastTime.Text.ToString());
lstSkillData[i].dReactionTime = Convert.ToDouble(txt_SkillReactionTime.Text.ToString());
lstSkillData[i].dStraightTime = Convert.ToDouble(txt_SkillStraightTime.Text.ToString());
lstSkillData[i].strImage = txt_SkillImage.Text.ToString();
lb_SkillDetail.Items.Remove(lb_SkillDetail.SelectedItem);
lb_SkillDetail.Items.Add(txt_SkillName.Text.ToString());
MessageBox.Show("保存成功!", "信息", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
}
}
这里面用到的相关函数代码如下:
private SkillType GetSkillType(string type)
{
SkillType skillType = SkillType.MonomerDamage;
switch (type)
{
case "MonomerDamage":
skillType = SkillType.MonomerDamage;
break;
case "GroupDamage":
skillType = SkillType.GroupDamage;
break;
case "SoftControl":
skillType = SkillType.SoftControl;
break;
case "HardControl":
skillType = SkillType.HardControl;
break;
default:
break;
}
return skillType;
}
private SkillEffectData GetSkillEffect(string effect)
{
SkillEffectData skillEffectData = new SkillEffectData();
for (int i = 0; i < lstSkillEffectData.Count; i++)
{
if(lstSkillEffectData[i].strName == effect)
{
skillEffectData = lstSkillEffectData[i];
continue;
}
}
return skillEffectData;
}
private AttackRange GetSkillAttackRange(string range)
{
AttackRange attackRange = AttackRange.Close;
switch (range)
{
case "Close":
attackRange = AttackRange.Close;
break;
case "Middle":
attackRange = AttackRange.Middle;
break;
case "Remote":
attackRange = AttackRange.Remote;
break;
case "OutOfRange":
attackRange = AttackRange.OutOfRange;
break;
default:
break;
}
return attackRange;
}
由于技能数据里有一个技能效果类型的成员,因此,我们需要写一个GetSkillEffect的方法,来得到相关的技能效果数据。其余代码不做过多解释,比较简单。
切换技能数据
private void lb_SkillDetail_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (lb_SkillDetail.SelectedItem != null)
{
for (int i = 0; i < lstSkillData.Count; i++)
{
if (lb_SkillDetail.SelectedItem.ToString() == lstSkillData[i].strName.ToString())
{
txt_SkillName.Text = lstSkillData[i].strName.ToString();
JudgeSkillType(lstSkillData[i].skillType);
txt_SkillDamage.Text = lstSkillData[i].dDamage.ToString();
if (lstSkillData[i].skillEffect != null)
{
JudgeSkillEffect(lstSkillData[i].skillEffect.strName);
}
txt_SkillBlueLoss.Text = lstSkillData[i].iBlueLoss.ToString();
JudgeSkillAttackRange(lstSkillData[i].attackRange);
txt_SkillPreCastTime.Text = lstSkillData[i].dPreCastTime.ToString();
txt_SkillAfterCastTime.Text = lstSkillData[i].dAfterCastTime.ToString();
txt_SkillReactionTime.Text = lstSkillData[i].dReactionTime.ToString();
txt_SkillStraightTime.Text = lstSkillData[i].dStraightTime.ToString();
txt_SkillImage.Text = lstSkillData[i].strImage.ToString();
return;
}
}
}
}
这里面用到的相关函数代码如下:
private void JudgeSkillType(SkillType type)
{
switch (type)
{
case SkillType.MonomerDamage:
cb_SkillType.SelectedIndex = 0;
break;
case SkillType.GroupDamage:
cb_SkillType.SelectedIndex = 1;
break;
case SkillType.SoftControl:
cb_SkillType.SelectedIndex = 2;
break;
case SkillType.HardControl:
cb_SkillType.SelectedIndex = 3;
break;
default:
break;
}
}
private void JudgeSkillEffect(string effect)
{
for(int i = 0; i < cb_SkillEffect.Items.Count; i++)
{
if(cb_SkillEffect.Items[i].ToString() == effect)
{
cb_SkillEffect.SelectedItem = cb_SkillEffect.Items[i];
return;
}
}
}
private void JudgeSkillAttackRange(AttackRange range)
{
switch (range)
{
case AttackRange.Close:
cb_SkillAttackRange.SelectedIndex = 0;
break;
case AttackRange.Middle:
cb_SkillAttackRange.SelectedIndex = 1;
break;
case AttackRange.Remote:
cb_SkillAttackRange.SelectedIndex = 2;
break;
case AttackRange.OutOfRange:
cb_SkillAttackRange.SelectedIndex = 3;
break;
default:
break;
}
}
由于技能数据里有一个技能效果类型的成员,因此,我们需要写一个JudgeSkillEffect的方法,来显示出当前技能到底是哪一个技能效果。其余代码不做过多解释,比较简单。
技能效果和Boss界面相关代码
技能效果这里就不多说了,与技能的类似。Boss的这里说一点,因为Boss的数据界面需要有一处显示Boss的可选技能,并且显示出当前Boss究竟会哪些技能。这就用到我们之前定义的全局变量chbBossSkills了。
在本文上面,我们在LoadData方法里把从GameData.xml文件里读取到的所有技能都初始化到chbBossSkills里了,然后赋给了一个WarpPanel对象显示出来。所以,接下来,我们只要在切换Boss数据和保存Boss数据的时候利用chbBossSkills做一些操作就可以了。
直接贴上这两部分代码~
private void lb_BossDetail_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (lb_BossDetail.SelectedItem != null)
{
for (int i = 0; i < lstBossData.Count; i++)
{
if (lb_BossDetail.SelectedItem.ToString() == lstBossData[i].strName.ToString())
{
txt_BossName.Text = lstBossData[i].strName.ToString();
txt_BossMoveSpeed.Text = lstBossData[i].fMoveSpeed.ToString();
JudgeBossViglanceRange(lstBossData[i].viglanceRange);
JudgeBossAttackRange(lstBossData[i].attackRange);
txt_BossAttackForwardTime.Text = lstBossData[i].fAttackForwardTime.ToString();
txt_BossAttackBackTime.Text = lstBossData[i].fAttackBackTime.ToString();
txt_BossReactionTime.Text = lstBossData[i].fRectionTime.ToString();
txt_BossStraightTime.Text = lstBossData[i].fStraightTime.ToString();
for (int j = 0; j < lstBossData[i].lstBossSkills.Count; j++)
{
for(int k = 0; k < chbBossSkills.Length; k++)
{
if(lstBossData[i].lstBossSkills[j].strName.ToString() == chbBossSkills[k].Content.ToString())
{
chbBossSkills[k].IsChecked = true;
}
}
}
return;
}
}
}
}
private void btn_SaveBoss_Click(object sender, RoutedEventArgs e)
{
if (lb_BossDetail.SelectedItem == null)
{
MessageBox.Show("请选中要保存的项!", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
for (int i = 0; i < lstBossData.Count; i++)
{
if (lb_BossDetail.SelectedItem.ToString() == lstBossData[i].strName.ToString())
{
lstBossData[i].strName = txt_BossName.Text.ToString();
lstBossData[i].fMoveSpeed = Convert.ToSingle(txt_BossMoveSpeed.Text.ToString());
lstBossData[i].viglanceRange = GetSkillAttackRange(((ComboBoxItem)cb_BossViglanceRange.SelectedItem).Tag.ToString());
lstBossData[i].attackRange = GetSkillAttackRange(((ComboBoxItem)cb_BossAttackRange.SelectedItem).Tag.ToString());
lstBossData[i].fAttackForwardTime = Convert.ToSingle(txt_BossAttackForwardTime.Text.ToString());
lstBossData[i].fAttackBackTime = Convert.ToSingle(txt_BossAttackBackTime.Text.ToString());
lstBossData[i].fRectionTime = Convert.ToSingle(txt_BossReactionTime.Text.ToString());
lstBossData[i].fStraightTime = Convert.ToSingle(txt_BossStraightTime.Text.ToString());
List<SkillData> bossSkillData = new List<SkillData>();
for (int k = 0; k < chbBossSkills.Length; k++)
{
if (chbBossSkills[k].IsChecked == true)
{
for (int j = 0; j < lstSkillData.Count; j++)
{
if (chbBossSkills[k].Content.ToString() == lstSkillData[j].strName.ToString())
{
bossSkillData.Add(lstSkillData[j]);
}
}
}
}
lstBossData[i].lstBossSkills = bossSkillData;
lb_BossDetail.Items.Remove(lb_BossDetail.SelectedItem);
lb_BossDetail.Items.Add(txt_BossName.Text.ToString());
MessageBox.Show("保存成功!", "信息", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
}
}
保存到GameData.xml文件
至此,我们还差一步,就是把上面做的那些操作之后的新数据保存到我们的GameData.xml文件上。直接上代码~
private void btn_Save_Click(object sender, RoutedEventArgs e)
{
string path = Utils.SavePath;
GameData game = new GameData();
game.strVersion = txt_Version.Text.ToString();
game.dateTime = DateTime.Now;
game.lstSkill = lstSkillData;
game.lstSkillEffect = lstSkillEffectData;
game.lstBoss = lstBossData;
Utils.SerializerFile(path, game);
MessageBox.Show("保存到GameData.xml文件成功!", "信息", MessageBoxButton.OK, MessageBoxImage.Information);
LoadData();
}
保存到GameData.xml文件成功后,之所以调一下LoadData方法,是为了及时刷新界面,省的保存后需要重启软件才能看到新数据。