多人即时战斗游戏服务端系列[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);
}
}
}
多人即时战斗游戏服务端系列[2]--90坦克Online游戏对象介绍以及渲染机制的更多相关文章
- 为什么多数游戏服务端是用 C++ 来写
早年开发游戏必须用C++,这没得说,2000-2004年,java还没有nio,其他动态语言不抗重负,只能C/C++能开发出完整可用的游戏服务端.直到2005年,韩国的游戏很多都还是纯C++写服务端, ...
- 转: 基于netty+ protobuf +spring + hibernate + jgroups开发的游戏服务端
from: http://ybak.iteye.com/blog/1853335 基于netty+ protobuf +spring + hibernate + jgroups开发的游戏服务端 游戏服 ...
- kbengine开源分布式游戏服务端引擎
一款开源的支持多人同时在线实时游戏的服务端引擎,使用简单的约定协议就能够使客户端与服务端进行交互,使用KBEngine插件能够快速与(Unity3D.OGRE.Cocos2d.HTML5,等等)技术结 ...
- Pomelo:网易开源基于 Node.js 的游戏服务端框架
Pomelo:网易开源基于 Node.js 的游戏服务端框架 https://github.com/NetEase/pomelo/wiki/Home-in-Chinese
- 游戏服务端中使用Servlet和Java注解的一个好设计
SNS类游戏基本都是使用HTTP短连接,用Java来开发服务端时能够使用Servlet+Tomcat非常轻松的架构起服务端来.在这里介绍一种使用Servlet比較好的一种设计,我也见过非常多基于HTT ...
- 游戏服务端pomelo安装配置
一.安装环境 Linux Ubantu 二.安装需要的组件 1.安装nodejs 注:debian下nodejs没有相应的apt包,所以无法用apt-get安装,只能通过nodejs的源码包安装, 这 ...
- 从零开始开发IM(即时通讯)服务端(二)
好消息:IM1.0.0版本已经上线啦,支持特性: 私聊发送文本/文件 已发送/已送达/已读回执 支持使用ldap登录 支持接入外部的登录认证系统 提供客户端jar包,方便客户端开发 github链接: ...
- 游戏服务端pomelo完整安装配置过程
版权声明:本文为博主原创文章,转载或又一次发表请先与我联系. https://blog.csdn.net/jonahzheng/article/details/27658985 游戏服务端pomelo ...
- go语言游戏服务端开发(三)——服务机制
五邑隐侠,本名关健昌,12年游戏生涯. 本教程以Go语言为例. P2P网络为服务进程间.服务进程与客户端间通信提供了便利,在这个基础上可以搭建服务. 在服务层,通信包可以通过定义协议号来确定该包怎 ...
随机推荐
- EditPlus 1:更改默认编码方式
打开软件点击上面的菜单栏Tools(工具),再找到Configure User Tools(用户配置工具)点击,再找到左边栏File点击,这个时候可以看到右边栏的Default encoding点击可 ...
- Dijkstra TYVJ 1031热浪 Dijkstra测试数据
测试用邻接表写得Dijkstra 代码写得很烂. 描述 德克萨斯纯朴的民眾们这个夏天正在遭受巨大的热浪!!!他们的德克萨斯长角牛吃起来不错,可是他们并不是很擅长生產富含奶油的乳製品.Farmer Jo ...
- Hadoop Hive概念学习系列之Hive里的2维坐标系统(第一步定位行键 -> 第二步定位字段)(二十三)
HBase里的4维坐标系统(第一步定位行键 -> 第二步定位列簇 -> 第三步定位列修饰符 -> 第四步定位时间戳) HBase里的4维坐标系统(第一步定位行键 ...
- 自学Python五 爬虫基础练习之SmartQQ协议
BAT站在中国互联网的顶端,引导着中国互联网的发展走向...既受到了多数程序员的关注,也在被我们所惦记着... 关于SmartQQ的协议来自HexBlog,根据他的博客我自己也一步一步的去分析,去尝试 ...
- 浅谈html运行原理
浅谈HTML运行原理,所谓的HTML简单的来说就是一个网页,虽然第一节就讲html原理可能大家会听不懂,就当是给一个初步印象把,至少大概知道一个网页的运行流程是怎样的,下面上一张图: 大致的一个htm ...
- HTML+CSS(10)
n 组合选择器 多元素选择器 n 描述:给多个元素加同一个样式,多个选择器之间用逗号隔开. n 举例:h1,p,div,body{color:red;} 后代元素选择器(最常用) n 描述:给 ...
- T-SQL查询高级--理解SQL SERVER中非聚集索引的覆盖,连接,交叉和过滤
写在前面:这是第一篇T-SQL查询高级系列文章.但是T-SQL查询进阶系列还远远没有写完.这个主题放到高级我想是因为这个主题需要一些进阶的知识作为基础..如果文章中有错误的地方请不吝指正.本篇文章 ...
- std::string格式化输入输出
在C语言中: C函数有sprintf函数, 比较方便, 但是需要知道所需要的内存空间是多少. 在C++的框架MFC中: 在MFC中CString 有Format函数来格式化字符串. 很方便. 难过的是 ...
- day002 计算机基础之 操作系统和编程语言的分类
      今天主要针对计算机基础中的操作系统和编程语言的分类进行了讲解. 操作系统   ...
- 15.5.1【Task实现细节】 生成的代码
还在吗?我们开始吧.由于深入讲解需上百页的篇幅,因此这里我不会讲得太深.但我会提 供足够的背景知识,以有助于你对整个结构的理解.之后可通过阅读我近些年来撰写的博客文章, 来了解更加错综复杂的细节,或简 ...