Hump & transition image effects

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
wolf
Prole
Posts: 13
Joined: Tue Jan 12, 2021 10:24 am

Re: Hump & transition image effects

Post by wolf »

wolf wrote: Thu Jan 26, 2023 6:31 am
Bigfoot71 wrote: Tue Jan 24, 2023 2:29 pm
wolf wrote: Mon Jan 23, 2023 10:57 am I know this is a very old topic, but I wanted to add something that people might find useful.

For my own project I use hump as well and I wanted to have a crossfade animation between scenes (e.g. from menu to loading to game). Here's my approach using the SceneBase base class and Transition object.

Code: Select all

-- base class for scenes
SceneBase = Class { }

function SceneBase:init() end

function SceneBase:enter(previous, ...) end

function SceneBase:update(dt) end

function SceneBase:draw() end

function SceneBase:onStartTransition() end

function SceneBase:onFinishTransition() end

-- class that performs crossfade transitions
Transition = {}

-- duration of fade animation
local FADE_DURATION = 0.5

-- a flag to check whether initial Gamestate is set
local is_initialized = false

-- a list of pending transitions
local transitions = {}

-- validate a scene by ensuring all methods from SceneBase are included
local function assertScene(scene)
	assert(scene ~= nil, 'scene should be defined')
	for k, v in pairs(SceneBase) do
		-- ensure property is defined
		assert(scene[k] ~= nil, 'scene should inherit from SceneBase')
		-- ensure property type of scene is equal to property type of SceneBase
		assert(type(scene[k]) == type(v), 'scene should inherit from SceneBase')
	end
end

-- this callback is invoked when screenshot capture is completed
local function startTransition(imageData)
	-- create an image from original scene
	local image = love.graphics.newImage(imageData)

	-- remove transition from pending transitions list
	local fade = table.remove(transitions, 1)

	-- notify next scene we are starting the transition
	fade.to_scene:onStartTransition()

	-- store drawing function for next scene in local variable
	local to_scene_draw = fade.to_scene.draw

	-- for next scene, replace drawing function with custom implementation
	fade.to_scene.draw = function()
		-- first draw next scene
		love.graphics.setColor(1.0, 1.0, 1.0, 1.0)
		to_scene_draw(fade.to_scene)

		-- now draw an image of previous scene, but use alpha value for fade
		love.graphics.setColor(1.0, 1.0, 1.0, fade.alpha)
		love.graphics.draw(image)				
	end

	-- tween fade alpha value
	Timer.tween(FADE_DURATION, fade, { alpha = 0.0 }, 'in-out-quad', function()
		-- restore original draw function when fade is completed
		fade.to_scene.draw = to_scene_draw

		-- ensure colors are drawn normally on subsequent draw calls
		love.graphics.setColor(1.0, 1.0, 1.0, 1.0)

		-- notify next scene the transition is completed
		fade.to_scene.onFinishTransition(fade.to_scene)
	end)
	
	-- start switch to next scene
	Gamestate.switch(fade.to_scene, unpack(fade.to_args))
end

-- the initialize function should be called to load initial scene
Transition.init = function(to, ...)
	-- create a black dummy scene for fade in animation
	local DummyScene = Class { __includes = SceneBase }
	DummyScene.draw = function()
		love.graphics.setColor(0.0, 0.0, 0.0, 1.0)
		local window_w, window_h = love.window.getMode()
		love.graphics.rectangle('fill', 0, 0, window_w, window_h)
	end

	-- initialize Gamestate with the black dummy scene
	Gamestate.switch(DummyScene)
	is_initialized = true

	-- start a cross fade animation to target scene
	Transition.crossfade(DummyScene, to, ...)
end

-- perform a crossfade transition between scenes with optional arguments
Transition.crossfade = function(from, to, ...)
	assert(is_initialized, 'Transitions not initialized, ensure init is called first')
	assertScene(from)
	assertScene(to)

	-- add transition to pending transitions list
	transitions[#transitions + 1] = {
		from_scene = from,
		to_scene = to,
		to_args = {...},
		alpha = 1.0,
	}

	-- capture a screenshot of the current scene, callback is invoked on image captured
	love.graphics.captureScreenshot(startTransition)
end
As can be seen this code relies on `hump.timer` and `hump.gamestate`. The "class" is simply used as such:

Code: Select all

Transition.crossfade(self, Game)
In this case I assume self is a scene (e.g. Loading) and Game is another scene.
Here is a very simplistic approach, you can optimize it and add shaders if you want.
Very nice, smaller in lines of code than my approach. Nice use of canvas (probably a better idea than the screenshot capture I did here). And in your example GameState is decoupled from the Transition class itself, whereas in my example it's kinda coupled.
User avatar
Bigfoot71
Party member
Posts: 287
Joined: Fri Mar 11, 2022 11:07 am

Re: Hump & transition image effects

Post by Bigfoot71 »

wolf wrote: Thu Jan 26, 2023 6:31 am Very nice, even smaller in lines of code than my approach. Nice use of canvas (probably a better idea than the screenshot capture I did here). And in your example GameState is decoupled from the Transition class itself, whereas in my example it's kinda coupled.
And as I said the approach is very simplistic but what will allow you to adapt it to your tastes, by using the metatables you can even couple it as you say to your gamestates and avoid the use in the function `love.draw`, and many other customizations such as different transitions between states (even if for readability I preferred to keep it simple if it's a simple crossfade)

Edit: I realized then that what I was saying was not very clear, here is an example:
Image

And that with only this in the main.lua:

Code: Select all

TransState = require("lib.tState")
Gamestate = require("lib.gamestate")

function love.load()

    State_1 = TransState("states.state_1")
    State_2 = TransState("states.state_2")
    State_3 = TransState("states.state_3")

    Gamestate.switch(State_1)

end

function love.keyreleased(k)
    Gamestate.keyreleased(k)
end

function love.update(dt)
    Gamestate:update(dt)
end

function love.draw()
    Gamestate.draw()
    love.graphics.print("Press \"SPACE\" to switch")
end
I wrote it quickly without bothering to optimize it or test the slightest problem but it's already a good base rather simple to understand I think if you want to rework it. (and without even using a metatable like I have, is that a good thing, to see...)

Note: In my example I am loading the contents of the `State_X` files, so it cannot be used as is in a compressed file, but you can also define the "state" table as a parameter of `TransState`. There are certainly solutions but I just noticed it and I don't have time to look into it anymore ^^
Attachments
transition.zip
(4.73 KiB) Downloaded 53 times
My avatar code for the curious :D V1, V2, V3.
User avatar
pgimeno
Party member
Posts: 3541
Joined: Sun Oct 18, 2015 2:58 pm

Re: Hump & transition image effects

Post by pgimeno »

Am I right that these methods freeze the screen during the transition? Depending on whether the screen moves, that might be noticeable.
User avatar
Bigfoot71
Party member
Posts: 287
Joined: Fri Mar 11, 2022 11:07 am

Re: Hump & transition image effects

Post by Bigfoot71 »

pgimeno wrote: Thu Jan 26, 2023 8:16 pm Am I right that these methods freeze the screen during the transition? Depending on whether the screen moves, that might be noticeable.
I'm not sure I understand but no the display is not frozen, I think this impression (if I understand correctly) comes from the fact that the transition time is one second each in my example, but from the moment that the animation to start the new state is indeed updated and displayed accordingly. Is that what you meant?
My avatar code for the curious :D V1, V2, V3.
wolf
Prole
Posts: 13
Joined: Tue Jan 12, 2021 10:24 am

Re: Hump & transition image effects

Post by wolf »

Bigfoot71 wrote: Thu Jan 26, 2023 10:34 pm
pgimeno wrote: Thu Jan 26, 2023 8:16 pm Am I right that these methods freeze the screen during the transition? Depending on whether the screen moves, that might be noticeable.
I'm not sure I understand but no the display is not frozen, I think this impression (if I understand correctly) comes from the fact that the transition time is one second each in my example, but from the moment that the animation to start the new state is indeed updated and displayed accordingly. Is that what you meant?
I think he means that in my example I made a screenshot (hence no animation for duration of transition). In your example you draw at the start of the transition on a canvas (again, since canvas is not updated, no animation of say menu or game state is shown during transition.

I guess your example could be easily adapted to update the canvas with e.g. game state while transition is happening. Could one could supply the old state to the transition 'class' and drawing old state on the canvas on each update call. My example would likely be problematic as I believe taking screenshots is a bit slow.
Post Reply

Who is online

Users browsing this forum: No registered users and 19 guests