posts - 156,  comments - 5255,  trackbacks - 0
公告

全球首款Silverlight MMORPG<<窝窝世界>>震撼登场!伴着与XNA合体后的Silverlight 5 强势发布,一波Silverlight网游研发海啸即将席卷全球!

多磨的好事依旧让人激动,于是一不小心写下20多款全新的魔法效果旨在祝贺。今天的教程不会让你失望,没错,又是一场超豪华魔法盛宴!接下来您将看到的是本教程ARPG Demo战斗实景,一切灵感与临摹均来源于近期即将内测的2.5D大作《倩女幽魂Online》。动态光路之连锁闪电!野蛮冲撞之幻象刺杀!自适配地形之雷电风行!完美冰冻之暴风狂雪!环形突击之圆月斩!随机多段连环突刺~毒化!自定义波浪发散之烈火轰炸!连续多段灼烧之陨石坠落!扇形范围之石化穿梭箭!动态袭击之雷电辐射!地毯式燃烧之火舌喷射!召唤系之神魔降临!治愈系之天降甘露!守护系之战神之力!

在线演示Demo地址:http://cangod.com/

那么回溯到本节的主题:魔法系统是如何创建的呢?首先,我们可以将魔法和技能看做同一系统;魔法的原理与AI系统类似,从简单切入主要划分为“规则”与“使用”两个部分。“规则”包括魔法的基础性质/属性(等级、施放条件、抵抗条件、威力、附加效果、特效、物理引擎、粒子系统等等),魔法的分类(以元素为基础,日系的如气、水、火、土、光明、黑暗;中式的金、木、水、火、土,相生相克。以作用为根据,BuffDeBuff、召唤、复制、魔幻等等),以及魔法的炼化(金钱/经验/使用次数升级;装备加成;组合拆分等等)。而“使用”则面向所有不同类型的魔法从触发开始到完成所有伤害这整个流程。

规则这部分属于策划的范畴我们暂且放它一边,本节的重点是向大家讲解Silverlight MMORPG中魔法施放的整个流程。

首先是触发,主角施法过程通常由玩家操作鼠标右键或通过键盘的快捷键触发:

游戏中鼠标右键按下
        /// <summary>
        
/// 游戏中鼠标右键按下
        
/// </summary>
        void LayoutRoot_MouseRightButtonDown(object sender, MouseButtonEventArgs e) {
            e.Handled 
= true;
            Point p 
= e.GetPosition(space);
            leader.Target 
= null;
            leader.TurnTowardsTo(p);
            leader.Casting(
new MagicArgs() {
                Code 
= Convert.ToInt32(((ComboBoxItem)comboBox39.SelectedItem).Tag),
                Level 
= Convert.ToInt32(((ComboBoxItem)comboBox38.SelectedItem).Tag),
                SpaceLayer 
= leader.SpaceLayer,
                Scale 
= leader.Scale,
                Position 
= leader.Position,
                Destination 
= p,
            });
        }

而非控角色则通常由AI引导触发:

角色行为执行
        /// <summary>
        
/// 角色行为执行
        
/// </summary>
        void role_ActionDecide(object sender, EventArgs e) {
            RoleBase attacker 
= sender as RoleBase;
            RoleBase target 
= attacker.Target;
            
if (attacker.IsHostileTo(target) && attacker.IsVisible) {
                
//怯懦行为简单表现为不主动追击,发现被攻击时立刻逃跑
                if (target.ActionAI == ActionAIs.Cowardice) {
                    target.Target 
= null;
                    target.MoveTo(
new Point(ObjectBase.RandomSeed.Next((int)(target.Position.X - target.SightRange), (int)(target.Position.X + target.SightRange)), ObjectBase.RandomSeed.Next((int)(target.Position.Y - target.SightRange), (int)(target.Position.Y + target.SightRange))));
                }
                
if (target.InCircle(attacker.Position, attacker.AttackRange)) {
                    attacker.TurnTowardsTo(target.Position);
                    
#region 测试用,一定机会释放魔法/技能
                    
if (attacker.Action == Actions.Casting) { return; }
                    if (ObjectBase.RandomSeed.Next(100< (int)slider13.Value) {
                        
......
                        attacker.Casting(new MagicArgs() {
                            Code = code,
                            Level = ObjectBase.RandomSeed.Next(14),
                            SpaceLayer = attacker.SpaceLayer,
                            Scale = attacker.Scale,
                            Position = attacker.Position,
                            Destination = target.Position,
                        });
                    } 
else {
                        attacker.Attack();
                    }
                    
#endregion
                    
......
                } else if (target.InCircle(attacker.Position, attacker.SightRange) || attacker.ActionAI == ActionAIs.Persistent) {
                    
//固执行为简单表现为抓住一个目标一直追击,直至自己或目标死亡为止
                    attacker.MoveTo(new Point(ObjectBase.RandomSeed.Next((int)(target.Position.X - attacker.AttackRange), (int)(target.Position.X + attacker.AttackRange)), ObjectBase.RandomSeed.Next((int)(target.Position.Y - attacker.AttackRange), (int)(target.Position.Y + attacker.AttackRange))));
                } 
else {
                    attacker.Target 
= null;
                    attacker.Stop();
                }
            }
        }

接下来是动作播放,角色控件内部播放施法动作,无论是2D还是3D模型,当播放到“放出”关键帧时解析魔法配置并触发DoCasting事件:

施法关键帧
            //不同动作处理
            switch (Action) {
                
case Actions.Attack:
                    
if (frame.Current == frames.AttackEffect) {
                        
if (DoAttack != null) { DoAttack(this, e); }
                    }
                    
break;
                
case Actions.Casting:
                    
if (frame.Current == frames.AttackEffect) {
                        
if (DoCasting != null) {
                            
//解析等级魔法具体参数
                            XElement info = Infos["Magic"].DescendantsAndSelf("Magics").Elements().Single(X => X.Attribute("Code").Value == magicArgs.Code.ToString());
                            magicArgs.AnimationCode 
= (int)(info.Attribute("AnimationCode"));
                            magicArgs.MagicType 
= (MagicTypes)(int)info.Attribute("MagicType");
                            magicArgs.MagicLayer 
= (MagicLayers)(int)info.Attribute("MagicLayer");
                            magicArgs.MagicPosition 
= (MagicPositions)(int)info.Attribute("MagicPosition");
                            magicArgs.AdditionalEffect 
= (AdditionalEffects)(int)info.Attribute("AdditionalEffect");
                            magicArgs.SpecialEffect 
= (SpecialEffects)(int)info.Attribute("SpecialEffect");
                            XElement levelInfo 
= info.Element("Levels").Elements().Single(X => X.Attribute("ID").Value == magicArgs.Level.ToString());
                            magicArgs.DamageMin 
= (int)(levelInfo.Attribute("DamageMin"));
                            magicArgs.DamageMax 
= (int)(levelInfo.Attribute("DamageMax"));
                            magicArgs.Radius 
= (int)(levelInfo.Attribute("Radius"));
                            magicArgs.Number 
= (int)(levelInfo.Attribute("Number"));
                            DoCasting(
thisnew DoCastingEventArgs() { MagicArgs = magicArgs }); 
                        }
                    }
                    
break;
            }

在角色所注册的DoCasting事件中通过反射来创建魔法实例并运行重写MagicBaseRun方法:

角色开始技能/魔法攻击
        //角色开始技能/魔法攻击
        void role_DoCasting(object sender, DoCastingEventArgs e) {
            RoleBase caster 
= sender as RoleBase;
            
//通过反射来加载并实例化各类型魔法
            Assembly assembly = Assembly.Load("Components,Version=1.0.0.0");
            MagicBase magic 
= assembly.CreateInstance(string.Format("Components.Magic.{0}", e.MagicArgs.MagicType.ToString())) as MagicBase;
            magic.Run(caster, space, e.MagicArgs);
        }

依据魔法参数中的魔法类型,选择相应的具体魔法类执行具体逻辑,比如魔法实体及子魔法实体的创建、移动、特效显示、伤害处理等等,以一个圆形范围的群攻魔法为例,其逻辑组成代码如下:

圆形范围魔法(基础群攻)
namespace Components.Magic {

    
/// <summary>
    
/// 圆形范围魔法
    
/// </summary>
    public sealed class CircleMagic : MagicBase {

        
public override void Run(RoleBase caster, Space space, MagicArgs args) {
            args.Position 
= args.MagicPosition == MagicPositions.Position ? args.Position : args.Destination;
            Point p 
= space.Terrain.GetCoordinateFromPosition(args.Position);
            
if (space.Terrain.InEffectiveRange(p)) {
                
if ((args.SpaceLayer == SpaceLayers.Ground && space.Terrain.Matrix[(int)p.X, (int)p.Y] != 0|| args.SpaceLayer == SpaceLayers.Sky) {
                    AnimationBase magic 
= new AnimationBase() { Code = args.AnimationCode, SpaceLayer = args.SpaceLayer, Position = args.Position, Z = args.MagicLayer == MagicLayers.Ground ? -1 : (int)args.Position.Y };
                    EventHandler handler 
= null;
                    magic.Disposed 
+= handler = delegate {
                        magic.Disposed 
-= handler; 
                        space.RemoveAnimation(magic);
                    };
                    space.AddAnimation(magic);
                }
            }
            
//space.Wave(args.Position);
            for (int i = space.AllRoles().Count - 1; i >= 0; i--) {
                RoleBase target 
= space.AllRoles()[i];
                
if (caster.IsHostileTo(target) && target.InCircle(args.Position, args.Radius * args.Scale)) {
                    Targets.Add(target);
                }
            }
            
//对精灵表中所有精灵进行魔法/技能伤害
            foreach (RoleBase role in Targets) {
                caster.CastingToHurt(role, args);
            }
            Targets.Clear();
        }

    }
}

    当然,实际网游开发中,每个角色所能使用/已学会的魔法在其创建之初(或随等级提升/金钱购买/卷轴习得等)配置就已解析保存完毕,因此不会再像单机这样存在后续解析问题。

    一款网游能够吸引玩家的几大模块不外乎魔法、装备与探索;随着个人电脑性能跨越式飙升,当下网游更青睐以画面取悦玩家;没错,确实是屡试不爽。而魔法又是网游画面与体验的形象代言。可见,在网游开发中如能把握住魔法工序的每道细节,比如魔法与障碍的碰撞检测、动态运动轨迹、自适应及随机性、分帧处理架构、修饰及特效的组合布局等等,那么你的团队打造出一款世界顶级的网游必定指日可待,还在垂涎魔兽世界而望其项背吗?你也能做到的!

再返回本文的开头,或许你会说这样的画面哪能和倩女幽魂Online比呀?

请记住了,我们只是Silverlight Game Engine Developer,你懂的。Silverlight 5 目前已完美集成了XNA3D API,我相信如果您拥有一只美工水准堪称媲美雷火工作室的美术团队,使用Silverlight 5绝对能够研发出与之一拼的极品网游,甚至超越。

创新工厂给当下国内网游的定义是“太重。没错,轻量化的网游正需要像Silverlight这样高研发效率、高性能、完全动态布局的RIA来实现;Silverlight 2.5D/3D网游时代已扬帆起航,4月28日,《WOWO世界》出中国乃至世界的第一步!所有的一切都是从零开始,完全自主的知识产权,完全自主研发的核心引擎、辅助工具,高度可维护性与拓展性,零外部依赖;俗话说的好:拽在自己手中的东西才最踏实,而Silverlight让我们开发者从心记忆着每个工序流程,没错,一切都属于你。我们可以面朝世界昂首呐喊:Silverlight 网游,中国造!

这才是我们国人的骄傲!

兵法曰:兵贵神速。不需要“十年一剑”,一年足矣。缔造的神话只有伴着酸甜苦辣走来的我们深刻体会。今日,当3D页游领域依旧仿若白纸一张时,我相信谁都迫切渴望通过最短的时间攻占世界前沿技术之巅峰!

Silverlight 5 正预言着这么一个奇迹:中国的网游将在世界重新崛起,变革便在今朝!

本节源码请到目录中下载

在线演示地址:http://cangod.com

posted on 2011-04-19 22:05 深蓝色右手 阅读(...) 评论(...) 编辑 收藏