golang基于实体组件系统概念构建游戏引擎插件库ecs的使用

Golang基于实体组件系统概念构建游戏引擎插件库ECS的使用

ECS Logo

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框架。

基本概念

  1. 实体(Entities):游戏中的基本对象,通常只是一个唯一标识符
  2. 组件(Components):纯数据容器,描述实体的属性和特征
  3. 系统(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()
    }
}

性能优化考虑

  1. 组件存储优化:可以使用连续内存存储同类型组件,提高缓存命中率
  2. 查询优化:实现更高效的实体查询机制,如位掩码
  3. 并行处理:利用Go的goroutine并行处理不相关的系统

扩展功能

  1. 事件系统:添加事件发布/订阅机制
  2. 资源管理:集成资源加载和管理
  3. 序列化:实现实体和组件的序列化/反序列化

这个简单的ECS实现提供了基本功能,可以根据项目需求进一步扩展和完善。ECS架构特别适合需要高度灵活性和性能的游戏开发场景。

回到顶部