EngoEngine / engo

Engo is an open-source 2D game engine written in Go.
https://engoengine.github.io
MIT License
1.74k stars 136 forks source link

Sprite "random-lines" when zoom-out/move #679

Closed inkeliz closed 5 years ago

inkeliz commented 5 years ago

I tested the "TrafficManager" demo, then I notice one bug when move (using the mouse at the edge of the screen) or zoom-out (using the mouse wheel). However, that bug ONLY happens against "sprites".

I use the following code to reproduce that issue:

grass := make([]*Grass, 0)

// Using image
image, _ := common.LoadedSprite("tiles/mapTile_077.png")
for x := float32(0); x < 512; x += 64 {
    for y := float32(0); y < 512; y += 64 {
        tile := &Grass{BasicEntity: ecs.NewBasic()}
        tile.SpaceComponent.Position = engo.Point{X: x, Y: y}
        tile.RenderComponent.Drawable = image
        tile.RenderComponent.SetZIndex(1)
        grass = append(grass, tile)
    }
}

// Using Sprite
sprite := Spritesheet.Cell(50)
for x := float32(0); x < 512; x += 64 {
    for y := float32(512); y < 1024; y += 64 {
        tile := &Grass{BasicEntity: ecs.NewBasic()}
        tile.SpaceComponent.Position = engo.Point{X: x, Y: y}
        tile.RenderComponent.Drawable = sprite
        tile.RenderComponent.SetZIndex(1)
        grass = append(grass, tile)
    }
}

That is the result:

image

When I use the image, there are no such "lines" between these images. Using "images": I'm able to move, zoom and zoom-out without issue. However, the "sprites" display some "lines" at random.


The full code:

package main

import (
    "bytes"
    "github.com/EngoEngine/ecs"
    "github.com/EngoEngine/engo"
    "github.com/EngoEngine/engo/common"
    "golang.org/x/image/font/gofont/gosmallcaps"
    "image/color"
    "path/filepath"
    "strings"
)

const (
    KeyboardScrollSpeed = 400
    EdgeScrollSpeed     = KeyboardScrollSpeed
    EdgeWidth           = 20
    ZoomSpeed           = -0.125
)

var Spritesheet *common.Spritesheet

type Grass struct {
    ecs.BasicEntity
    common.RenderComponent
    common.SpaceComponent
}

type myScene struct{}

// Type uniquely defines your game type
func (*myScene) Type() string { return "myGame" }

// Preload is called before loading any assets from the disk, to allow you to register / queue them
func (*myScene) Preload() {

    m, _ := filepath.Glob("assets/PNG/*")
    for _, v := range m {
        engo.Files.Load(strings.TrimPrefix(filepath.ToSlash(v), "assets/"))
    }

    engo.Files.LoadReaderData("go.ttf", bytes.NewReader(gosmallcaps.TTF))
    engo.Files.Load("Spritesheet/mapPack_spritesheet.png")

    Spritesheet = common.NewSpritesheetFromFile("Spritesheet/mapPack_spritesheet.png", 64, 64)
}

// Setup is called before the main loop starts. It allows you to add entities and systems to your Scene.
func (*myScene) Setup(u engo.Updater) {
    world, _ := u.(*ecs.World)
    common.SetBackground(color.White)

    world.AddSystem(&common.RenderSystem{})
    world.AddSystem(&common.MouseSystem{})
    world.AddSystem(common.NewKeyboardScroller(KeyboardScrollSpeed, engo.DefaultHorizontalAxis, engo.DefaultVerticalAxis))
    world.AddSystem(&common.EdgeScroller{ScrollSpeed: EdgeScrollSpeed, EdgeMargin: EdgeWidth})
    world.AddSystem(&common.MouseZoomer{ZoomSpeed: ZoomSpeed})

    grass := make([]*Grass, 0)

    // Using image
    image, _ := common.LoadedSprite("PNG/mapTile_077.png")
    for x := float32(0); x < 512; x += 64 {
        for y := float32(0); y < 512; y += 64 {
            tile := &Grass{BasicEntity: ecs.NewBasic()}
            tile.SpaceComponent.Position = engo.Point{X: x, Y: y}
            tile.RenderComponent.Drawable = image
            tile.RenderComponent.SetZIndex(1)
            grass = append(grass, tile)
        }
    }

    // Using Sprite
    sprite := Spritesheet.Cell(50)
    for x := float32(0); x < 512; x += 64 {
        for y := float32(512); y < 1024; y += 64 {
            tile := &Grass{BasicEntity: ecs.NewBasic()}
            tile.SpaceComponent.Position = engo.Point{X: x, Y: y}
            tile.RenderComponent.Drawable = sprite
            tile.RenderComponent.SetZIndex(1)
            grass = append(grass, tile)
        }
    }

    for _, system := range world.Systems() {
        switch sys := system.(type) {
        case *common.RenderSystem:
            for _, v := range grass {
                sys.Add(&v.BasicEntity, &v.RenderComponent, &v.SpaceComponent)
            }
        }
    }

    common.CameraBounds = engo.AABB{Min: engo.Point{X: 0, Y: 0}, Max: engo.Point{X: 1024, Y: 1024}}
}

func main() {
    opts := engo.RunOptions{
        Title:          "Test",
        Width:          800,
        Height:         800,
        StandardInputs: true,
    }

    engo.Run(opts, &myScene{})
}

The assets are from https://opengameart.org/content/map-pack-180-assets, just download the zip and extract "PNG" and "Spritesheet" to "assets".

Noofbiz commented 5 years ago

Thanks for this very detailed issue! I believe it has something to do with the way the viewport is calculated in the sprite sheet, probably something to do with float rounding. I'll take a look when I have some time

inkeliz commented 5 years ago

I don't have any experience with graphics or developing games. I guess that the float is the problem, but even choosing some points like {1, 1} the same "lines" appear, with sprites (not images).

I could fix that problem by changing the function:

func (s *basicShader) Draw(ren *RenderComponent, space *SpaceComponent) {
//...
}

The original file has one switch to trigger the engo.Gl.NEAREST:

if s.lastMinFilter != ren.minFilter {
    s.flush()
    var val int
    switch ren.minFilter {
    case FilterNearest:
        val = engo.Gl.NEAREST
    case FilterLinear:
        val = engo.Gl.LINEAR
    }
    engo.Gl.TexParameteri(engo.Gl.TEXTURE_2D, engo.Gl.TEXTURE_MIN_FILTER, val)
}

I just replace that switch to:

engo.Gl.TexParameteri(engo.Gl.TEXTURE_2D, engo.Gl.TEXTURE_MIN_FILTER, engo.Gl.NEAREST)

I'm searching how to trigger that if and switch correctly. I'm just pointing out that change fixes the issue. I'm not aware of any unintended effect of that change, more tests are needed.