mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 10:48:53 +01:00
ebiten: add FinalScreenDrawer
FinalScreenDrawer is an interface for a custom screen rendering. If a game implements FinalScreenDrawer and is passed to RunGame, its DrawFinalScreen is called after Draw. Also this adds `-crt` option to examples/flappy. Closes #2046
This commit is contained in:
parent
0803342d01
commit
30cc36b1ba
42
examples/flappy/crt.go
Normal file
42
examples/flappy/crt.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2022 The Ebitengine Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build ignore
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
// Reference: a public domain CRT effect
|
||||||
|
// https://github.com/libretro/glsl-shaders/blob/master/crt/shaders/crt-lottes.glsl
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func Warp(pos vec2) vec2 {
|
||||||
|
const (
|
||||||
|
warpX = 0.031
|
||||||
|
warpY = 0.041
|
||||||
|
)
|
||||||
|
pos = pos*2 - 1
|
||||||
|
pos *= vec2(1+(pos.y*pos.y)*warpX, 1+(pos.x*pos.x)*warpY)
|
||||||
|
return pos/2 + 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||||
|
// Adjust the texture position to [0, 1].
|
||||||
|
pos := texCoord
|
||||||
|
origin, size := imageSrcRegionOnTexture()
|
||||||
|
pos -= origin
|
||||||
|
pos /= size
|
||||||
|
|
||||||
|
pos = Warp(pos)
|
||||||
|
return imageSrc0At(pos*size + origin)
|
||||||
|
}
|
@ -19,6 +19,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
_ "embed"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
@ -43,6 +45,11 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten/v2/text"
|
"github.com/hajimehoshi/ebiten/v2/text"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var flagCRT = flag.Bool("crt", false, "enable the CRT effect")
|
||||||
|
|
||||||
|
//go:embed crt.go
|
||||||
|
var crtGo []byte
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
}
|
}
|
||||||
@ -159,9 +166,12 @@ type Game struct {
|
|||||||
hitPlayer *audio.Player
|
hitPlayer *audio.Player
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGame() *Game {
|
func NewGame(crt bool) ebiten.Game {
|
||||||
g := &Game{}
|
g := &Game{}
|
||||||
g.init()
|
g.init()
|
||||||
|
if crt {
|
||||||
|
return &GameWithCRTEffect{Game: g}
|
||||||
|
}
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,10 +449,35 @@ func (g *Game) drawGopher(screen *ebiten.Image) {
|
|||||||
screen.DrawImage(gopherImage, op)
|
screen.DrawImage(gopherImage, op)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GameWithCRTEffect struct {
|
||||||
|
ebiten.Game
|
||||||
|
|
||||||
|
crtShader *ebiten.Shader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GameWithCRTEffect) DrawFinalScreen(screen ebiten.FinalScreen, offscreen *ebiten.Image) {
|
||||||
|
if g.crtShader == nil {
|
||||||
|
s, err := ebiten.NewShader(crtGo)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("flappy: filed to compiled the CRT shader: %v", err))
|
||||||
|
}
|
||||||
|
g.crtShader = s
|
||||||
|
}
|
||||||
|
|
||||||
|
ow, oh := offscreen.Size()
|
||||||
|
sw, sh := screen.Size()
|
||||||
|
|
||||||
|
op := &ebiten.DrawRectShaderOptions{}
|
||||||
|
op.Images[0] = offscreen
|
||||||
|
op.GeoM.Scale(float64(sw)/float64(ow), float64(sh)/float64(oh))
|
||||||
|
screen.DrawRectShader(ow, oh, g.crtShader, op)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
ebiten.SetWindowSize(screenWidth, screenHeight)
|
ebiten.SetWindowSize(screenWidth, screenHeight)
|
||||||
ebiten.SetWindowTitle("Flappy Gopher (Ebitengine Demo)")
|
ebiten.SetWindowTitle("Flappy Gopher (Ebitengine Demo)")
|
||||||
if err := ebiten.RunGame(NewGame()); err != nil {
|
if err := ebiten.RunGame(NewGame(*flagCRT)); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,6 +148,11 @@ func (g *gameForUI) DrawOffscreen() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *gameForUI) DrawScreen() {
|
func (g *gameForUI) DrawScreen() {
|
||||||
|
if d, ok := g.game.(FinalScreenDrawer); ok {
|
||||||
|
d.DrawFinalScreen(g.screen, g.offscreen)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
scale, offsetX, offsetY := g.ScreenScaleAndOffsets()
|
scale, offsetX, offsetY := g.ScreenScaleAndOffsets()
|
||||||
switch {
|
switch {
|
||||||
case !isScreenFilterEnabled(), math.Floor(scale) == scale:
|
case !isScreenFilterEnabled(), math.Floor(scale) == scale:
|
||||||
|
4
image.go
4
image.go
@ -1090,3 +1090,7 @@ func colorMToScale(colorm affine.ColorM) (newColorM affine.ColorM, r, g, b, a fl
|
|||||||
|
|
||||||
return affine.ColorMIdentity{}, r * a, g * a, b * a, a
|
return affine.ColorMIdentity{}, r * a, g * a, b * a, a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private implements FinalScreen.
|
||||||
|
func (*Image) private() {
|
||||||
|
}
|
||||||
|
38
run.go
38
run.go
@ -16,6 +16,8 @@ package ebiten
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/clock"
|
"github.com/hajimehoshi/ebiten/v2/internal/clock"
|
||||||
@ -80,6 +82,34 @@ type Game interface {
|
|||||||
Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int)
|
Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FinalScreen represents the final screen image.
|
||||||
|
// FinalScreen implements a part of Image functions.
|
||||||
|
type FinalScreen interface {
|
||||||
|
Bounds() image.Rectangle
|
||||||
|
Size() (int, int)
|
||||||
|
|
||||||
|
DrawImage(img *Image, options *DrawImageOptions)
|
||||||
|
DrawTriangles(vertices []Vertex, indices []uint16, img *Image, options *DrawTrianglesOptions)
|
||||||
|
DrawRectShader(width, height int, shader *Shader, options *DrawRectShaderOptions)
|
||||||
|
DrawTrianglesShader(vertices []Vertex, indices []uint16, shader *Shader, options *DrawTrianglesShaderOptions)
|
||||||
|
Clear()
|
||||||
|
Fill(clr color.Color)
|
||||||
|
|
||||||
|
// private prevents other packages from implementing this interface.
|
||||||
|
// A new function might be added to this interface in the future
|
||||||
|
// even if the Ebitengine major version is not updated.
|
||||||
|
private()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FinalScreenDrawer is an interface for a custom function to render the final screen.
|
||||||
|
// For an actual usage, see examples/flappy.
|
||||||
|
type FinalScreenDrawer interface {
|
||||||
|
// DrawFinalScreen draws the final screen.
|
||||||
|
// If a game implementing FinalScreenDrawer is passed to RunGame, DrawFinalScreen is called after Draw.
|
||||||
|
// screen is the final screen. offscreen is the offscreen modified at Draw.
|
||||||
|
DrawFinalScreen(screen FinalScreen, offscreen *Image)
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultTPS represents a default ticks per second, that represents how many times game updating happens in a second.
|
// DefaultTPS represents a default ticks per second, that represents how many times game updating happens in a second.
|
||||||
const DefaultTPS = clock.DefaultTPS
|
const DefaultTPS = clock.DefaultTPS
|
||||||
|
|
||||||
@ -132,6 +162,8 @@ func IsScreenClearedEveryFrame() bool {
|
|||||||
// The default state is true.
|
// The default state is true.
|
||||||
//
|
//
|
||||||
// SetScreenFilterEnabled is concurrent-safe, but takes effect only at the next Draw call.
|
// SetScreenFilterEnabled is concurrent-safe, but takes effect only at the next Draw call.
|
||||||
|
//
|
||||||
|
// Deprecated: as of v2.5. Use FinalScreenDrawer instead.
|
||||||
func SetScreenFilterEnabled(enabled bool) {
|
func SetScreenFilterEnabled(enabled bool) {
|
||||||
setScreenFilterEnabled(enabled)
|
setScreenFilterEnabled(enabled)
|
||||||
}
|
}
|
||||||
@ -139,6 +171,8 @@ func SetScreenFilterEnabled(enabled bool) {
|
|||||||
// IsScreenFilterEnabled returns true if Ebitengine's "screen" filter is enabled.
|
// IsScreenFilterEnabled returns true if Ebitengine's "screen" filter is enabled.
|
||||||
//
|
//
|
||||||
// IsScreenFilterEnabled is concurrent-safe.
|
// IsScreenFilterEnabled is concurrent-safe.
|
||||||
|
//
|
||||||
|
// Deprecated: as of v2.5. Use FinalScreenDrawer instead.
|
||||||
func IsScreenFilterEnabled() bool {
|
func IsScreenFilterEnabled() bool {
|
||||||
return isScreenFilterEnabled()
|
return isScreenFilterEnabled()
|
||||||
}
|
}
|
||||||
@ -151,6 +185,10 @@ var Termination = ui.RegularTermination
|
|||||||
// game's Draw function is called every frame to draw the screen.
|
// game's Draw function is called every frame to draw the screen.
|
||||||
// game's Layout function is called when necessary, and you can specify the logical screen size by the function.
|
// game's Layout function is called when necessary, and you can specify the logical screen size by the function.
|
||||||
//
|
//
|
||||||
|
// If game implements FinalScreenDrawer, its DrawFinalScreen is called after Draw.
|
||||||
|
// The argument screen represents the final screen. The argument offscreen is an offscreen modified at Draw.
|
||||||
|
// If game does not implement FinalScreenDrawer, the dafault rendering for the final screen is used.
|
||||||
|
//
|
||||||
// game's functions are called on the same goroutine.
|
// game's functions are called on the same goroutine.
|
||||||
//
|
//
|
||||||
// On browsers, it is strongly recommended to use iframe if you embed an Ebitengine application in your website.
|
// On browsers, it is strongly recommended to use iframe if you embed an Ebitengine application in your website.
|
||||||
|
Loading…
Reference in New Issue
Block a user