@osbjs/osbjs
Version:
a minimalist osu! storyboarding framework
89 lines (77 loc) • 3.33 kB
text/typescript
import {
Vector2,
Beatmap,
Circle,
Slider,
PlayfieldToStoryboardOffset,
Component,
Easing,
Layer,
Sprite,
OsbVector2,
parseOsuTimestamp,
IHitObjectHighlight,
} from '@osbjs/osbjs'
export class MyHitObjectHighlight extends Component {
name = 'MyHitObjectHighlight'
osbPath: string
startTime: number
endTime: number
beatmap: Beatmap
circles: Circle[]
sliders: Slider[]
beat: number
options = { startScale: 1, endScale: 1.2, fadeDuration: 200, beatDivisor: 8, followSliderPath: true }
constructor(osbPath: string, startTime: number | string, endTime: number | string, beatmap: Beatmap, options?: IHitObjectHighlightOptions) {
super()
this.osbPath = osbPath
this.beatmap = beatmap
this.startTime = typeof startTime == 'string' ? parseOsuTimestamp(startTime) : Math.round(startTime)
this.endTime = typeof endTime == 'string' ? parseOsuTimestamp(endTime) : Math.round(endTime)
let firstUninherited = this.beatmap.timingPoints
.filter((tPoint) => tPoint.uninherited && tPoint.time <= this.startTime)
.sort((t1, t2) => t2.time - t1.time)[0]
this.startTime = Math.max(firstUninherited.time, this.startTime)
this.options = { ...this.options, ...options }
this.circles = this.beatmap.hitObjects.circles.filter((c) => c.startTime >= this.startTime && c.startTime <= this.endTime)
this.sliders = this.beatmap.hitObjects.sliders.filter((s) => s.startTime >= this.startTime && s.startTime <= this.endTime)
this.beat = this.beatmap.timingPoints
.filter((tPoint) => tPoint.time <= this.startTime && tPoint.uninherited)
.sort((t1, t2) => t2.time - t1.time)[0].beatLength
}
override generate() {
this.circles.forEach((circle) => {
let sprite = new Sprite(this.osbPath, Layer.Background)
sprite.MoveAtTime(circle.startTime, OsbVector2.fromVector2(Vector2.add(circle.position, PlayfieldToStoryboardOffset)))
sprite.Scale(circle.startTime, circle.startTime + this.options.fadeDuration, this.options.startScale, this.options.endScale, Easing.In)
sprite.Fade(circle.startTime, circle.startTime + Math.round(this.beat / 2), 1, 0)
this.registerComponents(sprite)
})
let timestep = Math.round(this.beat / this.options.beatDivisor)
this.sliders.forEach((slider) => {
let sprite = new Sprite(this.osbPath, Layer.Background)
sprite.Scale(slider.startTime, slider.endTime + this.options.fadeDuration, this.options.startScale, this.options.endScale, Easing.In)
sprite.Fade(slider.startTime, Math.round(slider.endTime + this.options.fadeDuration), 1, 0)
if (this.options.followSliderPath) {
let startTime = slider.startTime
let totalStep = Math.round((slider.endTime - slider.startTime) / timestep)
for (let i = 0; i < totalStep; i++) {
let prevEndTime = startTime + timestep * i
let endTime = startTime + timestep * (i + 1)
let startPosition = slider.getPositionAtTime(prevEndTime)
let endPosition = slider.getPositionAtTime(endTime)
sprite.Move(
prevEndTime,
endTime,
OsbVector2.fromVector2(Vector2.add(startPosition, PlayfieldToStoryboardOffset)),
OsbVector2.fromVector2(Vector2.add(endPosition, PlayfieldToStoryboardOffset))
)
}
}
this.registerComponents(sprite)
})
}
}
export interface MyHitObjectHighlightOptions extends IHitObjectHighlightOptions {
// your options here
}