Collision Box Updates
* The F4 key to draw collision boxes works reliably again: it draws the player's hitbox in world-space using the canvas.DrawStrokes() function, rather than in screen-space so it follows the player reliably. * The F4 key also draws hitboxes for ALL other actors in the level: buttons, enemies, doors, etc. * The level geometry collision function is updated to respect a doodad's declared Hitbox from their script, which may result in a smaller box than their raw Canvas size. The result is tighter collision between doodads, and Boy's sprite is rather narrow for its square Canvas so collision on rightward geometry is tighter for the player character. * Collision checks between actors also respect the actor's declared hitboxes now, allowing for Boy to get even closer to a locked door before being blocked.
This commit is contained in:
parent
fcb5d27290
commit
d14eaf7df2
|
@ -5,6 +5,7 @@ function main() {
|
||||||
|
|
||||||
var direction = "right";
|
var direction = "right";
|
||||||
|
|
||||||
|
Self.SetHitbox(0, 0, 32, 32)
|
||||||
Self.SetMobile(true);
|
Self.SetMobile(true);
|
||||||
Self.SetGravity(true);
|
Self.SetGravity(true);
|
||||||
Self.AddAnimation("walk-left", 100, ["red-wl1", "red-wl2", "red-wl3", "red-wl4"]);
|
Self.AddAnimation("walk-left", 100, ["red-wl1", "red-wl2", "red-wl3", "red-wl4"]);
|
||||||
|
|
|
@ -9,7 +9,7 @@ function main() {
|
||||||
|
|
||||||
Self.SetMobile(true);
|
Self.SetMobile(true);
|
||||||
Self.SetGravity(true);
|
Self.SetGravity(true);
|
||||||
Self.SetHitbox(0, 0, 16, 52);
|
Self.SetHitbox(0, 0, 32, 52);
|
||||||
Self.AddAnimation("walk-left", 200, ["stand-left", "walk-left-1", "walk-left-2", "walk-left-3", "walk-left-2", "walk-left-1"]);
|
Self.AddAnimation("walk-left", 200, ["stand-left", "walk-left-1", "walk-left-2", "walk-left-3", "walk-left-2", "walk-left-1"]);
|
||||||
Self.AddAnimation("walk-right", 200, ["stand-right", "walk-right-1", "walk-right-2", "walk-right-3", "walk-right-2", "walk-right-1"]);
|
Self.AddAnimation("walk-right", 200, ["stand-right", "walk-right-1", "walk-right-2", "walk-right-3", "walk-right-2", "walk-right-1"]);
|
||||||
|
|
||||||
|
|
3
go.mod
3
go.mod
|
@ -43,6 +43,3 @@ require (
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
replace git.kirsle.net/go/render => /home/kirsle/SketchyMaze/deps/render
|
|
||||||
replace git.kirsle.net/go/ui => /home/kirsle/SketchyMaze/deps/ui
|
|
||||||
replace git.kirsle.net/go/audio => /home/kirsle/SketchyMaze/deps/audio
|
|
||||||
|
|
|
@ -40,3 +40,13 @@ func GetBoundingRectHitbox(a Actor, hitbox render.Rect) render.Rect {
|
||||||
}
|
}
|
||||||
return rect
|
return rect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SizePlusHitbox adjusts an actor's canvas Size() to better fit the
|
||||||
|
// declared Hitbox by the actor's script.
|
||||||
|
func SizePlusHitbox(size render.Rect, hitbox render.Rect) render.Rect {
|
||||||
|
size.X += hitbox.X
|
||||||
|
size.Y += hitbox.Y
|
||||||
|
size.W -= size.W - hitbox.W
|
||||||
|
size.H -= size.H - hitbox.H
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ type Actor interface {
|
||||||
Size() render.Rect
|
Size() render.Rect
|
||||||
Grounded() bool
|
Grounded() bool
|
||||||
SetGrounded(bool)
|
SetGrounded(bool)
|
||||||
|
Hitbox() render.Rect
|
||||||
}
|
}
|
||||||
|
|
||||||
// BoxCollision holds the result of a collision BetweenBoxes.
|
// BoxCollision holds the result of a collision BetweenBoxes.
|
||||||
|
|
|
@ -57,6 +57,7 @@ func CollidesWithGrid(d Actor, grid *level.Chunker, target render.Point) (*Colli
|
||||||
var (
|
var (
|
||||||
P = d.Position()
|
P = d.Position()
|
||||||
S = d.Size()
|
S = d.Size()
|
||||||
|
hitbox = d.Hitbox()
|
||||||
|
|
||||||
result = &Collide{
|
result = &Collide{
|
||||||
MoveTo: P,
|
MoveTo: P,
|
||||||
|
@ -71,8 +72,11 @@ func CollidesWithGrid(d Actor, grid *level.Chunker, target render.Point) (*Colli
|
||||||
hitFloor bool
|
hitFloor bool
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Adjust the actor's bounding rect by its stated Hitbox from its script.
|
||||||
|
S = SizePlusHitbox(S, hitbox)
|
||||||
|
|
||||||
// Test all of the bounding boxes for a collision with level geometry.
|
// Test all of the bounding boxes for a collision with level geometry.
|
||||||
if ok := result.ScanBoundingBox(GetBoundingRect(d), grid); ok {
|
if ok := result.ScanBoundingBox(GetBoundingRectHitbox(d, hitbox), grid); ok {
|
||||||
// We've already collided! Try to wiggle free.
|
// We've already collided! Try to wiggle free.
|
||||||
if result.Bottom {
|
if result.Bottom {
|
||||||
if !d.Grounded() {
|
if !d.Grounded() {
|
||||||
|
|
38
pkg/fps.go
38
pkg/fps.go
|
@ -6,7 +6,8 @@ import (
|
||||||
|
|
||||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||||
"git.kirsle.net/apps/doodle/pkg/collision"
|
"git.kirsle.net/apps/doodle/pkg/collision"
|
||||||
"git.kirsle.net/apps/doodle/pkg/doodads"
|
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/uix"
|
||||||
"git.kirsle.net/go/render"
|
"git.kirsle.net/go/render"
|
||||||
"git.kirsle.net/go/ui"
|
"git.kirsle.net/go/ui"
|
||||||
)
|
)
|
||||||
|
@ -133,10 +134,9 @@ func (d *Doodle) DrawDebugOverlay() {
|
||||||
|
|
||||||
// DrawCollisionBox draws the collision box around a Doodad.
|
// DrawCollisionBox draws the collision box around a Doodad.
|
||||||
//
|
//
|
||||||
// TODO: move inside the Canvas. Currently it takes an actor's World Position
|
// The canvas will be the level Canvas, and the collision box is drawn in world
|
||||||
// and draws the box as if it were a relative (to the window) position, so the
|
// space using the canvas.DrawStrokes function.
|
||||||
// hitbox drifts off when the level scrolls away from 0,0
|
func (d *Doodle) DrawCollisionBox(canvas *uix.Canvas, actor *uix.Actor) {
|
||||||
func (d *Doodle) DrawCollisionBox(actor doodads.Actor) {
|
|
||||||
if !DebugCollision {
|
if !DebugCollision {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -144,12 +144,32 @@ func (d *Doodle) DrawCollisionBox(actor doodads.Actor) {
|
||||||
var (
|
var (
|
||||||
rect = collision.GetBoundingRect(actor)
|
rect = collision.GetBoundingRect(actor)
|
||||||
box = collision.GetCollisionBox(rect)
|
box = collision.GetCollisionBox(rect)
|
||||||
|
hitbox = actor.Hitbox()
|
||||||
)
|
)
|
||||||
|
|
||||||
d.Engine.DrawLine(render.DarkGreen, box.Top[0], box.Top[1])
|
// Adjust the actor's bounding rect by its stated Hitbox from its script.
|
||||||
d.Engine.DrawLine(render.DarkBlue, box.Bottom[0], box.Bottom[1])
|
rect = collision.SizePlusHitbox(rect, hitbox)
|
||||||
d.Engine.DrawLine(render.DarkYellow, box.Left[0], box.Left[1])
|
|
||||||
d.Engine.DrawLine(render.Red, box.Right[0], box.Right[1])
|
box = collision.GetCollisionBox(rect)
|
||||||
|
|
||||||
|
// The stroke data for drawing the collision box "inside" the level Canvas,
|
||||||
|
// so it scrolls and works in world units not screen units.
|
||||||
|
var strokes = []struct{
|
||||||
|
Color render.Color
|
||||||
|
PointA render.Point
|
||||||
|
PointB render.Point
|
||||||
|
}{
|
||||||
|
{render.DarkGreen, box.Top[0], box.Top[1]},
|
||||||
|
{render.DarkBlue, box.Bottom[0], box.Bottom[1]},
|
||||||
|
{render.DarkYellow, box.Left[0], box.Left[1]},
|
||||||
|
{render.Red, box.Right[0], box.Right[1]},
|
||||||
|
}
|
||||||
|
for _, cfg := range strokes {
|
||||||
|
stroke := drawtool.NewStroke(drawtool.Line, cfg.Color)
|
||||||
|
stroke.PointA = cfg.PointA
|
||||||
|
stroke.PointB = cfg.PointB
|
||||||
|
canvas.DrawStrokes(d.Engine, []*drawtool.Stroke{stroke})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrackFPS shows the current FPS once per second.
|
// TrackFPS shows the current FPS once per second.
|
||||||
|
|
|
@ -427,7 +427,11 @@ func (s *PlayScene) Draw(d *Doodle) error {
|
||||||
s.drawing.Present(d.Engine, s.drawing.Point())
|
s.drawing.Present(d.Engine, s.drawing.Point())
|
||||||
|
|
||||||
// Draw out bounding boxes.
|
// Draw out bounding boxes.
|
||||||
d.DrawCollisionBox(s.Player.Drawing)
|
if DebugCollision {
|
||||||
|
for _, actor := range s.drawing.Actors() {
|
||||||
|
d.DrawCollisionBox(s.drawing, actor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Draw the UI screen and any widgets that attached to it.
|
// Draw the UI screen and any widgets that attached to it.
|
||||||
s.screen.Compute(d.Engine)
|
s.screen.Compute(d.Engine)
|
||||||
|
|
|
@ -112,7 +112,7 @@ func (w *Canvas) loopActorCollision() error {
|
||||||
w.loopContainActorsInsideLevel(a)
|
w.loopContainActorsInsideLevel(a)
|
||||||
|
|
||||||
// Store this actor's bounding box after they've moved.
|
// Store this actor's bounding box after they've moved.
|
||||||
boxes[i] = collision.GetBoundingRect(a)
|
boxes[i] = collision.SizePlusHitbox(collision.GetBoundingRect(a), a.Hitbox())
|
||||||
}(i, a)
|
}(i, a)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ func (w *Canvas) loopActorCollision() error {
|
||||||
// Call the OnCollide handler for A informing them of B's intersection.
|
// Call the OnCollide handler for A informing them of B's intersection.
|
||||||
if w.scripting != nil {
|
if w.scripting != nil {
|
||||||
var (
|
var (
|
||||||
rect = collision.GetBoundingRect(b)
|
rect = collision.SizePlusHitbox(collision.GetBoundingRect(b), b.Hitbox())
|
||||||
lastGoodBox = render.Rect{
|
lastGoodBox = render.Rect{
|
||||||
X: originalPositions[b.ID()].X,
|
X: originalPositions[b.ID()].X,
|
||||||
Y: originalPositions[b.ID()].Y,
|
Y: originalPositions[b.ID()].Y,
|
||||||
|
@ -205,7 +205,7 @@ func (w *Canvas) loopActorCollision() error {
|
||||||
// Did A protest?
|
// Did A protest?
|
||||||
if err == scripting.ErrReturnFalse {
|
if err == scripting.ErrReturnFalse {
|
||||||
// Are they on top?
|
// Are they on top?
|
||||||
aHitbox := collision.GetBoundingRectHitbox(a, a.Hitbox())
|
aHitbox := collision.SizePlusHitbox(collision.GetBoundingRect(a), a.Hitbox())
|
||||||
if render.AbsInt(test.Y+test.H-aHitbox.Y) == 0 {
|
if render.AbsInt(test.Y+test.H-aHitbox.Y) == 0 {
|
||||||
onTop = true
|
onTop = true
|
||||||
onTopY = test.Y
|
onTopY = test.Y
|
||||||
|
|
|
@ -31,6 +31,11 @@ func (w *Canvas) InstallActors(actors level.ActorMap) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Actors returns the list of actors currently in the Canvas.
|
||||||
|
func (w *Canvas) Actors() []*Actor {
|
||||||
|
return w.actors
|
||||||
|
}
|
||||||
|
|
||||||
// ClearActors removes all the actors from the Canvas.
|
// ClearActors removes all the actors from the Canvas.
|
||||||
func (w *Canvas) ClearActors() {
|
func (w *Canvas) ClearActors() {
|
||||||
w.actors = []*Actor{}
|
w.actors = []*Actor{}
|
||||||
|
|
|
@ -126,7 +126,7 @@ func (w *Canvas) presentStrokes(e render.Engine) {
|
||||||
for _, stroke := range w.strokes {
|
for _, stroke := range w.strokes {
|
||||||
strokes = append(strokes, stroke)
|
strokes = append(strokes, stroke)
|
||||||
}
|
}
|
||||||
w.drawStrokes(e, strokes)
|
w.DrawStrokes(e, strokes)
|
||||||
|
|
||||||
// Dynamic actor links visible in the ActorTool and LinkTool.
|
// Dynamic actor links visible in the ActorTool and LinkTool.
|
||||||
if w.Tool == drawtool.ActorTool || w.Tool == drawtool.LinkTool {
|
if w.Tool == drawtool.ActorTool || w.Tool == drawtool.LinkTool {
|
||||||
|
@ -201,12 +201,12 @@ func (w *Canvas) presentActorLinks(e render.Engine) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w.drawStrokes(e, strokes)
|
w.DrawStrokes(e, strokes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawStrokes is the common base function behind presentStrokes and
|
// DrawStrokes is the common base function behind presentStrokes and
|
||||||
// presentActorLinks to actually draw the lines to the canvas.
|
// presentActorLinks to actually draw the lines to the canvas.
|
||||||
func (w *Canvas) drawStrokes(e render.Engine, strokes []*drawtool.Stroke) {
|
func (w *Canvas) DrawStrokes(e render.Engine, strokes []*drawtool.Stroke) {
|
||||||
var (
|
var (
|
||||||
P = ui.AbsolutePosition(w) // Canvas point in UI
|
P = ui.AbsolutePosition(w) // Canvas point in UI
|
||||||
VP = w.ViewportRelative() // Canvas scroll viewport
|
VP = w.ViewportRelative() // Canvas scroll viewport
|
||||||
|
|
Loading…
Reference in New Issue
Block a user