炉石传说 C# 设计文档(序)

经过3个月的开发,有很多感触。

以前一直以为技术是开发成败的第一因素,现在发现,等到你代码写的时间够长,经验够丰富,什么功能都能随手完成,对于业务的分析能力变成了第一位。

炉石山寨版的BS版本用到的HTML5的SVG,我看了一个下午的教程,借鉴以前GUI+和HTML的经验,很快就能写点东西出来了。

WebSocket,Github上找了一个开源的C#项目,通讯这块也是几个小时就搞定了。Javascript不是很熟悉,当时闭包这样的一些概念也算听说过,Js也是无障碍就写成了。

整个项目的技术壁垒其实不是很高,难的是对于炉石的业务的理解。

设计一个项目,就是将项目分割成若干个子系统,然后用适当的设计模式,让代码出现在它该出现的地方。

一个项目的代码量,肯定是不断增加,然后通过重构减少,然后又加入新的子系统,导致代码量的增加,再通过重构减少,这样的螺旋形的折腾和反复(迭代)使得代码越来越完善。

同时,随着对于业务的理解,很有可能会对原来的设计产生颠覆性的修改。炉石的开发中,我3次做了颠覆性的设计的修改。

血的教训告诉我们,开发之前,一定要做好业务的研究。

如果你觉得你的代码这样写也不好,那样写也不对,不用纠结了,好好睡一觉,然后重新整理一下业务,修改一下你的业务模型,然后所有问题就迎刃而解了。

中国的外包公司,包括一些有名的大公司,都不喜欢写文档,或者文档落后与代码。

代码可以上线,可以取得业绩,文档似乎完全只是为了应付CMM的规定,所以,别说考虑文档的美观了,就是一个和代码同步的文档,对于大多数公司来说,也很奢侈。

很多人有一个观点,好的代码是不需要注释和文档的,代码就是最好的注释和文档。

其实,我觉得,文档不是伪代码,应该是对于业务的一种解释,以及编码的一个依据。

我觉得需要这些枚举,是通过怎样的调查分析得到的结论。

我觉得业务流程是怎样的,我通过分析业务,画出流程图得到的一个结果。

很多人说,炉石的C#代码通过反编译可以看得到,为什么要重复去山寨呢?

这段时间工作很空闲,写代码写得旧了,所以想通过山寨炉石来提高自己的分析和设计能力。

炉石其实你真正的考虑怎么设计的好,怎么使得你的设计可以同时满足BS,CS,网络版,单机版,也是非常不容易的。然后炉石的业务,如果很多地方不考虑扩展性,也不是很复杂,一个月足够完成所有的编码工作了。

但是,如果要考虑到扩展性,考虑到重复使用,考虑到IOC这样的东西,则需要好好考虑的。

例如,施放法术,

我写到最后就是一个施放法术的接口(抽象),然后各种法术的具体实现,

施法流程是调用了施放法术的接口,通过法术数据去调用具体的实现。

法术接口:

(博客园的插入代码推荐有了,旁边那个插入代码没有存在的意义了,个人觉得)

using Engine.Action;
using Engine.Card;
using Engine.Client;
using System;
using System.Collections.Generic;

namespace Engine.Effect
{
    public interface IAtomicEffect
    {
        /// <summary>
        /// 对方复原操作
        /// </summary>
        /// <param name="game"></param>
        /// <param name="actField"></param>
        void ReRunEffect(ActionStatus game, String[] actField);
        /// <summary>
        /// 对英雄动作
        /// </summary>
        /// <param name="game"></param>
        /// <param name="PlayInfo"></param>
        /// <returns></returns>
        String DealHero(ActionStatus game, PublicInfo PlayInfo);
        /// <summary>
        /// 对随从动作
        /// </summary>
        /// <param name="game"></param>
        /// <param name="Minion"></param>
        /// <returns></returns>
        String DealMinion(ActionStatus game, MinionCard Minion);
        /// <summary>
        /// 获得效果信息
        /// </summary>
        /// <param name="InfoArray"></param>
        void GetField(List<String> InfoArray);
    }
}

一个伤害效果

using Engine.Action;
using Engine.Client;
using Engine.Control;
using Engine.Utility;
using System;
using System.Collections.Generic;

namespace Engine.Effect
{
    /// <summary>
    /// 攻击效果
    /// </summary>
    public class AttackEffect : IAtomicEffect
    {
        /// <summary>
        /// 效果表达式
        /// </summary>
        public String 伤害效果表达式 = String.Empty;
        /// <summary>
        /// 伤害加成
        /// </summary>
        public Boolean 伤害加成 = false;
        /// <summary>
        /// 获得效果信息
        /// </summary>
        /// <param name="InfoArray"></param>
        void IAtomicEffect.GetField(List<string> InfoArray)
        {
            伤害效果表达式 = InfoArray[0];
            伤害加成 = ExpressHandler.GetBooleanExpress(InfoArray[1]);
        }
        /// <summary>
        /// 对英雄动作
        /// </summary>
        /// <param name="game"></param>
        /// <param name="PlayInfo"></param>
        /// <returns></returns>
        String IAtomicEffect.DealHero(ActionStatus game, Client.PublicInfo PlayInfo)
        {
            int AttackPoint = ExpressHandler.GetEffectPoint(game, 伤害效果表达式);
            //调整伤害值
            if (伤害加成) AttackPoint += game.AllRole.MyPublicInfo.BattleField.AbilityDamagePlus;
            if (PlayInfo.Hero.AfterBeAttack(AttackPoint))
            {
                game.battleEvenetHandler.事件池.Add(new Engine.Utility.CardUtility.全局事件()
                {
                    触发事件类型 = CardUtility.事件类型枚举.受伤,
                    触发位置 = PlayInfo.Hero.战场位置
                });
            }
            return Server.ActionCode.strAttack + CardUtility.strSplitMark + PlayInfo.Hero.战场位置.ToString() + CardUtility.strSplitMark + AttackPoint.ToString();
        }
        /// <summary>
        /// 对随从动作
        /// </summary>
        /// <param name="game"></param>
        /// <param name="Minion"></param>
        /// <returns></returns>
        String IAtomicEffect.DealMinion(ActionStatus game, Card.MinionCard Minion)
        {
            int AttackPoint = ExpressHandler.GetEffectPoint(game, 伤害效果表达式);
            //调整伤害值
            if (伤害加成) AttackPoint += game.AllRole.MyPublicInfo.BattleField.AbilityDamagePlus;
            if (Minion.设置被攻击后状态(AttackPoint))
            {
                game.battleEvenetHandler.事件池.Add(new Engine.Utility.CardUtility.全局事件()
                {
                    触发事件类型 = CardUtility.事件类型枚举.受伤,
                    触发位置 = Minion.战场位置
                });
            }
            return Server.ActionCode.strAttack + CardUtility.strSplitMark + Minion.战场位置.ToString() + CardUtility.strSplitMark + AttackPoint.ToString();
        }
        /// <summary>
        /// 对方复原操作
        /// </summary>
        /// <param name="game"></param>
        /// <param name="actField"></param>
        void IAtomicEffect.ReRunEffect(ActionStatus game, string[] actField)
        {
            int AttackPoint = int.Parse(actField[3]);
            if (actField[1] == CardUtility.strYou)
            {
                //MyInfo
                if (actField[2] == Client.BattleFieldInfo.HeroPos.ToString("D1"))
                {
                    game.AllRole.MyPublicInfo.Hero.AfterBeAttack(AttackPoint);
                }
                else
                {
                    game.AllRole.MyPublicInfo.BattleField.BattleMinions[int.Parse(actField[2]) - 1].设置被攻击后状态(AttackPoint);
                }
            }
            else
            {
                //YourInfo
                if (actField[2] == Client.BattleFieldInfo.HeroPos.ToString("D1"))
                {
                    game.AllRole.MyPublicInfo.Hero.AfterBeAttack(AttackPoint);
                }
                else
                {
                    game.AllRole.MyPublicInfo.BattleField.BattleMinions[int.Parse(actField[2]) - 1].设置被攻击后状态(AttackPoint);
                }
            }
        }
    }
}
        /// <summary>
        /// 实施效果
        /// </summary>
        /// <param name="singleEffect"></param>
        /// <param name="game"></param>
        /// <param name="RandomSeed"></param>
        /// <returns></returns>
        public static List<string> RunSingleEffect(EffectDefine singleEffect, ActionStatus game, int RandomSeed)
        {
            List<string> Result = new List<string>();
            List<string> PosList = SelectUtility.GetTargetList(singleEffect.AbliltyPosPicker, game, RandomSeed);
            foreach (string PosInfo in PosList)
            {
                var PosField = PosInfo.Split(CardUtility.strSplitMark.ToCharArray());
                var strResult = string.Empty;
                if (PosField[0] == CardUtility.strMe)
                {
                    switch (int.Parse(PosField[1]))
                    {
                        case BattleFieldInfo.HeroPos:
                            Result.Add(GetEffectHandler(singleEffect, game, CardUtility.strMe + CardUtility.strSplitMark + BattleFieldInfo.HeroPos.ToString("D1")).DealHero(game, game.AllRole.MyPublicInfo));
                            break;
                        case BattleFieldInfo.AllMinionPos:
                            for (int i = 0; i < game.AllRole.MyPublicInfo.BattleField.MinionCount; i++)
                            {
                                Result.Add(GetEffectHandler(singleEffect, game, CardUtility.strMe + CardUtility.strSplitMark + (i + 1).ToString("D1")).DealMinion(game, game.AllRole.MyPublicInfo.BattleField.BattleMinions[i]));
                            }
                            break;
                        case BattleFieldInfo.AllRolePos:
                            Result.Add(GetEffectHandler(singleEffect, game, CardUtility.strMe + CardUtility.strSplitMark + BattleFieldInfo.HeroPos.ToString("D1")).DealHero(game, game.AllRole.MyPublicInfo));
                            for (int i = 0; i < game.AllRole.MyPublicInfo.BattleField.MinionCount; i++)
                            {
                                Result.Add(GetEffectHandler(singleEffect, game, CardUtility.strMe + CardUtility.strSplitMark + (i + 1).ToString("D1")).DealMinion(game, game.AllRole.MyPublicInfo.BattleField.BattleMinions[i]));
                            }
                            break;
                        default:
                            Result.Add(GetEffectHandler(singleEffect, game, PosInfo).DealMinion(game, game.AllRole.MyPublicInfo.BattleField.BattleMinions[int.Parse(PosField[1]) - 1]));
                            break;
                    }
                }
                else
                {
                    switch (int.Parse(PosField[1]))
                    {
                        case BattleFieldInfo.HeroPos:
                            Result.Add(GetEffectHandler(singleEffect, game, CardUtility.strYou + CardUtility.strSplitMark + BattleFieldInfo.HeroPos.ToString("D1")).DealHero(game, game.AllRole.YourPublicInfo));
                            break;
                        case BattleFieldInfo.AllMinionPos:
                            for (int i = 0; i < game.AllRole.YourPublicInfo.BattleField.MinionCount; i++)
                            {
                                Result.Add(GetEffectHandler(singleEffect, game, CardUtility.strYou + CardUtility.strSplitMark + (i + 1).ToString("D1")).DealMinion(game, game.AllRole.YourPublicInfo.BattleField.BattleMinions[i]));
                            }
                            break;
                        case BattleFieldInfo.AllRolePos:
                            Result.Add(GetEffectHandler(singleEffect, game, CardUtility.strYou + CardUtility.strSplitMark + BattleFieldInfo.HeroPos.ToString("D1")).DealHero(game, game.AllRole.YourPublicInfo));
                            for (int i = 0; i < game.AllRole.YourPublicInfo.BattleField.MinionCount; i++)
                            {
                                Result.Add(GetEffectHandler(singleEffect, game, CardUtility.strYou + CardUtility.strSplitMark + (i + 1).ToString("D1")).DealMinion(game, game.AllRole.YourPublicInfo.BattleField.BattleMinions[i]));
                            }
                            break;
                        default:
                            Result.Add(GetEffectHandler(singleEffect, game, PosInfo).DealMinion(game, game.AllRole.YourPublicInfo.BattleField.BattleMinions[int.Parse(PosField[1]) - 1]));
                            break;
                    }
                }
            }
            return Result;
        }

最后,通过对于业务的不断理解,有一些看上去不一样的东西变得一样了。

法术,光环,战吼,亡语,其实都是一样的

一些看上去一样的东西,变得不一样了

奥秘有的是修改触发行为,有的是追加效果;光环有的是影响战场的其他随从,有的是被战场影响

等到你将业务真正分析清楚了,写代码就是一个体力活了。

而且,作为程序员,修改自己的代码,大家都不情愿,辛辛苦苦写的代码不愿意修改。

如果文档先行,修改一下文档,大家还是很乐意的,不用测试,不用返工。而且修改文档是时间成本最小的。如果上线后再修改BUG,那个时间成本。。。。。

题外话:

VS14CTP已经开始使用了,ASPNET的vNext版本也开始尝试了,然后KRE的Self-Host功能也做个试验了,部署在远程服务器上,本地完全可以访问。

但是,性能不能和原生的IIS相比,园子里面有一篇文章介绍过的,地址忘记了,大概相差两三倍吧。

第一次玩MVC(技术面试的时候,这个好像一定要问的,没有玩过MVC好像就不懂ASP一样,一直纯手工写WebForm的人,情何以堪,IOC好像也是必问的,现在的技术面试太看重理论了)

,蛮有趣的,但是我有个疑问,MVC加上EF对于关系型数据库支持的很少,如何让MVC和NOSQL一起工作呢?

NOSQL的元数据对于MVC来说,总觉得会出现不兼容的情况。。。。

炉石传说 C# 设计文档(序)

时间: 2024-10-10 11:09:50

炉石传说 C# 设计文档(序)的相关文章

《团队-科学计算器-设计文档》

设计文档: 项目:科学计算器 编辑器:python 所运用知识: 1.字符串的处理 2.正则表达式的运用 3.函数递归 基本思路: 需要优先处理内层括号运算--外层括号运算--先乘除后加减的原则: 1.正则处理用户输入的字符串,然后对其进行判断,判断计算公式是否有括号,有就先将计算公式进行正则处理,先获取最里层的每一个数据,然后一一计算 2.把有括号的计算公式计算出来的结果替换原来初始公式的位置,计算之前分别对重复运算符进行处理需要处理的重复运算 3.然后依次从里到外去除括号并进行计算,和位置替

《结对编项目作业名称-设计文档》

项目:关灯游戏,所用软件,pygame 成员:祁昊,刘孝东 关灯游戏设计文档: pygame作为一种游戏编程语言,以其简单性.可移植性等优点,得到了广泛地应用,特别是py使用比c,c++等语言简便,使其成为网络编程首选编程语言.,Pygame是跨平台Python模块,专为电子游戏设计.基于这样一个设想,所有需要的游戏功能和理念都(主要是图像方面)都完全简化为游戏逻辑本身,所有的资源结构都可以由高级语言提供,如Python.工具tile编辑器和一个关卡编辑器.得到广大程序员的接受和认可. "关灯游

Storm项目:流数据监控1《设计文档…

该文档为实实在在的原创文档,转载请注明作者及出处. 类型 详细 备注 2 该文档为原创模拟项目:流数据监控<1>文档<流数据监控设计文档>,相继会给出流数据监控<2>文档<流数据监控代码解析>及其他文档 2  该部分有源码(熬夜写出来的哦) CSDN中相应项目CODE链接:戳这里     相关描述 2  有任何其他想法,可以邮件[email protected] 2 文档及相关资料下载请到个人360云盘http://yunpan.cn/QGf2GDaRFpc

Atitit.atiagent &#160;agent分销系统 代理系统 设计文档

Atitit.atiagent  agent分销系统 代理系统 设计文档 1. 启动项目1 2. 首也2 3. 登录功能2 4. 用户中心2 5. 充值查询3 6. 授权下级代理4 7. 我的提成5 8. 查看下级玩家6 9. 查看下级代理7 10. 数据库文档 agent7 10.1. Acc 用户帐号以及上级代理id关联字段7 10.2. 充值记录表8 1. 启动项目 C:\0workspace\AtiPlatf_cms\resin run q2b_game.bat Prj::cms 数据库

《结对-自然语言进行数据库查询系统-设计文档》

二〇一七年九月十四日十点一刻少两分钟 关于结对编程的设计文档: 题目:自然语言进行数据库查询系统 编程语言:C# 数据库:MySql ,其他逐渐扩展 软件所要实现的功能: 用户打开软件之后可以连接到数据库,并且通过自然语言进行数据库的查询,例如我想知道小明的学号,如果在数据库中查询需要输入 select ID from 学生表 where name = "小明"才能实现,我们要做的是,输入查询小明的学号,软件就可以将自然语言转换成sql语句进行数据库的查询. 所要实现的功能: 1.进行

为什么要写设计文档

日趋一日,程序员能够在更少的时间内完成更多的事情.使用今日的高级编程语言,开发环境,工具和“快速应用开发”思想,程序员和经理都已经习惯于急速的开发周期.今日的程序员更倾向于直接跳入到编码之中,害怕花费在非编码工作中的每一小时,都会导致项目截止日期前的周末多加一个小时班. 编码之前做设计这一过程已经变得过时了,将设计文档化就更罕见了.很多程序员从来没有写过设计文档,面对要写设计文档这一想法都畏缩不前.即使被要求写,通常来说也只是产出了一大堆的交互图和类图,这些图表大多没有表达程序员在设计阶段的思考

设计文档

在大多数软件项目中,要末不作详细设计,要么开发完成后再补详细设计文档,质量也不容乐观,文档与系统往往不能同步,使详细设计文档完全流于形式,对工作没有起到实际的帮助. 那到底应不应该写详细设计文档呢,怎么使详细设计文档起到他应有的作用呢,下面就让我们来认识一下详细设计及写详细设计文档的好处和问题. 什么是详细设计 详细设计是相对概要设计而言的,是瀑布开发流程的一个重要环节,在概要设计的高层设计的基础上,从逻辑上实现了每一模块的功能,是编码阶段的主要参考资料,是从高层到低层.逐步精化思想的具体实现.

Chrome设计文档-多进程资源加载

原文:Multi-process Resource Loading 背景 浏览器主进程及browser process处理所有的网络通信.原因有三点: Browser process可以控制每一个renderer进程的网络访问 Browser process可以在进程间管理session状态,保持其一致性 Browser process可以针对每个host管理最大链接数 概述 作为一个多进程应用,Chrome分为三层.最底层的是webkit库,它主要负责页面渲染工作.之上是渲染进程Rendere

??怎样写具体设计文档

怎样写具体设计文档是一个非常头疼的话题,简单的说是需求文档的升华,也能够说是开发者开发程序的根据,当然根据具体设计文档的粒度进行.好的具体设计文档是需求人员和开发者之间的桥梁,只是眼下好多程序开发都是先开发后,然后为了应付审核,公司制度,文档规范,开发完毕后兴许补上该文档.假设这种方式,具体设计文档的的作用就忽略了. 在大多数软件项目中,要末不作具体设计,要么开发完毕后再补具体设计文档,质量也不容乐观,文档与系统往往不能同步,使具体设计文档全然流于形式,对工作没有起到实际的帮助. 那究竟应不应该