1. 程式人生 > >多人即時戰鬥遊戲服務端系列[2]--90坦克Online遊戲對象介紹以及渲染機制

多人即時戰鬥遊戲服務端系列[2]--90坦克Online遊戲對象介紹以及渲染機制

d+ ica 部分 avi 產生 4.4 1.8 timer lock

先上類圖,略大,點擊此處放大:

技術分享


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);
                }
            }
        }

多人即時戰鬥遊戲服務端系列[2]--90坦克Online遊戲對象介紹以及渲染機制