mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 02:38: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 (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
@ -43,6 +45,11 @@ import (
|
||||
"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() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
@ -159,9 +166,12 @@ type Game struct {
|
||||
hitPlayer *audio.Player
|
||||
}
|
||||
|
||||
func NewGame() *Game {
|
||||
func NewGame(crt bool) ebiten.Game {
|
||||
g := &Game{}
|
||||
g.init()
|
||||
if crt {
|
||||
return &GameWithCRTEffect{Game: g}
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
@ -439,10 +449,35 @@ func (g *Game) drawGopher(screen *ebiten.Image) {
|
||||
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() {
|
||||
flag.Parse()
|
||||
ebiten.SetWindowSize(screenWidth, screenHeight)
|
||||
ebiten.SetWindowTitle("Flappy Gopher (Ebitengine Demo)")
|
||||
if err := ebiten.RunGame(NewGame()); err != nil {
|
||||
if err := ebiten.RunGame(NewGame(*flagCRT)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
@ -148,6 +148,11 @@ func (g *gameForUI) DrawOffscreen() error {
|
||||
}
|
||||
|
||||
func (g *gameForUI) DrawScreen() {
|
||||
if d, ok := g.game.(FinalScreenDrawer); ok {
|
||||
d.DrawFinalScreen(g.screen, g.offscreen)
|
||||
return
|
||||
}
|
||||
|
||||
scale, offsetX, offsetY := g.ScreenScaleAndOffsets()
|
||||
switch {
|
||||
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
|
||||
}
|
||||
|
||||
// private implements FinalScreen.
|
||||
func (*Image) private() {
|
||||
}
|
||||
|
38
run.go
38
run.go
@ -16,6 +16,8 @@ package ebiten
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"image"
|
||||
"image/color"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/clock"
|
||||
@ -80,6 +82,34 @@ type Game interface {
|
||||
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.
|
||||
const DefaultTPS = clock.DefaultTPS
|
||||
|
||||
@ -132,6 +162,8 @@ func IsScreenClearedEveryFrame() bool {
|
||||
// The default state is true.
|
||||
//
|
||||
// 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) {
|
||||
setScreenFilterEnabled(enabled)
|
||||
}
|
||||
@ -139,6 +171,8 @@ func SetScreenFilterEnabled(enabled bool) {
|
||||
// IsScreenFilterEnabled returns true if Ebitengine's "screen" filter is enabled.
|
||||
//
|
||||
// IsScreenFilterEnabled is concurrent-safe.
|
||||
//
|
||||
// Deprecated: as of v2.5. Use FinalScreenDrawer instead.
|
||||
func IsScreenFilterEnabled() bool {
|
||||
return isScreenFilterEnabled()
|
||||
}
|
||||
@ -151,6 +185,10 @@ var Termination = ui.RegularTermination
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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