golang基于实体组件系统概念构建游戏引擎插件库ecs的使用
Golang基于实体组件系统概念构建游戏引擎插件库ECS的使用
ECS - 实体组件系统
在Golang中基于实体组件系统概念构建自己的游戏引擎。
特性
- 提供易于使用的框架来从头构建游戏引擎
- 无依赖其他模块或特定游戏库 - 自由选择适合您需求的工具
- 最小开销 - 只使用真正需要的部分
完整示例Demo
项目结构
首先创建基本项目布局:
mkdir ecs-example
cd ecs-example
go mod init example
mkdir components systems
主程序
创建main.go
文件:
package main
import (
"github.com/andygeiss/ecs"
)
func main() {
em := ecs.NewEntityManager()
sm := ecs.NewSystemManager()
de := ecs.NewDefaultEngine(em, sm)
de.Setup()
defer de.Teardown()
de.Run()
}
移动系统
创建systems/movement.go
:
package systems
import (
"github.com/andygeiss/ecs"
)
type movementSystem struct{}
func (a *movementSystem) Process(em ecs.EntityManager) (state int) {
// 这个状态告诉引擎在第一次调用后停止
return ecs.StateEngineStop
}
func (a *movementSystem) Setup() {}
func (a *movementSystem) Teardown() {}
func NewMovementSystem() ecs.System {
return &movementSystem{}
}
然后在main.go
中添加系统:
sm := ecs.NewSystemManager()
sm.Add(systems.NewMovementSystem()) // <-- 添加移动系统
de := ecs.NewDefaultEngine(em, sm)
玩家实体
创建组件掩码components/components.go
:
package components
const (
MaskPosition = uint64(1 << 0)
MaskVelocity = uint64(1 << 1)
)
创建位置组件components/position.go
:
package components
type Position struct {
X float32 `json:"x"`
Y float32 `json:"y"`
}
func (a *Position) Mask() uint64 {
return MaskPosition
}
func (a *Position) WithX(x float32) *Position {
a.X = x
return a
}
func (a *Position) WithY(y float32) *Position {
a.Y = y
return a
}
func NewPosition() *Position {
return &Position{}
}
在main.go
中添加玩家实体:
em := ecs.NewEntityManager()
em.Add(ecs.NewEntity("player", []ecs.Component{ // <-- 添加玩家实体
components.NewPosition().
WithX(10).
WithY(10),
components.NewVelocity().
WithX(100).
WithY(100),
})) // -->
扩展移动系统
完善移动系统的行为:
func (a *movementSystem) Process(em ecs.EntityManager) (state int) {
for _, e := range em.FilterByMask(components.MaskPosition | components.MaskVelocity) {
position := e.Get(components.MaskPosition).(*components.Position)
velocity := e.Get(components.MaskVelocity).(*components.Velocity)
position.X += velocity.X * rl.GetFrameTime()
position.Y += velocity.Y * rl.GetFrameTime()
}
return ecs.StateEngineStop
}
渲染系统示例
使用raylib的渲染系统示例:
func (a *renderingSystem) Setup() {
rl.InitWindow(a.width, a.height, a.title)
}
func (a *renderingSystem) Process(em core.EntityManager) (state int) {
// 首先检查应用是否应该停止
if rl.WindowShouldClose() {
return core.StateEngineStop
}
// 清屏
if rl.IsWindowReady() {
rl.BeginDrawing()
rl.ClearBackground(rl.Black)
rl.DrawFPS(10, 10)
rl.EndDrawing()
}
return core.StateEngineContinue
}
func (a *renderingSystem) Teardown() {
rl.CloseWindow()
}
这个ECS框架提供了构建游戏引擎的基础,您可以根据需要添加更多系统和组件来实现完整的游戏功能。
更多关于golang基于实体组件系统概念构建游戏引擎插件库ecs的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang基于实体组件系统概念构建游戏引擎插件库ecs的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 中的实体组件系统(ECS)游戏引擎实现
实体组件系统(ECS)是一种在游戏开发中流行的架构模式,它将游戏对象分解为三个主要部分:实体(Entities)、组件(Components)和系统(Systems)。下面我将介绍如何在Go语言中实现一个简单的ECS框架。
基本概念
- 实体(Entities):游戏中的基本对象,通常只是一个唯一标识符
- 组件(Components):纯数据容器,描述实体的属性和特征
- 系统(Systems):处理具有特定组件组合的实体的逻辑
基础实现
1. 定义核心结构
package ecs
import "sync"
// World 管理所有实体、组件和系统
type World struct {
entities map[Entity]struct{}
components map[ComponentType]map[Entity]Component
systems []System
mutex sync.RWMutex
nextID Entity
}
// Entity 是实体的唯一标识符
type Entity uint64
// Component 是所有组件必须实现的接口
type Component interface {
Type() ComponentType
}
// ComponentType 标识组件类型
type ComponentType string
// System 是处理特定组件组合的逻辑
type System interface {
Update(world *World)
RequiredComponents() []ComponentType
}
// NewWorld 创建一个新的 ECS 世界
func NewWorld() *World {
return &World{
entities: make(map[Entity]struct{}),
components: make(map[ComponentType]map[Entity]Component),
systems: make([]System, 0),
}
}
2. 实体管理
// CreateEntity 创建一个新实体
func (w *World) CreateEntity() Entity {
w.mutex.Lock()
defer w.mutex.Unlock()
id := w.nextID
w.nextID++
w.entities[id] = struct{}{}
return id
}
// RemoveEntity 移除一个实体及其所有组件
func (w *World) RemoveEntity(e Entity) {
w.mutex.Lock()
defer w.mutex.Unlock()
delete(w.entities, e)
for _, compMap := range w.components {
delete(compMap, e)
}
}
3. 组件管理
// AddComponent 为实体添加组件
func (w *World) AddComponent(e Entity, c Component) {
w.mutex.Lock()
defer w.mutex.Unlock()
compType := c.Type()
if _, ok := w.components[compType]; !ok {
w.components[compType] = make(map[Entity]Component)
}
w.components[compType][e] = c
}
// GetComponent 获取实体的特定组件
func (w *World) GetComponent(e Entity, compType ComponentType) (Component, bool) {
w.mutex.RLock()
defer w.mutex.RUnlock()
if compMap, ok := w.components[compType]; ok {
comp, exists := compMap[e]
return comp, exists
}
return nil, false
}
// RemoveComponent 从实体移除特定组件
func (w *World) RemoveComponent(e Entity, compType ComponentType) {
w.mutex.Lock()
defer w.mutex.Unlock()
if compMap, ok := w.components[compType]; ok {
delete(compMap, e)
}
}
4. 系统管理
// AddSystem 添加一个新系统
func (w *World) AddSystem(s System) {
w.mutex.Lock()
defer w.mutex.Unlock()
w.systems = append(w.systems, s)
}
// Update 更新所有系统
func (w *World) Update() {
w.mutex.RLock()
defer w.mutex.RUnlock()
for _, system := range w.systems {
system.Update(w)
}
}
使用示例
定义组件
type PositionComponent struct {
X, Y float64
}
func (p *PositionComponent) Type() ComponentType {
return "Position"
}
type VelocityComponent struct {
X, Y float64
}
func (v *VelocityComponent) Type() ComponentType {
return "Velocity"
}
type RenderableComponent struct {
Sprite string
}
func (r *RenderableComponent) Type() ComponentType {
return "Renderable"
}
定义系统
type MovementSystem struct{}
func (m *MovementSystem) Update(w *World) {
// 获取所有具有位置和速度组件的实体
for e := range w.entities {
pos, hasPos := w.GetComponent(e, "Position")
vel, hasVel := w.GetComponent(e, "Velocity")
if hasPos && hasVel {
p := pos.(*PositionComponent)
v := vel.(*VelocityComponent)
// 更新位置
p.X += v.X
p.Y += v.Y
}
}
}
func (m *MovementSystem) RequiredComponents() []ComponentType {
return []ComponentType{"Position", "Velocity"}
}
type RenderingSystem struct{}
func (r *RenderingSystem) Update(w *World) {
// 获取所有可渲染实体
for e := range w.entities {
if pos, hasPos := w.GetComponent(e, "Position"); hasPos {
if render, hasRender := w.GetComponent(e, "Renderable"); hasRender {
p := pos.(*PositionComponent)
r := render.(*RenderableComponent)
fmt.Printf("Rendering %s at (%.2f, %.2f)\n", r.Sprite, p.X, p.Y)
}
}
}
}
func (r *RenderingSystem) RequiredComponents() []ComponentType {
return []ComponentType{"Position", "Renderable"}
}
主程序
func main() {
world := ecs.NewWorld()
// 添加系统
world.AddSystem(&MovementSystem{})
world.AddSystem(&RenderingSystem{})
// 创建实体
player := world.CreateEntity()
world.AddComponent(player, &PositionComponent{X: 0, Y: 0})
world.AddComponent(player, &VelocityComponent{X: 1, Y: 0.5})
world.AddComponent(player, &RenderableComponent{Sprite: "player.png"})
enemy := world.CreateEntity()
world.AddComponent(enemy, &PositionComponent{X: 10, Y: 5})
world.AddComponent(enemy, &VelocityComponent{X: -0.5, Y: 0})
world.AddComponent(enemy, &RenderableComponent{Sprite: "enemy.png"})
// 模拟游戏循环
for i := 0; i < 5; i++ {
fmt.Printf("--- Frame %d ---\n", i+1)
world.Update()
}
}
性能优化考虑
- 组件存储优化:可以使用连续内存存储同类型组件,提高缓存命中率
- 查询优化:实现更高效的实体查询机制,如位掩码
- 并行处理:利用Go的goroutine并行处理不相关的系统
扩展功能
- 事件系统:添加事件发布/订阅机制
- 资源管理:集成资源加载和管理
- 序列化:实现实体和组件的序列化/反序列化
这个简单的ECS实现提供了基本功能,可以根据项目需求进一步扩展和完善。ECS架构特别适合需要高度灵活性和性能的游戏开发场景。