Improvements

This commit is contained in:
Dennis Postma 2025-03-11 23:53:27 +01:00
parent 5ca41cfd38
commit 36c9522e8a
2 changed files with 77 additions and 65 deletions

View File

@ -76,12 +76,8 @@ export default class SpriteUpdateEvent extends BaseEvent {
if (actionData.sprites.length === 0) continue if (actionData.sprites.length === 0) continue
// Generate and save the sprite sheet // Generate and save the sprite sheet
const frameSize = await this.generateAndSaveSpriteSheet( const frameDimensions = await this.generateAndSaveSpriteSheet(actionData.sprites, sprite.getId(), actionData.action)
actionData.sprites, if (!frameDimensions) return false
sprite.getId(),
actionData.action
)
if (!frameSize) return false
// Create and save sprite action // Create and save sprite action
const spriteAction = new SpriteAction() const spriteAction = new SpriteAction()
@ -93,8 +89,8 @@ export default class SpriteUpdateEvent extends BaseEvent {
.setSprites(actionData.sprites) .setSprites(actionData.sprites)
.setOriginX(actionData.originX) .setOriginX(actionData.originX)
.setOriginY(actionData.originY) .setOriginY(actionData.originY)
.setFrameWidth(frameSize.width) .setFrameWidth(frameDimensions.frameWidth)
.setFrameHeight(frameSize.height) .setFrameHeight(frameDimensions.frameHeight)
.setFrameRate(actionData.frameRate) .setFrameRate(actionData.frameRate)
await this.spriteRepository.getEntityManager().persistAndFlush(spriteAction) await this.spriteRepository.getEntityManager().persistAndFlush(spriteAction)
@ -107,63 +103,75 @@ export default class SpriteUpdateEvent extends BaseEvent {
} }
} }
private async generateAndSaveSpriteSheet( private async generateAndSaveSpriteSheet(sprites: SpriteImage[], spriteId: string, action: string): Promise<{ frameWidth: number; frameHeight: number } | null> {
sprites: SpriteImage[],
spriteId: string,
action: string
): Promise<{ width: number, height: number } | null> {
try { try {
if (sprites.length === 0) return { width: 0, height: 0 } if (sprites.length === 0) return { frameWidth: 0, frameHeight: 0 }
// Extract image data from sprites // Extract image data and get image metadata
const imageBuffers = await Promise.all( const imagesData = await Promise.all(
sprites.map(sprite => { sprites.map(async (sprite) => {
const base64Data = sprite.url.split(';base64,').pop() const base64Data = sprite.url.split(';base64,').pop()
if (!base64Data) throw new Error('Invalid base64 image') if (!base64Data) throw new Error('Invalid base64 image')
return Buffer.from(base64Data, 'base64') const buffer = Buffer.from(base64Data, 'base64')
const metadata = await sharp(buffer).metadata()
return {
buffer,
width: metadata.width || 0,
height: metadata.height || 0
}
}) })
) )
// Get metadata for all images to find the maximum dimensions // Skip creation if any image has invalid dimensions
const metadataList = await Promise.all( if (imagesData.some((data) => data.width === 0 || data.height === 0)) {
imageBuffers.map(buffer => sharp(buffer).metadata()) console.error('One or more sprites have invalid dimensions')
)
// Calculate the maximum width and height across all frames
const maxWidth = Math.max(...metadataList.map(meta => meta.width || 0))
const maxHeight = Math.max(...metadataList.map(meta => meta.height || 0))
// Skip creation if we couldn't determine dimensions
if (maxWidth === 0 || maxHeight === 0) {
console.error('Could not determine sprite dimensions')
return null return null
} }
// Resize all frames to the same dimensions // Calculate the maximum width and height to use for all frames
const resizedFrames = await Promise.all( const maxWidth = Math.max(...imagesData.map((data) => data.width))
imageBuffers.map(async (buffer) => { const maxHeight = Math.max(...imagesData.map((data) => data.height))
return sharp(buffer)
.resize({ // Create frames of uniform size with the original sprites centered
const uniformFrames = await Promise.all(
imagesData.map(async (imageData) => {
// Calculate centering offsets to position the sprite in the middle of the frame
const xOffset = Math.floor((maxWidth - imageData.width) / 2)
const yOffset = Math.floor((maxHeight - imageData.height) / 2)
// Create a uniform-sized frame with the sprite centered
return sharp({
create: {
width: maxWidth, width: maxWidth,
height: maxHeight, height: maxHeight,
fit: 'contain', channels: 4,
background: { r: 0, g: 0, b: 0, alpha: 0 } background: { r: 0, g: 0, b: 0, alpha: 0 }
}
}) })
.composite([
{
input: imageData.buffer,
left: xOffset,
top: yOffset
}
])
.png()
.toBuffer() .toBuffer()
}) })
) )
// Create sprite sheet with uniformly sized frames // Create the sprite sheet with uniform frames
const spriteSheet = await sharp({ const spriteSheet = await sharp({
create: { create: {
width: maxWidth * resizedFrames.length, width: maxWidth * uniformFrames.length,
height: maxHeight, height: maxHeight,
channels: 4, channels: 4,
background: { r: 0, g: 0, b: 0, alpha: 0 } background: { r: 0, g: 0, b: 0, alpha: 0 }
} }
}) })
.composite( .composite(
resizedFrames.map((buffer, index) => ({ uniformFrames.map((buffer, index) => ({
input: buffer, input: buffer,
left: index * maxWidth, left: index * maxWidth,
top: 0 top: 0
@ -177,7 +185,11 @@ export default class SpriteUpdateEvent extends BaseEvent {
await fs.promises.mkdir(dir, { recursive: true }) await fs.promises.mkdir(dir, { recursive: true })
await fs.promises.writeFile(`${dir}/${action}.png`, spriteSheet) await fs.promises.writeFile(`${dir}/${action}.png`, spriteSheet)
return { width: maxWidth, height: maxHeight } // Return the uniform frame dimensions
return {
frameWidth: maxWidth,
frameHeight: maxHeight
}
} catch (error) { } catch (error) {
console.error('Error generating sprite sheet:', error) console.error('Error generating sprite sheet:', error)
return null return null

View File

@ -1,7 +1,7 @@
import serverConfig from '@/application/config' import serverConfig from '@/application/config'
import { Migrator } from '@mikro-orm/migrations' import { Migrator } from '@mikro-orm/migrations'
// import { defineConfig, MariaDbDriver } from '@mikro-orm/mariadb' import { defineConfig, MariaDbDriver } from '@mikro-orm/mariadb'
import { defineConfig, MySqlDriver } from '@mikro-orm/mysql' // import { defineConfig, MySqlDriver } from '@mikro-orm/mysql'
import { TsMorphMetadataProvider } from '@mikro-orm/reflection' import { TsMorphMetadataProvider } from '@mikro-orm/reflection'
export default defineConfig({ export default defineConfig({
@ -9,7 +9,7 @@ export default defineConfig({
metadataProvider: TsMorphMetadataProvider, metadataProvider: TsMorphMetadataProvider,
entities: ['./src/entities/*.ts'], entities: ['./src/entities/*.ts'],
entitiesTs: ['./src/entities/*.ts'], entitiesTs: ['./src/entities/*.ts'],
driver: MySqlDriver, driver: MariaDbDriver,
host: serverConfig.DB_HOST, host: serverConfig.DB_HOST,
port: serverConfig.DB_PORT, port: serverConfig.DB_PORT,
user: serverConfig.DB_USER, user: serverConfig.DB_USER,