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) {
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)
} else {
t.Color = gg.Rgba(1, 0, 1, 1)

View file

@ -6,19 +6,25 @@ package gg
// if the the bigger collider that
// contains more complicated (accurate) structure
// do collide.
type ColliderSimplifier interface {
ColliderSimplify() Triangle
}
//type ColliderSimplifier interface {
//ColliderSimplify() Triangle
//}
// The structure represents all
// information on collisions.
type CollisionType int
const (
CollisionTypePhys CollisionType = iota
CollisionTypeTrigger
)
// The structure contains collision information.
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
// to determine if the object collides with anything.
@ -26,8 +32,60 @@ type PointContainer interface {
// inner structure field as first argument.
// The Collide method will be called on collisions.
type Collider interface {
Collides(Collider) *Collision
Collide(*Collision)
// Checks whether the object is collidable
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 (
startContext contextType = iota
updateContext
resolveContext
eventContext
drawContext
deleteContext
@ -12,6 +13,7 @@ const (
type Context struct {
typ contextType
Events []any
Collisions []Collision
*Engine
*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
}
func (e *engine) Update() error {
func (e *engine) UpdateEvents() []any {
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.keys = inpututil.
AppendPressedKeys(e.keys[:0])
@ -261,10 +256,10 @@ func (e *engine) Update() error {
}
x, y := ebiten.Wheel()
eng.wheel = V(x, y)
if !(eng.wheel.Eq(ZV)) {
e.wheel = V(x, y)
if !(e.wheel.Eq(ZV)) {
events = append(events, &WheelChange{
Offset: eng.wheel,
Offset: e.wheel,
})
}
@ -284,22 +279,34 @@ func (e *engine) Update() error {
}
realPos := eng.cursorPosition()
if !realPos.Eq(eng.cursorPos) {
if !realPos.Eq(e.cursorPos) {
absM := eng.Camera.AbsMatrix()
absPrevPos :=eng.cursorPos.Apply(absM)
absPrevPos := e.cursorPos.Apply(absM)
absPos := realPos.Apply(absM)
events = append(events, &MouseMove{
Real: realPos.Sub(eng.cursorPos),
Real: realPos.Sub(e.cursorPos),
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.
// Maybe should think of the better way,
// but for it is simple enough.
events := e.UpdateEvents()
c := &Context{
Engine: eng,
typ: updateContext,
@ -310,11 +317,26 @@ func (e *engine) Update() error {
object.Input() <- c
}
e.wg.Wait()
e.Resolve()
e.lastTime = time.Now()
e.frame++
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 (
fullPageIndexes = func() [MaxVertices]uint16 {
ret := [MaxVertices]uint16{}

120
line.go
View file

@ -1,88 +1,15 @@
package gg
import (
"math"
//"fmt"
)
// The type represents mathematical equation of line and line itself.
type Line struct {
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.
func (l Line) Line() Line {
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 {
buf := Line{0, p.Y}
pc, ok := l.crossesLine(buf)
@ -93,26 +20,6 @@ func (l Line) ContainsPoint(p Point) bool {
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) {
if LinersParallel(l1, l2) {
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
}
func Sqrt(v Float) Float {
return math.Sqrt(v)
}
func Asin(v Float) Float {
return math.Asin(v)
}

View file

@ -54,14 +54,19 @@ type Objecter interface {
Input() chan *Context
Starter
Updater
Collider
Resolver
Drawer
Deleter
}
// The type for embedding into engine-in types.
type Object struct {
Collidability
Resolvability
Layer
Visibility
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) Start(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) 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
}
func (p Polygon) ContainsPoint(pnt Point) bool {
return p.MakeTriangles().ContainsPoint(pnt)
}
// Polygon that can be drawn.
type DrawablePolygon struct {
Polygon
ShaderOptions
Visibility
Colority
func (p Polygon) ContainedPoints(pts Points) (Points) {
return p.MakeTriangles().ContainedPoints(pts)
}
func (p Polygon) MakeTriangles() Triangles {
@ -37,6 +28,14 @@ func (p Polygon) MakeTriangles() Triangles {
return ret
}
// Polygon that can be drawn.
type DrawablePolygon struct {
Polygon
ShaderOptions
Visibility
Colority
}
func (p *DrawablePolygon) Draw(c *Context) []EVertex {
return (&DrawableTriangles{
Colority: p.Colority,

49
rect.go
View file

@ -7,28 +7,16 @@ import (
//"image"
)
// The type describes rectangle geometry.
// The type describes rectangle geometry with
// way to move, rotate and scale it.
type Rectangle struct {
Object
Transform
}
// 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
Width, Height Float
}
// Return points of vertices of the rectangle.
func (r Rectangle) Vertices() Points {
func (r Rectangle) Vertices() Vertices {
m := r.Matrix()
p1 := V(0, 0).Apply(m)
p2 := V(1, 0).Apply(m)
@ -39,11 +27,11 @@ func (r Rectangle) Vertices() Points {
func (r Rectangle) Edges() Edges {
vs := r.Vertices()
return LineSegments{
LineSegment{vs[0], vs[1]},
LineSegment{vs[1], vs[2]},
LineSegment{vs[2], vs[3]},
LineSegment{vs[3], vs[0]},
return Edges{
Edge{vs[0], vs[1]},
Edge{vs[1], vs[2]},
Edge{vs[2], vs[3]},
Edge{vs[3], vs[0]},
}
}
@ -62,8 +50,23 @@ func (r Rectangle) MakeTriangles() Triangles {
}
// Check whether the rectangle contains the point.
func (r Rectangle) ContainsPoint(p Point) bool {
return r.MakeTriangles().ContainsPoint(p)
func (r Rectangle) ContainedPoints(pts Points) (Points) {
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.

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
Floating bool
Visibility
Collidability
Resolvability
}
var (

View file

@ -15,7 +15,6 @@ type Vertex struct {
}
type Triangle [3]Vector
type Triangles []Triangle
type DrawableTriangles struct {
Triangles
Colority
@ -53,9 +52,9 @@ func sqr(v Float) Float {
// Return squares of lengths of sides of the triangle.
func (t Triangle) SideLengthSquares() ([3]Float) {
l1 := LineSegment{t[0], t[1]}.LenSqr()
l2 := LineSegment{t[1], t[2]}.LenSqr()
l3 := LineSegment{t[2], t[0]}.LenSqr()
l1 := Edge{t[0], t[1]}.LenSqr()
l2 := Edge{t[1], t[2]}.LenSqr()
l3 := Edge{t[2], t[0]}.LenSqr()
return [3]Float{l1, l2, l3}
}
@ -72,19 +71,36 @@ func (t Triangle) ContainsPoint(p Point) bool {
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 {
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)
}
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 {
if t.ContainsPoint(p) {
return true
}
ps := t.ContainedPoints(pts)
ret = append(ret, ps...)
}
return false
return ret
}
func (r *DrawableTriangles) MakeEVertices(c *Context) []EVertex {

View file

@ -15,11 +15,7 @@ type Vector struct {
func (v Vector) XY() (Float, Float) {
return v.X, v.Y
}
type Point = Vector
//type Vertex = Vector
type Vectors []Vector
type Points []Point
func V(x, y Float) Vector {
return Vector{x, y}
@ -94,11 +90,11 @@ func (v Vector) Norm() Vector {
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))
for _, pt := range pts {
if c.ContainsPoint(pt) {
if !c.ContainedPoints(Points{pt}).Empty() {
ret = append(ret, pt)
}
}

5
vertice.go Normal file
View file

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