1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2025-02-06 00:26:40 -05:00

entity debug rendering (#609)

* entity debug rendering, with command

needed to add getter method to MapEntity interface, so that's the reasoning for changing objects/npcs/player/etc

this currently brings allocs/s up to 10 (you can check by running `fps` command, se we need to look into d2debugutils and how it handles drawing text

* reverting velocity copy, broke the map entity tests. debug display wont show velocity currently.

* adding velocity debug
This commit is contained in:
lord 2020-07-21 05:51:09 -07:00 committed by GitHub
parent d0c6cd61dd
commit 362147848d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 112 additions and 30 deletions

View File

@ -1,10 +1,13 @@
package d2interface package d2interface
import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
// MapEntity is something that can be positioned on and rendered on the game map // MapEntity is something that can be positioned on and rendered on the game map
type MapEntity interface { type MapEntity interface {
Render(target Surface) Render(target Surface)
Advance(tickTime float64) Advance(tickTime float64)
GetPosition() (float64, float64) GetPosition() d2vector.Position
GetVelocity() d2vector.Vector
GetLayer() int GetLayer() int
GetPositionF() (float64, float64) GetPositionF() (float64, float64)
Name() string Name() string

View File

@ -26,6 +26,7 @@ func newMapEntity(x, y int) mapEntity {
return mapEntity{ return mapEntity{
Position: pos, Position: pos,
Target: pos, Target: pos,
velocity: d2vector.NewVector(0, 0),
} }
} }
@ -65,22 +66,23 @@ func (m *mapEntity) Step(tickTime float64) {
m.done = nil m.done = nil
} }
m.velocity.SetLength(0)
return return
} }
// Set velocity (speed and direction)
m.setVelocity(tickTime * m.Speed) m.setVelocity(tickTime * m.Speed)
// This loop handles the situation where the velocity exceeds the distance to the current target. Each repitition applies v := m.velocity.Clone() // Create a new vector
// the remaining velocity in the direction of the next path target.
for { for {
applyVelocity(&m.Position.Vector, &m.velocity, &m.Target.Vector) applyVelocity(&m.Position.Vector, &v, &m.Target.Vector) // Pass the new vector to the function which alters it
if m.atTarget() { if m.atTarget() {
m.nextPath() m.nextPath()
} }
if m.velocity.IsZero() { if v.IsZero() { // Check if the new vector is zero (keeping this as m.velocity.IsZero() would break the test (infinite loop)
break break
} }
} }

View File

@ -19,6 +19,14 @@ type Missile struct {
record *d2datadict.MissileRecord record *d2datadict.MissileRecord
} }
func (m *Missile) GetPosition() d2vector.Position {
return m.AnimatedEntity.Position
}
func (m *Missile) GetVelocity() d2vector.Vector {
return m.AnimatedEntity.velocity
}
// CreateMissile creates a new Missile and initializes it's animation. // CreateMissile creates a new Missile and initializes it's animation.
func CreateMissile(x, y int, record *d2datadict.MissileRecord) (*Missile, error) { func CreateMissile(x, y int, record *d2datadict.MissileRecord) (*Missile, error) {
animation, err := d2asset.LoadAnimation( animation, err := d2asset.LoadAnimation(

View File

@ -1,6 +1,7 @@
package d2mapentity package d2mapentity
import ( import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
"math/rand" "math/rand"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
@ -206,3 +207,13 @@ func (v *NPC) Selectable() bool {
func (v *NPC) Name() string { func (v *NPC) Name() string {
return v.name return v.name
} }
// GetPosition returns the NPC's position
func (v *NPC) GetPosition() d2vector.Position {
return v.mapEntity.Position
}
// GetVelocity returns the NPC's velocity vector
func (v *NPC) GetVelocity() d2vector.Vector {
return v.mapEntity.velocity
}

View File

@ -1,10 +1,10 @@
package d2mapentity package d2mapentity
import ( import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero"
@ -223,3 +223,13 @@ func (v *Player) Selectable() bool {
// Players are selectable when in town // Players are selectable when in town
return v.IsInTown() return v.IsInTown()
} }
// GetPosition returns the entity's position
func (p *Player) GetPosition() d2vector.Position {
return p.mapEntity.Position
}
// GetVelocity returns the entity's velocity vector
func (p *Player) GetVelocity() d2vector.Vector {
return p.mapEntity.velocity
}

View File

@ -17,14 +17,16 @@ import (
// MapRenderer manages the game viewport and Camera. It requests tile and entity data from MapEngine and renders it. // MapRenderer manages the game viewport and Camera. It requests tile and entity data from MapEngine and renders it.
type MapRenderer struct { type MapRenderer struct {
renderer d2interface.Renderer // Used for drawing operations renderer d2interface.Renderer // Used for drawing operations
mapEngine *d2mapengine.MapEngine // The map engine that is being rendered mapEngine *d2mapengine.MapEngine // The map engine that is being rendered
palette d2interface.Palette // The palette used for this map palette d2interface.Palette // The palette used for this map
viewport *Viewport // Used for rendering offsets viewport *Viewport // Used for rendering offsets
Camera Camera // Used to determine where on the map we are rendering Camera Camera // Used to determine where on the map we are rendering
debugVisLevel int // Debug visibility index (0=none, 1=tiles, 2=sub-tiles) mapDebugVisLevel int // Map debug visibility index (0=none, 1=tiles,
lastFrameTime float64 // The last time the map was rendered // 2=sub-tiles)
currentFrame int // Current render frame (for animations) entityDebugVisLevel int // Entity Debug visibility index (0=none, 1=vectors)
lastFrameTime float64 // The last time the map was rendered
currentFrame int // Current render frame (for animations)
} }
// CreateMapRenderer creates a new MapRenderer, sets the required fields and returns a pointer to it. // CreateMapRenderer creates a new MapRenderer, sets the required fields and returns a pointer to it.
@ -36,12 +38,16 @@ func CreateMapRenderer(renderer d2interface.Renderer, mapEngine *d2mapengine.Map
} }
result.Camera = Camera{} result.Camera = Camera{}
startPosition := d2vector.NewPosition(0,0) startPosition := d2vector.NewPosition(0, 0)
result.Camera.position = &startPosition result.Camera.position = &startPosition
result.viewport.SetCamera(&result.Camera) result.viewport.SetCamera(&result.Camera)
term.BindAction("mapdebugvis", "set map debug visualization level", func(level int) { term.BindAction("mapdebugvis", "set map debug visualization level", func(level int) {
result.debugVisLevel = level result.mapDebugVisLevel = level
})
term.BindAction("entitydebugvis", "set entity debug visualization level", func(level int) {
result.entityDebugVisLevel = level
}) })
if mapEngine.LevelType().ID != 0 { if mapEngine.LevelType().ID != 0 {
@ -87,12 +93,16 @@ func (mr *MapRenderer) Render(target d2interface.Surface) {
mr.renderPass1(target, startX, startY, endX, endY) mr.renderPass1(target, startX, startY, endX, endY)
mr.renderPass2(target, startX, startY, endX, endY) mr.renderPass2(target, startX, startY, endX, endY)
if mr.debugVisLevel > 0 { if mr.mapDebugVisLevel > 0 {
mr.renderDebug(mr.debugVisLevel, target, startX, startY, endX, endY) mr.renderMapDebug(mr.mapDebugVisLevel, target, startX, startY, endX, endY)
} }
mr.renderPass3(target, startX, startY, endX, endY) mr.renderPass3(target, startX, startY, endX, endY)
mr.renderPass4(target, startX, startY, endX, endY) mr.renderPass4(target, startX, startY, endX, endY)
if mr.entityDebugVisLevel > 0 {
mr.renderEntityDebug(target)
}
} }
// MoveCameraTo sets the position of the Camera to the given x and y coordinates. // MoveCameraTo sets the position of the Camera to the given x and y coordinates.
@ -146,7 +156,9 @@ func (mr *MapRenderer) renderPass2(target d2interface.Surface, startX, startY, e
// TODO: Do not loop over every entity every frame // TODO: Do not loop over every entity every frame
for _, mapEntity := range *mr.mapEngine.Entities() { for _, mapEntity := range *mr.mapEngine.Entities() {
entityX, entityY := mapEntity.GetPosition() pos := mapEntity.GetPosition()
vec := pos.World()
entityX, entityY := vec.X(), vec.Y()
if mapEntity.GetLayer() != 1 { if mapEntity.GetLayer() != 1 {
continue continue
@ -176,7 +188,9 @@ func (mr *MapRenderer) renderPass3(target d2interface.Surface, startX, startY, e
// TODO: Do not loop over every entity every frame // TODO: Do not loop over every entity every frame
for _, mapEntity := range *mr.mapEngine.Entities() { for _, mapEntity := range *mr.mapEngine.Entities() {
entityX, entityY := mapEntity.GetPosition() pos := mapEntity.GetPosition()
vec := pos.World()
entityX, entityY := vec.X(), vec.Y()
if mapEntity.GetLayer() == 1 { if mapEntity.GetLayer() == 1 {
continue continue
@ -299,16 +313,46 @@ func (mr *MapRenderer) renderShadow(tile d2ds1.FloorShadowRecord, target d2inter
target.Render(img) target.Render(img)
} }
func (mr *MapRenderer) renderDebug(debugVisLevel int, target d2interface.Surface, startX, startY, endX, endY int) { func (mr *MapRenderer) renderMapDebug(mapDebugVisLevel int, target d2interface.Surface, startX, startY, endX, endY int) {
for tileY := startY; tileY < endY; tileY++ { for tileY := startY; tileY < endY; tileY++ {
for tileX := startX; tileX < endX; tileX++ { for tileX := startX; tileX < endX; tileX++ {
mr.viewport.PushTranslationWorld(float64(tileX), float64(tileY)) mr.viewport.PushTranslationWorld(float64(tileX), float64(tileY))
mr.renderTileDebug(tileX, tileY, debugVisLevel, target) mr.renderTileDebug(tileX, tileY, mapDebugVisLevel, target)
mr.viewport.PopTranslation() mr.viewport.PopTranslation()
} }
} }
} }
func (mr *MapRenderer) renderEntityDebug(target d2interface.Surface) {
entities := *mr.mapEngine.Entities()
for idx := range entities {
e := entities[idx]
pos := e.GetPosition()
world := pos
x, y := world.X()/5, world.Y()/5
velocity := e.GetVelocity()
velocity = velocity.Clone()
// velocity.Scale(60) // arbitrary scale value, just to make it easy to see
vx, vy := mr.viewport.WorldToOrtho(velocity.X(), velocity.Y())
screenX, screenY := mr.viewport.WorldToScreen(x, y)
offX, offY := 40, -40
mr.viewport.PushTranslationWorld(x, y)
target.PushTranslation(screenX, screenY)
target.DrawLine(offX, offY, color.RGBA{255, 255, 255, 128})
target.PushTranslation(offX+10, offY-20)
target.PushTranslation(-10, -10)
target.DrawRect(200, 50, color.RGBA{0, 0, 0, 64})
target.Pop()
target.DrawTextf("World (%.2f, %.2f)\nVelocity (%.2f, %.2f)", x, y, vx, vy)
target.Pop()
target.DrawLine(int(vx), int(vy), color.RGBA{64, 255, 0, 255})
target.Pop()
mr.viewport.PopTranslation()
}
}
// WorldToScreen returns the screen (pixel) position for the given isometric world position as two ints. // WorldToScreen returns the screen (pixel) position for the given isometric world position as two ints.
func (mr *MapRenderer) WorldToScreen(x, y float64) (int, int) { func (mr *MapRenderer) WorldToScreen(x, y float64) (int, int) {
return mr.viewport.WorldToScreen(x, y) return mr.viewport.WorldToScreen(x, y)

View File

@ -122,12 +122,6 @@ func (ob *Object) GetLayer() int {
return ob.drawLayer return ob.drawLayer
} }
// GetPosition of the object
func (ob *Object) GetPosition() (x, y float64) {
w := ob.Position.Tile()
return w.X(), w.Y()
}
// GetPositionF of the object but differently // GetPositionF of the object but differently
func (ob *Object) GetPositionF() (x, y float64) { func (ob *Object) GetPositionF() (x, y float64) {
w := ob.Position.World() w := ob.Position.World()
@ -138,3 +132,13 @@ func (ob *Object) GetPositionF() (x, y float64) {
func (ob *Object) Name() string { func (ob *Object) Name() string {
return ob.name return ob.name
} }
// GetPosition returns the object's position
func (ob *Object) GetPosition() d2vector.Position {
return ob.Position
}
// GetVelocity returns the object's velocity vector
func (ob *Object) GetVelocity() d2vector.Vector {
return d2vector.NewVector(0, 0)
}