Unity怎么释放一个dnf奶爸技能释放顺序始终从你的正面出来

> 博客详情
摘要: unity3d 技能 编辑器
##一:技能系统整体结构以及相关系统
技能包括技能数据和技能执行器
技能数据包括客户端表现数据以及客户端服务器共用的 逻辑计算数据
技能执行器主要包括: 技能状态机,技能Layout, 技能粒子效果,技能DamageShape伤害体,技能Buff,子弹,单位生成器等
##二:技能系统每个组件的详细解释
1: 技能状态机: 管理技能整体状态,其包含了若干个技能Layout,以及技能的buff, 同时监听有多个外部事件,例如技能开始事件,技能攻击触发事件,技能子弹碰撞事件等,在不同的事件下,技能状态机 激活特定的技能Layout 层。
2:技能Layout: 管理一些技能的粒子效果,技能的单位生成器,技能伤害体
3:单位生成器,用于生成技能的 子弹, 生成技能的召唤宠物等
4:伤害体, 对于AOE技能 生成伤害范围,伤害运动轨迹
##三:技能设计方式详解
1:近战攻击技能 配置举例
首先在配置表中 配置 角色职业的基本攻击技能的编号
接着创建一个 空物体,挂上技能基本数据配置 组件, 为该组件添加事件,例如添加武器砍击造成伤害事件;
给对应事件添加一个 技能layout配置;
技能layout配置中,添加DamageShape 伤害碰撞体;
配置伤害碰撞体,为基于角度的检测伤害对象;
这样一个基本的基于角度的近战攻击技能就配置完成了。
2:远程普通攻击技能 配置
首先配置表中 添加 相关的技能,设置角色的基本攻击技能ID;
接着创建技能 基本数据配置,
添加 武器砍击事件;
为事件添加一个 技能L
为layout配置,添加一个UnitSpawn, 单位生成器;
配置UnitSpawn 单位生成器,生成一个 Missile 子弹;
配置Missile子弹的 发射时粒子, 飞行粒子, 碰撞粒子, 死亡粒子;
基本的远程攻击就配置好了
3:召唤宠物技能 配置
配置职业技能;
为技能创建基本数据配置, 添加 技能启动事件 和 砍击事件, 子弹死亡事件;
添加三个 layout, 第一个 在启动时, 播放启动粒子效果, 第二个负责 砍击事件 生成子弹; 第三个处理子弹死亡事件;
配置子弹死亡事件 layout, 添加一个UnitSpawn,
在子弹死亡位置 生成宠物;
为技能添加一个 buff, 目标是宠物,当召唤出宠物之后,为宠物添加固定时间后消失的buff。
从上可以看出,通过这些技能组件的划分,是可以比较方便配置各种技能效果。
后续将上传一个展示技能的Demo 代码。
带技能 实现的 demo
”在线下联结了各位 OSCer,推广开源项目和理念,很荣幸有你的参与~
领取条件:参与过开源中国“源创会”的 OSCer 可以领取
http://www.unitymanual.com/thread-.html
http://www.unitymanual.com/thread-.html
用状态机监控动画时间,同时监控玩家的按钮输入事件,当连击时间点到的时候,检测一下玩家上次点击按钮时间,如果距离现在不超过一定时间,则切换动作进入下一个连击,否则没有连击。
用状态机监控动画时间,同时监控玩家的按钮输入事件,当连击时间点到的时候,检测一下玩家上次点击按钮时间,如果距离现在不超过一定时间,则切换动作进入下一个连击,否则没有连击。嗯!这个我明白,我看了你的关于火炬之光的技能设计文章及代码,写的很好,我想在基础上实现一套很好的动作连击系统,因为项目现在也需要…
用状态机监控动画时间,同时监控玩家的按钮输入事件,当连击时间点到的时候,检测一下玩家上次点击按钮时间,如果距离现在不超过一定时间,则切换动作进入下一个连击,否则没有连击。嗯!这个我明白,我看了你的关于火炬之光的技能设计文章及代码,写的很好,我想在基础上实现一套很好的动作连击系统,因为项目现在也需要… 你好,我能加你微信吗,大哥!
用状态机监控动画时间,同时监控玩家的按钮输入事件,当连击时间点到的时候,检测一下玩家上次点击按钮时间,如果距离现在不超过一定时间,则切换动作进入下一个连击,否则没有连击。嗯!这个我明白,我看了你的关于火炬之光的技能设计文章及代码,写的很好,我想在基础上实现一套很好的动作连击系统,因为项目现在也需要… 你好,我能加你微信吗,大哥! 可以呀~ 我微信号 liyonghelpme
支付宝支付
微信扫码支付
打赏金额: ¥
已支付成功
打赏金额: ¥程序写累了,就来玩玩酷跑小游戏吧,嘿嘿。
雨松MOMO送你一首歌曲,嘿嘿。
Unity3D研究院之利用缓存池解决Instantiate慢的问题(七十三)
Unity3D研究院之利用缓存池解决Instantiate慢的问题(七十三)
围观71870次
编辑日期: 字体:
Unity3D做项目有三个地方处理不好游戏整体就会出现卡顿的问题。
1.NGUI直接打开界面卡,建议看看我之前写的这一篇文章
(本文就不赘述了)
2.角色放技能的时候卡
尤其是放群体攻击技能时, 因为每个人身上都要产生一个技能特效。技能都是用粒子特效做的,虽然Unity中粒子特效也是一个GameObject.但是 Particle System这个组件太特殊了。Instantiate以后会自动的执行脚本的初始化工作,Particle System组件肯定也是个脚本,虽然我们看不到它实现的方式,但是Instantiate以后它定会先执行Awake()和OnEnable()一类初始化的方法。
经过我的测试发现,粒子特效真正慢的地方在于Play()的时候,Play内部肯定是启了协同一类的方法。因为根据粒子特效的原理,粒子特效其实就是个脚本,当播放的时候它会自动创建Mesh,从而生成它的运动轨迹。所以我们一定要控制同屏幕同时播放的粒子数量。
所以美术在做粒子特效的时候要注意3点
1.同屏的粒子数量一定要控制在200以内,每个粒子的发射数量不要超过50个。不然在iPhone4或者一些比较烂的Android手机上就会有问题
2.尽量减少粒子的面积,面积越大就会越卡。
3.粒子最好不要用Alfa Test(但是有的特效又不能不用,这个看美术吧) 、如下图所示,粒子的贴图用黑底的这种,然后用Particles/Additive 这种Shader,贴图必须要2的幂次方,这样渲染的效率会高很多。个人建议 粒子特效的贴图在64左右,千万不要太大。。
在回到粒子卡的话题上,Play()方法我们是控制不了的,所以我们能做的就是在播放Play方法之前让粒子特效所有的准备工作都已经完成。
1.粒子特效的GameObject实例化完毕。
2.确保粒子所用到的贴图载入内存
3.让粒子进行一次预热(目前预热功能只能在循环的粒子特效里面使用,所以不循环的粒子特效是不能用的)
//实例化粒子特效。GameObject newGo = GameObject.Instantiate(go) as GameObject;//把它的状态设置成隐藏newGo.SetActive(false);
因为实例化粒子特效以后,实际上粒子的脚本就已经完成了初始化的工作,也就是Awake()和OnEnable()方法。然后设置SetActive(false)仅仅是把粒子特效隐藏起来。
上述操作完毕以后,让游戏中真正要播放粒子特效的时候,粒子不用在载入它的贴图,也不用实例化,仅仅是执行一下SetActive(true)。SetActive(true)的时候就不会执行粒子特效的Awake()方法,但是它会执行OnEnable方法。
3.载入模型的时候卡
一般在战斗场景,突然出现一大堆怪的时候, 屏幕会卡一下。角色的骨骼数量一定要少于30根,你可以用Profiler 里面看看,当你实例化一个动画模型的时候时间都卡在加载动画这块。如下图所示,在QualitySettings里面,一般手游我们都选择Good 选项,下面有一些别的选项,能关就关了,垂直同步也一定就关了。
如果要想游戏运行时不卡,我们必须要进行预加载,意思就是放技能或者出现怪物的时候,程序只需要SetActivie(true) 就可以了。但是你又不能预加载的东西太多,因为预加载和内存就像一把天枰,一旦预加载过多了你的游戏内存可能就爆了。
所以我觉得用Unity3D开发游戏,你必须要用缓存池。啥意思呢?
1. Instantiate 一个动画模型,这时候unity会先判断模型身上的资源是否在内存里,如果内存没有加入内存。
2.GameObject实例化完毕后,会同步执行它身上所有脚本的初始化工作,这里执行的不止是我们自己写的脚本,U3D自身的组件脚本也会初始化,比如动画这块很卡的地方就是Animation这个组件。
3.Destroy(gameObject), 它不会把模型所用的贴图资源释放掉,但是它会把游戏对象和脚本释放掉。 啥意思呢?就是如果你再次Instantiate的时候,它不会再去载入模型所用到的贴图,但是它要执行脚本的初始化工作。我们不知道U3D内部组件脚本是如何初始化的,但是就自己写的脚本而言,它必然要同步执行Awake()和OnEnable()这两个方法,如果这里有耗时操作,那么必然会卡一下。
所以一些使用频繁的模型,不用的时候不要把它直接Destory掉,而是SetActive(false)。 这样当你再次使用的时候只需要SetActivie(true) 这样对应这个游戏对象来说 它只会执行OnEnable()这一个方法,所以载入速度是最快的。
所以我们用缓存池也是,在Loading进入战斗场景的时候,把频繁用到的模型,特效,全部Instantiate进去 SetActivie(false) 放入缓存池,当程序用到的时候在去池子里面拿,这样你就不会发现卡了。
缓存池你可以自己去写,不过网络上已经有一些缓存池的工具了。比如 PoolManager,这里我有做的详细的例子, 他是一个例子工程,写的很清晰。改一改就可以直接拿来用了,很方便。。
欢迎大家提出自己的看法, 我们可以互相讨论互相学习,嘿嘿。
本文固定链接:
转载请注明:
雨松MOMO提醒您:亲,如果您觉得本文不错,快快将这篇文章分享出去吧 。另外请点击网站顶部彩色广告或者捐赠支持本站发展,谢谢!
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
如果您愿意花10块钱请我喝一杯咖啡的话,请用手机扫描二维码即可通过支付宝直接向我捐款哦。
您可能还会对这些文章感兴趣!Unity制作技能冷却效果-GAD腾讯游戏开发者平台【Unity3D实战】技能系统与技能编辑器的设计
作者有话说
有一段时间没更新博客了,因为自六月下旬开始,一直在忙实习的事情,前几天登陆CSDN博客的时候,还发现因博客被人盗用大量发表无关外链导致博客被封了半个月,多亏CSDN的工作人员帮忙解锁了,在此再次对CSDN的工作人员认真负责的态度表示衷心感谢!
最近,由于实习工作接触的项目中,需要完成技能系统,但是由于公司精力放在另外一款大型的精品手游上,我所在的项目只有我和主程两个程序猿=。=,主程又比较忙,所以这个项目就一直是我自己在慢慢摸索。
一开始,主程说直接写个技能的结构体挂载Prefab上,用于demo演示就行了,但是这样表现确实很差,技能灵活性很低,尤其策划给的技能表中,技能的表现多种多样,若是直接根据技能类型判断,整个结构看上去会很丑很难看。所以,我的第一版技能系统宣告失败。
我的第二版技能系统,将技能的效果拆分出来,一个技能由多个技能效果组成,这样我可以对底层的技能效果做处理,让技能系统看上去比前一版好多了,然而这样还是有一些问题,各效果的执行顺序上会很乱,而且每次修改还是得对系统核心部分就行修改。
于是,在网上搜寻了挺长一段时间,后来找到了大叔才是主流大神所写的技能系统
这也是我早先想要完成的,用脚本的方式去描述一个技能的效果,在大叔大神的文章的帮助下,总算是达到了我想要的效果。
大叔大神所写的原文在上面已经给出,技能系统的设计部分可以直接参考大叔大神的文章。接下来,是笔者要分享给大家的拙作,笔者基于大叔大神的技能系统所写的技能编辑器的设计,这样就可以不用直接对文本文档进行操作,防止不必要的错误。
工具类的设计
要读写技能脚本,自然需要做读写文件的操作,大叔大神已经给大家贴出读取文件的工具类,不过为了大家阅读方便,笔者还是会在下处贴出读取文件的工具类的设计:
读取文件:
using System.IO;
public class FileReaderProxy
public static StreamReader ReadFile(string fileName)
StreamReader sr = null;
sr = File.OpenText(fileName);
catch (Exception)
return null;
写入文件:
using System.IO;
public class FileWriterProxy
public static bool Write(string fileName, string content)
StreamWriter sw = null;
sw = new StreamWriter(fileName, false);
sw.Write(content);
sw.Close();
catch (Exception)
if(sw != null)
sw.Close();
return false;
return true;
添加技能编辑器
using UnityE
using System.Collections.G
using System.IO;
using UnityE
public class SkillScriptEditor : EditorWindow {
[MenuItem("SkillScriptEditor/Open Script Editor")]
static void OpenSkillScriptEditor()
SkillScriptEditor window = GetWindow&SkillScriptEditor&(true, "SkillScriptEditor");
window.Init();
private int skillId = 1000;
private int selectContentIndex = -1;
private Vector2 scrollPos = Vector2.
private static List&string& scriptContent = new List&string&();
void Init()
selectContentIndex = -1;
scriptContent.Clear();
void OnGUI()
EditorGUILayout.BeginHorizontal();
EditorGUILayout.BeginVertical("box", GUILayout.Width(400), GUILayout.Height(400));
#region ScrollView - Script Content
EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Width(400), GUILayout.Height(400));
for (int i = 0; i & scriptContent.C i++)
int index =
if (selectContentIndex == index)
GUI.color = Color.
if (GUILayout.Button(scriptContent[index]))
selectContentIndex =
GUI.color = Color.
EditorGUILayout.EndScrollView();
#endregion
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical("box", GUILayout.Width(200), GUILayout.Height(400));
#region UI
skillId = EditorGUILayout.IntField("script ID:", skillId);
if(GUILayout.Button("Load Script by Script ID"))
LoadScript();
if(GUILayout.Button("Save Script"))
SaveScript();
if(GUILayout.Button("New Or Clear Script"))
ClearContent();
if(GUILayout.Button("Delete Line"))
DeleteContent();
if (GUILayout.Button("Move Up"))
MoveUpContent();
if (GUILayout.Button("Move Down"))
MoveDownContent();
#region Add Trigger
EditorGUILayout.BeginVertical("box");
GUI.color = Color.
if (GUILayout.Button("PlayAnimationTrigger"))
SkillTriggerScriptEditor.ShowSkillTriggerScriptWindow(SkillTriggerScriptEditor.TriigerType.PlayAnimation, this.position);
if (GUILayout.Button("SingleDamageTrigger"))
SkillTriggerScriptEditor.ShowSkillTriggerScriptWindow(SkillTriggerScriptEditor.TriigerType.SingleDamage, this.position);
GUI.color = Color.
EditorGUILayout.EndVertical();
#endregion
#endregion
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
void DeleteContent()
if (selectContentIndex != -1)
scriptContent.RemoveAt(selectContentIndex);
selectContentIndex = -1;
void ClearContent()
scriptContent.Clear();
public static void AddContent(string str)
scriptContent.Add(str);
void MoveUpContent()
if (selectContentIndex != -1 && selectContentIndex & 0)
string temp = scriptContent[selectContentIndex];
scriptContent[selectContentIndex] = scriptContent[selectContentIndex - 1];
scriptContent[selectContentIndex - 1] =
selectContentIndex -= 1;
void MoveDownContent()
if (selectContentIndex != -1 && selectContentIndex & scriptContent.Count-1)
string temp = scriptContent[selectContentIndex];
scriptContent[selectContentIndex] = scriptContent[selectContentIndex + 1];
scriptContent[selectContentIndex + 1] =
selectContentIndex += 1;
void SaveScript()
string content = String.E
content += string.Format("skill({0})", skillId);
content += "\r\n{\r\n";
foreach (var str in scriptContent)
content += "\t";
content +=
content += "\r\n";
content += "}";
bool ret = FileWriterProxy.Write(Application.streamingAssetsPath + "/" + "SkillScript" + skillId.ToString() + ".txt", content);
Debug.Log(ret);
void LoadScript()
ClearContent();
bool ret = ParseScript(Application.streamingAssetsPath + "/" + "SkillScript" + skillId.ToString() + ".txt");
private bool ParseScript(string filename)
bool ret = false;
StreamReader sr = null;
sr = FileReaderProxy.ReadFile(filename);
if (sr != null)
ret = LoadScriptFromStream(sr);
catch (Exception e)
string err = "Exception:" + e.Message + "\n" + e.StackTrace + "\n";
if(sr != null)
sr.Close();
private bool LoadScriptFromStream(StreamReader sr)
bool bracket = false;
string line = sr.ReadLine();
if (line == null)
line = line.Trim();
if (line.StartsWith("//") || line == "")
if (line.StartsWith("skill"))
int start = line.IndexOf("(");
int end = line.IndexOf(")");
if (start == -1 || end == -1)
Debug.LogError(string.Format("ParseScript Error, start == -1 || end == -1
{0}", line));
int length = end - start - 1;
if (length &= 0)
Debug.LogError(string.Format("ParseScript Error, length &= 1, {0}", line));
return false;
string args = line.Substring(start + 1, length);
int skillId = (int)Convert.ChangeType(args, typeof(int));
else if (line.StartsWith("{"))
bracket = true;
else if (line.StartsWith("}"))
bracket = false;
if (bracket == true)
int start = line.IndexOf("(");
int end = line.IndexOf(")");
if (start == -1 || end == -1)
Debug.LogError(string.Format("ParseScript Error, {0}", line));
int length = end - start - 1;
if (length &= 0)
Debug.LogError(string.Format("ParseScript Error, length &= 1,
{0}", line));
return false;
line = line.Replace("\t", "");
scriptContent.Add(line);
} while (true);
return true;
添加技能效果触发器编辑器
using UnityE
using System.Collections.G
using UnityE
public class SkillTriggerScriptEditor : EditorWindow
private static SkillTriggerScriptEditor instance = null;
public enum TriigerType
PlayAnimation,
SingleDamage
private static List&string&[] paramTitles = new List&string&[]
new List&string&(),
new List&string&() {"StartTime:", "Animation ID:"},
new List&string&()
"StartTime:", "ConstPhyDmg:", "PercentPhyDmg:", "ConstSprDmg:", "PercentSprDmg:",
"ConstRealDmg:", "PercentRealDmg:", "CertainHit", "CertainCrit"
private TriigerType _triigerType = TriigerType.N
private float[] _
public static void ShowSkillTriggerScriptWindow(TriigerType triigerType, Rect pos)
if (instance == null)
instance = new SkillTriggerScriptEditor();
instance._triigerType = triigerT
instance._params = new float[paramTitles[(int)triigerType].Count];
float width = 300;
float height = 20 * (paramTitles[(int)triigerType].Count + 1);
float left = pos.center.x - width/2;
float top = pos.center.y - height/2;
instance.position = new Rect(left, top, width, height);
instance.ShowPopup();
void OnDestroy()
instance = null;
void OnGUI()
GUILayout.BeginVertical();
for(int i = 0; i & paramTitles[(int)_triigerType].C i++)
_params[i] = EditorGUILayout.FloatField(paramTitles[(int) _triigerType][i], _params[i]);
GUILayout.EndVertical();
GUILayout.BeginHorizontal();
GUI.color = Color.
if (GUILayout.Button("Add"))
SkillScriptEditor.AddContent(ConnectParams());
this.Close();
GUI.color = Color.
if (GUILayout.Button("Cancel"))
this.Close();
GUI.color = Color.
GUILayout.EndHorizontal();
string ConnectParams()
string ret = String.E
switch (_triigerType)
case TriigerType.PlayAnimation:
ret += "PlayAnimation";
case TriigerType.SingleDamage:
ret += "SingleDamage";
return String.E
ret += "(";
for (int i = 0; i & _params.L i++)
string str = _params[i].ToString();
if (i != _params.Length - 1)
ret += ", ";
ret += ");";
编辑器效果演示
技能编辑器面板:
技能效果触发器面板:
保存后的文件:
保存后的文件内容:
读取文件(根据所填写的script id 读取):
读取文件后:
因为能力有限,写的都是比较简单的方法,用的都是比较浅显的知识,仅在此抛砖引玉,分享一下自己的拙作,因为所用到的函数在手册中均搜的到,所以便不做解释,有什么问题或者有什么错误的地方,还请大家私信或留言给我,谢谢!
再次感谢大叔才是主流的帮助,谢谢!
看过本文的人也看了:
我要留言技术领域:
取消收藏确定要取消收藏吗?
删除图谱提示你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?
删除节点提示无法删除该知识节点,因该节点下仍保存有相关知识内容!
删除节点提示你确定要删除该知识节点吗?他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)}

我要回帖

更多关于 王者荣耀技能释放方式 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信