golang基于原型的实体组件系统(ECS)框架插件Ark的使用

Golang基于原型的实体组件系统(ECS)框架插件Ark的使用

Ark是一个基于原型的Go语言实体组件系统(ECS)框架,它是Arche框架的继任者。

Ark (logo)

特性

  • 为高性能设计并高度优化
  • 文档完善,类型安全的API,以及全面的用户指南
  • 实体关系作为一等特性
  • 快速的批量操作进行大规模操作
  • 没有系统,只有查询。使用你自己的结构
  • 通过ark-serde实现世界序列化和反序列化

安装

要在Go项目中使用Ark,运行:

go get github.com/mlange-42/ark

使用

下面是一个经典的Position/Velocity示例,展示了Ark的基本用法。

package main

import (
	"math/rand/v2"

	"github.com/mlange-42/ark/ecs"
)

// Position 组件
type Position struct {
	X float64
	Y float64
}

// Velocity 组件
type Velocity struct {
	X float64
	Y float64
}

func main() {
	// 创建一个新的World
	world := ecs.NewWorld()

	// 创建一个组件映射器
	// 永久保存映射器并重复使用以获得最佳性能
	mapper := ecs.NewMap2[Position, Velocity](&world)

	// 创建实体
	for range 1000 {
		// 创建一个带有组件的新实体
		_ = mapper.NewEntity(
			&Position{X: rand.Float64() * 100, Y: rand.Float64() * 100},
			&Velocity{X: rand.NormFloat64(), Y: rand.NormFloat64()},
		)
	}

	// 创建一个过滤器
	// 永久保存过滤器并重复使用以获得最佳性能
	f

更多关于golang基于原型的实体组件系统(ECS)框架插件Ark的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang基于原型的实体组件系统(ECS)框架插件Ark的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Ark - Golang基于原型的ECS框架插件使用指南

Ark是一个基于原型的实体组件系统(ECS)框架插件,专为Golang设计。ECS是一种游戏开发架构模式,将实体(Entity)、组件(Component)和系统(System)分离,提供高度灵活性和性能优化。

基本概念

  1. 实体(Entity): 游戏中的对象,由多个组件组成
  2. 组件(Component): 纯数据结构,描述实体的某方面特性
  3. 系统(System): 处理具有特定组件组合的实体的逻辑

安装Ark

go get github.com/bytearena/ark

基本用法

1. 定义组件

type Position struct {
    X, Y float64
}

type Velocity struct {
    X, Y float64
}

type Renderable struct {
    SpriteID string
    Width, Height float64
}

2. 创建世界和原型

import "github.com/bytearena/ark"

func main() {
    // 创建世界
    world := ark.NewWorld()
    
    // 注册组件
    world.RegisterComponent(Position{})
    world.RegisterComponent(Velocity{})
    world.RegisterComponent(Renderable{})
    
    // 定义玩家原型
    playerProto := world.NewPrototype("player").
        AddComponent(Position{X: 0, Y: 0}).
        AddComponent(Velocity{X: 0, Y: 0}).
        AddComponent(Renderable{
            SpriteID: "player_sprite",
            Width:    32,
            Height:   32,
        })
    
    // 从原型创建实体
    player := playerProto.Spawn()
    
    // 运行世界
    world.Run()
}

3. 创建系统

type MovementSystem struct {
    *ark.System
}

func NewMovementSystem() *MovementSystem {
    return &MovementSystem{
        System: ark.NewSystemWithRequirements(
            ark.ComponentRequirement(Position{}),
            ark.ComponentRequirement(Velocity{}),
        ),
    }
}

func (s *MovementSystem) Update(dt float64) {
    s.IterateEntities(func(entity ark.EntityID, components ...interface{}) {
        pos := components[0].(*Position)
        vel := components[1].(*Velocity)
        
        pos.X += vel.X * dt
        pos.Y += vel.Y * dt
    })
}

// 注册系统
world.AddSystem(NewMovementSystem())

4. 查询实体

// 查询所有有Position和Renderable组件的实体
query := world.NewQuery().
    WithComponent(Position{}).
    WithComponent(Renderable{})

query.Each(func(entity ark.EntityID, components ...interface{}) {
    pos := components[0].(*Position)
    render := components[1].(*Renderable)
    
    fmt.Printf("Entity %d at (%.2f, %.2f) with sprite %s\n", 
        entity, pos.X, pos.Y, render.SpriteID)
})

高级特性

1. 事件系统

// 定义事件
type CollisionEvent struct {
    Entity1, Entity2 ark.EntityID
}

// 发送事件
world.Emit(CollisionEvent{
    Entity1: player1,
    Entity2: player2,
})

// 监听事件
world.AddListener(func(evt CollisionEvent) {
    fmt.Printf("Collision between %d and %d\n", evt.Entity1, evt.Entity2)
})

2. 原型继承

// 基础敌人原型
enemyProto := world.NewPrototype("enemy").
    AddComponent(Position{}).
    AddComponent(Velocity{X: 0.5, Y: 0}).
    AddComponent(Health{Value: 100})

// 特殊敌人继承基础敌人
bossProto := world.NewPrototype("boss", "enemy").
    OverrideComponent(Health{Value: 500}).
    AddComponent(BossAI{})

3. 系统优先级

// 设置系统执行顺序
world.AddSystem(NewInputSystem(), ark.WithPriority(10))  // 最先执行
world.AddSystem(NewMovementSystem(), ark.WithPriority(20))
world.AddSystem(NewRenderSystem(), ark.WithPriority(100)) // 最后执行

性能优化技巧

  1. 批量处理: 在系统中使用IterateEntities而不是单独处理每个实体
  2. 组件布局: 将经常一起访问的组件放在相邻内存位置
  3. 查询缓存: 复用查询对象而不是每次都创建新的
  4. 适当的系统粒度: 不要在一个系统中做太多事情

完整示例

package main

import (
	"fmt"
	"github.com/bytearena/ark"
)

// 定义组件
type Position struct{ X, Y float64 }
type Velocity struct{ X, Y float64 }
type Renderable struct{ SpriteID string }

// 移动系统
type MovementSystem struct{ *ark.System }

func NewMovementSystem() *MovementSystem {
	return &MovementSystem{
		System: ark.NewSystemWithRequirements(
			ark.ComponentRequirement(Position{}),
			ark.ComponentRequirement(Velocity{}),
		),
	}
}

func (s *MovementSystem) Update(dt float64) {
	s.IterateEntities(func(entity ark.EntityID, components ...interface{}) {
		pos := components[0].(*Position)
		vel := components[1].(*Velocity)
		pos.X += vel.X * dt
		pos.Y += vel.Y * dt
		fmt.Printf("Entity %d moved to (%.2f, %.2f)\n", entity, pos.X, pos.Y)
	})
}

// 渲染系统
type RenderSystem struct{ *ark.System }

func NewRenderSystem() *RenderSystem {
	return &RenderSystem{
		System: ark.NewSystemWithRequirements(
			ark.ComponentRequirement(Position{}),
			ark.ComponentRequirement(Renderable{}),
		),
	}
}

func (s *RenderSystem) Update(dt float64) {
	s.IterateEntities(func(entity ark.EntityID, components ...interface{}) {
		pos := components[0].(*Position)
		render := components[1].(*Renderable)
		fmt.Printf("Rendering %s at (%.2f, %.2f)\n", render.SpriteID, pos.X, pos.Y)
	})
}

func main() {
	world := ark.NewWorld()

	// 注册组件
	world.RegisterComponent(Position{})
	world.RegisterComponent(Velocity{})
	world.RegisterComponent(Renderable{})

	// 创建玩家原型
	playerProto := world.NewPrototype("player").
		AddComponent(Position{X: 0, Y: 0}).
		AddComponent(Velocity{X: 1, Y: 0.5}).
		AddComponent(Renderable{SpriteID: "player_sprite"})

	// 创建敌人原型
	enemyProto := world.NewPrototype("enemy").
		AddComponent(Position{X: 10, Y: 10}).
		AddComponent(Velocity{X: -0.5, Y: -0.2}).
		AddComponent(Renderable{SpriteID: "enemy_sprite"})

	// 生成实体
	playerProto.Spawn()
	enemyProto.Spawn()

	// 添加系统
	world.AddSystem(NewMovementSystem())
	world.AddSystem(NewRenderSystem())

	// 模拟游戏循环
	for i := 0; i < 5; i++ {
		fmt.Printf("\n--- Frame %d ---\n", i+1)
		world.Update(1.0) // dt = 1.0
	}
}

Ark框架提供了简洁而强大的ECS实现,特别适合需要高性能和灵活架构的游戏或模拟应用。通过原型系统,可以轻松创建和管理大量相似实体,而组件-系统架构则确保了代码的良好组织和可维护性。

回到顶部