前言:根据不同的需求,在一个技能上添加不同的动画,声音,特效(这里编辑器拓展就不说了,还是主要说EC思想,代码中其他方法不用看)
1.首先还是EC思想,先从E(entity)开始,创建一个人物实体e(以下简称为"e"),e拥有不同的技能(技能1,技能2),每个技能的动画(特效,音效,以下以动画为例)不同,所以对应来添加这些动画,特效,音效.
public class Player : MonoBehaviour
{
//玩家身上,的技能集合
//技能名称为key,技能组件集合为value
public Dictionary<string, List<SkillBase>> skillsList = new Dictionary<string, List<SkillBase>>();
//动画部分
RuntimeAnimatorController controller; //动画控制器
public AnimatorOverrideController overrideController; //动画覆盖控制器
//特效部分
public Transform effectsparent; //特效图节点
//音效
AudioSource audioSource; //音效
//定义一个技能id
string m_strSkillId;
static string m_strPath;
//Player
Animator anim;
public void InitData()
{
//动画控制器
overrideController = new AnimatorOverrideController();
controller = Resources.Load<RuntimeAnimatorController>("Player");
overrideController.runtimeAnimatorController = controller;
anim.runtimeAnimatorController = overrideController;
audioSource = gameObject.AddComponent<AudioSource>(); //音效是添加上的
effectsparent = transform.Find("effectsparent"); // 特效的父节点
// gameObject.name = path;
//加载技能配置表
LoadAllSkill();
}
public void play()
{
//根据条件判断播放不同的组件 遍历 skillsList item.player
//_Anim.Play();
//声音的
//特效的
//根据技能组件播放一遍。
Debug.Log("skillsList" + " " + skillsList.Count);
if(m_strSkillId != null && skillsList.ContainsKey(m_strSkillId))
{
foreach(var item in skillsList[m_strSkillId])
{
item.Play();
}
}
}
private Skill_Anim _Anim;
private Skill_Audio _Aduio;
private Skill_Effects _Effect;
//在人物身上配置技能数据
public void SetData(string skillName)
{
m_strSkillId = skillName;
List<SkillXml> skillList = GameData.Instance.GetSkillsByRoleName("Teddy");
//Debug.Log(skillList[0]);
foreach (var item in skillList) //
{
if (item.name == skillName) //找到指定的技能配置,把技能配置转换成类对象。
{
foreach (var ite in item.skills)
{
foreach (var it in ite.Value)
{
if (ite.Key.Equals("动画"))
{
//加载一个动画片段
AnimationClip clip = AssetDatabase.LoadAssetAtPath<AnimationClip>("Assets/GameDate/Anim/" + it + ".anim");
if (_Anim == null) _Anim = new Skill_Anim(this); //创建一个技能的类对象
_Anim.SetAnimClip(clip);
//skillsList[item.name].Add(_Anim);
}
else if (ite.Key.Equals("音效"))
{
//加载一个音效的资源
AudioClip clip = AssetDatabase.LoadAssetAtPath<AudioClip>("Assets/GameDate/Audio/" + it + ".mp3");
if (_Aduio == null) _Aduio = new Skill_Audio(this); //创建一个音效的对象。
_Aduio.SetAnimClip(clip);
//skillsList[item.name].Add(_Anim);
}
else if (ite.Key.Equals("特效")) //特效
{
GameObject clip = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/GameDate/Effect/Skill/" + it + ".prefab");
if(_Effect == null) _Effect = new Skill_Effects(this);
_Effect.SetGameClip(clip); //
//skillsList[item.name].Add(_Anim);
}
}
}
}
}
}
public static Player Init(string path)
{
if (path != null)
{
m_strPath = path;
Debug.Log("Init" + path);
Debug.Log("strPath" + m_strPath);
string str = "Assets/aaa/" + path + ".prefab";
GameObject obj = AssetDatabase.LoadAssetAtPath<GameObject>(str);
if (obj != null)
{
//动画
Player player = Instantiate(obj).GetComponent<Player>();
player.overrideController = new AnimatorOverrideController();
player.controller = Resources.Load<RuntimeAnimatorController>("Player");
player.overrideController.runtimeAnimatorController = player.controller;
player.anim.runtimeAnimatorController = player.overrideController;
//添加音效组件
player.audioSource = player.gameObject.AddComponent<AudioSource>();
//特效的父节点
player.effectsparent = player.transform.Find("effectsparent");
player.gameObject.name = path;
//加载技能配置表
player.LoadAllSkill();
return player;
}
}
return null;
}
//加载玩家身上的技能配置表
void LoadAllSkill()
{
Debug.Log("LoadAllSkill" + m_strPath);
//根据角色的名字,来加载技能文件,
if (File.Exists("Assets/" + "Teddy" + ".txt"))
{
string str = File.ReadAllText("Assets/" + "Teddy" + ".txt");
List<SkillXml> skills = JsonConvert.DeserializeObject<List<SkillXml>>(str);
foreach (var item in skills) //解析技能配置表,技能列表
{
//技能列表的组件
skillsList.Add(item.name, new List<SkillBase>());
foreach (var ite in item.skills) //解析技能组件
{
foreach (var it in ite.Value)
{
if (ite.Key.Equals("动画")) //根据类型,创建对应的功能组件
{
AnimationClip clip = AssetDatabase.LoadAssetAtPath<AnimationClip>("Assets/GameDate/Anim/" + it + ".anim");
Skill_Anim _Anim = new Skill_Anim(this); //动画技能的组件
_Anim.SetAnimClip(clip);
skillsList[item.name].Add(_Anim);
}
else if (ite.Key.Equals("音效"))
{
AudioClip clip = AssetDatabase.LoadAssetAtPath<AudioClip>("Assets/GameDate/Audio/" + it + ".mp3");
Skill_Audio _Anim = new Skill_Audio(this);
_Anim.SetAnimClip(clip);
skillsList[item.name].Add(_Anim);
}
else if (ite.Key.Equals("特效"))
{
GameObject clip = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/GameDate/Effect/Skill/" + it + ".prefab");
Skill_Effects _Anim = new Skill_Effects(this);
_Anim.SetGameClip(clip);
skillsList[item.name].Add(_Anim);
}
else if(ite.Key.Equals("移动")) //移动的组件
{
Skill_Move Amin = new Skill_Move(this); //玩家信息传入移动技能里面\
Amin.moveToPos(it); // 传递坐标数组
//加入技能字典
skillsList[item.name].Add(Amin);
}
}
}
}
}
}
private void Awake()
{
anim = gameObject.GetComponent<Animator>();
}
public List<SkillBase> GetSkill(string skillName)
{
if (skillsList.ContainsKey(skillName))
{
return skillsList[skillName];
}
return null;
}
public List<SkillBase> AddNewSkill(string newSkillName)
{
if (skillsList.ContainsKey(newSkillName))
{
return skillsList[newSkillName];
}
skillsList.Add(newSkillName, new List<SkillBase>());
return skillsList[newSkillName];
}
public void RevSkill(string newSkillName)
{
if (skillsList.ContainsKey(newSkillName))
{
skillsList.Remove(newSkillName);
}
}
public void Destroy()
{
Destroy(gameObject);
}
//玩家销毁的时候的时候,保存技能按钮
private void OnDestroy()
{
// return;
List<SkillXml> skills = new List<SkillXml>();
foreach (var item in skillsList)
{
SkillXml skillXml = new SkillXml();
skillXml.name = item.Key;
foreach (var ite in item.Value)
{
if (ite is Skill_Anim)
{
if (!skillXml.skills.ContainsKey("动画"))
{
skillXml.skills.Add("动画", new List<string>());
}
skillXml.skills["动画"].Add(ite.name);
}
else if (ite is Skill_Audio)
{
if (!skillXml.skills.ContainsKey("音效"))
{
skillXml.skills.Add("音效", new List<string>());
}
skillXml.skills["音效"].Add(ite.name);
}
else if (ite is Skill_Effects)
{
if (!skillXml.skills.ContainsKey("特效"))
{
skillXml.skills.Add("特效", new List<string>());
}
skillXml.skills["特效"].Add(ite.name);
}
//保存移动技能
else if(ite is Skill_Move)
{
if(!skillXml.skills.ContainsKey("移动"))
{
skillXml.skills.Add("移动", new List<string>());
}
// 保存移动数据
skillXml.skills["移动"].Add(ite.name);
}
}
skills.Add(skillXml);
}
string str = JsonConvert.SerializeObject(skills);
File.WriteAllText("Assets/" + gameObject.name + ".txt", str); //json数据保存,
}
}
//文件数据转内存数据
public class SkillXml
{
public string name; //技能的名字
//组成这个技能的,组件的集合
public Dictionary<string, List<string>> skills = new Dictionary<string, List<string>>();
}
2.在e身上的肯定需要一个库(字典)来存放这些动画,特效,音效,就是所谓的"C"这些动画,特效,音效又需要统一管理起来,,所以他们需要继承一个"SkillBase"基类
//技能基类
public class SkillBase
{
public string name = string.Empty;
public virtual void Play() //播放
{
}
public virtual void Init() //初始化
{
}
public virtual void Stop() //停止
{
}
}
(这个库和这个基类在EC思想中很重要)
写完这个基类之后,以动画(这里用的是动画重写控制器)为例
public class Skill_Anim : SkillBase
{
Player player;
Animator anim;
public AnimationClip animClip;
AnimatorOverrideController controller;
public Skill_Anim(Player _player)
{
player = _player;
anim = player.gameObject.GetComponent<Animator>();
controller = player.overrideController;
}
public override void Init()
{
controller["Start"] = animClip;
}
public void SetAnimClip(AnimationClip _animClip)
{
animClip = _animClip;
name = animClip.name;
controller["Start"] = animClip;
}
public override void Play()
{
base.Play();
anim.StopPlayback();
AnimatorStateInfo stateInfo = anim.GetCurrentAnimatorStateInfo(0);
if (stateInfo.IsName("Idle1"))
{
anim.SetTrigger("Play");
}
}
public override void Stop()
{
base.Play();
anim.StartPlayback();
}
}
3. 这里放一下编辑的代码(但是需要与我的unity的资源一样,比如有对应的文件夹)
这个是选择人物窗口
public class SkillEditorWindow : EditorWindow
{
class PlayerEditor
{
public int _characterIndex = 0; //角色的id号
public int _folderIndex = 0; //文件的id
public string characterName = string.Empty;
public string folderName = string.Empty;
public string characterFilter = string.Empty;
public List<string> characteList = new List<string>(); //文件下模型名字的集合
public Player player = null; //玩家信息的业务逻辑
}
PlayerEditor m_player = new PlayerEditor();
/// <summary>
/// 文件名
/// </summary>
List<string> m_folderList = new List<string>();
/// <summary>
/// 所有预制体名
/// </summary>
List<string> m__characterList = new List<string>();
/// <summary>
/// 按文件名储存 预制体
/// </summary>
Dictionary<string, List<string>> m_folderPrefabs = new Dictionary<string, List<string>>();
/// <summary>
/// 技能详情窗口
/// </summary>
SkillWindow skillWindow;
/// <summary>
/// 储存 创建新的技能 的名字
/// </summary>
string newSkillName = string.Empty;
/// <summary>
/// 窗口初始化 (打开窗口)
/// </summary>
[MenuItem("Tools/技能编译器")]
public static void Init()
{
//if (Application.isPlaying) //应用程序是否在运行状态,在运行状态,打开技能编辑器
{
SkillEditorWindow window = EditorWindow.GetWindow<SkillEditorWindow>("Editor");
if (window != null)
{
window.Show();
}
}
}
// 先走这个方法,里面放的都是解析数据的方法
private void OnEnable()
{
//查找文件夹
DoSearchFolder(); //敌人,玩家
//查找文件夹下面的角色名,
DoSearchCharacter(); //角色,
}
/// <summary>
/// 模型文件夹名称 , 比如敌人,玩家
/// </summary>
void DoSearchFolder()
{
//文件夹列表
m_folderList.Clear();
m_folderList.Add("all");//添加一个all,代表下面要显示敌人和玩家所有模型的名称
//获取当前路径下的文件夹
string[] folders = Directory.GetDirectories(GetCharacterPath());//存放model下面的文件夹的名称
foreach (var item in folders)
{
m_folderList.Add(Path.GetFileName(item));
}
}
/// <summary>
/// 索引所有的 预制体
/// </summary>
void DoSearchCharacter()
{
//查找当前文件下,所有后缀名".prefab"为的名字,
string[] files = Directory.GetFiles(GetCharacterPath(), "*.prefab", SearchOption.AllDirectories);
m__characterList.Clear();
foreach (var item in files)
{
m__characterList.Add(Path.GetFileNameWithoutExtension(item));//把所有模型(敌人,玩家)不带后缀名(.prefab)放入集合中
}
m__characterList.Sort();
m__characterList.Insert(0, "null");//插入第一个null值,就是没选角色
m_player.characteList.AddRange(m__characterList);//赋值给角色数据里的模型集合
}
string GetCharacterPath()
{
return Application.dataPath + "/GameDate/Model";
}
private void OnGUI()
{
//枚举,选择类型:敌人类,还是玩家类
int folderIndex = EditorGUILayout.Popup(m_player._folderIndex, m_folderList.ToArray()); //folderIndex返回已选择的,m_player._folderIndex初始的值(0),刚开始是"null"
if (folderIndex != m_player._folderIndex) //判断选择的类型和之前的是否一致,一致不变
{
m_player._folderIndex = folderIndex;//让默认值等你选的值
m_player._characterIndex = -1;
string folderName = m_folderList[m_player._folderIndex];
//通过你选的角色类型,来展示不同的人物模型
List<string> list;
if (folderName.Equals("all"))//如果选的all就直接把所有的模型放到集合里
{
list = m__characterList;
}
else
{
if (!m_folderPrefabs.TryGetValue(folderName, out list))//如果选择敌人或者玩家,那就再多读一层 ,根据点的,来读玩家文件夹或者敌人文件夹,然后把里面的模型放入集合
{
list = new List<string>();
string[] files = Directory.GetFiles(GetCharacterPath() + "/" + folderName, "*.prefab", SearchOption.AllDirectories);
foreach (var item in files)
{
list.Add(Path.GetFileNameWithoutExtension(item));
}
m_folderPrefabs[folderName] = list;
}
//SetPlayerCharacters(m_player, list);
}
m_player.characteList.Clear(); //角色的集合,当前选择的类型文件目录下的所有角色,加入到角色集合
m_player.characteList.AddRange(list);//所选类型的模型放入到此文件夹
}
int characterIndex = EditorGUILayout.Popup(m_player._characterIndex, m_player.characteList.ToArray()); //选择模型的下拉列表,
if (characterIndex != m_player._characterIndex)
{
m_player._characterIndex = characterIndex;
if (m_player.characterName != m_player.characteList[m_player._characterIndex])
{
m_player.characterName = m_player.characteList[m_player._characterIndex];
if (!string.IsNullOrEmpty(m_player.characterName))
{
if (m_player.player != null) //m_player有一个player属性,如果创建过,就把之前的模型删掉
{
m_player.player.Destroy(); //调用玩家的销毁函数
}
m_player.player = Player.Init(m_player.characterName);//然后再创建新的角色
}
}
}
/*float speed = EditorGUILayout.Slider((m_player.player == null ? 0f : m_player.player.AnimSpeed), 0.1f, 5);
if (m_player.player != null && m_player.player.AnimSpeed != speed)
{
m_player.player.AnimSpeed = speed;
}*/
newSkillName = GUILayout.TextField(newSkillName); //输入新技能,
if (GUILayout.Button("创建新的技能"))
{
if (!string.IsNullOrEmpty(newSkillName) && m_player.player != null)//如果输入了新技能,并且任务也创建了
{
List<SkillBase> skills = m_player.player.AddNewSkill(newSkillName);//掉用方法添加新技能
OpenSkillWindow(newSkillName, skills);//打开技能窗口
newSkillName = "";//然后将上面的输入框置空
}
}
if (m_player.player != null)//如果选择玩家,
{
ScrollViewPos = GUILayout.BeginScrollView(ScrollViewPos, false, true);
foreach (var item in m_player.player.skillsList) //遍历玩家身上的技能列表,
{
GUILayout.BeginHorizontal();
if (GUILayout.Button(item.Key)) //根据的技能名字,创建一个按钮,技能名称为key,技能组件集合为value
{
List<SkillBase> skills = m_player.player.GetSkill(item.Key);
foreach (var ite in skills)//循环技能组件
{
ite.Init();//给他们依次赋上各自的属性
}
OpenSkillWindow(item.Key, skills);//打开技能窗口(预览)
}
GUILayoutOption[] option = new GUILayoutOption[] {
GUILayout.Width(60),
GUILayout.Height(19)
};
if (GUILayout.Button("删除技能", option))
{
m_player.player.RevSkill(item.Key);
break;
}
GUILayout.EndHorizontal();
}
GUILayout.EndScrollView();
}
}
Vector2 ScrollViewPos = new Vector2(0, 0);
//打开新的技能面板
void OpenSkillWindow(string newSkillName, List<SkillBase> skills)
{
if (skills != null)
{
if (skillWindow == null)
{
skillWindow = EditorWindow.GetWindow<SkillWindow>(""); //打开技能面板的窗口
}
skillWindow.titleContent = new GUIContent(newSkillName); //设置第二个技能面板的标题
skillWindow.SetInitSkill(skills, m_player.player); //给技能面板设置数据, 技能数据和玩家模型传值
//if (skillWindow == null)
//{
// skillWindow = EditorWindow.GetWindow<Skill2Window>(""); //打开技能面板的窗口
//}
//skillWindow.titleContent = new GUIContent(newSkillName); //设置第二个技能面板的标题
//skillWindow.setSkillData(skills, m_player.player); //给技能面板设置数据, 技能数据和玩家模型传值
skillWindow.Show(); //显示技能面板
skillWindow.Repaint(); //技能面板重新绘制。
}
}
}
人物窗口选择完
点击"123"技能
下面是技能窗口代码(注意看点击播放按钮)
public class SkillWindow : EditorWindow
{
Player player;//玩家
List<SkillBase> skills;//技能组件集合
float currSpeed = 1;
public void SetInitSkill(List<SkillBase> _skills, Player _player)
{
player = _player;
// player.AnimSpeed = 1;
currSpeed = 1;
skills = _skills;
}
//技能类型列表
string[] skillComponent = new string[] { "null", "动画", "声音", "特效", "移动" };
int skillComponentIndex = 0;
private void OnGUI()
{
GUILayout.BeginHorizontal();
if (GUILayout.Button("播放"))//这里的简单了,当你点击播放的时候,循环此技能的组件列表,都播放就行了
{
foreach (var item in skills)
{
item.Play();
}
}
if (GUILayout.Button("停止"))
{
foreach (var item in skills)
{
item.Stop();
}
}
GUILayout.EndHorizontal();
GUILayout.Label("速度");//调整技能的组件的速度属性
float speed = EditorGUILayout.Slider(currSpeed, 0, 5);
if (speed != currSpeed)
{
currSpeed = speed;
Time.timeScale = currSpeed;
}
GUILayout.BeginHorizontal();
skillComponentIndex = EditorGUILayout.Popup(skillComponentIndex, skillComponent);
if (GUILayout.Button("添加"))//添加一个新组件
{
switch (skillComponentIndex)
{
case 1:
skills.Add(new Skill_Anim(player));
break;
case 2:
skills.Add(new Skill_Audio(player));
break;
case 3:
skills.Add(new Skill_Effects(player));
break;
case 4:
skills.Add(new Skill_Move(player));
break;
}
}
GUILayout.EndHorizontal();
ScrollViewPos = GUILayout.BeginScrollView(ScrollViewPos, false, true);
foreach (var item in skills)
{
GUILayout.BeginHorizontal();
GUILayout.Label(item.name);
if (GUILayout.Button("删除"))//循环技能组件,先展示删除
{
skills.Remove(item);
break;
}
GUILayout.EndHorizontal();
if (item is Skill_Anim)//如果动画,那么展示动画,
{
ShowSkill_Anim(item as Skill_Anim);
}
else if (item is Skill_Audio)
{
Skill_Audio(item as Skill_Audio);
}
else if (item is Skill_Effects)
{
Skill_Effects(item as Skill_Effects);
}
else if(item is Skill_Move) //显示移动坐标
{
Skill_Move(item as Skill_Move);
}
GUILayout.Space(0.5f);
}
GUILayout.EndScrollView();
}
Vector2 ScrollViewPos = new Vector2(0, 0);
void ShowSkill_Anim(Skill_Anim _Anim)
{
AnimationClip animClip = EditorGUILayout.ObjectField(_Anim.animClip, typeof(AnimationClip), false) as AnimationClip;
if (_Anim.animClip != animClip)
{
_Anim.SetAnimClip(animClip);//把这个animClip创过 通过方法给他赋值上去
}
}
void Skill_Audio(Skill_Audio _Audio)
{
AudioClip audioClip = EditorGUILayout.ObjectField(_Audio.audioClip, typeof(AudioClip), false) as AudioClip;
if (_Audio.audioClip != audioClip)
{
_Audio.SetAnimClip(audioClip);
}
}
void Skill_Effects(Skill_Effects _Effects)
{
GameObject gameClip = EditorGUILayout.ObjectField(_Effects.gameClip, typeof(GameObject), false) as GameObject;
if (_Effects.gameClip != gameClip)
{
_Effects.SetGameClip(gameClip);
}
}
//显示移动的面板
void Skill_Move(Skill_Move _move)
{
if(_move != null)
{
GUILayoutOption[] option = new GUILayoutOption[] {
GUILayout.Width(60),
GUILayout.Height(19)
};
_move.m_vPos = EditorGUILayout.Vector3Field("移动目标", _move.m_vPos);
编辑一个X坐标
//EditorGUILayout.LabelField("X:", option);
//_move.m_vPos.x = EditorGUILayout.FloatField(_move.m_vPos.x, option);
编辑一个Y坐标
//EditorGUILayout.LabelField("Y:", option);
//_move.m_vPos.y = EditorGUILayout.FloatField(_move.m_vPos.y, option);
编辑一个X坐标
//EditorGUILayout.LabelField("Z:", option);
//_move.m_vPos.z = EditorGUILayout.FloatField(_move.m_vPos.z, option);
}
}
}