golang实现2D网格的射线投射、阴影投射和路径查找插件库grid的使用

Golang实现2D网格的射线投射、阴影投射和路径查找插件库grid的使用

功能特性

  • DDA射线投射
  • A-Star路径查找
  • 基于射线的视线检测
  • 递归阴影投射
  • Dijkstra地图
  • Bresenham直线算法
  • 100%测试覆盖率

基本使用

值类型示例

import (
    "image"

    "github.com/s0rg/grid"
)

const mapW, mapH = 100, 100

func valueExample() {
    // 使用值类型很简单
    g := grid.New[int](image.Rect(0, 0, mapW, mapH))

    // 现在网格填充了该类型的零值
    // 你可以用其他值重新填充它:
    g.Fill(func() int {
        return 1
    })
}

指针类型示例

func pointerExample() {
    // 使用指针类型类似,但需要预先填充
    type mycell struct {}

    g := grid.New[*mycell](image.Rect(0, 0, mapW, mapH))

    // 现在网格填充了nil,所以需要预先填充一些值
    // 否则通过Get/MustGet方法访问的将是nil
    g.Fill(func() *mycell {
        return &mycell{}
    })
}

使用示例

func usageExample() {
    type mycell struct {
        wall bool
    }

    g := grid.New[*mycell](image.Rect(0, 0, mapW, mapH))

    g.Fill(func() *mycell {
        return &mycell{}
    })

    pt := image.Pt(10, 10)

    // 设置新值
    g.Set(pt, &mycell{wall: true})

    // 更新现有值
    if v, ok := g.Get(pt); ok {
        v.wall = false
    }

    // 简写形式,如果越界访问会panic
    g.MustGet(pt).wall = true

    // 遍历所有单元格
    g.Iter(func(p image.Point, c *mycell) (next bool) {
        if c.wall {
            // 找到墙
        }

        return true
    })
}

完整示例

package main

import (
	"fmt"
	"image"
	"log"

	"github.com/s0rg/grid"
)

type cell struct {
	wall bool
}

func main() {
	// 创建100x100的网格
	g := grid.New[*cell](image.Rect(0, 0, 100, 100))
	
	// 填充初始单元格
	g.Fill(func() *cell {
		return &cell{}
	})

	// 设置一些墙
	for x := 20; x < 80; x++ {
		g.Set(image.Pt(x, 20), &cell{wall: true})
		g.Set(image.Pt(x, 80), &cell{wall: true})
	}
	for y := 20; y < 80; y++ {
		g.Set(image.Pt(20, y), &cell{wall: true})
		g.Set(image.Pt(80, y), &cell{wall: true})
	}

	// 起点和终点
	start := image.Pt(10, 10)
	end := image.Pt(90, 90)

	// 使用A*算法查找路径
	path, ok := g.Path(
		start,
		end,
		func(p image.Point) bool {
			// 检查点是否可通行
			c, ok := g.Get(p)
			return ok && !c.wall
		},
		grid.Diagonal,
	)
	if !ok {
		log.Fatal("no path found")
	}

	// 打印路径
	fmt.Println("Path from", start, "to", end)
	for _, p := range path {
		fmt.Println(p)
	}

	// 视线检测
	visible := g.LineOfSight(
		start,
		end,
		func(p image.Point) bool {
			c, ok := g.Get(p)
			return ok && !c.wall
		},
	)
	fmt.Println("Is visible:", visible)

	// 阴影投射
	var visibleCells []image.Point
	g.CastShadow(
		start,
		10, // 半径
		func(p image.Point) bool {
			c, ok := g.Get(p)
			return ok && !c.wall
		},
		func(p image.Point) {
			visibleCells = append(visibleCells, p)
		},
	)
	fmt.Println("Visible cells count:", len(visibleCells))
}

性能基准

运行基准测试:

make bench

测试结果示例:

goos: linux
goarch: amd64
pkg: github.com/s0rg/grid
cpu: AMD Ryzen 5 5500U with Radeon Graphics
BenchmarkGrid/Set-12         	1000000000	        0.8108 ns/op	      0 B/op	      0 allocs/op
BenchmarkGrid/Get-12         	641611768	        1.764 ns/op	      0 B/op	      0 allocs/op
BenchmarkGrid/Neighbours-12  	52243890	       23.41 ns/op	      0 B/op	      0 allocs/op
BenchmarkGrid/LineBresenham-12         	4416172	      269.0 ns/op	      0 B/op	      0 allocs/op
BenchmarkGrid/CastRay-12               	3829839	      321.1 ns/op	      0 B/op	      0 allocs/op
BenchmarkGrid/CastShadow-12            	  32648	    36950 ns/op	      0 B/op	      0 allocs/op
BenchmarkGrid/LineOfSight-12           	   9897	   114576 ns/op	      0 B/op	      0 allocs/op
BenchmarkGrid/DijkstraMap-12           	   1029	  1190195 ns/op	  20656 B/op	      3 allocs/op
BenchmarkGrid/Path-12                  	    372	  3225325 ns/op	 997588 B/op	  13643 allocs/op
PASS
ok  	github.com/s0rg/grid	12.098s

更多关于golang实现2D网格的射线投射、阴影投射和路径查找插件库grid的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现2D网格的射线投射、阴影投射和路径查找插件库grid的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 2D网格处理库 grid 使用指南

grid 是一个用于处理2D网格的Go语言库,提供了射线投射(ray casting)、阴影投射(shadow casting)和路径查找(path finding)等功能。下面我将详细介绍如何使用这个库。

安装

go get github.com/sidav/grid

基本概念

grid 库主要围绕 Grid 类型工作,它代表一个2D网格。网格中的每个单元格可以包含任意值。

初始化网格

package main

import (
	"fmt"
	"github.com/sidav/grid"
)

func main() {
	// 创建一个10x10的网格,默认值为0
	g := grid.NewGrid(10, 10)
	
	// 设置单元格值
	g.SetCell(3, 4, 1) // 将(3,4)位置的值设为1
	
	// 获取单元格值
	val := g.GetCell(3, 4)
	fmt.Println("Value at (3,4):", val)
}

射线投射(Ray Casting)

射线投射用于检测从起点到终点之间的直线路径是否被阻挡。

func rayCastingExample(g *grid.Grid) {
	// 定义障碍物检查函数
	isObstacle := func(x, y int) bool {
		return g.GetCell(x, y) == 1 // 假设1表示障碍物
	}
	
	// 从(1,1)到(8,8)投射射线
	visible := grid.RayTrace(1, 1, 8, 8, isObstacle)
	
	if visible {
		fmt.Println("路径畅通")
	} else {
		fmt.Println("路径被阻挡")
	}
	
	// 获取射线经过的所有点
	points := grid.GetRay(1, 1, 8, 8)
	fmt.Println("射线经过的点:", points)
}

阴影投射(Shadow Casting)

阴影投射用于计算从某个点出发的视野范围。

func shadowCastingExample(g *grid.Grid) {
	// 定义视野范围回调函数
	visitFunc := func(x, y, dx, dy int) bool {
		fmt.Printf("可见点: (%d,%d)\n", x, y)
		// 返回true表示继续传播视野,false表示停止
		return g.GetCell(x, y) != 1 // 遇到障碍物停止
	}
	
	// 从(5,5)点开始,半径7的视野
	grid.ShadowCast(5, 5, 7, visitFunc)
}

路径查找(Path Finding)

grid 库提供了A*算法实现路径查找。

func pathFindingExample(g *grid.Grid) {
	// 定义移动成本函数
	costFunc := func(fromX, fromY, toX, toY int) int {
		if g.GetCell(toX, toY) == 1 { // 障碍物
			return -1 // 不可通过
		}
		return 1 // 普通移动成本
	}
	
	// 查找从(1,1)到(8,8)的路径
	path := grid.FindPath(g, 1, 1, 8, 8, costFunc)
	
	if path == nil {
		fmt.Println("找不到路径")
	} else {
		fmt.Println("找到路径:", path)
	}
}

完整示例

package main

import (
	"fmt"
	"github.com/sidav/grid"
)

func main() {
	// 创建20x20的网格
	g := grid.NewGrid(20, 20)
	
	// 添加一些障碍物
	for i := 5; i < 15; i++ {
		g.SetCell(i, 10, 1)
	}
	
	// 射线投射测试
	fmt.Println("--- 射线投射测试 ---")
	rayCastingExample(g)
	
	// 阴影投射测试
	fmt.Println("\n--- 阴影投射测试 ---")
	shadowCastingExample(g)
	
	// 路径查找测试
	fmt.Println("\n--- 路径查找测试 ---")
	pathFindingExample(g)
}

func rayCastingExample(g *grid.Grid) {
	isObstacle := func(x, y int) bool {
		return g.GetCell(x, y) == 1
	}
	
	visible := grid.RayTrace(2, 2, 18, 18, isObstacle)
	fmt.Println("从(2,2)到(18,18)是否可见:", visible)
}

func shadowCastingExample(g *grid.Grid) {
	visitFunc := func(x, y, dx, dy int) bool {
		fmt.Printf("(%d,%d) ", x, y)
		return g.GetCell(x, y) != 1
	}
	
	fmt.Println("从(10,5)的视野范围:")
	grid.ShadowCast(10, 5, 5, visitFunc)
	fmt.Println()
}

func pathFindingExample(g *grid.Grid) {
	costFunc := func(fromX, fromY, toX, toY int) int {
		if g.GetCell(toX, toY) == 1 {
			return -1
		}
		return 1
	}
	
	path := grid.FindPath(g, 2, 2, 18, 18, costFunc)
	
	if path == nil {
		fmt.Println("找不到从(2,2)到(18,18)的路径")
	} else {
		fmt.Println("找到路径:")
		for _, p := range path {
			fmt.Printf("(%d,%d) ", p.X, p.Y)
		}
		fmt.Println()
	}
}

高级功能

  1. 自定义距离计算:
// 使用曼哈顿距离
distanceFunc := grid.ManhattanDistance
grid.SetDistanceFunction(distanceFunc)
  1. 网格操作:
// 复制网格
g2 := g.Clone()

// 调整网格大小
g.Resize(30, 30)

// 遍历所有单元格
g.ForEachCell(func(x, y int, value interface{}) {
	fmt.Printf("(%d,%d)=%v ", x, y, value)
})

grid 库提供了强大的2D网格处理能力,适用于游戏开发、地图处理、AI寻路等多种场景。通过合理使用射线投射、阴影投射和路径查找功能,可以实现复杂的空间分析和移动逻辑。

回到顶部