diff --git a/go.mod b/go.mod index d765c8b..c551cd6 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/nats-io/nats.go v1.25.0 github.com/panjf2000/gnet v1.6.6 + github.com/smartystreets/goconvey v1.8.0 github.com/sony/sonyflake v1.1.0 github.com/tealeg/xlsx v1.0.5 github.com/tidwall/gjson v1.14.4 @@ -46,7 +47,6 @@ require ( github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/smartystreets/assertions v1.13.1 // indirect - github.com/smartystreets/goconvey v1.8.0 // indirect github.com/templexxx/cpu v0.0.9 // indirect github.com/templexxx/xorsimd v0.4.1 // indirect github.com/tidwall/match v1.1.1 // indirect @@ -59,11 +59,9 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.9.0 // indirect - golang.org/x/mod v0.9.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect - golang.org/x/tools v0.7.0 // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect diff --git a/go.sum b/go.sum index f572a41..4f7cfe0 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,6 @@ github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= -github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -72,7 +70,6 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU= github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= @@ -210,8 +207,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -264,8 +259,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/utils/geometry/geometry.go b/utils/geometry/geometry.go index 0d88d10..c6b9617 100644 --- a/utils/geometry/geometry.go +++ b/utils/geometry/geometry.go @@ -71,7 +71,7 @@ func GetDirectionNextWithCoordinateArray[V generic.SignedNumber](direction Direc } // GetDirectionNextWithPos 获取位置在特定宽度和特定方向上的下一个位置 -// - 需要注意的是,在左右方向时,当下一个位置不在游戏区域内时,将会返回上一行的末位置或下一行的首位置 +// - 需要注意的是,在左右方向时,当下一个位置不在矩形区域内时,将会返回上一行的末位置或下一行的首位置 func GetDirectionNextWithPos[V generic.SignedNumber](direction Direction, width, pos V) V { switch direction { case DirectionUp: diff --git a/utils/geometry/line.go b/utils/geometry/line.go index 62bfcee..b4605b9 100644 --- a/utils/geometry/line.go +++ b/utils/geometry/line.go @@ -14,9 +14,29 @@ func NewLine[V generic.SignedNumber](start, end Point[V]) Line[V] { return Line[V]{start, end} } +// NewLineCap 创建一根包含数据的线段 +func NewLineCap[V generic.SignedNumber, Data any](start, end Point[V], data Data) LineCap[V, Data] { + return LineCap[V, Data]{NewLine(start, end), data} +} + +// NewLineCapWithLine 通过已有线段创建一根包含数据的线段 +func NewLineCapWithLine[V generic.SignedNumber, Data any](line Line[V], data Data) LineCap[V, Data] { + return LineCap[V, Data]{line, data} +} + // Line 通过两个点表示一根线段 type Line[V generic.SignedNumber] [2]Point[V] +// LineCap 可以包含一份额外数据的线段 +type LineCap[V generic.SignedNumber, Data any] struct { + Line[V] + Data Data +} + +func (slf *LineCap[V, Data]) GetData() Data { + return slf.Data +} + // GetPoints 获取该线段的两个点 func (slf Line[V]) GetPoints() [2]Point[V] { return slf @@ -92,11 +112,13 @@ func CalcLineIsCollinear[V generic.SignedNumber](line1, line2 Line[V], tolerance // CalcLineIsOverlap 通过对点进行排序来检查两条共线线段是否重叠,返回重叠线段 func CalcLineIsOverlap[V generic.SignedNumber](line1, line2 Line[V]) (line Line[V], overlap bool) { - var shapes = []Shape[V]{ - {line1.GetStart(), line1.GetEnd(), line1.GetStart()}, - {line1.GetStart(), line1.GetEnd(), line1.GetEnd()}, - {line2.GetStart(), line2.GetEnd(), line2.GetStart()}, - {line2.GetStart(), line2.GetEnd(), line2.GetEnd()}, + l1ps, l1pe := NewPointCapWithPoint(line1.GetStart(), true), NewPointCapWithPoint(line1.GetEnd(), true) + l2ps, l2pe := NewPointCapWithPoint(line2.GetStart(), false), NewPointCapWithPoint(line2.GetEnd(), false) + var shapes = [][]PointCap[V, bool]{ + {l1ps, l1pe, l1ps}, + {l1ps, l1pe, l1pe}, + {l2ps, l2pe, l2ps}, + {l2ps, l2pe, l2pe}, } sort.Slice(shapes, func(i, j int) bool { a, b := shapes[i], shapes[j] @@ -109,10 +131,10 @@ func CalcLineIsOverlap[V generic.SignedNumber](line1, line2 Line[V]) (line Line[ } }) - notOverlap := shapes[0][0].Equal(shapes[1][0]) && shapes[0][1].Equal(shapes[1][1]) - singlePointOverlap := shapes[1][2].Equal(shapes[2][2]) + notOverlap := shapes[1][0].GetData() == shapes[2][0].GetData() + singlePointOverlap := shapes[1][2].Equal(shapes[2][2].Point) if notOverlap || singlePointOverlap { return line, false } - return NewLine(shapes[1][2], shapes[2][2]), true + return NewLine(shapes[1][2].Point, shapes[2][2].Point), true } diff --git a/utils/geometry/navmesh/navmesh.go b/utils/geometry/navmesh/navmesh.go index 1f6a0ee..04bda21 100644 --- a/utils/geometry/navmesh/navmesh.go +++ b/utils/geometry/navmesh/navmesh.go @@ -1,9 +1,9 @@ package navmesh import ( - "github.com/kercylan98/minotaur/utils/astar" "github.com/kercylan98/minotaur/utils/generic" "github.com/kercylan98/minotaur/utils/geometry" + "github.com/kercylan98/minotaur/utils/geometry/astar" "github.com/kercylan98/minotaur/utils/maths" ) @@ -178,6 +178,9 @@ func (slf *NavMesh[V]) FindPath(start, end geometry.Point[V]) (result []geometry for i := 0; i < len(path)-1; i++ { current := path[i] next := path[i+1] + if current.id == next.id { + continue + } var portal geometry.Line[V] var find bool @@ -208,8 +211,7 @@ func (slf *NavMesh[V]) FindPath(start, end geometry.Point[V]) (result []geometry } func (slf *NavMesh[V]) generateLink() { - refer := len(slf.meshShapes) - for i := 0; i < refer; i++ { + for i := 0; i < len(slf.meshShapes); i++ { shapePkg := slf.meshShapes[i] shapeCentroid := shapePkg.Centroid() shapeBoundingRadius := shapePkg.BoundingRadius() diff --git a/utils/geometry/navmesh/navmesh_example_test.go b/utils/geometry/navmesh/navmesh_example_test.go new file mode 100644 index 0000000..5075147 --- /dev/null +++ b/utils/geometry/navmesh/navmesh_example_test.go @@ -0,0 +1,119 @@ +package navmesh_test + +import ( + "fmt" + "github.com/kercylan98/minotaur/utils/geometry" + "github.com/kercylan98/minotaur/utils/geometry/navmesh" + "github.com/kercylan98/minotaur/utils/maths" +) + +func ExampleNavMesh_FindPath() { + fp := geometry.FloorPlan{ + "=================================", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "X X", + "=================================", + } + + var walkable []geometry.Shape[int] + walkable = append(walkable, + geometry.NewShape( + geometry.NewPoint(5, 5), + geometry.NewPoint(15, 5), + geometry.NewPoint(15, 15), + geometry.NewPoint(5, 15), + ), + geometry.NewShape( + geometry.NewPoint(15, 5), + geometry.NewPoint(25, 5), + geometry.NewPoint(25, 15), + geometry.NewPoint(15, 15), + ), + geometry.NewShape( + geometry.NewPoint(15, 15), + geometry.NewPoint(25, 15), + geometry.NewPoint(25, 25), + geometry.NewPoint(15, 25), + ), + ) + + for _, shape := range walkable { + for _, edge := range shape.Edges() { + sx, bx := maths.MinMax(edge.GetStart().GetX(), edge.GetEnd().GetX()) + sy, by := maths.MinMax(edge.GetStart().GetY(), edge.GetEnd().GetY()) + + for x := sx; x <= bx; x++ { + for y := sy; y <= by; y++ { + fp.Put(geometry.NewPoint[int](int(x), int(y)), '+') + } + } + } + } + + nm := navmesh.NewNavMesh(walkable, 0) + path := nm.FindPath( + geometry.NewPoint(6, 6), + geometry.NewPoint(18, 24), + ) + for _, point := range path { + fp.Put(geometry.NewPoint(point.GetX(), point.GetY()), 'G') + } + + fmt.Println(fp) + + // Output: + // ================================= + // X X + // X X + // X X + // X X + // X +++++++++++++++++++++ X + // X +G + + X + // X + + + X + // X + + + X + // X + + + X + // X + + + X + // X + + + X + // X + + + X + // X + + + X + // X + + + X + // X ++++++++++G++++++++++ X + // X + + X + // X + + X + // X + + X + // X + + X + // X + + X + // X + + X + // X + + X + // X + + X + // X + G + X + // X +++++++++++ X + // X X + // X X + // ================================= +} diff --git a/utils/geometry/position.go b/utils/geometry/position.go index f718976..0ea8bef 100644 --- a/utils/geometry/position.go +++ b/utils/geometry/position.go @@ -74,6 +74,14 @@ func NewPointCapWithData[V generic.SignedNumber, D any](x, y V, data D) PointCap } } +// NewPointCapWithPoint 通过设置数据的方式创建一个由已有坐标组成的点,这个点具有一个数据容量 +func NewPointCapWithPoint[V generic.SignedNumber, D any](point Point[V], data D) PointCap[V, D] { + return PointCap[V, D]{ + Point: point, + Data: data, + } +} + // PointCap 表示了一个由 x、y 坐标组成的点,这个点具有一个数据容量 type PointCap[V generic.SignedNumber, D any] struct { Point[V] diff --git a/utils/geometry/rectangle.go b/utils/geometry/rectangle.go index cef4ff1..49e9aa2 100644 --- a/utils/geometry/rectangle.go +++ b/utils/geometry/rectangle.go @@ -328,7 +328,10 @@ func GetRectangleFullPos[V generic.SignedNumber](width, height V) (result []V) { func CalcRectangleCentroid[V generic.SignedNumber](shape Shape[V]) Point[V] { x, y := V(0), V(0) length := V(shape.PointCount()) - + for _, point := range shape.Points() { + x += point.GetX() + y += point.GetY() + } x /= length y /= length return NewPoint(x, y) diff --git a/utils/geometry/shape.go b/utils/geometry/shape.go index 8c80107..d1912e0 100644 --- a/utils/geometry/shape.go +++ b/utils/geometry/shape.go @@ -71,7 +71,7 @@ func (slf Shape[V]) PointCount() int { func (slf Shape[V]) Contains(point Point[V]) bool { x, y := point.GetXY() inside := false - for i, j := -1, len(slf)-1; i < len(slf); j, i = i, i+1 { + for i, j := 0, len(slf)-1; i < len(slf); i, j = i+1, i { ix := slf[i].GetX() iy := slf[i].GetY() jx := slf[j].GetX()