Files
aego/asset/builtin_anim.go
T
sanspie 515f4ca1b3 Stage 11a: animation (D333–D340, D344, D354–D357)
Clips (.aeanim) and controllers (.aectrl) with importers, AnimationPlayer and Animator with crossfades, root motion and events on fixed steps, rewind support. Editor: Animation panel (dope sheet, curves, record, preview, trajectory), Animator panel, sprite sheet slicer and flipbooks, Programming workspace, export panel redesign. Motion 2D ledge climb. 11c: the component and Create palette.
2026-10-25 15:31:22 +03:00

166 lines
4.2 KiB
Go

package asset
import (
"math"
"strconv"
"strings"
"sync"
"aego/core/ids"
"aego/gpu"
"aego/render/texture"
)
const (
runnerSize = 16
runnerFirst = 0xb00
runnerCount = 8
)
var (
BuiltinRunClip = ids.AssetID{Hi: builtinHi, Lo: 0xb10}
BuiltinBobClip = ids.AssetID{Hi: builtinHi, Lo: 0xb11}
)
var BuiltinPlatformerCtrl = ids.AssetID{Hi: builtinHi, Lo: 0xb20}
func BuiltinRunner(i int) ids.AssetID {
return ids.AssetID{Hi: builtinHi, Lo: runnerFirst + uint64(i%runnerCount)}
}
var runnerData [runnerCount]struct {
once sync.Once
data *texture.Data
}
func BuiltinTexture(id ids.AssetID) (*texture.Data, bool) {
switch {
case id == BuiltinWhite:
return &texture.Data{Width: 1, Height: 1, Format: gpu.TexSRGBA8, Pixels: []byte{255, 255, 255, 255}, Premultiplied: true}, true
case id.Hi == builtinHi && id.Lo >= runnerFirst && id.Lo < runnerFirst+runnerCount:
k := id.Lo - runnerFirst
c := &runnerData[k]
c.once.Do(func() { c.data = runnerFrame(int(k)) })
return c.data, true
}
return nil, false
}
func runnerFrame(i int) *texture.Data {
const n = runnerSize
px := make([]byte, n*n*4)
body := [4]byte{236, 236, 236, 255}
eye := [4]byte{24, 28, 36, 255}
set := func(x, y int, c [4]byte) {
if x >= 0 && y >= 0 && x < n && y < n {
copy(px[(y*n+x)*4:], c[:])
}
}
ph := float64(i) / runnerCount * 2 * math.Pi
bob := int(math.Round(math.Abs(math.Sin(ph))))
for y := 3; y <= 9; y++ {
for x := 4; x <= 11; x++ {
if (y == 3 || y == 9) && (x == 4 || x == 11) {
continue
}
set(x, y-bob, body)
}
}
set(9, 5-bob, eye)
set(10, 5-bob, eye)
for leg, off := range [2]float64{0, math.Pi} {
hip := 6 + leg*3
foot := hip + int(math.Round(math.Sin(ph+off)*2.5))
for y := 10 - bob; y <= 14; y++ {
t := float64(y-(10-bob)) / float64(4+bob)
set(hip+int(math.Round(float64(foot-hip)*t)), y, body)
}
}
return &texture.Data{Width: n, Height: n, Format: gpu.TexSRGBA8, Pixels: px, Premultiplied: true}
}
func assetLit(id ids.AssetID) string {
return `asset("` + id.String() + `")`
}
func runClipText() string {
var b strings.Builder
b.WriteString("anim version=1 length=0.8 loop=loop {\n frames \"\" fps=10 {\n sprites=[")
for i := range runnerCount {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(assetLit(BuiltinRunner(i)))
}
b.WriteString("]\n }\n event t=0.2 name=\"Footstep\" mode=presentation {}\n event t=0.6 name=\"Footstep\" mode=presentation {}\n}\n")
return b.String()
}
const bobClipText = `anim version=1 length=1 loop=loop {
track "" prop="transform.rotation" kind=float32 authority=simulation {
key t=0 v=-0.2 {}
key t=0.5 v=0.2 {}
key t=1 v=-0.2 {}
}
event t=0.5 name="Hit" payload=1 {}
}
`
func BuiltinAnimText(id ids.AssetID) (string, bool) {
switch id {
case BuiltinRunClip:
return runClipText(), true
case BuiltinBobClip:
return bobClipText, true
}
return "", false
}
func platformerCtrlText() string {
return `controller version=1 {
entry="Idle"
param "Speed" kind=float {}
param "Grounded" kind=bool default=true {}
param "Attack" kind=trigger {}
state "Idle" {}
state "Run" clip=` + assetLit(BuiltinRunClip) + ` {}
state "Jump" {}
state "Hit" {}
transition "Idle" to="Run" duration=0.1 {
cond "Speed" op=gt value=0.1 {}
}
transition "Run" to="Idle" duration=0.1 {
cond "Speed" op=le value=0.1 {}
}
transition "any" to="Jump" interrupt=true {
cond "Grounded" op=eq value=false {}
}
transition "Jump" to="Idle" duration=0.05 {
cond "Grounded" {}
}
transition "any" to="Hit" interrupt=true {
cond "Attack" {}
}
transition "Hit" to="Idle" exitTime=1 duration=0.05 {}
}
`
}
func BuiltinCtrlText(id ids.AssetID) (string, bool) {
if id == BuiltinPlatformerCtrl {
return platformerCtrlText(), true
}
return "", false
}
func builtinAnimEntries() []Builtin {
out := make([]Builtin, 0, runnerCount+3)
for i := range runnerCount {
out = append(out, Builtin{ID: BuiltinRunner(i), Name: "engine/sprites/runner_" + strconv.Itoa(i), Kind: ArtTexture})
}
return append(out,
Builtin{ID: BuiltinRunClip, Name: "engine/anim/run", Kind: ArtAnim},
Builtin{ID: BuiltinBobClip, Name: "engine/anim/bob", Kind: ArtAnim},
Builtin{ID: BuiltinPlatformerCtrl, Name: "engine/anim/platformer", Kind: ArtCtrl})
}