Added exploding monsters.
This commit is contained in:
parent
3198659d11
commit
aab65a984a
@ -15,9 +15,11 @@ type appContext struct {
|
|||||||
Score *tins2021.ScoreState
|
Score *tins2021.ScoreState
|
||||||
Debug bool
|
Debug bool
|
||||||
|
|
||||||
StarTexture tins2021.AnimatedTexture
|
StarTexture tins2021.AnimatedTexture
|
||||||
HeartTexture tins2021.AnimatedTexture
|
HeartTexture tins2021.AnimatedTexture
|
||||||
MonsterTextures map[tins2021.MonsterType]tins2021.AnimatedTexture
|
MonsterTextureNames map[tins2021.MonsterType]string
|
||||||
|
MonsterTextures map[tins2021.MonsterType]tins2021.AnimatedTexture
|
||||||
|
DyingMonsterTextures map[tins2021.MonsterType]tins2021.AnimatedTexture
|
||||||
|
|
||||||
Audio *AudioPlayer
|
Audio *AudioPlayer
|
||||||
MenuMusic *Music
|
MenuMusic *Music
|
||||||
@ -28,17 +30,27 @@ type appContext struct {
|
|||||||
func newAppContext(ctx ui.Context, settings *tins2021.Settings, score *tins2021.ScoreState, setView func(ui.Control)) *appContext {
|
func newAppContext(ctx ui.Context, settings *tins2021.Settings, score *tins2021.ScoreState, setView func(ui.Control)) *appContext {
|
||||||
textures := textureGenerator{}
|
textures := textureGenerator{}
|
||||||
app := &appContext{
|
app := &appContext{
|
||||||
setView: setView,
|
setView: setView,
|
||||||
Settings: settings,
|
Settings: settings,
|
||||||
Audio: NewAudioPlayer(ctx.Resources(), "resources/sounds/"),
|
Audio: NewAudioPlayer(ctx.Resources(), "resources/sounds/"),
|
||||||
Score: score,
|
Score: score,
|
||||||
StarTexture: newAnimatedTexture(ctx, "star", defaultAnimationFrames, textures.Star),
|
StarTexture: newAnimatedTexture(ctx, "star", defaultAnimationFrames, textures.Star),
|
||||||
HeartTexture: newAnimatedTexture(ctx, "heart", defaultAnimationFrames, textures.Heart),
|
HeartTexture: newAnimatedTexture(ctx, "heart", defaultAnimationFrames, textures.Heart),
|
||||||
MonsterTextures: map[tins2021.MonsterType]tins2021.AnimatedTexture{
|
MonsterTextureNames: map[tins2021.MonsterType]string{},
|
||||||
tins2021.MonsterTypeStraight: newAnimatedTexture(ctx, "straight-walking-monster", defaultAnimationFrames, textures.StraightWalkingMonster),
|
MonsterTextures: map[tins2021.MonsterType]tins2021.AnimatedTexture{},
|
||||||
tins2021.MonsterTypeRandom: newAnimatedTexture(ctx, "random-walking-monster", defaultAnimationFrames, textures.RandomWalkingMonster),
|
DyingMonsterTextures: map[tins2021.MonsterType]tins2021.AnimatedTexture{},
|
||||||
tins2021.MonsterTypeChaser: newAnimatedTexture(ctx, "chasing-monster", defaultAnimationFrames, textures.ChasingMonster),
|
}
|
||||||
},
|
|
||||||
|
monsterNames := map[tins2021.MonsterType]string{
|
||||||
|
tins2021.MonsterTypeStraight: "straight-walking",
|
||||||
|
tins2021.MonsterTypeRandom: "random-walking",
|
||||||
|
tins2021.MonsterTypeChaser: "chasing",
|
||||||
|
}
|
||||||
|
for typ, name := range monsterNames {
|
||||||
|
textureName := fmt.Sprintf("%s-monster", name)
|
||||||
|
app.MonsterTextureNames[typ] = textureName
|
||||||
|
app.MonsterTextures[typ] = newAnimatedTexture(ctx, textureName, defaultAnimationFrames, textures.Monster(typ))
|
||||||
|
app.DyingMonsterTextures[typ] = newAnimatedTexture(ctx, fmt.Sprintf("%s-dying-monster", name), defaultAnimationFrames, textures.DyingMonster(typ))
|
||||||
}
|
}
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
@ -44,6 +44,7 @@ func (l *infoLegend) Render(ctx ui.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newInfo(app *appContext, ctx ui.Context) ui.Control {
|
func newInfo(app *appContext, ctx ui.Context) ui.Control {
|
||||||
|
monsterName := func(typ tins2021.MonsterType) string { return app.MonsterTextureNames[typ] }
|
||||||
legend := ui.BuildStackPanel(ui.OrientationVertical, func(p *ui.StackPanel) {
|
legend := ui.BuildStackPanel(ui.OrientationVertical, func(p *ui.StackPanel) {
|
||||||
p.AddChild(&infoLegend{
|
p.AddChild(&infoLegend{
|
||||||
Icon: ctx.Textures().ScaledByName("star", infoLegendIconSize),
|
Icon: ctx.Textures().ScaledByName("star", infoLegendIconSize),
|
||||||
@ -54,15 +55,15 @@ func newInfo(app *appContext, ctx ui.Context) ui.Control {
|
|||||||
Description: "Gives (back) a life.",
|
Description: "Gives (back) a life.",
|
||||||
})
|
})
|
||||||
p.AddChild(&infoLegend{
|
p.AddChild(&infoLegend{
|
||||||
Icon: ctx.Textures().ScaledByName("straight-walking-monster", infoLegendIconSize),
|
Icon: ctx.Textures().ScaledByName(monsterName(tins2021.MonsterTypeStraight), infoLegendIconSize),
|
||||||
Description: "Monster that walks over a fixed diagonal.",
|
Description: "Monster that walks over a fixed diagonal.",
|
||||||
})
|
})
|
||||||
p.AddChild(&infoLegend{
|
p.AddChild(&infoLegend{
|
||||||
Icon: ctx.Textures().ScaledByName("random-walking-monster", infoLegendIconSize),
|
Icon: ctx.Textures().ScaledByName(monsterName(tins2021.MonsterTypeRandom), infoLegendIconSize),
|
||||||
Description: "Monster that walks randomly.",
|
Description: "Monster that walks randomly.",
|
||||||
})
|
})
|
||||||
p.AddChild(&infoLegend{
|
p.AddChild(&infoLegend{
|
||||||
Icon: ctx.Textures().ScaledByName("chasing-monster", infoLegendIconSize),
|
Icon: ctx.Textures().ScaledByName(monsterName(tins2021.MonsterTypeChaser), infoLegendIconSize),
|
||||||
Description: "Monster that walks towards you.",
|
Description: "Monster that walks towards you.",
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@ -24,8 +25,10 @@ type levelController struct {
|
|||||||
Inverted tins2021.NamedTexture
|
Inverted tins2021.NamedTexture
|
||||||
Animations map[string]*tins2021.Animations
|
Animations map[string]*tins2021.Animations
|
||||||
|
|
||||||
IdleMonsters *tins2021.Animations
|
IdleMonsters *tins2021.Animations
|
||||||
MovingMonsters *tins2021.Animations
|
MovingMonsters *tins2021.Animations
|
||||||
|
DyingMonsters *tins2021.Animations
|
||||||
|
DyingMonsterTypes map[geom.Point]tins2021.MonsterType
|
||||||
|
|
||||||
SmallFont *tins2021.BitmapFont
|
SmallFont *tins2021.BitmapFont
|
||||||
|
|
||||||
@ -134,17 +137,26 @@ func (r *levelController) Handle(ctx ui.Context, e ui.Event) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
monsterHit := func(hit *tins2021.MonsterHit) {
|
||||||
|
r.app.Audio.PlaySample("player_hurt.mp3")
|
||||||
|
if hit == nil {
|
||||||
|
log.Printf("player was hit by monster but we don't know exactly where?\n")
|
||||||
|
}
|
||||||
|
r.DyingMonsters.Frame(hit.Position)
|
||||||
|
r.DyingMonsterTypes[hit.Position] = hit.Type
|
||||||
|
}
|
||||||
|
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case *ui.KeyDownEvent:
|
case *ui.KeyDownEvent:
|
||||||
dir, ok := r.Controls[e.Key]
|
dir, ok := r.Controls[e.Key]
|
||||||
if ok {
|
if ok {
|
||||||
stars, lives := r.Level.StarsCollected, r.Level.Lives
|
stars, lives := r.Level.StarsCollected, r.Level.Lives
|
||||||
r.Level.MovePlayer(dir)
|
_, hit := r.Level.MovePlayer(dir)
|
||||||
switch {
|
switch {
|
||||||
case r.Level.StarsCollected > stars:
|
case r.Level.StarsCollected > stars:
|
||||||
r.app.Audio.PlaySample("player_collect_star.mp3")
|
r.app.Audio.PlaySample("player_collect_star.mp3")
|
||||||
case r.Level.Lives < lives:
|
case r.Level.Lives < lives:
|
||||||
r.app.Audio.PlaySample("player_hurt.mp3")
|
monsterHit(hit)
|
||||||
case r.Level.Lives > lives:
|
case r.Level.Lives > lives:
|
||||||
r.app.Audio.PlaySample("player_collect_heart.mp3")
|
r.app.Audio.PlaySample("player_collect_heart.mp3")
|
||||||
}
|
}
|
||||||
@ -161,6 +173,7 @@ func (r *levelController) Handle(ctx ui.Context, e ui.Event) bool {
|
|||||||
}
|
}
|
||||||
r.IdleMonsters.Update()
|
r.IdleMonsters.Update()
|
||||||
r.MovingMonsters.Update()
|
r.MovingMonsters.Update()
|
||||||
|
r.DyingMonsters.Update()
|
||||||
var jumped []geom.Point
|
var jumped []geom.Point
|
||||||
for pos, animation := range r.MovingMonsters.Values {
|
for pos, animation := range r.MovingMonsters.Values {
|
||||||
if animation.Frame < 40 { // after 40 frames the player hit is checked
|
if animation.Frame < 40 { // after 40 frames the player hit is checked
|
||||||
@ -168,10 +181,10 @@ func (r *levelController) Handle(ctx ui.Context, e ui.Event) bool {
|
|||||||
}
|
}
|
||||||
target := r.Level.MonsterTargets[pos]
|
target := r.Level.MonsterTargets[pos]
|
||||||
if target == r.Level.Player { // player is hit
|
if target == r.Level.Player { // player is hit
|
||||||
|
monsterHit(&tins2021.MonsterHit{Position: target, Type: r.Level.Monsters[pos].Type()})
|
||||||
r.Level.DestroyMonster(pos)
|
r.Level.DestroyMonster(pos)
|
||||||
jumped = append(jumped, pos)
|
jumped = append(jumped, pos)
|
||||||
r.Level.DecrementLive()
|
r.Level.DecrementLive()
|
||||||
r.app.Audio.PlaySample("player_hurt.mp3")
|
|
||||||
checkGameOver()
|
checkGameOver()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -224,6 +237,8 @@ func (r *levelController) Play(level *tins2021.Level) {
|
|||||||
}
|
}
|
||||||
r.IdleMonsters = tins2021.NewAnimations(40*time.Millisecond, 100, false, false)
|
r.IdleMonsters = tins2021.NewAnimations(40*time.Millisecond, 100, false, false)
|
||||||
r.MovingMonsters = tins2021.NewAnimations(16*time.Millisecond, 50, false, false)
|
r.MovingMonsters = tins2021.NewAnimations(16*time.Millisecond, 50, false, false)
|
||||||
|
r.DyingMonsters = tins2021.NewAnimations(16*time.Millisecond, 20, false, false)
|
||||||
|
r.DyingMonsterTypes = map[geom.Point]tins2021.MonsterType{}
|
||||||
for monster := range level.Monsters {
|
for monster := range level.Monsters {
|
||||||
r.IdleMonsters.Frame(monster)
|
r.IdleMonsters.Frame(monster)
|
||||||
}
|
}
|
||||||
@ -272,6 +287,10 @@ func (r levelController) Render(ctx ui.Context) {
|
|||||||
for typ, animation := range r.app.MonsterTextures {
|
for typ, animation := range r.app.MonsterTextures {
|
||||||
monsterTextures[typ] = animation.Scale(scale * .4)
|
monsterTextures[typ] = animation.Scale(scale * .4)
|
||||||
}
|
}
|
||||||
|
dyingMonsterTextures := map[tins2021.MonsterType]tins2021.AnimatedTexture{}
|
||||||
|
for typ, animation := range r.app.DyingMonsterTextures {
|
||||||
|
dyingMonsterTextures[typ] = animation.Scale(scale * .4)
|
||||||
|
}
|
||||||
propHeight := star.FrameSize(0).Y
|
propHeight := star.FrameSize(0).Y
|
||||||
propOffset := geom.PtF32(-.5*float32(propHeight), -.8*float32(propHeight))
|
propOffset := geom.PtF32(-.5*float32(propHeight), -.8*float32(propHeight))
|
||||||
|
|
||||||
@ -338,6 +357,22 @@ func (r levelController) Render(ctx ui.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var died []geom.Point
|
||||||
|
for pos, monster := range r.DyingMonsterTypes {
|
||||||
|
frame := r.DyingMonsters.Frame(pos)
|
||||||
|
if frame == 20 {
|
||||||
|
died = append(died, pos)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
texture := dyingMonsterTextures[monster]
|
||||||
|
_, platformPos := positionOfTile(pos)
|
||||||
|
texture.Draw(renderer, platformPos.Add(propOffset), frame)
|
||||||
|
}
|
||||||
|
for _, pos := range died {
|
||||||
|
delete(r.DyingMonsters.Values, pos)
|
||||||
|
delete(r.DyingMonsterTypes, pos)
|
||||||
|
}
|
||||||
|
|
||||||
textColor := ctx.Style().Palette.Text
|
textColor := ctx.Style().Palette.Text
|
||||||
scoreFont := ctx.Fonts().Font("score")
|
scoreFont := ctx.Fonts().Font("score")
|
||||||
fontOffsetY := .5 * (float32(propHeight) - scoreFont.Height())
|
fontOffsetY := .5 * (float32(propHeight) - scoreFont.Height())
|
||||||
|
BIN
cmd/tins2021/resources/textures/chasing-dying-monster.png
Normal file
BIN
cmd/tins2021/resources/textures/chasing-dying-monster.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
BIN
cmd/tins2021/resources/textures/random-walking-dying-monster.png
Normal file
BIN
cmd/tins2021/resources/textures/random-walking-dying-monster.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
@ -16,12 +16,29 @@ func (textureGenerator) Heart() image.Image {
|
|||||||
return tins2021.AnimatePolygon(tins2021.CreateHeart(), tins2021.Red, defaultAnimationFrames, tins2021.MeshRotateAnimation{})
|
return tins2021.AnimatePolygon(tins2021.CreateHeart(), tins2021.Red, defaultAnimationFrames, tins2021.MeshRotateAnimation{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (textureGenerator) monster(color string) image.Image {
|
func (g textureGenerator) MonsterTypeColor(typ tins2021.MonsterType) string {
|
||||||
return tins2021.AnimatePolygon(tins2021.CreateHexagon(), color, defaultAnimationFrames, tins2021.MeshWobbleTransformation{Wobble: 30})
|
switch typ {
|
||||||
|
case tins2021.MonsterTypeStraight:
|
||||||
|
return tins2021.Green
|
||||||
|
case tins2021.MonsterTypeRandom:
|
||||||
|
return tins2021.Blue
|
||||||
|
case tins2021.MonsterTypeChaser:
|
||||||
|
return tins2021.Purple
|
||||||
|
default:
|
||||||
|
panic("monster does not have a color")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g textureGenerator) ChasingMonster() image.Image { return g.monster(tins2021.Purple) }
|
func (g textureGenerator) Monster(typ tins2021.MonsterType) func() image.Image {
|
||||||
|
color := g.MonsterTypeColor(typ)
|
||||||
|
return func() image.Image {
|
||||||
|
return tins2021.AnimatePolygon(tins2021.CreateHexagon(), color, defaultAnimationFrames, tins2021.MeshWobbleTransformation{Wobble: 30})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (g textureGenerator) RandomWalkingMonster() image.Image { return g.monster(tins2021.Blue) }
|
func (g textureGenerator) DyingMonster(typ tins2021.MonsterType) func() image.Image {
|
||||||
|
color := g.MonsterTypeColor(typ)
|
||||||
func (g textureGenerator) StraightWalkingMonster() image.Image { return g.monster(tins2021.Green) }
|
return func() image.Image {
|
||||||
|
return tins2021.Animate(color, defaultAnimationFrames, &tins2021.ExplodingHexagonAnimation{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
16
level.go
16
level.go
@ -92,10 +92,15 @@ func (l *Level) MoveMonster(target, source geom.Point) {
|
|||||||
l.DestroyMonster(source)
|
l.DestroyMonster(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Level) MovePlayer(dir Direction) bool {
|
type MonsterHit struct {
|
||||||
|
Position geom.Point
|
||||||
|
Type MonsterType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Level) MovePlayer(dir Direction) (bool, *MonsterHit) {
|
||||||
towards, allowed := l.CanPlayerMove(dir)
|
towards, allowed := l.CanPlayerMove(dir)
|
||||||
if !allowed {
|
if !allowed {
|
||||||
return false
|
return false, nil
|
||||||
}
|
}
|
||||||
l.Player = towards
|
l.Player = towards
|
||||||
tile := l.Tiles[towards]
|
tile := l.Tiles[towards]
|
||||||
@ -109,13 +114,18 @@ func (l *Level) MovePlayer(dir Direction) bool {
|
|||||||
tile.Star = false
|
tile.Star = false
|
||||||
l.Score += 25
|
l.Score += 25
|
||||||
}
|
}
|
||||||
|
var hit *MonsterHit
|
||||||
if l.Monsters[towards] != nil {
|
if l.Monsters[towards] != nil {
|
||||||
|
hit = &MonsterHit{
|
||||||
|
Position: towards,
|
||||||
|
Type: l.Monsters[towards].Type(),
|
||||||
|
}
|
||||||
l.DecrementLive()
|
l.DecrementLive()
|
||||||
l.DestroyMonster(towards)
|
l.DestroyMonster(towards)
|
||||||
l.Score -= 5
|
l.Score -= 5
|
||||||
}
|
}
|
||||||
l.Score -= 1 // for every move
|
l.Score -= 1 // for every move
|
||||||
return true
|
return true, hit
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Level) Randomize(difficulty int, stars int) {
|
func (l *Level) Randomize(difficulty int, stars int) {
|
||||||
|
15
math.go
Normal file
15
math.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package tins2021
|
||||||
|
|
||||||
|
import "opslag.de/schobers/geom"
|
||||||
|
|
||||||
|
func Polar(a, r float64) geom.PointF {
|
||||||
|
return geom.PtF(r*geom.Cos(a), r*geom.Sin(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
func PolarUnity(a float64) geom.PointF {
|
||||||
|
return geom.PtF(geom.Cos(a), geom.Sin(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Percentage(i, n int) float64 {
|
||||||
|
return float64(i) / float64(n)
|
||||||
|
}
|
148
meshanimation.go
148
meshanimation.go
@ -28,19 +28,17 @@ var (
|
|||||||
light = fauxgl.V(.5, 1, .75).Normalize() // light direction
|
light = fauxgl.V(.5, 1, .75).Normalize() // light direction
|
||||||
)
|
)
|
||||||
|
|
||||||
func animateMesh(mesh *fauxgl.Mesh, hexColor string, frames int, transform MeshAnimationTransformer) image.Image {
|
func Animate(hexColor string, frames int, animator MeshAnimator) image.Image {
|
||||||
const scale = 4
|
const scale = 4
|
||||||
const s = 1.1
|
const s = 1.1
|
||||||
|
|
||||||
mesh.BiUnitCube()
|
|
||||||
|
|
||||||
matrix := fauxgl.Orthographic(-s, s, -s, s, near, far).Mul(fauxgl.LookAt(eye, center, up))
|
matrix := fauxgl.Orthographic(-s, s, -s, s, near, far).Mul(fauxgl.LookAt(eye, center, up))
|
||||||
animation := image.NewNRGBA(image.Rect(0, 0, TextureSize*frames, TextureSize))
|
animation := image.NewNRGBA(image.Rect(0, 0, TextureSize*frames, TextureSize))
|
||||||
|
|
||||||
threads := ints.Max(1, runtime.NumCPU())
|
threads := ints.Max(1, runtime.NumCPU())
|
||||||
framesC := make(chan int, threads)
|
framesC := make(chan int, threads)
|
||||||
|
|
||||||
wait := parallel(threads, func() {
|
wait := parallel(1, func() {
|
||||||
context := fauxgl.NewContext(TextureSize*scale, TextureSize*scale)
|
context := fauxgl.NewContext(TextureSize*scale, TextureSize*scale)
|
||||||
color := fauxgl.HexColor(hexColor)
|
color := fauxgl.HexColor(hexColor)
|
||||||
|
|
||||||
@ -53,9 +51,8 @@ func animateMesh(mesh *fauxgl.Mesh, hexColor string, frames int, transform MeshA
|
|||||||
shader.AmbientColor = fauxgl.MakeColor(mustHexColor(`#7F7F7F`))
|
shader.AmbientColor = fauxgl.MakeColor(mustHexColor(`#7F7F7F`))
|
||||||
context.Shader = shader
|
context.Shader = shader
|
||||||
|
|
||||||
copy := mesh.Copy()
|
mesh := animator.animate(FrameState{Current: i, TotalFrames: frames})
|
||||||
transform.transform(copy, FrameState{Current: i, TotalFrames: frames})
|
context.DrawMesh(mesh)
|
||||||
context.DrawMesh(copy)
|
|
||||||
|
|
||||||
frame := resize.Resize(TextureSize, TextureSize, context.Image(), resize.Bilinear)
|
frame := resize.Resize(TextureSize, TextureSize, context.Image(), resize.Bilinear)
|
||||||
draw.Copy(animation, image.Pt(i*TextureSize, 0), frame, frame.Bounds(), draw.Src, nil)
|
draw.Copy(animation, image.Pt(i*TextureSize, 0), frame, frame.Bounds(), draw.Src, nil)
|
||||||
@ -70,12 +67,12 @@ func animateMesh(mesh *fauxgl.Mesh, hexColor string, frames int, transform MeshA
|
|||||||
return animation
|
return animation
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnimatePolygon(polygon geom.PolygonF, hexColor string, frames int, transform MeshAnimationTransformer) image.Image {
|
func AnimatePolygon(polygon geom.PolygonF, hexColor string, frames int, transformer MeshTransformer) image.Image {
|
||||||
mesh := generateMeshFromPolygon(polygon, .2)
|
animation := newMeshAnimation(generateMeshFromPolygon(polygon, .2), transformer)
|
||||||
return animateMesh(mesh, hexColor, frames, transform)
|
return Animate(hexColor, frames, animation)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnimateSTL(resources ui.PhysicalResources, name, hexColor string, frames int, transform MeshAnimationTransformer) image.Image {
|
func AnimateSTL(resources ui.PhysicalResources, name, hexColor string, frames int, transformer MeshTransformer) image.Image {
|
||||||
path, err := resources.FetchResource(name)
|
path, err := resources.FetchResource(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -84,7 +81,53 @@ func AnimateSTL(resources ui.PhysicalResources, name, hexColor string, frames in
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return animateMesh(mesh, hexColor, frames, transform)
|
return Animate(hexColor, frames, newMeshAnimation(mesh, transformer))
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExplodingHexagonAnimation struct {
|
||||||
|
Biunit fauxgl.Matrix
|
||||||
|
}
|
||||||
|
|
||||||
|
func rotateMeshAround(mesh *fauxgl.Mesh, around fauxgl.Vector, angle float64) {
|
||||||
|
mesh.Transform(fauxgl.Translate(fauxgl.V(-around.X, -around.Y, -around.Z)))
|
||||||
|
mesh.Transform(fauxgl.Rotate(fauxgl.V(0, 1, 0), angle))
|
||||||
|
mesh.Transform(fauxgl.Translate(around))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ExplodingHexagonAnimation) animate(s FrameState) *fauxgl.Mesh {
|
||||||
|
ani := s.Animation()
|
||||||
|
|
||||||
|
mesh := fauxgl.NewEmptyMesh()
|
||||||
|
const parts = 6
|
||||||
|
|
||||||
|
const oneThird = float64(1) / 3
|
||||||
|
const twoThirds = float64(2) / 3
|
||||||
|
const da = twoThirds * geom.Pi
|
||||||
|
partHeight := geom.Sqrt(3) / 2
|
||||||
|
for part := 0; part < parts; part++ {
|
||||||
|
a := 2 * geom.Pi * float64(part) / float64(parts)
|
||||||
|
|
||||||
|
closeLength := (float64(.25) + .75*(1-ani))
|
||||||
|
|
||||||
|
center := Polar(a, twoThirds*partHeight)
|
||||||
|
aa := a + ani*geom.Pi
|
||||||
|
far := center.Add(Polar(aa, oneThird*partHeight))
|
||||||
|
farWidth := float64(.5) * (1 - ani)
|
||||||
|
right := far.Add(Polar(aa-.5*geom.Pi, farWidth))
|
||||||
|
left := far.Add(Polar(aa+.5*geom.Pi, farWidth))
|
||||||
|
close := center.Add(Polar(aa+geom.Pi, closeLength*twoThirds*partHeight))
|
||||||
|
|
||||||
|
partMesh := generateMeshFromPolygon(geom.PolF(right, left, close), .2)
|
||||||
|
rotateMeshAround(partMesh, fauxgl.V(-center.X, -center.Y, 0), ani*geom.Pi)
|
||||||
|
mesh.Add(partMesh)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Current == 0 {
|
||||||
|
a.Biunit = mesh.BiUnitCube()
|
||||||
|
} else {
|
||||||
|
mesh.Transform(a.Biunit)
|
||||||
|
}
|
||||||
|
return mesh
|
||||||
}
|
}
|
||||||
|
|
||||||
type FrameState struct {
|
type FrameState struct {
|
||||||
@ -94,7 +137,7 @@ type FrameState struct {
|
|||||||
|
|
||||||
func (s FrameState) Animation() float64 { return float64(s.Current) / float64(s.TotalFrames) }
|
func (s FrameState) Animation() float64 { return float64(s.Current) / float64(s.TotalFrames) }
|
||||||
|
|
||||||
func generateMeshFromPolygon(polygon geom.PolygonF, thickness float64) *fauxgl.Mesh {
|
func generateTrianglesForPolygon(polygon geom.PolygonF, thickness float64) []*fauxgl.Triangle {
|
||||||
vec := func(p geom.PointF, z float64) fauxgl.Vector { return fauxgl.V(p.X, p.Y, z) }
|
vec := func(p geom.PointF, z float64) fauxgl.Vector { return fauxgl.V(p.X, p.Y, z) }
|
||||||
tri := fauxgl.NewTriangleForPoints
|
tri := fauxgl.NewTriangleForPoints
|
||||||
face := func(q, r, s geom.PointF, n float64) *fauxgl.Triangle {
|
face := func(q, r, s geom.PointF, n float64) *fauxgl.Triangle {
|
||||||
@ -115,7 +158,11 @@ func generateMeshFromPolygon(polygon geom.PolygonF, thickness float64) *fauxgl.M
|
|||||||
q, r, s, t := vec(p, back), vec(next, back), vec(next, front), vec(p, front)
|
q, r, s, t := vec(p, back), vec(next, back), vec(next, front), vec(p, front)
|
||||||
triangles = append(triangles, tri(q, r, s), tri(q, s, t))
|
triangles = append(triangles, tri(q, r, s), tri(q, s, t))
|
||||||
}
|
}
|
||||||
|
return triangles
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateMeshFromPolygon(polygon geom.PolygonF, thickness float64) *fauxgl.Mesh {
|
||||||
|
triangles := generateTrianglesForPolygon(polygon, thickness)
|
||||||
mesh := fauxgl.NewTriangleMesh(triangles)
|
mesh := fauxgl.NewTriangleMesh(triangles)
|
||||||
return mesh
|
return mesh
|
||||||
}
|
}
|
||||||
@ -130,6 +177,54 @@ func iterate(n int, threads int) <-chan int {
|
|||||||
return iterator
|
return iterator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MeshAnimation struct {
|
||||||
|
MeshTransformer
|
||||||
|
|
||||||
|
mesh *fauxgl.Mesh
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMeshAnimation(mesh *fauxgl.Mesh, transformer MeshTransformer) *MeshAnimation {
|
||||||
|
mesh.BiUnitCube()
|
||||||
|
return &MeshAnimation{transformer, mesh}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *MeshAnimation) animate(s FrameState) *fauxgl.Mesh {
|
||||||
|
mesh := a.mesh.Copy()
|
||||||
|
a.MeshTransformer.transform(mesh, s)
|
||||||
|
return mesh
|
||||||
|
}
|
||||||
|
|
||||||
|
type MeshAnimator interface {
|
||||||
|
animate(FrameState) *fauxgl.Mesh
|
||||||
|
}
|
||||||
|
|
||||||
|
type MeshRotateAnimation struct{}
|
||||||
|
|
||||||
|
func (MeshRotateAnimation) transform(mesh *fauxgl.Mesh, s FrameState) {
|
||||||
|
mesh.Transform(fauxgl.Rotate(up, 2*geom.Pi*s.Animation()))
|
||||||
|
}
|
||||||
|
|
||||||
|
type MeshTransformer interface {
|
||||||
|
transform(*fauxgl.Mesh, FrameState)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MeshWobbleTransformation struct {
|
||||||
|
Wobble float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a MeshWobbleTransformation) wobble(s FrameState) float64 {
|
||||||
|
animation := float64(s.Current) / float64(s.TotalFrames)
|
||||||
|
animation += .25
|
||||||
|
if animation >= 1 {
|
||||||
|
animation -= 1
|
||||||
|
}
|
||||||
|
return geom.Abs(animation*4-2) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a MeshWobbleTransformation) transform(mesh *fauxgl.Mesh, s FrameState) {
|
||||||
|
mesh.Transform(fauxgl.Rotate(up, a.wobble(s)*a.Wobble*geom.Pi/180))
|
||||||
|
}
|
||||||
|
|
||||||
func parallel(n int, action func()) *sync.WaitGroup {
|
func parallel(n int, action func()) *sync.WaitGroup {
|
||||||
wait := &sync.WaitGroup{}
|
wait := &sync.WaitGroup{}
|
||||||
wait.Add(n)
|
wait.Add(n)
|
||||||
@ -142,33 +237,6 @@ func parallel(n int, action func()) *sync.WaitGroup {
|
|||||||
return wait
|
return wait
|
||||||
}
|
}
|
||||||
|
|
||||||
type MeshAnimationTransformer interface {
|
|
||||||
transform(*fauxgl.Mesh, FrameState)
|
|
||||||
}
|
|
||||||
|
|
||||||
type MeshRotateAnimation struct{}
|
|
||||||
|
|
||||||
func (MeshRotateAnimation) transform(mesh *fauxgl.Mesh, s FrameState) {
|
|
||||||
mesh.Transform(fauxgl.Rotate(up, 2*geom.Pi*s.Animation()))
|
|
||||||
}
|
|
||||||
|
|
||||||
type MeshWobbleTransformation struct {
|
|
||||||
Wobble float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a MeshWobbleTransformation) animate(s FrameState) float64 {
|
|
||||||
animation := float64(s.Current) / float64(s.TotalFrames)
|
|
||||||
animation += .25
|
|
||||||
if animation >= 1 {
|
|
||||||
animation -= 1
|
|
||||||
}
|
|
||||||
return geom.Abs(animation*4-2) - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a MeshWobbleTransformation) transform(mesh *fauxgl.Mesh, s FrameState) {
|
|
||||||
mesh.Transform(fauxgl.Rotate(up, a.animate(s)*a.Wobble*geom.Pi/180))
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveMeshSTL(path, name string, mesh *fauxgl.Mesh) error {
|
func saveMeshSTL(path, name string, mesh *fauxgl.Mesh) error {
|
||||||
stl, err := os.Create(path)
|
stl, err := os.Create(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"opslag.de/schobers/geom"
|
"opslag.de/schobers/geom"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var AllMonsterTypes = []MonsterType{MonsterTypeStraight, MonsterTypeRandom, MonsterTypeChaser}
|
||||||
|
|
||||||
type ChasingMonster struct{}
|
type ChasingMonster struct{}
|
||||||
|
|
||||||
func (m ChasingMonster) Type() MonsterType { return MonsterTypeChaser }
|
func (m ChasingMonster) Type() MonsterType { return MonsterTypeChaser }
|
||||||
|
24
polygons.go
24
polygons.go
@ -13,7 +13,7 @@ func CreateHeart() geom.PolygonF {
|
|||||||
var polygon geom.PolygonF
|
var polygon geom.PolygonF
|
||||||
const segments = 100
|
const segments = 100
|
||||||
for segment := 0; segment < 100; segment++ {
|
for segment := 0; segment < 100; segment++ {
|
||||||
t := 2 * geom.Pi * float64(segment) / segments
|
t := 2 * geom.Pi * Percentage(segment, segments)
|
||||||
st := geom.Sin(t)
|
st := geom.Sin(t)
|
||||||
polygon.Points = append(polygon.Points, geom.PtF(
|
polygon.Points = append(polygon.Points, geom.PtF(
|
||||||
16*st*st*st,
|
16*st*st*st,
|
||||||
@ -23,15 +23,15 @@ func CreateHeart() geom.PolygonF {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CreateHexagon() geom.PolygonF {
|
func CreateHexagon() geom.PolygonF {
|
||||||
var polygon geom.PolygonF
|
|
||||||
pt := func(rotation float64) geom.PointF {
|
|
||||||
a := .5*geom.Pi + 2*geom.Pi*rotation
|
|
||||||
return geom.PtF(geom.Cos(a), geom.Sin(a))
|
|
||||||
}
|
|
||||||
const sides = 6
|
const sides = 6
|
||||||
for side := 0; side < 6; side++ {
|
return CreateRegularPolygon(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateRegularPolygon(sides int) geom.PolygonF {
|
||||||
|
var polygon geom.PolygonF
|
||||||
|
for side := 0; side < sides; side++ {
|
||||||
polygon.Points = append(polygon.Points,
|
polygon.Points = append(polygon.Points,
|
||||||
pt(float64(side)/float64(sides)),
|
PolarUnity(float64(side)/float64(sides)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return polygon
|
return polygon
|
||||||
@ -39,14 +39,10 @@ func CreateHexagon() geom.PolygonF {
|
|||||||
|
|
||||||
func CreateStar(sides int) geom.PolygonF {
|
func CreateStar(sides int) geom.PolygonF {
|
||||||
var polygon geom.PolygonF
|
var polygon geom.PolygonF
|
||||||
pt := func(rotation float64) geom.PointF {
|
|
||||||
a := .5*geom.Pi + 2*geom.Pi*rotation
|
|
||||||
return geom.PtF(geom.Cos(a), geom.Sin(a))
|
|
||||||
}
|
|
||||||
for side := 0; side < sides; side++ {
|
for side := 0; side < sides; side++ {
|
||||||
polygon.Points = append(polygon.Points,
|
polygon.Points = append(polygon.Points,
|
||||||
pt(float64(side)/float64(sides)),
|
PolarUnity(float64(side)/float64(sides)),
|
||||||
pt((float64(side)+0.5)/float64(sides)).Mul(.5),
|
PolarUnity((float64(side)+0.5)/float64(sides)).Mul(.5),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return polygon
|
return polygon
|
||||||
|
Loading…
Reference in New Issue
Block a user