多人即时战斗游戏服务端系列[2]--90坦克Online游戏对象介绍以及渲染机制

先上类图,略大,点击此处放大:



1.先说下方接口

  1.1 场景物品接口 ISceneObject : OpLog.IOpItem, IStackPoolObject

全部场景对象的基本接口,包含类型定义,通用渲染接口,所在场景,子对象树,尺寸,坐标等..

  1.2  游戏场景接口 IScene : ISceneObject 

继承于基本场景接口,拥有加入对象,对象列表,获取相邻对象,等其它逻辑.

   1.3  Buff基类 IBuff 

buff表现,拥有持续时间,加入/删除/移动/开火/渲染/被击中时触发事件

   1.4  爆裂物接口 IHitSceneObject

假设须要造成伤害/或者破坏物体,都须要先拥有一个爆裂物,爆裂物会检測爆裂范围内的接口,然后逐个进行碰撞.

   1.5 可移动物品接口 IMoveSceneObject

拥有方向,速度,以及射程,继承此接口对象,当你设置一个速度,在每帧渲染时,都会移动,直到收到停止或最大可移动距离

   1.6 可加入Buff对象 ICanBuffSceneObject

        继承此接口物品可加入/移除buff,并受ibuff事件触发.

   1.7 可使用装置接口(主动/被动技能) IDevice 

分为2类一般,发射一个子弹,或者加入一个buff,或者两者都有

    1.8 地图上可获取道具接口 IItem

仅參与碰撞,被碰撞后会对碰撞对象加入一个触发buff.

2.然后是上方buff,全部的对象改动功能大部分都是buff触发 无敌/武器升级/无法移动/减速/回血等..

3武器 装置和buff的关系

3.1 全部的buff有个持续时间,有一到两个可选參数.

3.2 全部武器都会发射子弹,全部子弹都会创建一个爆裂物,爆裂物能够对碰撞对象产生伤害和加入buff

3.3 全部子弹/爆裂物都有碰撞列表,仅会对有效的目标进行伤害和加入buff

4 游戏世界 GameWorld

4.1 每场游戏都是一个游戏世界

4.2 每一个游戏时间包括一个场景

4.3 每帧这个世界会进行活动.通过fpstimer

4.4 不同的游戏世界有不同的游戏模式,通过继承GameWorld

5.渲染

5.1 基类实现,检查是否有子对象,然后进行子对象渲染(调用一次 ,对象有渲染等级,也就是渲染优先级,有些每帧渲染,有些每5帧渲染以此类推)

        /// <summary>
        /// 渲染
        /// </summary>
        /// <param name="ms"></param>
        public virtual void Render(int ms, int lv)
        {
            if (SubObjects != null && SubObjects.Count > 0)
            {
                Assert.IsNull(SubObjects, "SubObjects");
                foreach (var sceneObject in SubObjects)
                {
                    sceneObject.Render(ms, lv);
                }
            }
        }
5.2 场景渲染方法 (管理加入物体和移除物体)

        /// <summary>
        /// 渲染全部物体
        /// </summary>
        /// <param name="ms"></param>
        public override void Render(int ms, int lv)
        {

            base.Render(ms, lv);
            Assert.IsNull(SceneObjects, "SceneObjects");
#if TEST
            var sw = new System.Diagnostics.Stopwatch();
            sw.Start();
#endif
            //加入延迟对象
            if (DelaySceneObjects.Count > 0)
            {
                lock (DelaySceneObjects)
                {
                    var removeList = new List<DelaySceneObject>();
                    foreach (var sceneObject in DelaySceneObjects)
                    {
                        if (sceneObject != null && sceneObject.ActiveTime < CurrentTimeGetter.Now)
                        {
                            removeList.Add(sceneObject);
                            if (OnAddSceneObject == null || OnAddSceneObject(sceneObject.SceneObject))
                                AddObject(sceneObject.SceneObject);
                        }
                    }

                    foreach (var delaySceneObject in removeList)
                    {
                        DelaySceneObjects.Remove(delaySceneObject);
                    }
                }

#if TEST
                if (sw.ElapsedMilliseconds > 20)
                    logger.Debug("DelaySceneObjects Add:" + sw.ElapsedMilliseconds);
#endif
            }
            var list = RenderList;
            if (OthersLv.Count > 0)
            {
                list = RenderList.Where(r => !(r is OtherSceneObject) ||
                                             (r as OtherSceneObject).Lv <= 0 ||
                                             lv % (r as OtherSceneObject).Lv == 0).ToList();
            }
            foreach (var sceneObject in list)
            {
                //Assert.IsNull(sceneObject, "sceneObject");
                if (sceneObject != null && sceneObject.Hp > 0)
                {
                    sceneObject.Render(ms, lv);
                }
                else
                {
                    SceneObjects.Remove(sceneObject);
                }
            }
}

5.3 游戏场景渲染

        /// <summary>
        /// 渲染事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer_Elapsed(object sender, FpsTimer.FpsElapsedEventArgs e)
        {

            if (ThreadNum == 0)
            {
                ThreadNum = System.Threading.Thread.CurrentThread.ManagedThreadId;
            }
            else
            {
                if (System.Threading.Thread.CurrentThread.ManagedThreadId != ThreadNum)
                {
#if DEBUG
                    System.Diagnostics.Debugger.Break();
#endif
                }
            }
#if !TEST
            try
            {
#endif
              

                if (!IsStart)
                {
                    if ((CurrentTimeGetter.Now - StartTime).TotalMilliseconds > TankConfigs.AutoStartGameSleep)
                    {
                        IsStart = true;
                        Statues = GameStatues.Start;
                        CurrentScene.AddMessage(new Message()
                                                    {
                                                        Type = MessageType.RoundStart,
                                                        Content = CurrentTimeGetter.Now + ":回合開始:" + OpId
                                                    });
                    }
                }

                if (!IsStart)
                {
                    return;
                }


#if DEBUG
                long lastms = sw.ElapsedMilliseconds;
#endif
                Ms += (int) e.ElapsedMilliseconds;
                if (CurrentTimeGetter.Now.Second != lastReaderTime.Second)
                {
                    if (Math.Abs((Avg - TankConfigs.RenderFps)) > TankConfigs.RenderFps/5 || Math.Abs((Ms - 1000)) > 200)
                        logger.Warn("[" + OpId + "]Fps:" + Avg + " MS:" + Ms);
                    Ms = Avg = 0;

                }




                lastReaderTime = CurrentTimeGetter.Now;

                LeftMs += (int)e.ElapsedMilliseconds;

                this.BeforeRender();

                for (int i = 0; i < (LeftMs / (TankConfigs.RenderTimer)); i++)
                {
                    Avg++;
                    if (CurrentScene != null)
                    {
                        CurrentScene.Render((int)(TankConfigs.RenderTimer), LoopSeed++ % 100);
                    }
                    LeftMs -= (int) (TankConfigs.RenderTimer);
                }


               
                if (LeftMs >= TankConfigs.RenderTimer)
                {
                    Avg++;
                    if (CurrentScene != null)
                    {
                        CurrentScene.Render((int)(LeftMs), LoopSeed++ % 100);
                    }
                    LeftMs = 0;
                }

                this.AfterRender();

                if (Time <= 0)
                {
                    Close();
                }

#if DEBUG
                if (sw.ElapsedMilliseconds - lastms > TankConfigs.RenderTimer*5)
                {
                    logger.Warn("渲染时间过长:" + (sw.ElapsedMilliseconds - lastms));
                }
#endif
#if !TEST
            }
            catch (Exception ex)
            {
                logger.ErrorException(ex, "渲染出错!");
                ErrorCount++;
                if (ErrorCount >= TankConfigs.MaxReaderErrorCount)
                {
                    RoundEnd(0);
                }
            }
#endif
        }

保证每秒能渲染到设定的帧数60fps,过快或者过慢,过慢进行渲染补偿(连续渲染,过快进行跳过,等待下秒)

这里并不採用每一个世界一个线程的渲染方式,而是使用线程池,相应一个线程渲染几个特定的游戏世界,用于避开一些多线程锁操作.


    6.玩家操作流程

6.1 玩家命令会进入相应操作的坦克队列

    /// <summary>
        /// 开火
        /// </summary>
        /// <param name="index">武器序号</param>
        /// <param name="arg">投掷參数1-100</param>
        public void Fire(int index = 0, int arg = 0)
        {
            if (CheckStatues())
            {
                if (Tank.Devices.Count > index)
                {
                    Tank.EnqueueCmd(new Tank.TankCmd(Tank.TankCmd.OpCmd.Fire, index, arg));
                }
            }
        }

   6.2 坦克会在渲染时运行队列中的命令

        public override void Render(int ms, int lv)
        {
            while (true)
            {
                TankCmd cmd = null;
                Cmds.TryDequeue(out cmd);
                if (cmd != null)
                {
                    switch (cmd.Cmd)
                    {
                        case TankCmd.OpCmd.Fire:
                            FireOnWeapon(Devices[(int) cmd.Args[0]], (int) cmd.Args[1]);
                            break;
                        case TankCmd.OpCmd.Move:
                            MoveOn((Direction) cmd.Args[0]);
                            break;

                        case TankCmd.OpCmd.StopMove:
                            Block();
                            break;
                    }
                }
                else
                {
                    break;
                }
            }



            base.Render(ms,lv);
            //检查装置CD
            foreach (var device in Devices)
            {
                device.Render(ms);
            }
            //Buff渲染触发
            foreach (var buff in Buffs)
            {
                buff.OnReader(this, ms);
            }

            //移除时间已经到了的buff
            foreach (var buff in Buffs)
            {
                if (CurrentTimeGetter.Now > buff.EndTime)
                {
                    buff.OnBuffRemove(this);
                }
            }
        }

原文地址:https://www.cnblogs.com/jhcelue/p/6918648.html