1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-11 18:20:42 +00: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:
j0y 2019-12-03 04:55:48 +07:00 committed by Tim Sarbin
parent 51d78bfcbf
commit c6235411b7
4 changed files with 81 additions and 23 deletions

View File

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

View File

@ -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() {

View File

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

View File

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