zoukankan      html  css  js  c++  java
  • 多人即时战斗游戏服务端系列[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);
                    }
                }
            }

  • 相关阅读:
    Dockerfile构建镜像
    00基础复习
    docker的网络(基础)
    02-Mysql中的运算符
    01-mysql中的数据类型
    Docker客户端连接Docker Daemon的方式
    docker-ce快速部署
    ubuntu18.04 server配置静态ip
    html语义化小记录
    webpack导入es6的简单应用
  • 原文地址:https://www.cnblogs.com/jhcelue/p/6918648.html
Copyright © 2011-2022 走看看