mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-20 07:27:19 -05:00
Add basic movement (#240)
* Add basic movement * Calculate step length based on tick time between updates, teleport to target if within one step. * Smooth camera, hero movement removed float to int conversions in Render and IsoToScreen functions * Render hero in the center of the screen (assuming 800x600 resolution) * Revert changing Render() parameters type * Render hero in the tile loop hero will naturally render in front of the walls of the current tile but behind the walls of the tile below * Smoother steps near target coordinates remove jitter from trying to get one step away from target on both axis
This commit is contained in:
parent
51d78bfcbf
commit
c6235411b7
@ -78,8 +78,8 @@ func (v *Game) Load() []func() {
|
||||
// TODO: This needs to be different depending on the act of the player
|
||||
v.mapEngine.GenerateMap(d2enum.RegionAct1Town, 1, 0)
|
||||
region := v.mapEngine.GetRegion(0)
|
||||
rx, ry := d2helper.IsoToScreen(int(region.Region.StartX), int(region.Region.StartY), 0, 0)
|
||||
v.mapEngine.CenterCameraOn(float64(rx), float64(ry))
|
||||
rx, ry := d2helper.IsoToScreen(region.Region.StartX, region.Region.StartY, 0, 0)
|
||||
v.mapEngine.CenterCameraOn(rx, ry)
|
||||
v.mapEngine.Hero = d2core.CreateHero(
|
||||
int32((region.Region.StartX*5)+3),
|
||||
int32((region.Region.StartY*5)+3),
|
||||
@ -102,22 +102,19 @@ func (v Game) Render(screen *ebiten.Image) {
|
||||
|
||||
func (v *Game) Update(tickTime float64) {
|
||||
// TODO: Pathfinding
|
||||
|
||||
if v.mapEngine.Hero.AnimatedEntity.LocationX != v.mapEngine.Hero.AnimatedEntity.TargetX ||
|
||||
v.mapEngine.Hero.AnimatedEntity.LocationY != v.mapEngine.Hero.AnimatedEntity.TargetY {
|
||||
v.mapEngine.Hero.AnimatedEntity.Step(tickTime)
|
||||
}
|
||||
|
||||
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
|
||||
mx, my := ebiten.CursorPosition()
|
||||
px, py := d2helper.ScreenToIso(float64(mx)-v.mapEngine.OffsetX, float64(my)-v.mapEngine.OffsetY)
|
||||
angle := 359 - d2helper.GetAngleBetween(
|
||||
v.mapEngine.Hero.AnimatedEntity.LocationX,
|
||||
v.mapEngine.Hero.AnimatedEntity.LocationY,
|
||||
px,
|
||||
py,
|
||||
)
|
||||
|
||||
newDirection := int((float64(angle) / 360.0) * 16.0)
|
||||
if newDirection != v.mapEngine.Hero.AnimatedEntity.GetDirection() {
|
||||
v.mapEngine.Hero.AnimatedEntity.SetMode(d2enum.AnimationModePlayerTownNeutral.String(), v.mapEngine.Hero.Equipment.RightHand.GetWeaponClass(), newDirection)
|
||||
}
|
||||
v.mapEngine.Hero.AnimatedEntity.SetTarget(px, py)
|
||||
}
|
||||
|
||||
rx, ry := d2helper.IsoToScreen(int(v.mapEngine.Hero.AnimatedEntity.LocationX), int(v.mapEngine.Hero.AnimatedEntity.LocationY), 0, 0)
|
||||
rx, ry := d2helper.IsoToScreen(v.mapEngine.Hero.AnimatedEntity.LocationX, v.mapEngine.Hero.AnimatedEntity.LocationY, 0, 0)
|
||||
v.mapEngine.CenterCameraOn(float64(rx), float64(ry))
|
||||
}
|
||||
|
@ -149,9 +149,9 @@ func (v *MapEngineTest) LoadRegionByIndex(n int, levelPreset, fileIndex int) {
|
||||
v.mapEngine = d2mapengine.CreateMapEngine(v.gameState, v.soundManager, v.fileProvider) // necessary for map name update
|
||||
v.mapEngine.GenerateMap(d2enum.RegionIdType(n), levelPreset, fileIndex)
|
||||
}
|
||||
isox, isoy := d2helper.IsoToScreen(v.mapEngine.GetRegion(0).Rect.Width/2,
|
||||
v.mapEngine.GetRegion(0).Rect.Height/2, 0, 0)
|
||||
v.mapEngine.CenterCameraOn(float64(isox), float64(isoy))
|
||||
isox, isoy := d2helper.IsoToScreen(float64(v.mapEngine.GetRegion(0).Rect.Width)/2,
|
||||
float64(v.mapEngine.GetRegion(0).Rect.Height)/2, 0, 0)
|
||||
v.mapEngine.CenterCameraOn(isox, isoy)
|
||||
}
|
||||
|
||||
func (v *MapEngineTest) Load() []func() {
|
||||
|
@ -56,6 +56,8 @@ type AnimatedEntity struct {
|
||||
object *d2datadict.ObjectLookupRecord
|
||||
layerCache []LayerCacheEntry
|
||||
drawOrder [][]d2enum.CompositeType
|
||||
TargetX float64
|
||||
TargetY float64
|
||||
}
|
||||
|
||||
// CreateAnimatedEntity creates an instance of AnimatedEntity
|
||||
@ -72,6 +74,8 @@ func CreateAnimatedEntity(x, y int32, object *d2datadict.ObjectLookupRecord, fil
|
||||
result.dccLayers = make(map[string]d2dcc.DCC)
|
||||
result.LocationX = float64(x) / 5
|
||||
result.LocationY = float64(y) / 5
|
||||
result.TargetX = result.LocationX
|
||||
result.TargetY = result.LocationY
|
||||
|
||||
result.subcellX = 1 + math.Mod(float64(x), 5)
|
||||
result.subcellY = 1 + math.Mod(float64(y), 5)
|
||||
@ -303,3 +307,60 @@ func (v *AnimatedEntity) updateFrameCache() {
|
||||
func (v AnimatedEntity) GetDirection() int {
|
||||
return v.direction
|
||||
}
|
||||
|
||||
func (v *AnimatedEntity) getStepLength(tickTime float64) (float64, float64) {
|
||||
speed := 2.5
|
||||
length := tickTime * speed
|
||||
|
||||
angle := 359 - d2helper.GetAngleBetween(
|
||||
v.LocationX,
|
||||
v.LocationY,
|
||||
v.TargetX,
|
||||
v.TargetY,
|
||||
)
|
||||
radians := (math.Pi / 180.0) * float64(angle)
|
||||
oneStepX := length * math.Cos(radians)
|
||||
oneStepY := length * math.Sin(radians)
|
||||
return oneStepX, oneStepY
|
||||
}
|
||||
|
||||
func (v *AnimatedEntity) Step(tickTime float64) {
|
||||
stepX, stepY := v.getStepLength(tickTime)
|
||||
|
||||
if d2helper.AlmostEqual(v.LocationX, v.TargetX, stepX) {
|
||||
v.LocationX = v.TargetX
|
||||
}
|
||||
if d2helper.AlmostEqual(v.LocationY, v.TargetY, stepY) {
|
||||
v.LocationY = v.TargetY
|
||||
}
|
||||
if v.LocationX != v.TargetX {
|
||||
v.LocationX += stepX
|
||||
}
|
||||
if v.LocationY != v.TargetY {
|
||||
v.LocationY += stepY
|
||||
}
|
||||
if v.LocationX == v.TargetX && v.LocationY == v.TargetY {
|
||||
if v.animationMode != d2enum.AnimationModePlayerTownNeutral.String() {
|
||||
v.SetMode(d2enum.AnimationModePlayerTownNeutral.String(), v.weaponClass, v.direction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetTarget sets target coordinates and changes animation based on proximity and direction
|
||||
func (v *AnimatedEntity) SetTarget(tx, ty float64) {
|
||||
angle := 359 - d2helper.GetAngleBetween(
|
||||
v.LocationX,
|
||||
v.LocationY,
|
||||
tx,
|
||||
ty,
|
||||
)
|
||||
newAnimationMode := d2enum.AnimationModePlayerTownNeutral.String()
|
||||
if tx != v.LocationX || ty != v.LocationY {
|
||||
v.TargetX, v.TargetY = tx, ty
|
||||
newAnimationMode = d2enum.AnimationModePlayerTownWalk.String()
|
||||
}
|
||||
newDirection := int((float64(angle) / 360.0) * 16.0)
|
||||
if newDirection != v.GetDirection() || newAnimationMode != v.animationMode {
|
||||
v.SetMode(newAnimationMode, v.weaponClass, newDirection)
|
||||
}
|
||||
}
|
||||
|
@ -101,9 +101,9 @@ func (v *Engine) GenerateAct1Overworld() {
|
||||
v.GenTilesCache(&v.regions[i])
|
||||
}
|
||||
|
||||
sx, sy := d2helper.IsoToScreen(int(region.StartX), int(region.StartY), 0, 0)
|
||||
v.OffsetX = float64(sx) - 400
|
||||
v.OffsetY = float64(sy) - 300
|
||||
sx, sy := d2helper.IsoToScreen(region.StartX, region.StartY, 0, 0)
|
||||
v.OffsetX = sx - 400
|
||||
v.OffsetY = sy - 300
|
||||
}
|
||||
|
||||
func (v *Engine) GetRegionAt(x, y int) *EngineRegion {
|
||||
@ -183,7 +183,7 @@ func (v *Engine) GenTilesCache(region *EngineRegion) {
|
||||
|
||||
func (v *Engine) RenderRegion(region EngineRegion, target *ebiten.Image) {
|
||||
for tileIdx := range region.Tiles {
|
||||
sx, sy := d2helper.IsoToScreen(region.Tiles[tileIdx].tileX+region.Rect.Left, region.Tiles[tileIdx].tileY+region.Rect.Top, int(v.OffsetX), int(v.OffsetY))
|
||||
sx, sy := d2helper.IsoToScreen(float64(region.Tiles[tileIdx].tileX+region.Rect.Left), float64(region.Tiles[tileIdx].tileY+region.Rect.Top), v.OffsetX, v.OffsetY)
|
||||
if sx > -160 && sy > -380 && sx <= 880 && sy <= 1240 {
|
||||
region.Region.UpdateAnimations()
|
||||
v.RenderPass1(region.Region, region.Tiles[tileIdx].offX, region.Tiles[tileIdx].offY, region.Tiles[tileIdx].tileX, region.Tiles[tileIdx].tileY, target)
|
||||
@ -194,13 +194,13 @@ func (v *Engine) RenderRegion(region EngineRegion, target *ebiten.Image) {
|
||||
}
|
||||
}
|
||||
for tileIdx := range region.Tiles {
|
||||
sx, sy := d2helper.IsoToScreen(region.Tiles[tileIdx].tileX+region.Rect.Left, region.Tiles[tileIdx].tileY+region.Rect.Top, int(v.OffsetX), int(v.OffsetY))
|
||||
sx, sy := d2helper.IsoToScreen(float64(region.Tiles[tileIdx].tileX+region.Rect.Left), float64(region.Tiles[tileIdx].tileY+region.Rect.Top), v.OffsetX, v.OffsetY)
|
||||
if sx > -160 && sy > -380 && sx <= 880 && sy <= 1240 {
|
||||
v.RenderPass2(region.Region, region.Tiles[tileIdx].offX, region.Tiles[tileIdx].offY, region.Tiles[tileIdx].tileX, region.Tiles[tileIdx].tileY, target)
|
||||
}
|
||||
}
|
||||
for tileIdx := range region.Tiles {
|
||||
sx, sy := d2helper.IsoToScreen(region.Tiles[tileIdx].tileX+region.Rect.Left, region.Tiles[tileIdx].tileY+region.Rect.Top, int(v.OffsetX), int(v.OffsetY))
|
||||
sx, sy := d2helper.IsoToScreen(float64(region.Tiles[tileIdx].tileX+region.Rect.Left), float64(region.Tiles[tileIdx].tileY+region.Rect.Top), v.OffsetX, v.OffsetY)
|
||||
if sx > -160 && sy > -380 && sx <= 880 && sy <= 1240 {
|
||||
v.RenderPass3(region.Region, region.Tiles[tileIdx].offX, region.Tiles[tileIdx].offY, region.Tiles[tileIdx].tileX, region.Tiles[tileIdx].tileY, target)
|
||||
}
|
||||
@ -254,7 +254,7 @@ func (v *Engine) RenderPass2(region *Region, offX, offY, x, y int, target *ebite
|
||||
}
|
||||
}
|
||||
if v.Hero != nil && int(v.Hero.AnimatedEntity.LocationX) == x && int(v.Hero.AnimatedEntity.LocationY) == y {
|
||||
v.Hero.Render(target, offX+int(v.OffsetX), offY+int(v.OffsetY))
|
||||
v.Hero.Render(target, 400, 300)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user