feat: implemented the basic collider system.

This commit is contained in:
Andrey Parhomenko 2024-01-16 04:11:42 +03:00
parent f910d4f99b
commit 31940fa899
6 changed files with 128 additions and 34 deletions

View file

@ -2,7 +2,7 @@ package main
import (
//"math/rand"
//"fmt"
"fmt"
"time"
)
@ -28,12 +28,24 @@ func NewPlayer() *Player {
ret.Visible = true
ret.Layer = PlayerL
ret.Collidable = true
ret.Resolvable = true
return ret
}
func (p *Player) Start(c *Context) {
}
func (p *Player) Draw(c *Context) []gg.EVertex {
return p.AnimatedSprite.Draw(c)
prect := &gg.DrawableRectangle{
Rectangle: p.Rectangle(),
}
prect.Color = gg.Rgba(1, 1, 1, 1)
return prect.Draw(c)
}
func (p *Player) Update(c *Context) {
if p.Spawned {
return
@ -58,7 +70,7 @@ func (p *Player) Update(c *Context) {
case gg.KeyArrowRight:
cam.Position.X += p.MoveSpeed * dt
case gg.KeyW:
p.Position.Y += p.MoveSpeed * dt
p.Position.Y -= p.MoveSpeed * dt*10
walking = true
p.Animate(Walk)
case gg.KeyA:
@ -140,5 +152,26 @@ func (p *Player) Update(c *Context) {
ec.Offset.Y * dt * p.ScaleSpeed * 40,
))
}}
p.Position.Y += 2
}
func (p *Player) Resolve(c *Context) {
col := c.Collisions[0]
fmt.Printf("frame[%d]: the col[0] len(%d): %T, %T\n", c.Frame(), len(c.Collisions), col.What, col.With)
switch col.Type{
case gg.PhysCollision :
LOOP:
for {
p.Position.Y -= 0.2
shit, collides := gg.Collide(p, col.With)
fmt.Println("shit:", shit)
fmt.Println("do:", collides)
if !collides {
break LOOP
}
}
}
}

View file

@ -17,6 +17,9 @@ func NewRect() *Rect {
}
ret.Layer = RectL
ret.Visible = true
ret.Collidable = true
ret.Width = 100
ret.Height = 200
return ret
}

View file

@ -12,14 +12,14 @@ package gg
type CollisionType int
const (
CollisionTypePhys CollisionType = iota
CollisionTypeTrigger
PhysCollision CollisionType = iota
TriggerCollision
)
// The structure contains collision information.
type Collision struct {
Type CollisionType
What, With Objecter
What, With Collider
// Points of Self contained in
Points Points
Crosses []LineCross
@ -28,19 +28,21 @@ type Collision struct {
// Implementing the interface lets the engine
// to determine if the object collides with anything.
// Mostly will use the Collide function with some
// inner structure field as first argument.
// The Collide method will be called on collisions.
type Collider interface {
// Checks whether the object is collidable
CollisionType() CollisionType
IsCollidable() bool
Verticer
Edger
PointContainer
}
type Collidability struct {
Type CollisionType
Collidable bool
}
func (c Collidability) CollisionType() CollisionType {
return c.Type
}
func (c Collidability) IsCollidable() bool {
return c.Collidable
}
@ -75,6 +77,9 @@ func Collide(c1, c2 Collider) (Collision, bool) {
crosses, doCross := es1.LineCrossWith(es2)
pts := c2.ContainedPoints(vertices)
return Collision{
Type: c2.CollisionType(),
What: c1,
With: c2,
Points: pts,
Crosses: crosses,
}, len(pts) != 0 || doCross
@ -83,9 +88,13 @@ func Collide(c1, c2 Collider) (Collision, bool) {
func GetCollisions(c Collider, cs []Collider) []Collision {
ret := []Collision{}
for _, ic := range cs {
if !ic.IsCollidable() || ic == c {
if ic == c {
continue
}
col, has := Collide(c, ic)
if has {
ret = append(ret, col)
}
}
return ret
}

View file

@ -4,7 +4,7 @@ import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"vultras.su/core/gods/maps"
"fmt"
//"fmt"
"time"
"slices"
"sync"
@ -164,7 +164,12 @@ func (e *Engine) Spawn(b Objecter) error {
obj.input = make(chan *Context)
go func() {
for c := range obj.input {
switch c.typ{
case updateContext :
b.Update(c)
case resolveContext :
b.Resolve(c)
}
e.wg.Done()
}
}()
@ -300,7 +305,7 @@ func (e *engine) Update() error {
e.dt = time.Since(e.lastTime)
e.runes = ebiten.AppendInputChars(e.runes[:0])
fmt.Println("runes:", e.runes)
//fmt.Println("runes:", e.runes)
// Buffering the context for faster.
// Providing the events to the objects.
// Maybe should think of the better way,
@ -325,16 +330,39 @@ func (e *engine) Update() error {
}
func (e *engine) Resolve() {
eng := (*Engine)(e)
type resHold struct{
Resolver Resolver
input chan *Context
}
colliders := []Collider{}
resolvers := []Resolver{}
resolvers := []resHold{}
for _, object := range e.Objects.store {
if object.IsCollidable() {
colliders = append(colliders, object)
}
if object.IsResolvable() {
resolvers = append(resolvers, object)
resolvers = append(resolvers, resHold{
object,
object.Input(),
})
}
}
for _, resolver := range resolvers {
cols := GetCollisions(resolver.Resolver, colliders)
if len(cols) > 0 {
c := &Context{
typ: resolveContext,
Collisions: cols,
Engine: eng,
}
e.wg.Add(1)
resolver.input <- c
}
}
e.wg.Wait()
}
var (

22
rect.go
View file

@ -17,12 +17,15 @@ type Rectangle struct {
// Return points of vertices of the rectangle.
func (r Rectangle) Vertices() Vertices {
m := r.Matrix()
t := r.Transform
wh := V(r.Width, r.Height)
t.Around = t.Around.Scale(wh)
m := t.Matrix()
p1 := V(0, 0).Apply(m)
p2 := V(1, 0).Apply(m)
p3 := V(1, 1).Apply(m)
p4 := V(0, 1).Apply(m)
return Points{p1, p2, p3, p4}
p2 := V(wh.X, 0).Apply(m)
p3 := V(wh.X, wh.Y).Apply(m)
p4 := V(0, wh.Y).Apply(m)
return Vertices{p1, p2, p3, p4}
}
func (r Rectangle) Edges() Edges {
@ -63,15 +66,6 @@ type DrawableRectangle struct {
// 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.
func (r *DrawableRectangle) IsVisible() bool {
return r.Visible
}
func (r *DrawableRectangle) Draw(c *Context) []EVertex {

View file

@ -11,8 +11,6 @@ type Sprite struct {
ShaderOptions
Floating bool
Visibility
Collidability
Resolvability
}
var (
@ -25,7 +23,7 @@ func (s *Sprite) Draw(c *Context) []EVertex {
return nil
}
t := s.Rectangle().Transform
t := s.RectangleToDraw().Transform
m := t.Matrix()
if !s.Floating {
m.Concat(c.Camera.RealMatrix())
@ -49,10 +47,10 @@ func (s *Sprite) Draw(c *Context) []EVertex {
return nil
}
// Return the rectangle that contains the sprite.
func (s *Sprite) Rectangle() Rectangle {
// Return the rectangle that contains the sprite to draw.
func (s *Sprite) RectangleToDraw() Rectangle {
if s.Images[0] == nil {
panic("trying to get rectangle for nil image pointer")
return Rectangle{}
}
w, h := s.Images[0].Size()
@ -65,9 +63,38 @@ func (s *Sprite) Rectangle() Rectangle {
}
}
// Return the rectangle that contains the sprite in the engine.
func (s *Sprite) Rectangle() Rectangle {
if s.Images[0] == nil {
return Rectangle{
Transform: s.Transform,
}
}
w, h := s.Images[0].Size()
t := s.Transform
return Rectangle{
Transform: t,
Width: Float(w),
Height: Float(h),
}
}
// Get triangles of the rectangle that contains the sprite
// and has the same widght and height.
func (s *Sprite) MakeTriangles() Triangles {
return s.Rectangle().MakeTriangles()
}
func (s *Sprite) Vertices() Vertices {
return s.Rectangle().Vertices()
}
func (s *Sprite) Edges() Edges {
return s.Rectangle().Edges()
}
func (s *Sprite) ContainedPoints(pts Points) Points {
return s.Rectangle().ContainedPoints(pts)
}