title screen fade-in transition

This commit is contained in:
minjaesong
2026-04-04 22:33:10 +09:00
parent 6118f30d5f
commit 73c7c8942e
4 changed files with 67 additions and 34 deletions

23
.idea/csv-editor.xml generated
View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CsvFileAttributes">
<option name="attributeMap">
<map>
<entry key="/Terrarum.wiki/Items.md">
<value>
<Attribute>
<option name="separator" value="," />
</Attribute>
</value>
</entry>
<entry key="/Terrarum.wiki/Modules:Setup.md">
<value>
<Attribute>
<option name="separator" value="," />
</Attribute>
</value>
</entry>
</map>
</option>
</component>
</project>

View File

@@ -172,5 +172,5 @@ Both save/restore `camera.position` and call `setToOrtho` with `App.scr.wf/hf` o
- **`lateinit var` guards**: Use `::prop.isInitialized` checks in `resize()` and `dispose()` for properties set during async loading steps. - **`lateinit var` guards**: Use `::prop.isInitialized` checks in `resize()` and `dispose()` for properties set during async loading steps.
- **`ConsistentUpdateRate`**: Accumulates delta time; may call `updateFunction` multiple times before `renderFunction`. Call `.reset()` before transitioning to active rendering to avoid catch-up storms. - **`ConsistentUpdateRate`**: Accumulates delta time; may call `updateFunction` multiple times before `renderFunction`. Call `.reset()` before transitioning to active rendering to avoid catch-up storms.
- **Textures**: Use TGA format. Images with semitransparency must be TGA; opaque images may be PNG. - **Textures**: Use TGA format. Images with semitransparency must be TGA; opaque images may be PNG.
- **Coordinate system**: Y-down (camera `setToOrtho(true, ...)`). `setCameraPosition(0, 0)` places origin at screen top-left. - **Coordinate system**: Y-down (camera `setToOrtho(true, ...)`). `setCameraPosition(0, 0)` places origin at screen top-left, which is not LibGDX nor OpenGL works natively, hence the existence of `FlippingSpriteBatch`. If the user reports renders are flipped vertically, try draw()/drawFlipped() accordingly.
- **ROUNDWORLD**: World wraps horizontally. `WorldCamera.x` uses `fmod worldWidth`. Lightmap and tile drawing account for wrapping. - **ROUNDWORLD**: World wraps horizontally. `WorldCamera.x` uses `fmod worldWidth`. Lightmap and tile drawing account for wrapping.

View File

@@ -900,7 +900,7 @@ public class App implements ApplicationListener {
logoBatch.end(); logoBatch.end();
} }
int logoPosX = (int)(logoPosX0 + Math.round(100 * Math.sin(GLOBAL_RENDER_TIMER / 50.0))); int logoPosX = logoPosX0;//(int)(logoPosX0 + Math.round(100 * Math.sin(GLOBAL_RENDER_TIMER / 50.0)));
// draw logo reflection // draw logo reflection
logoBatch.setShader(shaderReflect); logoBatch.setShader(shaderReflect);

View File

@@ -5,6 +5,7 @@ import com.badlogic.gdx.Input
import com.badlogic.gdx.InputAdapter import com.badlogic.gdx.InputAdapter
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.OrthographicCamera import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
@@ -71,6 +72,10 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
private var glLoadStep = 0 private var glLoadStep = 0
private lateinit var titleLoadingThread: Thread private lateinit var titleLoadingThread: Thread
private var splashCapture: Texture? = null
private var settleFrames = 0
private var transitionState = 0 // 0=loading, 1=settling, 2=fading, 3=done
private lateinit var demoWorld: GameWorld private lateinit var demoWorld: GameWorld
private lateinit var cameraNodes: FloatArray // camera Y-pos private lateinit var cameraNodes: FloatArray // camera Y-pos
private val cameraNodeWidth = 15 private val cameraNodeWidth = 15
@@ -314,23 +319,73 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
} }
private val introUncoverTime: Second = 0.3f private val introFadeTime: Second = 0.5f
private var introUncoverDeltaCounter = 0f private var introFadeDeltaCounter = 0f
private val settleFrameCount = 12
override fun renderImpl(updateRate: Float) { override fun renderImpl(updateRate: Float) {
if (!loadDone) { when (transitionState) {
App.drawSplash() // Loading phase: show splash, advance GL load steps
processGLLoadStep() 0 -> {
return App.drawSplash()
processGLLoadStep()
if (loadDone) {
// capture the current framebuffer (splash) as a texture
val pixmap = Pixmap.createFromFrameBuffer(0, 0, App.scr.width, App.scr.height)
splashCapture = Texture(pixmap)
pixmap.dispose()
transitionState = 1
settleFrames = 0
}
}
// Settle phase: render title screen normally but paint captured splash on top
1 -> {
renderTitleScreen()
drawSplashCapture(1f)
settleFrames++
if (settleFrames >= settleFrameCount) {
transitionState = 2
introFadeDeltaCounter = 0f
}
}
// Fade phase: crossfade from captured splash to title screen
2 -> {
renderTitleScreen()
introFadeDeltaCounter += Gdx.graphics.deltaTime
val opacity = 1f - (introFadeDeltaCounter / introFadeTime).coerceIn(0f, 1f)
drawSplashCapture(opacity)
if (introFadeDeltaCounter >= introFadeTime) {
splashCapture?.dispose()
splashCapture = null
transitionState = 3
}
}
// Normal rendering
3 -> {
renderTitleScreen()
}
} }
}
private fun renderTitleScreen() {
IngameRenderer.setRenderedWorld(demoWorld) IngameRenderer.setRenderedWorld(demoWorld)
super.renderImpl(0f)
super.renderImpl(updateRate)
// async update and render
gameUpdateGovernor.update(Gdx.graphics.deltaTime, App.UPDATE_RATE, updateScreen, renderScreen) gameUpdateGovernor.update(Gdx.graphics.deltaTime, App.UPDATE_RATE, updateScreen, renderScreen)
} }
private fun drawSplashCapture(opacity: Float) {
val tex = splashCapture ?: return
setCameraPosition(0f, 0f)
blendNormalStraightAlpha(batch)
batch.inUse {
batch.shader = null
batch.color = Color(1f, 1f, 1f, opacity)
// framebuffer capture is Y-flipped; draw flipped
batch.drawFlipped(tex, 0f, 0f)
batch.color = Color.WHITE
}
}
private val updateScreen = { delta: Float -> private val updateScreen = { delta: Float ->
demoWorld.globalLight = WeatherMixer.globalLightNow demoWorld.globalLight = WeatherMixer.globalLightNow
@@ -586,6 +641,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
override fun dispose() { override fun dispose() {
if (::uiRemoCon.isInitialized) uiRemoCon.dispose() if (::uiRemoCon.isInitialized) uiRemoCon.dispose()
if (::demoWorld.isInitialized) demoWorld.dispose() if (::demoWorld.isInitialized) demoWorld.dispose()
splashCapture?.dispose()
warning32bitJavaIcon.texture.dispose() warning32bitJavaIcon.texture.dispose()
warningAppleRosettaIcon.texture.dispose() warningAppleRosettaIcon.texture.dispose()
} }