Unity plyGame插件技能模块分析
来源:互联网 发布:本地系统 网络受限 编辑:程序博客网 时间:2024/06/05 00:33
plyGame 插件
plyGame 是一款Unity游戏引擎的视觉游戏开发工具。它可以让开发者不必编程就可以创建游戏原型,同时仍然允许以脚本方式来与系统API进行交互。
plyGame 出于易用性的考虑,提供了用来创建砍杀类RPG游戏的组件和编辑器。
这里仅分析其技能模块。
Skill 技能模块
Basic 基本信息
- Execution Time 执行时间,这段时间内用来播放动作等,不能释放新技能
- Cooldown Time 冷却时间,下一次能使用的间隔时间
- Perform while move 是否可以移动的时候释放技能
- Force Stop 是否强制停止移动来释放技能,若未设置,则移动时候不能释放技能,当
Perform while move
未设置时有效 - Can be queued 是否允许放到队列,否则的话,只有当前没有使用其他技能的时候才会生效
- Auto Queue 自动放到队列,仅当
Can be queued
选项开启时有效
- Auto Queue 自动放到队列,仅当
- Actor must face target 必须朝向目标
Activation 技能激活类型
- User 主动技能
- Passive 被动技能
Delivery 技能生效类型
Instant 立即生效
如近战、治疗、远程非投射类型技能
- Direction/ Location 技能执行的方向和位置
- Actor Direction 角色当前朝向(方向技能)
- Mouse Direction 鼠标所指的方向(方向技能)
- Selected Direction 所选中目标的方向(方向技能)
- At Click Position 所点击的位置(位置技能)
- At Selected 目标被选中才能使用(位置技能)
- Mouse Over 鼠标之下必须有目标(位置技能)
- Camera Forward 摄像机的前进方向(方向技能)
- Camera Direction 摄像机的XZ平面(方向技能)
- Cross hairOver 通过射线来选择目标(位置技能)
- Hit Height % 击中的高度偏移,0在脚底,50在中间,100在头顶
- Hit Delay 击中事件延迟,若为0,则一收集到目标就触发击中事件
- Max Targets Select 最大的目标收集数量
- Max Distance from self 技能影响的最大距离(米)
- Targteting Range 方向技能收集目标的角度,位置技能收集目标的半径
Projectile 投射生效
创建投射物来击中目标
- Prefab(s) 投射物预制
- Move Method 投射物的方向和移动方法
- ActorFaceDirection 笔直地从角色发射出去
- AngledFromEach 成角度的发射,不止一个投射物,如多重箭
- Random Angle 是否设置随机角度,若否的话,则根据
Targeting Range
来设置角度
- Random Angle 是否设置随机角度,若否的话,则根据
- DirectToTarget 投射物寻找到目标,然后朝着目标移动
- Follow 是否跟随,若不设置,则投射物只会移动到投射物创建时目标的位置
- Hit Height % 击中的高度偏移,0在脚底,50在中间,100在头顶
- Max Projectiles 最大投射物的创建数量
- Create Delay 创建第一个投射物的延迟,使用这个来跟动画同步
- Between Create Delay 创建下一个投射物的延迟
- Use Vector offset 投射物创建时的偏移
- Create at Offset 相对角色本身的坐标偏移
- Create at Tagged 角色的虚拟体,以此虚拟体坐标来创建
- Move Speed 投射物的移动速度
- Collision Ray Width 射线碰撞检测宽度,若为0,则为直线检测,否则为球形检测
- Fizzle Distance 失败的距离,投射物最大的飞行距离。
DirectToTarget
移动方法的话,则没有这个选项 - Max Live Time 最大生存时间,通常使用这个选项或者
Fizzle Distance
选项来控制失败 - Trigger secondary on Fizzle 失败的时候,是否要触发二次效果
- … only if obstacle 是否仅当由于碰撞到障碍物导致失败,才触发二次效果
- Destroy Projectile on Hit 击中的时候销毁投射物,通常开启这个选项,除非像镭射光那种,穿过目标,并且对其后的目标也造成伤害,只有当时间到达或者距离到达的时候才会销毁投射物
- Prevent Projectile UpDown 锁定投射物的Y轴
- Actor Direction 技能执行的方向和位置
- Actor Direction 角色当前朝向(方向技能)
- Mouse Direction 鼠标所指的方向(方向技能)
- Selected Direction 所选中目标的方向(方向技能)
- At Click Position 所点击的位置(位置技能)
- At Selected 目标被选中才能使用(位置技能)
- Mouse Over 鼠标之下必须有目标(位置技能)
- Camera Forward 摄像机的前进方向(方向技能)
- Camera Direction 摄像机的XZ平面(方向技能)
- Cross hairOver 通过射线来选择目标(位置技能)
- Max Distance from self 技能影响的最大距离(米)
- Targteting Range 方向技能收集目标的角度,位置技能收集目标的半径
Custom projectile logic 自定义投射物逻辑
当一个投射物创建的时候,将会添加SkillProjectile
脚本组件到投射物物体上,这个组件驱动投射物。当要自定义投射物逻辑的时候,派生重载SkillProjectile
脚本。
Targeting Methods 目标搜集方法
- Self 自身作为目标,如治疗技能;
- Selected 所选择的目标;
- Auto 根据技能范围来自动选择有效的目标;
Valid Target(s) 有效目标
- Player 不管任何阵营;
- Friendly Actor 友方阵营;
- Neutral Actor 中立阵营;
- Hostile Actor 敌对阵营;
- RPG Object 任意对象;
ObstacleCheckMask 障碍物检测
检测技能释放者与目标之间是否有障碍物,使用 Layer 掩码来检测。
Secondary Hit 二次击中效果
当一个效果击中目标时,可以引起触发第二次效果。这第二次效果将会收集第一次效果半径周围的目标。
- Max Secondary Targets 最大二次效果目标数量,若为0,则表示关闭这个功能
- Obstacle Check 障碍物检测
- Check height offset 障碍物检测的高度
- Range 二次效果半径
Skill Event 技能事件
- On Skill Activate 当技能被激活的时候
- On Skill Effect 当技能效果被创建的时候,对投射技能来说是投射物创建的时候,对立即技能来说是技能被激活并且有效执行的时候
- On Skill Hit 当技能效果击中或影响目标的时候
- On Skill Secondary Hit 当技能效果由于二次效果而击中或影响目标的时候
- On Skill Fizzle 当投射技能由于超过生存时间或超过最大距离导致失败的时候
- On Validate 在技能被激活之前进行验证,返回True才能真正激活技能
技能流程
角色类 Actor
- executingSkill 当前正在执行的技能
- queuedSkill 队列里等待被执行的技能
// 角色触发执行技能public virtual void QueueSkillForExecution(Skill skill){ QueueSkillForExecution(skill, false, null, Vector3.zero, Vector3.zero);}// 将一个技能放到队列里面准备执行public virtual void QueueSkillForExecution(Skill skill, bool ifNoOtherQueued, Targetable forceSelectedObject, Vector3 forceSelectedPosition, Vector3 forceMousePosition){ if (skill == null) return; if (ifNoOtherQueued && queuedSkill != null) return; // 技能不能放到队列里面 if (skill.canBeQueued == false) { // 那么检查当前是否有正在执行的技能 if (executingSkill != null) { // 有其他技能正在执行,那么就失败 if (executingSkill.IsExecuting()) { ClearQueuedSkill(); return; } } // 技能还在冷却 if (skill.CoolingDown()) return; // 不能放到队列,因为当前角色还在移动 if (skill.forceStop == false && skill.mayPerformWhileMove == false) { if (character.Velocity().magnitude >= 0.01f) return; } } // 不确定有其他队列技能 if (false == ifNoOtherQueued) { // ...... 省略,根据技能方向和位置 // 求得queuedSkill_SelectedObject、queuedSkill_MousePosition if (queuedSkill_SelectedObject != null) { queuedSkill_TargetPosition = queuedSkill_SelectedObject.transform.position; } else { queuedSkill_TargetPosition = queuedSkill_MousePosition; } queuedSkill = skill; } else { queuedSkill = skill; queuedSkill_SelectedObject = forceSelectedObject; queuedSkill_MousePosition = forceMousePosition; queuedSkill_TargetPosition = forceSelectedPosition; }}protected void LateUpdate(){ // ... 检测是否死亡等 // 当前已经有正在执行的技能 if (executingSkill != null) { // 技能执行完毕 if (false == executingSkill.IsExecuting()) { // 重新开启角色移动 character.hint_DoNotMove = false; executingSkill = null; } } else { // 有等待被执行的技能 if (queuedSkill != null) { // 技能的选中目标和坐标 if (queuedSkill_SelectedObject != null) { queuedSkill_TargetPosition = queuedSkill_SelectedObject.transform.position; } // 是否可以执行技能的判断 if (CanPerformQueuedSkill()) { // 如果不能在移动的时候执行技能,那么禁止角色移动 if (false == queuedSkill.mayPerformWhileMove) { character.hint_DoNotMove = true; character.Stop(); } executingSkill = queuedSkill; queuedSkill = null; // 真正执行技能 executingSkill.Execute(queuedSkill_SelectedObject, queuedSkill_TargetPosition, queuedSkill_MousePosition); if (character.IsPlayer()) { if (executingSkill.targetLocation == Skill.TargetLocation.MouseOver || executingSkill.targetLocation == Skill.TargetLocation.CrosshairOver) { character.SelectTarget(queuedSkill_SelectedObject); } } } } }}// 是否可以执行技能的判断private bool CanPerformQueuedSkill(){ // 如果技能不能在移动的时候执行,或者不能强制停止移动 if (false == queuedSkill.mayPerformWhileMove && false == queuedSkill.forceStop) { // 那么就检查当前是否在移动,是的话就不允许技能执行 if (character.Velocity().magnitude >= 0.01f) { ClearQueuedSkill(); return false; } } // 技能是否不在CD冷却时间内 if (queuedSkill.IsReady()) { // 检查目标是否在技能范围内 if (false == queuedSkill.DistanceAcceptable(character._tr.position, queuedSkill_TargetPosition, queuedSkill_MousePosition, out targetPositionForSkill, queuedSkill_SelectedObject)) { // 如果不在范围内,那么走到范围内,再进行执行技能 // 如果走不到,则丢弃这个技能不再执行 if (false == character.RequestMoveTo(targetPositionForSkill, true)) { queuedSkill = null; return false; } } else { // 如果必须朝向目标,那么检查角色是否面向目标 if (queuedSkill.actorMustFaceTarget && false == queuedSkill.FacingAcceptable(character._tr.forward, queuedSkill_TargetPosition, queuedSkill_MousePosition, out targetDirectionForSkill)) { // 如果没有朝向目标,则转动方向 // 如果转动失败,则丢弃这个技能不再执行 if (false == character.RequestFaceDirection(targetDirectionForSkill, queuedSkill.executionTimeout > 0.1f ? queuedSkill.executionTimeout : 0.1f)) { queuedSkill = null; return false; } } else { return true; } } } return false;}
技能类 Skill
// 执行技能public virtual void Execute(Targetable selectedObject, Vector3 selectedPosition, Vector3 mousePosition){ // 调用验证事件 if (eventHandler != null) { if (eventHandler.OnValidateSkill(owner, this) == false) return; } gameObject.SetActive(true); // 开始计时执行时间和冷却时间 executeTimer = executionTimeout; cooldownTimer = cooldownTimeout; // 调用激活事件 if (eventHandler != null) { eventHandler.OnActivate(owner, this, selectedPosition, mousePosition); owner.gameObject.BroadcastMessage("OnUsesSkill", this, SendMessageOptions.DontRequireReceiver); } // 允许放到队列,并且自动放到队列有效的时候,就会在当前没有队列技能的时候放到队列里面 if (canBeQueued && autoQueue) owner.QueueSkillForExecution(this, true, selectedObject, selectedPosition, mousePosition); // 有效的目标 if (validTargetsMask != 0) { // 立即生效技能 if (deliveryMethod == DeliveryMethod.Instant) ExecuteInstant(selectedObject, selectedPosition, mousePosition); // 投射技能 else if (deliveryMethod == DeliveryMethod.Projectile) ExecuteProjectile(selectedObject, selectedPosition, mousePosition); }}
投射技能
角色当前朝向投射
Vector3 forward = owner.transform.forward;// ...... 根据目标方向和位置类型调整forward// 最终坐标点Vector3 tl = owner.transform.position + (forward * maxFlightDistance);tl.y += projectileCreateOffset.y;float waitTime = projectileCreateDelay;for (int i = 0; i < maxEffects; i++){ CreateProjectile(i, waitTime, forward, tl, null, Vector3.zero); waitTime += projectileCreateDelayBetween;}
成角度的发射
Vector3 forward = owner.transform.forward;// ...... 根据目标方向和位置类型调整forward// 最大投射只有1个的情况if (maxEffects == 1){ Vector3 tl = owner.transform.position + (forward * maxFlightDistance); tl.y += projectileCreateOffset.y; CreateProjectile(0, 0f, forward, tl, null, Vector3.zero);}else{ // 每个投射所占的角度 float angleUnit = targetingAngle / (maxEffects - 1); float maxAngle = (targetingAngle / 2); float minAngle = -maxAngle; float angle = minAngle; float waitTime = projectileCreateDelay; // 投射偏移 Vector3 pos = owner.transform.position; pos += (owner.transform.right * projectileCreateOffset.x); pos += (owner.transform.up * projectileCreateOffset.y); pos += (owner.transform.forward * projectileCreateOffset.z); for (int i = 0; i < maxEffects; i++) { // 随机角度 if (moveMethod_b_opt) angle = Random.Range(minAngle, maxAngle); Vector3 tl = pos + (Quaternion.AngleAxis(+angle, Vector3.up) * forward * maxFlightDistance); CreateProjectile(i, waitTime, forward, tl, null, Vector3.zero); waitTime += projectileCreateDelayBetween; angle += angleUnit; }}
直达目标投射
// 收集目标List<Targetable> targets = CollectTargetsBasedOnTargetingLocation(selectedObject, selectedPosition, mousePosition);float waitTime = projectileCreateDelay;if (targets.Count > 0){ int projectilesleft = maxEffects; while (projectilesleft > 0) { // 击中的高度比例偏移 float hitOffs = (float)hitHeightPercentage / 100f; for (int i = 0; i < targets.Count; i++) { if (targets[i] == null) continue; // 计算击中高度 float y = 1f; Collider col = targets[i].GetComponent<Collider>(); if (col != null) y = col.bounds.size.y; Vector3 followOffset = new Vector3(0f, y * hitOffs, 0f); // 跟随目标 if (moveMethod_b_opt) { CreateProjectile(i, waitTime, owner.transform.forward, targets[i].transform.position, targets[i].transform, followOffset); } else { Vector3 pos = targets[i].transform.position + followOffset; CreateProjectile(i, waitTime, owner.transform.forward, pos, null, followOffset); } waitTime += projectileCreateDelayBetween; projectilesleft--; if (projectilesleft <= 0) break; } }}else{ // 没有目标的话,随机投射 int leftToCreate = maxEffects; { Vector3 forward = owner.transform.forward; float maxAngle = (targetingAngle / 2); float minAngle = -maxAngle; float angle = minAngle; Vector3 pos = owner.transform.position; pos += (owner.transform.right * projectileCreateOffset.x); pos += (owner.transform.up * projectileCreateOffset.y); pos += (owner.transform.forward * projectileCreateOffset.z); for (int i = 0; i < leftToCreate; i++) { angle = Random.Range(minAngle, maxAngle); Vector3 tl = pos + (Quaternion.AngleAxis(+angle, Vector3.up) * forward * maxFlightDistance); CreateProjectile(i, waitTime, forward, tl, null, Vector3.zero); waitTime += projectileCreateDelayBetween; } }}
SkillProjectile
每个投射物都会挂上这个脚本,在Update
事件里来控制投射物的移动和触发事件。
protected void Update(){ if (GameGlobal.Paused) return; if (owner == null) { if (Skill.SkillProjectilePool != null) Skill.SkillProjectilePool.Destroy(gameObject); else Destroy(gameObject); // 拥有者无效的时候,则投射失败 return; } if (owner.owner.IsDead()) { if (Skill.SkillProjectilePool != null) Skill.SkillProjectilePool.Destroy(gameObject); else Destroy(gameObject); // 角色死亡的时候,也投射失败 return; } // 跟踪直达目标的时候,目标坐标要一直更新 if (targetTr != null) targetLocation = targetTr.position + followOffset; // 投射物当前的世界坐标 prevPos = _tr.position; // 根据速度沿着目的地前进 _tr.position = Vector3.MoveTowards(_tr.position, targetLocation, Time.deltaTime * moveSpeed); // 调整方向,对准目的地 Vector3 f = (targetLocation - _tr.position).normalized; if (f != Vector3.zero) _tr.forward = f; // 构建射线 ray.origin = prevPos; ray.direction = _tr.position - prevPos; distance = ray.direction.magnitude; if (collisionRayWidth > 0.0f) { // 球形射线检测目标 if (Physics.SphereCast(ray, collisionRayWidth, out hit, distance, validTargetsLayerMask)) { if (owner != null) { // 还未包含到击中列表 if (false == hitList.Contains(hit.transform.gameObject)) { // 尝试击中目标 if (owner.AttemptHit(hit.transform.gameObject, hit.point, gameObject)) { // 击中即销毁投射物的话 if (destroyProjectileOnHit) { if (Skill.SkillProjectilePool != null) Skill.SkillProjectilePool.Destroy(gameObject); else Destroy(gameObject); return; } else { hitList.Add(hit.transform.gameObject); } } } } else { // 拥有者无效的时候销毁投射物 if (Skill.SkillProjectilePool != null) Skill.SkillProjectilePool.Destroy(gameObject); else Destroy(gameObject); return; } } // 检查是否碰到了障碍物 if (obstacleCheckMask != 0) { if (Physics.SphereCast(ray, collisionRayWidth, out hit, distance, obstacleCheckMask)) { // 是的话,则投射失败 if (owner != null) { owner.ExecuteFizzleEvent(_tr.position, true, gameObject); if (triggerSecondaryOnFizzle) owner.ExecuteSecondary(_tr.position, null, gameObject); } if (Skill.SkillProjectilePool != null) Skill.SkillProjectilePool.Destroy(gameObject); else Destroy(gameObject); return; } } } else { // 检查射线 if (Physics.Raycast(ray, out hit, distance, validTargetsLayerMask)) { if (owner != null) { if (false == hitList.Contains(hit.transform.gameObject)) { if (owner.AttemptHit(hit.transform.gameObject, hit.point, gameObject)) { if (destroyProjectileOnHit) { if (Skill.SkillProjectilePool != null) Skill.SkillProjectilePool.Destroy(gameObject); else Destroy(gameObject); return; } else { hitList.Add(hit.transform.gameObject); } } } } else { if (Skill.SkillProjectilePool != null) Skill.SkillProjectilePool.Destroy(gameObject); else Destroy(gameObject); return; } } if (obstacleCheckMask != 0) { if (Physics.Raycast(ray, out hit, distance, obstacleCheckMask)) { if (owner != null) { owner.ExecuteFizzleEvent(_tr.position, true, gameObject); if (triggerSecondaryOnFizzle) owner.ExecuteSecondary(_tr.position, null, gameObject); } if (Skill.SkillProjectilePool != null) Skill.SkillProjectilePool.Destroy(gameObject); else Destroy(gameObject); return; } } } // 当已经到达目的地的时候,或者时间已经到了,则投射失败 maxLiveTime -= Time.deltaTime; if ((targetLocation - _tr.position).sqrMagnitude < 0.01f || maxLiveTime <= 0.0f) { if (owner != null) { owner.ExecuteFizzleEvent(_tr.position, false, gameObject); if (triggerSecondaryOnFizzle && triggerSecondaryOnFizzleOnlyIfObstacle == false) { owner.ExecuteSecondary(_tr.position, null, gameObject); } } if (Skill.SkillProjectilePool != null) Skill.SkillProjectilePool.Destroy(gameObject); else Destroy(gameObject); }}
阅读全文
1 0
- Unity plyGame插件技能模块分析
- Unity插件NGUI实现技能冷却效果
- Unity插件开发:基于Flux的技能编辑器
- TCPMP流媒体插件模块分析
- TCPMP界面插件模块分析
- 技能冷却效果-[Unity]
- unity 技能系统设计
- unity技能冷却
- 技能模块第一版
- 技能模块二代机
- Unity插件
- Unity插件
- Unity插件
- 【unity实用技能】unity ios快捷打包
- UNITY之技能CD脚本
- Unity制作技能冷却效果
- Unity运动残影技能
- Unity NGUI实现技能冷却
- POJ 3186 Treats for the Cows(区间DP)
- Invoking Page() in async task.
- 中英文对照 —— 饮食与美食
- maven pom.xml添加日志支持
- druid 连接池
- Unity plyGame插件技能模块分析
- STL--<vector>学习
- 《Using OpenRefine》翻译~12
- Android.mk宏定义demo
- 《数据结构与算法分析—Java语言描述》pdf
- CSS样式有哪些常用的属性?
- spring jdbc 使用详解
- Android开发:为什么创建MyApplication类笔记
- 《Python算法教程_中文版》pdf