Doodad Animations Managed In-Engine
* Add animation support for Doodad actors (Play Mode) into the core engine, so that the Doodad script can register named animations and play them without managing all the details themselves. * Doodad API functions on Self: AddAnimation, PlayAnimation, StopAnimation, IsAnimating * CLI: the `doodad convert` command will name each layer after the filename used as the input image. * CLI: fix the `doodad convert` command creating duplicate Palette colors when converting a series of input images into a Doodad.
This commit is contained in:
parent
ac490473b3
commit
a73dec9f31
5
Makefile
5
Makefile
|
@ -34,6 +34,11 @@ build-debug:
|
||||||
go build $(LDFLAGS) -tags="developer" -i -o bin/doodle cmd/doodle/main.go
|
go build $(LDFLAGS) -tags="developer" -i -o bin/doodle cmd/doodle/main.go
|
||||||
go build $(LDFLAGS) -tags="developer" -i -o bin/doodad cmd/doodad/main.go
|
go build $(LDFLAGS) -tags="developer" -i -o bin/doodad cmd/doodad/main.go
|
||||||
|
|
||||||
|
# `make install` to install the Go binaries to your GOPATH.
|
||||||
|
.PHONY: install
|
||||||
|
install:
|
||||||
|
go install git.kirsle.net/apps/doodle/cmd/...
|
||||||
|
|
||||||
# `make doodads` to build the doodads from the dev-assets folder.
|
# `make doodads` to build the doodads from the dev-assets folder.
|
||||||
.PHONY: doodads
|
.PHONY: doodads
|
||||||
doodads:
|
doodads:
|
||||||
|
|
|
@ -135,6 +135,12 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
|
||||||
images = append(images, img)
|
images = append(images, img)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to translate image filenames into layer names.
|
||||||
|
toLayerName := func(filename string) string {
|
||||||
|
ext := filepath.Ext(filename)
|
||||||
|
return strings.TrimSuffix(filepath.Base(filename), ext)
|
||||||
|
}
|
||||||
|
|
||||||
// Generate the output drawing file.
|
// Generate the output drawing file.
|
||||||
switch strings.ToLower(filepath.Ext(outputFile)) {
|
switch strings.ToLower(filepath.Ext(outputFile)) {
|
||||||
case extDoodad:
|
case extDoodad:
|
||||||
|
@ -152,14 +158,16 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
|
||||||
palette, layer0 := imageToChunker(images[0], chroma, nil, chunkSize)
|
palette, layer0 := imageToChunker(images[0], chroma, nil, chunkSize)
|
||||||
doodad.Palette = palette
|
doodad.Palette = palette
|
||||||
doodad.Layers[0].Chunker = layer0
|
doodad.Layers[0].Chunker = layer0
|
||||||
|
doodad.Layers[0].Name = toLayerName(inputFiles[0])
|
||||||
|
|
||||||
// Write any additional layers.
|
// Write any additional layers.
|
||||||
if len(images) > 1 {
|
if len(images) > 1 {
|
||||||
for i, img := range images[1:] {
|
for i := 1; i < len(images); i++ {
|
||||||
log.Info("Converting extra layer %d", i+1)
|
img := images[i]
|
||||||
|
log.Info("Converting extra layer %d", i)
|
||||||
_, chunker := imageToChunker(img, chroma, palette, chunkSize)
|
_, chunker := imageToChunker(img, chroma, palette, chunkSize)
|
||||||
doodad.Layers = append(doodad.Layers, doodads.Layer{
|
doodad.Layers = append(doodad.Layers, doodads.Layer{
|
||||||
Name: fmt.Sprintf("layer-%d", i+1),
|
Name: toLayerName(inputFiles[i]),
|
||||||
Chunker: chunker,
|
Chunker: chunker,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -289,6 +297,7 @@ func imageToChunker(img image.Image, chroma render.Color, palette *level.Palette
|
||||||
|
|
||||||
// Cache a palette of unique colors as we go.
|
// Cache a palette of unique colors as we go.
|
||||||
var uniqueColor = map[string]*level.Swatch{}
|
var uniqueColor = map[string]*level.Swatch{}
|
||||||
|
var newColors = map[string]*level.Swatch{} // new ones discovered this time
|
||||||
for _, swatch := range palette.Swatches {
|
for _, swatch := range palette.Swatches {
|
||||||
uniqueColor[swatch.Color.String()] = swatch
|
uniqueColor[swatch.Color.String()] = swatch
|
||||||
}
|
}
|
||||||
|
@ -310,6 +319,7 @@ func imageToChunker(img image.Image, chroma render.Color, palette *level.Palette
|
||||||
Color: color,
|
Color: color,
|
||||||
}
|
}
|
||||||
uniqueColor[color.String()] = swatch
|
uniqueColor[color.String()] = swatch
|
||||||
|
newColors[color.String()] = swatch
|
||||||
}
|
}
|
||||||
|
|
||||||
chunker.Set(render.NewPoint(int32(x), int32(y)), swatch)
|
chunker.Set(render.NewPoint(int32(x), int32(y)), swatch)
|
||||||
|
@ -323,8 +333,10 @@ func imageToChunker(img image.Image, chroma render.Color, palette *level.Palette
|
||||||
}
|
}
|
||||||
sort.Strings(sortedColors)
|
sort.Strings(sortedColors)
|
||||||
for _, hex := range sortedColors {
|
for _, hex := range sortedColors {
|
||||||
|
if _, ok := newColors[hex]; ok {
|
||||||
palette.Swatches = append(palette.Swatches, uniqueColor[hex])
|
palette.Swatches = append(palette.Swatches, uniqueColor[hex])
|
||||||
}
|
}
|
||||||
|
}
|
||||||
palette.Inflate()
|
palette.Inflate()
|
||||||
|
|
||||||
return palette, chunker
|
return palette, chunker
|
||||||
|
|
|
@ -11,17 +11,61 @@ mkdir -p ../../assets/doodads
|
||||||
buttons() {
|
buttons() {
|
||||||
cd buttons/
|
cd buttons/
|
||||||
|
|
||||||
doodad convert -t "Sticky Button" sticky1.png sticky2.png sticky-button.doodad
|
doodad convert -t "Sticky Button" sticky1.png sticky2.png button-sticky.doodad
|
||||||
doodad install-script sticky.js sticky-button.doodad
|
doodad install-script sticky.js button-sticky.doodad
|
||||||
cp sticky-button.doodad ../../../assets/doodads/
|
|
||||||
|
|
||||||
doodad convert -t "Button" button1.png button2.png button.doodad
|
doodad convert -t "Button" button1.png button2.png button.doodad
|
||||||
doodad install-script button.js button.doodad
|
doodad install-script button.js button.doodad
|
||||||
cp button.doodad ../../../assets/doodads/
|
|
||||||
|
|
||||||
doodad convert -t "Button Type B" typeB1.png typeB2.png button-typeB.doodad
|
doodad convert -t "Button Type B" typeB1.png typeB2.png button-typeB.doodad
|
||||||
doodad install-script button.js button-typeB.doodad
|
doodad install-script button.js button-typeB.doodad
|
||||||
cp button-typeB.doodad ../../../assets/doodads/
|
|
||||||
|
cp button*.doodad ../../../assets/doodads/
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
doors() {
|
||||||
|
cd doors/
|
||||||
|
|
||||||
|
doodad convert -t "Red Door" red1.png red2.png door-red.doodad
|
||||||
|
doodad install-script locked-door.js door-red.doodad
|
||||||
|
|
||||||
|
doodad convert -t "Blue Door" blue1.png blue2.png door-blue.doodad
|
||||||
|
doodad install-script locked-door.js door-blue.doodad
|
||||||
|
|
||||||
|
doodad convert -t "Green Door" green1.png green2.png door-green.doodad
|
||||||
|
doodad install-script locked-door.js door-green.doodad
|
||||||
|
|
||||||
|
doodad convert -t "Yellow Door" yellow1.png yellow2.png door-yellow.doodad
|
||||||
|
doodad install-script locked-door.js door-yellow.doodad
|
||||||
|
|
||||||
|
doodad convert -t "Red Key" red-key.png key-red.doodad
|
||||||
|
doodad install-script keys.js key-red.doodad
|
||||||
|
|
||||||
|
doodad convert -t "Blue Key" blue-key.png key-blue.doodad
|
||||||
|
doodad install-script keys.js key-blue.doodad
|
||||||
|
|
||||||
|
doodad convert -t "Green Key" green-key.png key-green.doodad
|
||||||
|
doodad install-script keys.js key-green.doodad
|
||||||
|
|
||||||
|
doodad convert -t "Yellow Key" yellow-key.png key-yellow.doodad
|
||||||
|
doodad install-script keys.js key-yellow.doodad
|
||||||
|
|
||||||
|
doodad convert -t "Electric Door" electric{1,2,3,4}.png door-electric.doodad
|
||||||
|
doodad install-script electric-door.js door-electric.doodad
|
||||||
|
|
||||||
|
cp door-*.doodad key-*.doodad ../../../assets/doodads/
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
trapdoors() {
|
||||||
|
cd trapdoors/
|
||||||
|
|
||||||
|
doodad convert -t "Trapdoor" down{1,2,3,4}.png trapdoor-down.doodad
|
||||||
|
doodad install-script down.js trapdoor-down.doodad
|
||||||
|
|
||||||
|
cp trapdoor-*.doodad ../../../assets/doodads/
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
}
|
}
|
||||||
|
@ -32,10 +76,17 @@ azulians() {
|
||||||
doodad convert -t "Blue Azulian" blu-front.png blu-back.png \
|
doodad convert -t "Blue Azulian" blu-front.png blu-back.png \
|
||||||
blu-wr{1,2,3,4}.png blu-wl{1,2,3,4}.png azu-blu.doodad
|
blu-wr{1,2,3,4}.png blu-wl{1,2,3,4}.png azu-blu.doodad
|
||||||
doodad install-script azulian.js azu-blu.doodad
|
doodad install-script azulian.js azu-blu.doodad
|
||||||
cp azu-blu.doodad ../../../assets/doodads/
|
|
||||||
|
doodad convert -t "Red Azulian" red-front.png red-back.png \
|
||||||
|
red-wr{1,2,3,4}.png red-wl{1,2,3,4}.png azu-red.doodad
|
||||||
|
doodad install-script azulian-red.js azu-red.doodad
|
||||||
|
|
||||||
|
cp azu-*.doodad ../../../assets/doodads/
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
}
|
}
|
||||||
|
|
||||||
buttons
|
buttons
|
||||||
|
doors
|
||||||
|
trapdoors
|
||||||
azulians
|
azulians
|
||||||
|
|
|
@ -1,35 +1,16 @@
|
||||||
function main() {
|
function main() {
|
||||||
console.log("%s initialized!", Self.Doodad.Title);
|
console.log("%s initialized!", Self.Doodad.Title);
|
||||||
|
|
||||||
var timer = 0;
|
var err = Self.AddAnimation("open", 100, [0, 1, 2, 3]);
|
||||||
|
console.error("door error: %s", err)
|
||||||
|
var animating = false;
|
||||||
|
|
||||||
// Animation frames.
|
Events.OnCollide(function() {
|
||||||
var frame = 0;
|
if (animating) {
|
||||||
var frames = Self.LayerCount();
|
|
||||||
var animationDirection = 1; // forward or backward
|
|
||||||
var animationSpeed = 100; // interval between frames when animating
|
|
||||||
var animating = false; // true if animation is actively happening
|
|
||||||
|
|
||||||
console.warn("Electric Door has %d frames", frames);
|
|
||||||
|
|
||||||
// Animation interval function.
|
|
||||||
setInterval(function() {
|
|
||||||
if (!animating) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance the frame forwards or backwards.
|
animating = true;
|
||||||
frame += animationDirection;
|
Self.PlayAnimation("open", null);
|
||||||
if (frame >= frames) {
|
});
|
||||||
// Reached the last frame, start the pause and reverse direction.
|
|
||||||
animating = false;
|
|
||||||
frame = frames - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Self.ShowLayer(frame);
|
|
||||||
}, animationSpeed);
|
|
||||||
|
|
||||||
Events.OnCollide( function() {
|
|
||||||
animating = true; // start the animation
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
function main() {
|
function main() {
|
||||||
|
Self.AddAnimation("open", 0, [1]);
|
||||||
|
var unlocked = false;
|
||||||
|
|
||||||
Events.OnCollide(function(e) {
|
Events.OnCollide(function(e) {
|
||||||
Self.ShowLayer(1);
|
if (unlocked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlocked = true;
|
||||||
|
Self.PlayAnimation("open", null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,53 +3,23 @@ function main() {
|
||||||
|
|
||||||
var timer = 0;
|
var timer = 0;
|
||||||
|
|
||||||
// Animation frames.
|
var animationSpeed = 100;
|
||||||
var frame = 0;
|
var animating = false;
|
||||||
var frames = Self.LayerCount();
|
Self.AddAnimation("open", animationSpeed, ["down1", "down2", "down3", "down4"]);
|
||||||
var animationDirection = 1; // forward or backward
|
Self.AddAnimation("close", animationSpeed, ["down4", "down3", "down2", "down1"]);
|
||||||
var animationSpeed = 100; // interval between frames when animating
|
|
||||||
var animationDelay = 8; // delay ticks at the end before reversing, in
|
|
||||||
// multiples of animationSpeed
|
|
||||||
var delayCountdown = 0;
|
|
||||||
var animating = false; // true if animation is actively happening
|
|
||||||
|
|
||||||
console.warn("Trapdoor has %d frames", frames);
|
|
||||||
|
|
||||||
// Animation interval function.
|
|
||||||
setInterval(function() {
|
|
||||||
if (!animating) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// At the end of the animation (door is open), delay before resuming
|
|
||||||
// the close animation.
|
|
||||||
if (delayCountdown > 0) {
|
|
||||||
delayCountdown--;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advance the frame forwards or backwards.
|
|
||||||
frame += animationDirection;
|
|
||||||
if (frame >= frames) {
|
|
||||||
// Reached the last frame, start the pause and reverse direction.
|
|
||||||
delayCountdown = animationDelay;
|
|
||||||
animationDirection = -1;
|
|
||||||
|
|
||||||
// also bounds check it
|
|
||||||
frame = frames - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame < 0) {
|
|
||||||
// reached the start again
|
|
||||||
frame = 0;
|
|
||||||
animationDirection = 1;
|
|
||||||
animating = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Self.ShowLayer(frame);
|
|
||||||
}, animationSpeed);
|
|
||||||
|
|
||||||
Events.OnCollide( function() {
|
Events.OnCollide( function() {
|
||||||
animating = true; // start the animation
|
if (animating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
animating = true;
|
||||||
|
Self.PlayAnimation("open", function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
Self.PlayAnimation("close", function() {
|
||||||
|
animating = false;
|
||||||
|
});
|
||||||
|
}, 3000);
|
||||||
})
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"git.kirsle.net/apps/doodle/lib/render"
|
"git.kirsle.net/apps/doodle/lib/render"
|
||||||
"git.kirsle.net/apps/doodle/pkg/doodads"
|
"git.kirsle.net/apps/doodle/pkg/doodads"
|
||||||
"git.kirsle.net/apps/doodle/pkg/level"
|
"git.kirsle.net/apps/doodle/pkg/level"
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
uuid "github.com/satori/go.uuid"
|
uuid "github.com/satori/go.uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,6 +26,11 @@ type Actor struct {
|
||||||
|
|
||||||
activeLayer int // active drawing frame for display
|
activeLayer int // active drawing frame for display
|
||||||
flagDestroy bool // flag the actor for destruction
|
flagDestroy bool // flag the actor for destruction
|
||||||
|
|
||||||
|
// Animation variables.
|
||||||
|
animations map[string]*Animation
|
||||||
|
activeAnimation *Animation
|
||||||
|
animationCallback otto.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewActor sets up a uix.Actor.
|
// NewActor sets up a uix.Actor.
|
||||||
|
@ -49,6 +55,7 @@ func NewActor(id string, levelActor *level.Actor, doodad *doodads.Doodad) *Actor
|
||||||
Drawing: doodads.NewDrawing(id, doodad),
|
Drawing: doodads.NewDrawing(id, doodad),
|
||||||
Actor: levelActor,
|
Actor: levelActor,
|
||||||
Canvas: can,
|
Canvas: can,
|
||||||
|
animations: map[string]*Animation{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give the Canvas a pointer to its (parent) Actor so it can draw its debug
|
// Give the Canvas a pointer to its (parent) Actor so it can draw its debug
|
||||||
|
|
138
pkg/uix/actor_animation.go
Normal file
138
pkg/uix/actor_animation.go
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
package uix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/log"
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Animation holds a named animation for a doodad script.
|
||||||
|
type Animation struct {
|
||||||
|
Name string
|
||||||
|
Interval time.Duration
|
||||||
|
Layers []int
|
||||||
|
|
||||||
|
// runtime state variables
|
||||||
|
activeLayer int
|
||||||
|
nextFrameAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
TickAnimation advances an animation forward.
|
||||||
|
|
||||||
|
This method is called by canvas.Loop() only when the actor is currently
|
||||||
|
`animating` and their current animation's nextFrameAt has been reached by the
|
||||||
|
current time.Now().
|
||||||
|
|
||||||
|
Returns true when the animation has finished and false if there is still more
|
||||||
|
frames left to animate.
|
||||||
|
*/
|
||||||
|
func (a *Actor) TickAnimation(an *Animation) bool {
|
||||||
|
an.activeLayer++
|
||||||
|
if an.activeLayer < len(an.Layers) {
|
||||||
|
log.Warn("TickAnimation(%s): new layer=%d", a.activeAnimation.Name, an.Layers[an.activeLayer])
|
||||||
|
a.ShowLayer(an.Layers[an.activeLayer])
|
||||||
|
} else if an.activeLayer >= len(an.Layers) {
|
||||||
|
// final layer has been shown for 2 ticks, return that the animation has
|
||||||
|
// been concluded.
|
||||||
|
log.Warn("TickAnimation(%s): finished", a.activeAnimation.Name)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedule the next frame of animation.
|
||||||
|
an.nextFrameAt = time.Now().Add(an.Interval)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAnimation installs a new animation into the scripting engine for this actor.
|
||||||
|
//
|
||||||
|
// The layers can be an array of string names or integer indexes.
|
||||||
|
func (a *Actor) AddAnimation(name string, interval int64, layers []interface{}) error {
|
||||||
|
if len(layers) == 0 {
|
||||||
|
return errors.New("no named layers given to AddAnimation()")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all the layers by name.
|
||||||
|
var indexes []int
|
||||||
|
for _, name := range layers {
|
||||||
|
switch v := name.(type) {
|
||||||
|
case string:
|
||||||
|
var found bool
|
||||||
|
for i, layer := range a.Doodad.Layers {
|
||||||
|
if layer.Name == v {
|
||||||
|
indexes = append(indexes, i)
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return fmt.Errorf("layer named '%s' not found in doodad", v)
|
||||||
|
}
|
||||||
|
case int64:
|
||||||
|
// TODO: I want to find out if this is ever not an int64 coming from
|
||||||
|
// JavaScript.
|
||||||
|
if reflect.TypeOf(v).String() != "int64" {
|
||||||
|
log.Error("AddAnimation: expected an int64 from JavaScript but got a %s", reflect.TypeOf(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
iv := int(v)
|
||||||
|
if iv < len(a.Doodad.Layers) {
|
||||||
|
indexes = append(indexes, iv)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("layer numbered '%d' is out of bounds", iv)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(
|
||||||
|
"invalid type for layer '%+v': should be a string (named layer) "+
|
||||||
|
"or int (indexed layer) but was a %s", v, reflect.TypeOf(name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.animations[name] = &Animation{
|
||||||
|
Name: name,
|
||||||
|
Interval: time.Duration(interval) * time.Millisecond,
|
||||||
|
Layers: indexes,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlayAnimation starts an animation and then calls a JavaScript function when
|
||||||
|
// the last frame has played out. Set a null function to ignore the callback.
|
||||||
|
func (a *Actor) PlayAnimation(name string, callback otto.Value) error {
|
||||||
|
anim, ok := a.animations[name]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("animation named '%s' not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.activeAnimation = anim
|
||||||
|
a.animationCallback = callback
|
||||||
|
|
||||||
|
// Show the first layer.
|
||||||
|
anim.activeLayer = 0
|
||||||
|
anim.nextFrameAt = time.Now().Add(anim.Interval)
|
||||||
|
a.ShowLayer(anim.Layers[0])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAnimating returns if the current actor is playing an animation.
|
||||||
|
func (a *Actor) IsAnimating() bool {
|
||||||
|
return a.activeAnimation != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopAnimation stops any current animations.
|
||||||
|
func (a *Actor) StopAnimation() {
|
||||||
|
if a.activeAnimation == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
a.activeAnimation = nil
|
||||||
|
a.animationCallback = otto.NullValue()
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.kirsle.net/apps/doodle/lib/events"
|
"git.kirsle.net/apps/doodle/lib/events"
|
||||||
"git.kirsle.net/apps/doodle/lib/render"
|
"git.kirsle.net/apps/doodle/lib/render"
|
||||||
|
@ -15,6 +16,7 @@ import (
|
||||||
"git.kirsle.net/apps/doodle/pkg/log"
|
"git.kirsle.net/apps/doodle/pkg/log"
|
||||||
"git.kirsle.net/apps/doodle/pkg/scripting"
|
"git.kirsle.net/apps/doodle/pkg/scripting"
|
||||||
"git.kirsle.net/apps/doodle/pkg/wallpaper"
|
"git.kirsle.net/apps/doodle/pkg/wallpaper"
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Canvas is a custom ui.Widget that manages a single drawing.
|
// Canvas is a custom ui.Widget that manages a single drawing.
|
||||||
|
@ -171,6 +173,9 @@ func (w *Canvas) Loop(ev *events.State) error {
|
||||||
}
|
}
|
||||||
_ = w.loopConstrainScroll()
|
_ = w.loopConstrainScroll()
|
||||||
|
|
||||||
|
// Current time of this loop so we can advance animations.
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
// Remove any actors that were destroyed the previous tick.
|
// Remove any actors that were destroyed the previous tick.
|
||||||
var newActors []*Actor
|
var newActors []*Actor
|
||||||
for _, a := range w.actors {
|
for _, a := range w.actors {
|
||||||
|
@ -187,6 +192,19 @@ func (w *Canvas) Loop(ev *events.State) error {
|
||||||
// rectangles so we can later see if any pair of actors intersect each other.
|
// rectangles so we can later see if any pair of actors intersect each other.
|
||||||
boxes := make([]render.Rect, len(w.actors))
|
boxes := make([]render.Rect, len(w.actors))
|
||||||
for i, a := range w.actors {
|
for i, a := range w.actors {
|
||||||
|
// Advance any animations for this actor.
|
||||||
|
if a.activeAnimation != nil && a.activeAnimation.nextFrameAt.Before(now) {
|
||||||
|
if done := a.TickAnimation(a.activeAnimation); done {
|
||||||
|
// Animation has finished, run the callback script.
|
||||||
|
if a.animationCallback.IsFunction() {
|
||||||
|
a.animationCallback.Call(otto.NullValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the animation state.
|
||||||
|
a.StopAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get the actor's velocity to see if it's moving this tick.
|
// Get the actor's velocity to see if it's moving this tick.
|
||||||
v := a.Velocity()
|
v := a.Velocity()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user