feat: starting implementing collisions.

This commit is contained in:
Andrey Parhomenko 2024-01-16 01:08:21 +03:00
parent 7dcf0eeca8
commit f910d4f99b
17 changed files with 349 additions and 195 deletions

View file

@ -33,7 +33,7 @@ func NewTri() *Tri {
func (t *Tri) Update(c *Context) { func (t *Tri) Update(c *Context) {
dt := c.Dt().Seconds() dt := c.Dt().Seconds()
if t.ContainsPoint(c.AbsCursorPosition()) { if !t.ContainedPoints(gg.Points{c.AbsCursorPosition()}).Empty() {
t.Color = gg.Rgba(0, 1, 0, 1) t.Color = gg.Rgba(0, 1, 0, 1)
} else { } else {
t.Color = gg.Rgba(1, 0, 1, 1) t.Color = gg.Rgba(1, 0, 1, 1)

View file

@ -6,19 +6,25 @@ package gg
// if the the bigger collider that // if the the bigger collider that
// contains more complicated (accurate) structure // contains more complicated (accurate) structure
// do collide. // do collide.
type ColliderSimplifier interface { //type ColliderSimplifier interface {
ColliderSimplify() Triangle //ColliderSimplify() Triangle
} //}
// The structure represents all type CollisionType int
// information on collisions. const (
CollisionTypePhys CollisionType = iota
CollisionTypeTrigger
)
// The structure contains collision information.
type Collision struct { type Collision struct {
Current, With any Type CollisionType
What, With Objecter
// Points of Self contained in
Points Points
Crosses []LineCross
} }
type PointContainer interface {
ContainsPoint(Point) bool
}
// Implementing the interface lets the engine // Implementing the interface lets the engine
// to determine if the object collides with anything. // to determine if the object collides with anything.
@ -26,8 +32,60 @@ type PointContainer interface {
// inner structure field as first argument. // inner structure field as first argument.
// The Collide method will be called on collisions. // The Collide method will be called on collisions.
type Collider interface { type Collider interface {
Collides(Collider) *Collision // Checks whether the object is collidable
Collide(*Collision) IsCollidable() bool
Verticer
Edger
PointContainer
}
type Collidability struct {
Collidable bool
}
func (c Collidability) IsCollidable() bool {
return c.Collidable
}
type PointContainer interface {
ContainedPoints(Points) Points
}
type Verticer interface {
Vertices() Vertices
}
type Edger interface {
Edges() Edges
} }
// Implementing the type provides way
// to resolve collisions and other problems.
// The Resolve() is called right after the Update() of Updater.
type Resolver interface {
IsResolvable() bool
Resolve(c *Context)
Collider
}
type Resolvability struct {
Resolvable bool
}
func (r Resolvability) IsResolvable() bool {
return r.Resolvable
}
func Collide(c1, c2 Collider) (Collision, bool) {
vertices := c1.Vertices()
es1, es2 := c1.Edges(), c2.Edges()
crosses, doCross := es1.LineCrossWith(es2)
pts := c2.ContainedPoints(vertices)
return Collision{
Points: pts,
Crosses: crosses,
}, len(pts) != 0 || doCross
}
func GetCollisions(c Collider, cs []Collider) []Collision {
ret := []Collision{}
for _, ic := range cs {
if !ic.IsCollidable() || ic == c {
continue
}
}
return ret
}

View file

@ -4,6 +4,7 @@ type contextType int
const ( const (
startContext contextType = iota startContext contextType = iota
updateContext updateContext
resolveContext
eventContext eventContext
drawContext drawContext
deleteContext deleteContext
@ -12,6 +13,7 @@ const (
type Context struct { type Context struct {
typ contextType typ contextType
Events []any Events []any
Collisions []Collision
*Engine *Engine
*Image *Image
} }

78
edge.go Normal file
View file

@ -0,0 +1,78 @@
package gg
type Edges []Edge
// Get crosses of edges.
func (what Edges) LineCrossWith(with Edges) ([]LineCross, bool) {
ret := []LineCross{}
for i := range what {
for j := range with {
cross, doCross := LinersCross(what[i], with[j])
if doCross {
ret = append(ret, cross)
}
}
}
if len(ret) == 0 {
return nil, false
}
return ret, true
}
// The type represents a line segment. The name is for short.
type Edge [2]Vector
// Returns corresponding to the segment Line.
func (l Edge) Line() Line {
p0 := l[0]
p1 := l[1]
k := (p0.Y - p1.Y) / (p0.X - p1.X)
c := p0.Y - p0.X*k
return Line{k, c}
}
func (l Edge) ContainedPoints(pts Points) (Points) {
ret := Points{}
for i := range pts {
if l.ContainsPoint(pts[i]) {
ret = append(ret, pts[i])
}
}
return ret
}
// Check if the edge contains the specified point.
func (l Edge) ContainsPoint(p Point) bool {
line := l.Line()
if !line.ContainsPoint(p) {
return false
}
xMax := Max(l[0].X, l[1].X)
xMin := Min(l[0].X, l[1].X)
yMax := Max(l[0].Y, l[1].Y)
yMin := Min(l[0].Y, l[1].Y)
if !(xMin < p.X && p.X < xMax) ||
!(yMin < p.Y && p.Y < yMax) {
return false
}
return true
}
// Get square of length of line segment (for performance sometimes).
func (ls Edge) LenSqr() Float {
return Sqr(ls[0].X - ls[1].X) +
Sqr(ls[0].Y - ls[1].Y)
}
// Get length of the line segment.
func (ls Edge) Len() Float {
return Sqrt(ls.LenSqr())
}

View file

@ -232,14 +232,9 @@ func (e *Engine) Runes() []rune {
return e.runes return e.runes
} }
func (e *engine) Update() error { func (e *engine) UpdateEvents() []any {
eng := (*Engine)(e) eng := (*Engine)(e)
e.dt = time.Since(e.lastTime)
e.runes = ebiten.AppendInputChars(e.runes[:0])
fmt.Println("runes:", e.runes)
// Buffering the context for faster.
e.prevKeys = e.keys e.prevKeys = e.keys
e.keys = inpututil. e.keys = inpututil.
AppendPressedKeys(e.keys[:0]) AppendPressedKeys(e.keys[:0])
@ -261,10 +256,10 @@ func (e *engine) Update() error {
} }
x, y := ebiten.Wheel() x, y := ebiten.Wheel()
eng.wheel = V(x, y) e.wheel = V(x, y)
if !(eng.wheel.Eq(ZV)) { if !(e.wheel.Eq(ZV)) {
events = append(events, &WheelChange{ events = append(events, &WheelChange{
Offset: eng.wheel, Offset: e.wheel,
}) })
} }
@ -284,22 +279,34 @@ func (e *engine) Update() error {
} }
realPos := eng.cursorPosition() realPos := eng.cursorPosition()
if !realPos.Eq(eng.cursorPos) { if !realPos.Eq(e.cursorPos) {
absM := eng.Camera.AbsMatrix() absM := eng.Camera.AbsMatrix()
absPrevPos :=eng.cursorPos.Apply(absM) absPrevPos := e.cursorPos.Apply(absM)
absPos := realPos.Apply(absM) absPos := realPos.Apply(absM)
events = append(events, &MouseMove{ events = append(events, &MouseMove{
Real: realPos.Sub(eng.cursorPos), Real: realPos.Sub(e.cursorPos),
Abs: absPos.Sub(absPrevPos), Abs: absPos.Sub(absPrevPos),
}) })
eng.cursorPos = realPos e.cursorPos = realPos
} }
return events
}
func (e *engine) Update() error {
eng := (*Engine)(e)
e.dt = time.Since(e.lastTime)
e.runes = ebiten.AppendInputChars(e.runes[:0])
fmt.Println("runes:", e.runes)
// Buffering the context for faster.
// Providing the events to the objects. // Providing the events to the objects.
// Maybe should think of the better way, // Maybe should think of the better way,
// but for it is simple enough. // but for it is simple enough.
events := e.UpdateEvents()
c := &Context{ c := &Context{
Engine: eng, Engine: eng,
typ: updateContext, typ: updateContext,
@ -310,11 +317,26 @@ func (e *engine) Update() error {
object.Input() <- c object.Input() <- c
} }
e.wg.Wait() e.wg.Wait()
e.Resolve()
e.lastTime = time.Now() e.lastTime = time.Now()
e.frame++ e.frame++
return nil return nil
} }
func (e *engine) Resolve() {
colliders := []Collider{}
resolvers := []Resolver{}
for _, object := range e.Objects.store {
if object.IsCollidable() {
colliders = append(colliders, object)
}
if object.IsResolvable() {
resolvers = append(resolvers, object)
}
}
}
var ( var (
fullPageIndexes = func() [MaxVertices]uint16 { fullPageIndexes = func() [MaxVertices]uint16 {
ret := [MaxVertices]uint16{} ret := [MaxVertices]uint16{}

120
line.go
View file

@ -1,88 +1,15 @@
package gg package gg
import (
"math"
//"fmt"
)
// The type represents mathematical equation of line and line itself. // The type represents mathematical equation of line and line itself.
type Line struct { type Line struct {
K, C Float K, C Float
} }
type Liner interface {
Line() Line
}
type LinerPointContainer interface {
Liner
PointContainer
}
// The type represents a line segment.
type LineSegment [2]Point
// The type represents multiple line segments.
type LineSegments []LineSegment
type Edge = LineSegment
type Edges = LineSegments
// Check if two LinerPointContainers do cross and return the
// crossing point.
func LinersCross(lp1, lp2 LinerPointContainer) (Point, bool) {
l1 := lp1.Line()
l2 := lp2.Line()
p, crosses := l1.crossesLine(l2)
if !crosses ||
!lp1.ContainsPoint(p) ||
!lp2.ContainsPoint(p) {
return Point{}, false
}
return p, true
}
// Check whether the liner is parallel to the other liner.
func LinersParallel(first, second Liner) bool {
l1 := first.Line()
l2 := second.Line()
return l1.K == l2.K
}
// Returns angle between liners in radians.
// The value fits the -Pi < Value < Pi condition.
func LinersAngle(first, second Liner) Float {
l1 := first.Line()
l2 := second.Line()
if l1.K == l2.K {
return 0
}
return math.Atan(l1.K/l2.K)
}
// Returns the line itself. Made to implement the Liner interface. // Returns the line itself. Made to implement the Liner interface.
func (l Line) Line() Line { func (l Line) Line() Line {
return l return l
} }
// Returns corresponding to the segment line line.
func (l LineSegment) Line() Line {
p0 := l[0]
p1 := l[1]
k := (p0.Y - p1.Y) / (p0.X - p1.X)
c := p0.Y - p0.X*k
return Line{k, c}
}
func (l Line) ContainsPoint(p Point) bool { func (l Line) ContainsPoint(p Point) bool {
buf := Line{0, p.Y} buf := Line{0, p.Y}
pc, ok := l.crossesLine(buf) pc, ok := l.crossesLine(buf)
@ -93,26 +20,6 @@ func (l Line) ContainsPoint(p Point) bool {
return pc == p return pc == p
} }
func (l LineSegment) ContainsPoint(p Point) bool {
line := l.Line()
if !line.ContainsPoint(p) {
return false
}
xMax := Max(l[0].X, l[1].X)
xMin := Min(l[0].X, l[1].X)
yMax := Max(l[0].Y, l[1].Y)
yMin := Min(l[0].Y, l[1].Y)
if !(xMin < p.X && p.X < xMax) ||
!(yMin < p.Y && p.Y < yMax) {
return false
}
return true
}
func (l1 Line) crossesLine(l2 Line) (Point, bool) { func (l1 Line) crossesLine(l2 Line) (Point, bool) {
if LinersParallel(l1, l2) { if LinersParallel(l1, l2) {
return Point{}, false return Point{}, false
@ -124,30 +31,3 @@ func (l1 Line) crossesLine(l2 Line) (Point, bool) {
} }
// Get square of length of line segment.
func (ls LineSegment) LenSqr() Float {
return Sqr(ls[0].X - ls[1].X) +
Sqr(ls[0].Y - ls[1].Y)
}
// Get length of the line segment.
func (ls LineSegment) Len() Float {
return math.Sqrt(ls.LenSqr())
}
func (what LineSegments) Cross(with LineSegments) ([][2]int, Points) {
indexes := [][2]int{}
points := Points{}
for i := range what {
for j := range with {
p, cross := LinersCross(what[i], with[j])
if cross {
points = append(points, p)
indexes = append(indexes, [2]int{i, j})
}
}
}
return indexes, points
}

56
liner.go Normal file
View file

@ -0,0 +1,56 @@
package gg
type Liner interface {
Line() Line
}
type LinerPointContainer interface {
Liner
PointContainer
}
// The type represents the cross of 2 Liners.
type LineCross struct {
Pair [2]Line
Point Point
}
// Check if two LinerPointContainers do cross and return the
// crossing point.
func LinersCross(lp1, lp2 LinerPointContainer) (LineCross, bool) {
l1 := lp1.Line()
l2 := lp2.Line()
crossPt, doCross := l1.crossesLine(l2)
if !doCross ||
len(lp1.ContainedPoints([]Point{crossPt}))==0 ||
len(lp2.ContainedPoints([]Point{crossPt}))==0 {
return LineCross{}, false
}
return LineCross{
Pair: [2]Line{l1, l2},
Point: crossPt,
}, true
}
// Check whether the liner is parallel to the other liner.
func LinersParallel(first, second Liner) bool {
l1 := first.Line()
l2 := second.Line()
return l1.K == l2.K
}
// Returns angle between liners in radians.
// The value fits the -Pi < Value < Pi condition.
func LinersAngle(first, second Liner) Float {
l1 := first.Line()
l2 := second.Line()
if l1.K == l2.K {
return 0
}
return Atan(l1.K/l2.K)
}

View file

@ -24,6 +24,10 @@ func Sqr(v Float) Float {
return v * v return v * v
} }
func Sqrt(v Float) Float {
return math.Sqrt(v)
}
func Asin(v Float) Float { func Asin(v Float) Float {
return math.Asin(v) return math.Asin(v)
} }

View file

@ -54,14 +54,19 @@ type Objecter interface {
Input() chan *Context Input() chan *Context
Starter Starter
Updater Updater
Collider
Resolver
Drawer Drawer
Deleter Deleter
} }
// The type for embedding into engine-in types. // The type for embedding into engine-in types.
type Object struct { type Object struct {
Collidability
Resolvability
Layer Layer
Visibility Visibility
input chan *Context input chan *Context
} }
@ -69,7 +74,15 @@ func (o *Object) GetObject() *Object { return o }
func (o *Object) Input() chan *Context { return o.input } func (o *Object) Input() chan *Context { return o.input }
func (o *Object) Start(c *Context) {} func (o *Object) Start(c *Context) {}
func (o *Object) Update(c *Context) {} func (o *Object) Update(c *Context) {}
func (o *Object) ContainedPoints(Points) Points {return Points{}}
func (o *Object) Vertices() Vertices {return Vertices{}}
func (o *Object) Edges() Edges {return Edges{}}
func (o *Object) Resolve(c *Context) {}
func (o *Object) Draw(c *Context) []EVertex { return nil} func (o *Object) Draw(c *Context) []EVertex { return nil}
func (o *Object) Delete(c *Context) {} func (o *Object) Delete(c *Context) {}

8
point.go Normal file
View file

@ -0,0 +1,8 @@
package gg
type Point = Vector
type Points []Point
func (pts Points) Empty() bool {
return len(pts) == 0
}

View file

@ -10,17 +10,8 @@ type Polygon struct {
Triangles Triangles
} }
func (p Polygon) ContainsPoint(pnt Point) bool { func (p Polygon) ContainedPoints(pts Points) (Points) {
return p.MakeTriangles().ContainsPoint(pnt) return p.MakeTriangles().ContainedPoints(pts)
}
// Polygon that can be drawn.
type DrawablePolygon struct {
Polygon
ShaderOptions
Visibility
Colority
} }
func (p Polygon) MakeTriangles() Triangles { func (p Polygon) MakeTriangles() Triangles {
@ -37,6 +28,14 @@ func (p Polygon) MakeTriangles() Triangles {
return ret return ret
} }
// Polygon that can be drawn.
type DrawablePolygon struct {
Polygon
ShaderOptions
Visibility
Colority
}
func (p *DrawablePolygon) Draw(c *Context) []EVertex { func (p *DrawablePolygon) Draw(c *Context) []EVertex {
return (&DrawableTriangles{ return (&DrawableTriangles{
Colority: p.Colority, Colority: p.Colority,

49
rect.go
View file

@ -7,28 +7,16 @@ import (
//"image" //"image"
) )
// The type describes rectangle geometry. // The type describes rectangle geometry with
// way to move, rotate and scale it.
type Rectangle struct { type Rectangle struct {
Object Object
Transform Transform
} Width, Height Float
// The type describes rectangle that can be drawn.
type DrawableRectangle struct {
Rectangle
ShaderOptions
// Solid color of the rectangle.
// Will be ignored if the Shader
// field is not nil.
Colority
// Should be draw or not.
Visibility
} }
// Return points of vertices of the rectangle. // Return points of vertices of the rectangle.
func (r Rectangle) Vertices() Points { func (r Rectangle) Vertices() Vertices {
m := r.Matrix() m := r.Matrix()
p1 := V(0, 0).Apply(m) p1 := V(0, 0).Apply(m)
p2 := V(1, 0).Apply(m) p2 := V(1, 0).Apply(m)
@ -39,11 +27,11 @@ func (r Rectangle) Vertices() Points {
func (r Rectangle) Edges() Edges { func (r Rectangle) Edges() Edges {
vs := r.Vertices() vs := r.Vertices()
return LineSegments{ return Edges{
LineSegment{vs[0], vs[1]}, Edge{vs[0], vs[1]},
LineSegment{vs[1], vs[2]}, Edge{vs[1], vs[2]},
LineSegment{vs[2], vs[3]}, Edge{vs[2], vs[3]},
LineSegment{vs[3], vs[0]}, Edge{vs[3], vs[0]},
} }
} }
@ -62,8 +50,23 @@ func (r Rectangle) MakeTriangles() Triangles {
} }
// Check whether the rectangle contains the point. // Check whether the rectangle contains the point.
func (r Rectangle) ContainsPoint(p Point) bool { func (r Rectangle) ContainedPoints(pts Points) (Points) {
return r.MakeTriangles().ContainsPoint(p) return r.MakeTriangles().ContainedPoints(pts)
}
// The type describes rectangle that can be drawn.
type DrawableRectangle struct {
Rectangle
ShaderOptions
// Solid color of the rectangle.
// Will be ignored if the Shader
// field is not nil.
Colority
// Should be draw or not.
Visibility
Collidability
} }
// Check whether the drawable rectangle should be drawn. // Check whether the drawable rectangle should be drawn.

12
size.go Normal file
View file

@ -0,0 +1,12 @@
package gg
// The type describes a simple
// rectangle without transformations.
type Size struct {
// The upper left corner position point.
Position Point
// Absolute width and height.
Width, Height Float
}
//type (s Size) ContainsPoint

View file

@ -11,6 +11,8 @@ type Sprite struct {
ShaderOptions ShaderOptions
Floating bool Floating bool
Visibility Visibility
Collidability
Resolvability
} }
var ( var (

View file

@ -15,7 +15,6 @@ type Vertex struct {
} }
type Triangle [3]Vector type Triangle [3]Vector
type Triangles []Triangle
type DrawableTriangles struct { type DrawableTriangles struct {
Triangles Triangles
Colority Colority
@ -53,9 +52,9 @@ func sqr(v Float) Float {
// Return squares of lengths of sides of the triangle. // Return squares of lengths of sides of the triangle.
func (t Triangle) SideLengthSquares() ([3]Float) { func (t Triangle) SideLengthSquares() ([3]Float) {
l1 := LineSegment{t[0], t[1]}.LenSqr() l1 := Edge{t[0], t[1]}.LenSqr()
l2 := LineSegment{t[1], t[2]}.LenSqr() l2 := Edge{t[1], t[2]}.LenSqr()
l3 := LineSegment{t[2], t[0]}.LenSqr() l3 := Edge{t[2], t[0]}.LenSqr()
return [3]Float{l1, l2, l3} return [3]Float{l1, l2, l3}
} }
@ -72,19 +71,36 @@ func (t Triangle) ContainsPoint(p Point) bool {
return !(neg && pos) return !(neg && pos)
} }
func (t Triangle) ContainedPoints(pts Points) []Point {
ret := []Point{}
for i := range pts {
if t.ContainsPoint(pts[i]) {
ret = append(ret, pts[i])
}
}
return ret
}
func (t Triangle) Sgn() Float { func (t Triangle) Sgn() Float {
return (t[0].X - t[2].X) * (t[1].Y - t[2].Y) - return (t[0].X - t[2].X) * (t[1].Y - t[2].Y) -
(t[1].X - t[2].X) * (t[0].Y - t[2].Y) (t[1].X - t[2].X) * (t[0].Y - t[2].Y)
} }
func (ts Triangles) ContainsPoint(p Point) bool { func (t Triangle) Vertices() Vertices {
return Vertices{
t[0], t[1], t[2],
}
}
type Triangles []Triangle
func (ts Triangles) ContainedPoints(pts Points) (Points) {
ret := []Point{}
for _, t := range ts { for _, t := range ts {
if t.ContainsPoint(p) { ps := t.ContainedPoints(pts)
return true ret = append(ret, ps...)
}
} }
return false return ret
} }
func (r *DrawableTriangles) MakeEVertices(c *Context) []EVertex { func (r *DrawableTriangles) MakeEVertices(c *Context) []EVertex {

View file

@ -15,11 +15,7 @@ type Vector struct {
func (v Vector) XY() (Float, Float) { func (v Vector) XY() (Float, Float) {
return v.X, v.Y return v.X, v.Y
} }
type Point = Vector
//type Vertex = Vector
type Vectors []Vector type Vectors []Vector
type Points []Point
func V(x, y Float) Vector { func V(x, y Float) Vector {
return Vector{x, y} return Vector{x, y}
@ -94,11 +90,11 @@ func (v Vector) Norm() Vector {
return V(v.X / l, v.Y / l) return V(v.X / l, v.Y / l)
} }
func (pts Points) Contained(c PointContainer) Points { func (pts Points) ContainedIn(c PointContainer) Points {
ret := make([]Point, 0, len(pts)) ret := make([]Point, 0, len(pts))
for _, pt := range pts { for _, pt := range pts {
if c.ContainsPoint(pt) { if !c.ContainedPoints(Points{pt}).Empty() {
ret = append(ret, pt) ret = append(ret, pt)
} }
} }

5
vertice.go Normal file
View file

@ -0,0 +1,5 @@
package gg
type Vertice = Point
type Vertices = []Vertice