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:
Noah 2021-06-02 20:41:53 -07:00
parent fcb5d27290
commit d14eaf7df2
11 ha cambiato i file con 64 aggiunte e 22 eliminazioni

Vedi File

@ -5,6 +5,7 @@ function main() {
var direction = "right";
Self.SetHitbox(0, 0, 32, 32)
Self.SetMobile(true);
Self.SetGravity(true);
Self.AddAnimation("walk-left", 100, ["red-wl1", "red-wl2", "red-wl3", "red-wl4"]);

Vedi File

@ -9,7 +9,7 @@ function main() {
Self.SetMobile(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-right", 200, ["stand-right", "walk-right-1", "walk-right-2", "walk-right-3", "walk-right-2", "walk-right-1"]);

3
go.mod
Vedi File

@ -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

Vedi File

@ -40,3 +40,13 @@ func GetBoundingRectHitbox(a Actor, hitbox render.Rect) render.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
}

Vedi File

@ -14,6 +14,7 @@ type Actor interface {
Size() render.Rect
Grounded() bool
SetGrounded(bool)
Hitbox() render.Rect
}
// BoxCollision holds the result of a collision BetweenBoxes.

Vedi File

@ -57,6 +57,7 @@ func CollidesWithGrid(d Actor, grid *level.Chunker, target render.Point) (*Colli
var (
P = d.Position()
S = d.Size()
hitbox = d.Hitbox()
result = &Collide{
MoveTo: P,
@ -71,8 +72,11 @@ func CollidesWithGrid(d Actor, grid *level.Chunker, target render.Point) (*Colli
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.
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.
if result.Bottom {
if !d.Grounded() {

Vedi File

@ -6,7 +6,8 @@ import (
"git.kirsle.net/apps/doodle/pkg/balance"
"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/ui"
)
@ -133,10 +134,9 @@ func (d *Doodle) DrawDebugOverlay() {
// DrawCollisionBox draws the collision box around a Doodad.
//
// TODO: move inside the Canvas. Currently it takes an actor's World Position
// and draws the box as if it were a relative (to the window) position, so the
// hitbox drifts off when the level scrolls away from 0,0
func (d *Doodle) DrawCollisionBox(actor doodads.Actor) {
// The canvas will be the level Canvas, and the collision box is drawn in world
// space using the canvas.DrawStrokes function.
func (d *Doodle) DrawCollisionBox(canvas *uix.Canvas, actor *uix.Actor) {
if !DebugCollision {
return
}
@ -144,12 +144,32 @@ func (d *Doodle) DrawCollisionBox(actor doodads.Actor) {
var (
rect = collision.GetBoundingRect(actor)
box = collision.GetCollisionBox(rect)
hitbox = actor.Hitbox()
)
d.Engine.DrawLine(render.DarkGreen, box.Top[0], box.Top[1])
d.Engine.DrawLine(render.DarkBlue, box.Bottom[0], box.Bottom[1])
d.Engine.DrawLine(render.DarkYellow, box.Left[0], box.Left[1])
d.Engine.DrawLine(render.Red, box.Right[0], box.Right[1])
// Adjust the actor's bounding rect by its stated Hitbox from its script.
rect = collision.SizePlusHitbox(rect, hitbox)
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.

Vedi File

@ -427,7 +427,11 @@ func (s *PlayScene) Draw(d *Doodle) error {
s.drawing.Present(d.Engine, s.drawing.Point())
// 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.
s.screen.Compute(d.Engine)

Vedi File

@ -112,7 +112,7 @@ func (w *Canvas) loopActorCollision() error {
w.loopContainActorsInsideLevel(a)
// 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)
wg.Wait()
}
@ -133,7 +133,7 @@ func (w *Canvas) loopActorCollision() error {
// Call the OnCollide handler for A informing them of B's intersection.
if w.scripting != nil {
var (
rect = collision.GetBoundingRect(b)
rect = collision.SizePlusHitbox(collision.GetBoundingRect(b), b.Hitbox())
lastGoodBox = render.Rect{
X: originalPositions[b.ID()].X,
Y: originalPositions[b.ID()].Y,
@ -205,7 +205,7 @@ func (w *Canvas) loopActorCollision() error {
// Did A protest?
if err == scripting.ErrReturnFalse {
// 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 {
onTop = true
onTopY = test.Y

Vedi File

@ -31,6 +31,11 @@ func (w *Canvas) InstallActors(actors level.ActorMap) error {
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.
func (w *Canvas) ClearActors() {
w.actors = []*Actor{}

Vedi File

@ -126,7 +126,7 @@ func (w *Canvas) presentStrokes(e render.Engine) {
for _, stroke := range w.strokes {
strokes = append(strokes, stroke)
}
w.drawStrokes(e, strokes)
w.DrawStrokes(e, strokes)
// Dynamic actor links visible in the ActorTool and 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.
func (w *Canvas) drawStrokes(e render.Engine, strokes []*drawtool.Stroke) {
func (w *Canvas) DrawStrokes(e render.Engine, strokes []*drawtool.Stroke) {
var (
P = ui.AbsolutePosition(w) // Canvas point in UI
VP = w.ViewportRelative() // Canvas scroll viewport