- 好友
- 2
- 在线时间
- 8 小时
- 最后登录
- 2024-11-18
见习骑士
- UID
- 2880114
- 第纳尔
- 1576
- 精华
- 0
- 互助
- 0
- 荣誉
- 0
- 贡献
- 0
- 魅力
- 3
- 注册时间
- 2017-9-30
鲜花( 12) 鸡蛋( 0)
|
本帖最后由 djman 于 2023-5-24 10:58 编辑
游戏场景可以出现大规模的兵,成百上千,而且千人千面,数量取决于电脑性能,游戏里有限制数量,但这可以调,主要是说明,骑砍是ECS架构,而不是OOP,否则不会有这样的效果,NPC是预设好的兵,除此之外都是随机生成的兵,但都是调用同一组件。
ECS架构是组件,实体,系统三部分。
核心是系统文件,module里是组件和实体。
在游戏里看到的都是实体,实体是组件组成,也有实体+组件=实体,实体+实体=新的实体
组件大概有,动画组件,属性组件,模型组件,触发器组件,还有对话组件,交易组件
这很像乐高积木,每个场景就像一个平台,在不同场景可以把积木搭建不同的形式。比如在村镇的NPC,只给实体安装走路动画组件,对话组件,NPC 不会攻击,除非给个别任务NPC加上攻击相关的组件.
而在战场上,npc有攻击相关的组件,而没有交易组件对话组件,很灵活,比较复杂的组件有动画组件和技能属性组件,而组件也是可以拆分的,组件+组件=新的组件.
我们试图把大概理论框架理清楚,一切会显得简单一些.
攻击动画组件在武器上而不是角色上,这样用不同武器就有不同攻击方式。而角色只有移动相关的组件,比如跑跳走,骑马。
我们需要具体化,把每个实体对应匹配的组件一一找出来,在代码里理清楚。骑砍有很多随机系统,比如随机生成脸,随机生成地图,甚至我觉得大地图战争也有随机部分。实体是广义上的东西,实体是什么样完全取决于组件,给建筑物上安装移动属性组件,建筑物就可以跑,安装对话组件,你可以和建筑物对话。实体什么都不是,或只是一个点,或可以是万物。实体而且可以变化,在大地图上,一个实体是可以说是一个点,只是承载一些数据可移动的节点,只有简单的移动动画,而到一个场景,它又是装载很多组件,复杂的实体。
ECS理解与应用
ECS是 Entity-Component-System(实体-组件-系统)的缩写,其模式遵循组合优于继承的原则。
ECS的基本结构
一个使用ECS架构开发的游戏的基本结构如下:
- Entity 游戏内的每个基本单元都是一个实体,每个实体里面有多个组件。
- Component 每个组件仅包含代表其特征的数据,例如:移动相关的组件仅仅包含速度、位置、朝向等属性。一旦一个实体拥有了MoveComponent组件便可以认为它拥有了移动的能力。
- System 系统是用来处理拥有一个或者相同的几个组件的实体集合的工具,其只拥有行为(在系统中没有任何的数据)。在移动相关的组件的例子中,处理移动的系统仅仅关心拥有移动能力的实体,它会遍历所有拥有MoveComponent组件的实体,并且根据相关的数据(速度、位置、朝向等),更新实体中的组件的属性。
实体与组件之间是一对多的关系,实体拥有怎么样的能力,完全取决于其拥有哪些组件,通过动态的删除与修改实体中的组件,可以改变实体的行为。
组件
一 个组件可以使用C中的结构体来进行设计。它没有方法,只是用来存储一些数据,并不在它之上进行动作。一个经典的实现方式是,每一个不同的组件都继承至一个 抽象的Componet类,通过这样的方法我们能够在运行时动态的添加组件,识别组件。每一个组件都描述了实体的某个属性特征。当他们单独存在的时候,实 际上是没有任何意义的,但是当多个组件通过系统的方式组织在一起,就能够发挥强大的力量。我们可以使用空的组件来对实体进行标记,从而能够在运行时动态的 识别它。
一个组件是一堆数据的集合,可以使用C语言中的结构体来进行实现。它没有方法,即不存在任何的行为,只用来存储状态。
样例:
· PositionComponent(x, y)
· VelocityComponent(X, y)
· HealthComponent(value)
· PlayerComponent()
· EnemyComponent()
注:括号前为组件名,括号内为该组件拥有的数据
实体
一个实体指的是存在于你的游戏世界中的物体。实体在代码上就是一个组件的列表。由于实体的结构实在是太简单了,所以很多实现都没有专门的设计一个实体的数据结构。 相反的,一个实体就是一个ID,所有组成这个实体的组件将会被这个ID给标记,从而明确的知道哪些组件是属于哪个实体的。如果你想的话,你可以在运行时, 动态的将组件从实体中移除或者增加一个或多个你感兴趣的组件。比如说,如果玩家发出了一个冰系魔法,将敌人冻住,你只要简单的将它的速度组件移除,那么敌 人就静止住了。
实体是一个概念上的定义,是游戏世界中的一个独特的物体,是一系列组件的集合,为了方便区分不同的实体,在代码层面上一般用一个ID来进行表示。所有组成这个实体的组件将会被这个ID标记,从而明确哪些组件属于该实体。由于其是一系列组件的集合,因此可以在游戏运行的时候在实体中添加一个或者删除一个组件。
样例:
· Player(Position, Sprite, Velocity, Health)
· Enemy(Position, Sprite, Velocity, Health, AI)
· Tree(Position, Sprite)
注:括号前为实体名,括号内为该实体拥有的组件
系统
系统是游戏中用来处理逻辑的部分,一个系统就是对拥有一个或多个相同组件的实体集合进行操作的工具,它只有行为,没有状态,即不应该存放任何数据。
举个例子,游戏中玩家要操作对应的角色进行移动,由上面两部分可知,角色是一个实体,其拥有位置和速度组件,那么怎么根据实体拥有的速度去刷新其位置呢,MoveSystem(移动系统)登场,它可以得到所有拥有位置和速度组件的实体集合,遍历这个集合,根据每一个实体拥有的速度值和物理引擎去计算该实体应该所处的位置,并刷新该实体位置组件的值,至此,完成了玩家操控的角色移动了。
系统这里比较麻烦,还存在一个常见问题:由于代码逻辑分布于各个系统中,各个系统之间为了解耦又不能互相访问,那么如果有多个系统希望运行同样的逻辑,该如何解决,总不能把代码复制 N 份,放到各个系统之中。UtilityFunction(实用函数) 便是用来解决这一问题的,它将被多个系统调用的方法单独提取出来,放到统一的地方,各个系统通过 UtilityFunction 调用想执行的方法,同系统一样, UtilityFunction 中不能存放状态,它应该是拥有各个方法的纯净集合。
系统只会对相关联的组件进行操作,所以组件就定义了一个实体所应该具有的行为。比如说,如果一个实体有一个Position组件,但是没有 Velocity组件,那么我们就知道,这个物体是静止不动的,系统就不会对这个实体的Position组件进行操作了。当我们对这个实体增加了一个 Velocity组件的时候,系统就会使用Velocity组件来对物体进行移动。这样的行为可以使用被标记的组件来进行,被标记的组件能够重复的使用在 不同的上下文中。对一个实体,增加一个空的Player组件,将会为这个实体打上了Player的标签,那么PlayerControl系统,就会寻找带 有这个标签的所有组件,然后使用Input中的数据,进行操作。
样例:
· MoveSystem(Position, Velocity)
· RenderSystem(Position, Sprite)
注:括号前为系统名,括号内为该系统关心的组件集合
游戏逻辑是系统需要进行的工作。一个系统就是对所有相关联的组件记性操作,比如说,同一个实体的组件。举 个例子,人物的移动系统可能会对位置(Position),速度(Velocity),碰撞(Collider),和输入(Input)进行操作。每一个 系统,都会在每一帧中按照逻辑上的顺序进行更新。如果要让一个角色跳起来,我们只要检测下Input中的keyJump按键是否被按下,如果是,那么系统 就会查看下载Collider中是否有一个接触了地面,如果是,就将这个实体的Velocity的y速度设置一下,让这个物体跳起来。
战团引擎的ECS大致架构
|
|