Restructure significantly, remove command/events for now.
parent
cee1d19f36
commit
460316aaf6
|
@ -0,0 +1,21 @@
|
||||||
|
package animation
|
||||||
|
|
||||||
|
// AnimationMap maps each animation ID to the actual animation
|
||||||
|
var AnimationMap map[int]entityAnimation
|
||||||
|
|
||||||
|
// Enum containing IDs of all animations that exist to make them easy to reference.
|
||||||
|
const (
|
||||||
|
PENGUIN_WALK_RIGHT int = iota
|
||||||
|
PENGUIN_WALK_LEFT
|
||||||
|
PENGUIN_WALK_UP
|
||||||
|
PENGUIN_WALK_DOWN
|
||||||
|
PENGUIN_STATIONARY_RIGHT
|
||||||
|
PENGUIN_STATIONARY_LEFT
|
||||||
|
PENGUIN_STATIONARY_UP
|
||||||
|
PENGUIN_STATIONARY_DOWN
|
||||||
|
)
|
||||||
|
|
||||||
|
func DefineAnimations() {
|
||||||
|
AnimationMap = make(map[int]entityAnimation)
|
||||||
|
DefinePenguinAnimations()
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package animation
|
||||||
|
|
||||||
|
import rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
|
||||||
|
type animationTracker struct {
|
||||||
|
currentAnimation entityAnimation
|
||||||
|
animationStep int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAnimationTracker() *animationTracker {
|
||||||
|
return &animationTracker{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *animationTracker) Draw(windowPosition rl.Vector2, color rl.Color) error {
|
||||||
|
step := a.animationStep / a.currentAnimation.GetSpeed()
|
||||||
|
|
||||||
|
err := a.currentAnimation.Draw(step, windowPosition, color)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
a.animationStep = 1 + a.animationStep
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *animationTracker) SetAnimation(id int) {
|
||||||
|
newAnim := AnimationMap[id]
|
||||||
|
|
||||||
|
if newAnim != a.currentAnimation {
|
||||||
|
a.animationStep = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
a.currentAnimation = newAnim
|
||||||
|
}
|
|
@ -60,10 +60,6 @@ func (e entityAnimation) GetSpeed() int {
|
||||||
return e.speed
|
return e.speed
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefineAnimations() {
|
|
||||||
DefinePenguinAnimations()
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCenter(dimensions rl.Vector2) rl.Vector2 {
|
func getCenter(dimensions rl.Vector2) rl.Vector2 {
|
||||||
x := dimensions.X / 2.0
|
x := dimensions.X / 2.0
|
||||||
y := dimensions.Y / 2.0
|
y := dimensions.Y / 2.0
|
|
@ -0,0 +1,85 @@
|
||||||
|
package animation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"git.wisellama.rocks/Project-Ely/project-ely/internal/sprite"
|
||||||
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PENGUIN_DEFAULT = PENGUIN_STATIONARY_RIGHT
|
||||||
|
)
|
||||||
|
|
||||||
|
var penguinAnimations []int
|
||||||
|
|
||||||
|
func DefinePenguinAnimations() {
|
||||||
|
filename := sprite.DELILAHWALKING
|
||||||
|
|
||||||
|
penguinAnimations = make([]int, 0)
|
||||||
|
penguinAnimations = append(penguinAnimations, PENGUIN_WALK_RIGHT)
|
||||||
|
penguinAnimations = append(penguinAnimations, PENGUIN_WALK_LEFT)
|
||||||
|
penguinAnimations = append(penguinAnimations, PENGUIN_WALK_UP)
|
||||||
|
penguinAnimations = append(penguinAnimations, PENGUIN_WALK_DOWN)
|
||||||
|
penguinAnimations = append(penguinAnimations, PENGUIN_STATIONARY_RIGHT)
|
||||||
|
penguinAnimations = append(penguinAnimations, PENGUIN_STATIONARY_LEFT)
|
||||||
|
penguinAnimations = append(penguinAnimations, PENGUIN_STATIONARY_UP)
|
||||||
|
penguinAnimations = append(penguinAnimations, PENGUIN_STATIONARY_DOWN)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dimensions rl.Vector2
|
||||||
|
offset rl.Vector2
|
||||||
|
center rl.Vector2
|
||||||
|
length int
|
||||||
|
speed int
|
||||||
|
border int
|
||||||
|
)
|
||||||
|
|
||||||
|
dimensions = rl.Vector2{X: 13, Y: 17}
|
||||||
|
|
||||||
|
// Walking Right is in the spritesheet.
|
||||||
|
speed = 5
|
||||||
|
offset = rl.Vector2{X: 0, Y: 1}
|
||||||
|
length = 5
|
||||||
|
border = 1 // optional border around each sprite
|
||||||
|
center = getCenter(dimensions) // center is for rotation, nil will default to w/2 h/2
|
||||||
|
walkRight := sprite.NewAnimation(filename, dimensions, offset, length, border)
|
||||||
|
AnimationMap[PENGUIN_WALK_RIGHT] = NewEntityAnimation(walkRight, speed, length, 0, center, FLIP_NONE)
|
||||||
|
|
||||||
|
// Walking Left is just that flipped.
|
||||||
|
AnimationMap[PENGUIN_WALK_LEFT] = NewEntityAnimation(walkRight, speed, length, 0, center, FLIP_HORIZONTAL)
|
||||||
|
|
||||||
|
// Stationary Right/Left is just the first frame.
|
||||||
|
length = 1
|
||||||
|
stationaryRight := sprite.NewAnimation(filename, dimensions, offset, length, border)
|
||||||
|
AnimationMap[PENGUIN_STATIONARY_RIGHT] = NewEntityAnimation(stationaryRight, speed, length, 0, center, FLIP_NONE)
|
||||||
|
AnimationMap[PENGUIN_STATIONARY_LEFT] = NewEntityAnimation(stationaryRight, speed, length, 0, center, FLIP_HORIZONTAL)
|
||||||
|
|
||||||
|
// Walk Up
|
||||||
|
length = 4
|
||||||
|
offset = rl.Vector2{X: 0, Y: 3}
|
||||||
|
walkUp := sprite.NewAnimation(filename, dimensions, offset, length, border)
|
||||||
|
AnimationMap[PENGUIN_WALK_UP] = NewEntityAnimation(walkUp, speed, length, 0, center, FLIP_NONE)
|
||||||
|
|
||||||
|
// Stationary Up
|
||||||
|
length = 1
|
||||||
|
stationaryUp := sprite.NewAnimation(filename, dimensions, offset, length, border)
|
||||||
|
AnimationMap[PENGUIN_STATIONARY_UP] = NewEntityAnimation(stationaryUp, speed, length, 0, center, FLIP_NONE)
|
||||||
|
|
||||||
|
// Walk Down
|
||||||
|
length = 4
|
||||||
|
offset = rl.Vector2{X: 0, Y: 0}
|
||||||
|
walkDown := sprite.NewAnimation(filename, dimensions, offset, length, border)
|
||||||
|
AnimationMap[PENGUIN_WALK_DOWN] = NewEntityAnimation(walkDown, speed, length, 0, center, FLIP_NONE)
|
||||||
|
|
||||||
|
// Stationary Down
|
||||||
|
length = 1
|
||||||
|
stationaryDown := sprite.NewAnimation(filename, dimensions, offset, length, border)
|
||||||
|
AnimationMap[PENGUIN_STATIONARY_DOWN] = NewEntityAnimation(stationaryDown, speed, length, 0, center, FLIP_NONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandomPenguinAnimation() int {
|
||||||
|
n := len(penguinAnimations)
|
||||||
|
i := rand.Intn(n)
|
||||||
|
return penguinAnimations[i]
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package types
|
package entity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
rl "github.com/gen2brain/raylib-go/raylib"
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
@ -9,6 +9,11 @@ type EntityAnimation interface {
|
||||||
GetSpeed() int
|
GetSpeed() int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AnimationTracker interface {
|
||||||
|
Draw(windowPosition rl.Vector2, color rl.Color) error
|
||||||
|
SetAnimation(id int)
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// These line up with the VEC_DIRECTIONS slice
|
// These line up with the VEC_DIRECTIONS slice
|
||||||
DIR_LEFT int = iota
|
DIR_LEFT int = iota
|
|
@ -0,0 +1,114 @@
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"git.wisellama.rocks/Project-Ely/project-ely/internal/animation"
|
||||||
|
"git.wisellama.rocks/Project-Ely/project-ely/internal/physics"
|
||||||
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
)
|
||||||
|
|
||||||
|
type penguin struct {
|
||||||
|
animation AnimationTracker
|
||||||
|
object physics.Object
|
||||||
|
direction int
|
||||||
|
staticAnimation bool
|
||||||
|
color rl.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPenguin() *penguin {
|
||||||
|
object := physics.NewObject()
|
||||||
|
anim := animation.NewAnimationTracker()
|
||||||
|
anim.SetAnimation(animation.PENGUIN_DEFAULT)
|
||||||
|
|
||||||
|
return &penguin{
|
||||||
|
animation: anim,
|
||||||
|
object: object,
|
||||||
|
staticAnimation: false,
|
||||||
|
color: rl.White,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *penguin) Draw() error {
|
||||||
|
|
||||||
|
position := p.object.GetPosition()
|
||||||
|
|
||||||
|
// TODO?
|
||||||
|
//windowPosition := worldPosToWindowPos()
|
||||||
|
windowPosition := rl.Vector2{
|
||||||
|
X: float32(math.Round(float64(position.X))),
|
||||||
|
Y: float32(-1 * math.Round(float64(position.Y))),
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.animation.Draw(windowPosition, p.color)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *penguin) Update() error {
|
||||||
|
p.SetMoveAnimation()
|
||||||
|
|
||||||
|
return p.object.Update()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *penguin) GetObject() physics.Object {
|
||||||
|
return p.object
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *penguin) GetAnimationTracker() AnimationTracker {
|
||||||
|
return p.animation
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToggleStaticAnimation will set whether or not this entity should ever update its animation loop or not.
|
||||||
|
// True means this will never update, it is static.
|
||||||
|
// False means this will update the animation loop based on physical properties of this entity.
|
||||||
|
func (p *penguin) ToggleStaticAnimation() {
|
||||||
|
p.staticAnimation = !p.staticAnimation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *penguin) SetMoveAnimation() {
|
||||||
|
if p.staticAnimation {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
velocity := p.object.GetVelocity()
|
||||||
|
if velocity.X == 0 && velocity.Y == 0 {
|
||||||
|
// Stay facing whatever direction we were facing
|
||||||
|
direction := p.direction
|
||||||
|
switch direction {
|
||||||
|
case DIR_LEFT:
|
||||||
|
p.animation.SetAnimation(animation.PENGUIN_STATIONARY_LEFT)
|
||||||
|
case DIR_RIGHT:
|
||||||
|
p.animation.SetAnimation(animation.PENGUIN_STATIONARY_RIGHT)
|
||||||
|
case DIR_UP:
|
||||||
|
p.animation.SetAnimation(animation.PENGUIN_STATIONARY_UP)
|
||||||
|
case DIR_DOWN:
|
||||||
|
p.animation.SetAnimation(animation.PENGUIN_STATIONARY_DOWN)
|
||||||
|
default:
|
||||||
|
log.Printf("unknown direction: %v", direction)
|
||||||
|
p.animation.SetAnimation(animation.PENGUIN_DEFAULT)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Figure out which way we are facing now that we're moving
|
||||||
|
direction := determineClosestDirection(velocity)
|
||||||
|
p.direction = direction
|
||||||
|
|
||||||
|
switch direction {
|
||||||
|
case DIR_LEFT:
|
||||||
|
p.animation.SetAnimation(animation.PENGUIN_WALK_LEFT)
|
||||||
|
case DIR_RIGHT:
|
||||||
|
p.animation.SetAnimation(animation.PENGUIN_WALK_RIGHT)
|
||||||
|
case DIR_UP:
|
||||||
|
p.animation.SetAnimation(animation.PENGUIN_WALK_UP)
|
||||||
|
case DIR_DOWN:
|
||||||
|
p.animation.SetAnimation(animation.PENGUIN_WALK_DOWN)
|
||||||
|
default:
|
||||||
|
log.Printf("unknown direction: %v", direction)
|
||||||
|
p.animation.SetAnimation(animation.PENGUIN_DEFAULT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *penguin) SetColor(color rl.Color) {
|
||||||
|
p.color = color
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"git.wisellama.rocks/Project-Ely/project-ely/internal/animation"
|
||||||
|
"git.wisellama.rocks/Project-Ely/project-ely/internal/channels"
|
||||||
|
"git.wisellama.rocks/Project-Ely/project-ely/internal/entity"
|
||||||
|
"git.wisellama.rocks/Project-Ely/project-ely/internal/player"
|
||||||
|
"git.wisellama.rocks/Project-Ely/project-ely/internal/sprite"
|
||||||
|
"git.wisellama.rocks/Wisellama/gosimpleconf"
|
||||||
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Drawables can be rendered with OpenGL. See the 'entity' and 'animation' packages.
|
||||||
|
type Drawable interface {
|
||||||
|
Draw() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Objects can have physical interactions. See the 'entity' and 'physics' packages.
|
||||||
|
type Object interface {
|
||||||
|
Update() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run is the main function to start the game.
|
||||||
|
func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
framerate64 := gosimpleconf.Int64(configMap["game.framerate"])
|
||||||
|
framerate := int32(framerate64)
|
||||||
|
|
||||||
|
// Initialize the RayLib window
|
||||||
|
channels.RL.Do(func() {
|
||||||
|
rl.InitWindow(800, 600, configMap["game.title"])
|
||||||
|
rl.SetTargetFPS(framerate)
|
||||||
|
})
|
||||||
|
defer func() {
|
||||||
|
channels.RL.Do(func() {
|
||||||
|
rl.CloseWindow()
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Initialize our sprites and animations
|
||||||
|
sprite.InitSpriteCache()
|
||||||
|
animation.DefineAnimations()
|
||||||
|
|
||||||
|
drawables := make([]Drawable, 0)
|
||||||
|
objects := make([]Object, 0)
|
||||||
|
|
||||||
|
center := rl.Vector2{X: float32(rl.GetScreenWidth()) / 2, Y: float32(rl.GetScreenHeight()) / 2 * -1}
|
||||||
|
player1 := player.NewPlayer(ctx)
|
||||||
|
player1Penguin := entity.NewPenguin()
|
||||||
|
player1Penguin.SetColor(rl.Gold)
|
||||||
|
player1Penguin.GetObject().SetPosition(center)
|
||||||
|
player1.SetControlledObject(player1Penguin.GetObject())
|
||||||
|
drawables = append(drawables, player1Penguin)
|
||||||
|
objects = append(objects, player1Penguin)
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
p := entity.NewPenguin()
|
||||||
|
p.GetObject().SetPosition(rl.Vector2{
|
||||||
|
X: rand.Float32() * float32(rl.GetScreenWidth()),
|
||||||
|
Y: rand.Float32() * float32(rl.GetScreenHeight()*-1),
|
||||||
|
})
|
||||||
|
p.GetAnimationTracker().SetAnimation(animation.RandomPenguinAnimation())
|
||||||
|
p.ToggleStaticAnimation()
|
||||||
|
|
||||||
|
drawables = append(drawables, p)
|
||||||
|
objects = append(objects, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// And now starting the main loop
|
||||||
|
running := true
|
||||||
|
for running {
|
||||||
|
channels.RL.Do(func() {
|
||||||
|
if rl.WindowShouldClose() {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Allow us to exit early if the context is done
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
running = false
|
||||||
|
default:
|
||||||
|
// Keep running
|
||||||
|
}
|
||||||
|
if !running {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
player1.Update()
|
||||||
|
|
||||||
|
// Update physics
|
||||||
|
// TODO probably move out to something else to handle collisions.
|
||||||
|
for _, o := range objects {
|
||||||
|
err = o.Update()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error updating physics: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
channels.RL.Do(func() {
|
||||||
|
rl.BeginDrawing()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Draw stuff
|
||||||
|
for _, d := range drawables {
|
||||||
|
err = d.Draw()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error drawing: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
channels.RL.Do(func() {
|
||||||
|
rl.ClearBackground(rl.Black)
|
||||||
|
rl.DrawText("Some Text!", 190, 200, 20, rl.Blue)
|
||||||
|
rl.DrawText(fmt.Sprintf("%v FPS", rl.GetFPS()), 190, 250, 20, rl.Blue)
|
||||||
|
})
|
||||||
|
|
||||||
|
channels.RL.Do(func() {
|
||||||
|
rl.EndDrawing()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
sprite.CleanupSpriteCache()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,80 +0,0 @@
|
||||||
package animation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.wisellama.rocks/Project-Ely/project-ely/internal/game/sprite"
|
|
||||||
rl "github.com/gen2brain/raylib-go/raylib"
|
|
||||||
)
|
|
||||||
|
|
||||||
var PenguinAnimations map[int]entityAnimation
|
|
||||||
|
|
||||||
const (
|
|
||||||
PENGUIN_WALK_RIGHT int = iota
|
|
||||||
PENGUIN_WALK_LEFT
|
|
||||||
PENGUIN_WALK_UP
|
|
||||||
PENGUIN_WALK_DOWN
|
|
||||||
PENGUIN_STATIONARY_RIGHT
|
|
||||||
PENGUIN_STATIONARY_LEFT
|
|
||||||
PENGUIN_STATIONARY_UP
|
|
||||||
PENGUIN_STATIONARY_DOWN
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
PENGUIN_NUM_ANIMS = 8
|
|
||||||
PENGUIN_DEFAULT = PENGUIN_STATIONARY_RIGHT
|
|
||||||
)
|
|
||||||
|
|
||||||
func DefinePenguinAnimations() {
|
|
||||||
filename := sprite.DELILAHWALKING
|
|
||||||
|
|
||||||
var (
|
|
||||||
dimensions rl.Vector2
|
|
||||||
offset rl.Vector2
|
|
||||||
center rl.Vector2
|
|
||||||
length int
|
|
||||||
speed int
|
|
||||||
border int
|
|
||||||
)
|
|
||||||
|
|
||||||
dimensions = rl.Vector2{X: 13, Y: 17}
|
|
||||||
PenguinAnimations = make(map[int]entityAnimation)
|
|
||||||
|
|
||||||
// Walking Right is in the spritesheet.
|
|
||||||
speed = 5
|
|
||||||
offset = rl.Vector2{X: 0, Y: 1}
|
|
||||||
length = 5
|
|
||||||
border = 1 // optional border around each sprite
|
|
||||||
center = getCenter(dimensions) // center is for rotation, nil will default to w/2 h/2
|
|
||||||
walkRight := sprite.NewAnimation(filename, dimensions, offset, length, border)
|
|
||||||
PenguinAnimations[PENGUIN_WALK_RIGHT] = NewEntityAnimation(walkRight, speed, length, 0, center, FLIP_NONE)
|
|
||||||
|
|
||||||
// Walking Left is just that flipped.
|
|
||||||
PenguinAnimations[PENGUIN_WALK_LEFT] = NewEntityAnimation(walkRight, speed, length, 0, center, FLIP_HORIZONTAL)
|
|
||||||
|
|
||||||
// Stationary Right/Left is just the first frame.
|
|
||||||
length = 1
|
|
||||||
stationaryRight := sprite.NewAnimation(filename, dimensions, offset, length, border)
|
|
||||||
PenguinAnimations[PENGUIN_STATIONARY_RIGHT] = NewEntityAnimation(stationaryRight, speed, length, 0, center, FLIP_NONE)
|
|
||||||
PenguinAnimations[PENGUIN_STATIONARY_LEFT] = NewEntityAnimation(stationaryRight, speed, length, 0, center, FLIP_HORIZONTAL)
|
|
||||||
|
|
||||||
// Walk Up
|
|
||||||
length = 4
|
|
||||||
offset = rl.Vector2{X: 0, Y: 3}
|
|
||||||
walkUp := sprite.NewAnimation(filename, dimensions, offset, length, border)
|
|
||||||
PenguinAnimations[PENGUIN_WALK_UP] = NewEntityAnimation(walkUp, speed, length, 0, center, FLIP_NONE)
|
|
||||||
|
|
||||||
// Stationary Up
|
|
||||||
length = 1
|
|
||||||
stationaryUp := sprite.NewAnimation(filename, dimensions, offset, length, border)
|
|
||||||
PenguinAnimations[PENGUIN_STATIONARY_UP] = NewEntityAnimation(stationaryUp, speed, length, 0, center, FLIP_NONE)
|
|
||||||
|
|
||||||
// Walk Down
|
|
||||||
length = 4
|
|
||||||
offset = rl.Vector2{X: 0, Y: 0}
|
|
||||||
walkDown := sprite.NewAnimation(filename, dimensions, offset, length, border)
|
|
||||||
PenguinAnimations[PENGUIN_WALK_DOWN] = NewEntityAnimation(walkDown, speed, length, 0, center, FLIP_NONE)
|
|
||||||
|
|
||||||
// Stationary Down
|
|
||||||
length = 1
|
|
||||||
stationaryDown := sprite.NewAnimation(filename, dimensions, offset, length, border)
|
|
||||||
PenguinAnimations[PENGUIN_STATIONARY_DOWN] = NewEntityAnimation(stationaryDown, speed, length, 0, center, FLIP_NONE)
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package command
|
|
||||||
|
|
||||||
// List of keys supported by all entities
|
|
||||||
const (
|
|
||||||
MOVE_X int = iota
|
|
||||||
MOVE_Y
|
|
||||||
SET_SPEED
|
|
||||||
SET_POSITION
|
|
||||||
SET_ANIMATION
|
|
||||||
)
|
|
||||||
|
|
||||||
type Command struct {
|
|
||||||
key int
|
|
||||||
value float32
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCommand(key int, value float32) Command {
|
|
||||||
return Command{
|
|
||||||
key: key,
|
|
||||||
value: value,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,170 +0,0 @@
|
||||||
package command
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.wisellama.rocks/Project-Ely/project-ely/internal/channels"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Entity interface {
|
|
||||||
Draw() error
|
|
||||||
Update() error
|
|
||||||
MoveX(x float32)
|
|
||||||
MoveY(y float32)
|
|
||||||
SetSpeed(s float32)
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommandHandler struct {
|
|
||||||
ctx context.Context
|
|
||||||
timeout time.Duration
|
|
||||||
entity Entity
|
|
||||||
commandChan chan Command
|
|
||||||
drawRequestChan chan struct{}
|
|
||||||
drawResponseChan chan struct{}
|
|
||||||
updateRequestChan chan struct{}
|
|
||||||
updateResponseChan chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCommandHandler(ctx context.Context, entity Entity) *CommandHandler {
|
|
||||||
commandChan := make(chan Command, 10)
|
|
||||||
drawRequestChan := make(chan struct{})
|
|
||||||
drawResponseChan := make(chan struct{})
|
|
||||||
updateRequestChan := make(chan struct{})
|
|
||||||
updateResponseChan := make(chan struct{})
|
|
||||||
|
|
||||||
defaultTimeout := time.Second
|
|
||||||
return &CommandHandler{
|
|
||||||
ctx: ctx,
|
|
||||||
timeout: defaultTimeout,
|
|
||||||
entity: entity,
|
|
||||||
commandChan: commandChan,
|
|
||||||
drawRequestChan: drawRequestChan,
|
|
||||||
drawResponseChan: drawResponseChan,
|
|
||||||
updateRequestChan: updateRequestChan,
|
|
||||||
updateResponseChan: updateResponseChan,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandHandler) closeResponses() {
|
|
||||||
close(c.drawResponseChan)
|
|
||||||
close(c.updateResponseChan)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandHandler) CloseRequests() {
|
|
||||||
close(c.drawRequestChan)
|
|
||||||
close(c.updateRequestChan)
|
|
||||||
close(c.commandChan)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandHandler) CommandRequest() chan Command {
|
|
||||||
return c.commandChan
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandHandler) DrawRequest() chan struct{} {
|
|
||||||
return c.drawRequestChan
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandHandler) DrawResponse() chan struct{} {
|
|
||||||
return c.drawResponseChan
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandHandler) UpdateRequest() chan struct{} {
|
|
||||||
return c.updateRequestChan
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandHandler) UpdateResponse() chan struct{} {
|
|
||||||
return c.updateResponseChan
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandHandler) Run() {
|
|
||||||
defer c.closeResponses()
|
|
||||||
|
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
|
|
||||||
running := true
|
|
||||||
for running {
|
|
||||||
select {
|
|
||||||
case <-c.drawRequestChan:
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
c.DrawWithTimeout()
|
|
||||||
c.drawResponseChan <- struct{}{}
|
|
||||||
}()
|
|
||||||
case <-c.updateRequestChan:
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
c.UpdateWithTimeout()
|
|
||||||
c.updateResponseChan <- struct{}{}
|
|
||||||
}()
|
|
||||||
case cmd := <-c.commandChan:
|
|
||||||
wg.Add(1)
|
|
||||||
go func(cmd Command) {
|
|
||||||
defer wg.Done()
|
|
||||||
c.HandleWithTimeout(cmd)
|
|
||||||
}(cmd)
|
|
||||||
case <-c.ctx.Done():
|
|
||||||
// Graceful shutdown
|
|
||||||
running = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// Finish up anything in the queues
|
|
||||||
for cmd := range c.commandChan {
|
|
||||||
c.HandleWithTimeout(cmd)
|
|
||||||
}
|
|
||||||
for range c.drawRequestChan {
|
|
||||||
c.UpdateWithTimeout()
|
|
||||||
}
|
|
||||||
for range c.drawRequestChan {
|
|
||||||
c.DrawWithTimeout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandHandler) HandleWithTimeout(cmd Command) {
|
|
||||||
err := channels.RunWithTimeout(c.timeout, func() error {
|
|
||||||
return c.Handle(cmd)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("handle with timeout error: %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandHandler) Handle(cmd Command) error {
|
|
||||||
switch cmd.key {
|
|
||||||
case MOVE_X:
|
|
||||||
c.entity.MoveX(cmd.value)
|
|
||||||
case MOVE_Y:
|
|
||||||
c.entity.MoveY(cmd.value)
|
|
||||||
case SET_SPEED:
|
|
||||||
c.entity.SetSpeed(cmd.value)
|
|
||||||
default:
|
|
||||||
log.Printf("unknown entity command: %v", cmd.key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandHandler) DrawWithTimeout() {
|
|
||||||
err := channels.RunWithTimeout(c.timeout, func() error {
|
|
||||||
return c.entity.Draw()
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("draw with timeout error: %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandHandler) UpdateWithTimeout() {
|
|
||||||
err := channels.RunWithTimeout(c.timeout, func() error {
|
|
||||||
return c.entity.Update()
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("update with timeout error: %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,166 +0,0 @@
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"math"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"git.wisellama.rocks/Project-Ely/project-ely/internal/game/entity/animation"
|
|
||||||
rl "github.com/gen2brain/raylib-go/raylib"
|
|
||||||
)
|
|
||||||
|
|
||||||
type penguin struct {
|
|
||||||
mx sync.RWMutex
|
|
||||||
|
|
||||||
// Animation parameters
|
|
||||||
currentAnimation EntityAnimation // pointer to current animation loop
|
|
||||||
animationStep int // index of animation loop
|
|
||||||
direction int // direction facing for sprite animations
|
|
||||||
updateAnimation bool // if false, don't change the animation
|
|
||||||
|
|
||||||
// Physical parameters
|
|
||||||
worldPosition rl.Vector2 // where is the center of this object
|
|
||||||
velocity rl.Vector2 // movement direction to be applied each tick
|
|
||||||
speed float32 // movement magnitude to multiply with the velocity
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPenguin() penguin {
|
|
||||||
position := rl.Vector2{}
|
|
||||||
velocity := rl.Vector2{}
|
|
||||||
speed := float32(1)
|
|
||||||
|
|
||||||
return penguin{
|
|
||||||
currentAnimation: animation.PenguinAnimations[animation.PENGUIN_DEFAULT],
|
|
||||||
animationStep: 0,
|
|
||||||
direction: DIR_RIGHT,
|
|
||||||
|
|
||||||
worldPosition: position,
|
|
||||||
speed: speed,
|
|
||||||
velocity: velocity,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *penguin) Draw() error {
|
|
||||||
step := p.animationStep / p.currentAnimation.GetSpeed()
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
//windowPosition := worldPosToWindowPos()
|
|
||||||
windowPosition := rl.Vector2{
|
|
||||||
X: float32(math.Round(float64(p.worldPosition.X))),
|
|
||||||
Y: float32(math.Round(float64(-1 * p.worldPosition.Y))),
|
|
||||||
}
|
|
||||||
|
|
||||||
err := p.currentAnimation.Draw(step, windowPosition, rl.White)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
p.animationStep = 1 + p.animationStep
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *penguin) SetPosition(vec rl.Vector2) {
|
|
||||||
p.worldPosition = vec
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *penguin) SetDirection(dir int) {
|
|
||||||
p.direction = dir
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *penguin) SetAnimation(id int) {
|
|
||||||
a, exists := animation.PenguinAnimations[id]
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
log.Printf("animation does not exist: %v", id)
|
|
||||||
a = animation.PenguinAnimations[animation.PENGUIN_DEFAULT]
|
|
||||||
}
|
|
||||||
|
|
||||||
if a != p.currentAnimation {
|
|
||||||
p.animationStep = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
p.currentAnimation = a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *penguin) MoveX(x float32) {
|
|
||||||
p.mx.Lock()
|
|
||||||
defer p.mx.Unlock()
|
|
||||||
|
|
||||||
p.velocity.X = x
|
|
||||||
if p.velocity.X != 0 || p.velocity.Y != 0 {
|
|
||||||
p.velocity = rl.Vector2Normalize(p.velocity)
|
|
||||||
}
|
|
||||||
p.updateAnimation = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *penguin) MoveY(y float32) {
|
|
||||||
p.mx.Lock()
|
|
||||||
defer p.mx.Unlock()
|
|
||||||
|
|
||||||
p.velocity.Y = y
|
|
||||||
if p.velocity.X != 0 || p.velocity.Y != 0 {
|
|
||||||
p.velocity = rl.Vector2Normalize(p.velocity)
|
|
||||||
}
|
|
||||||
p.updateAnimation = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *penguin) SetSpeed(s float32) {
|
|
||||||
p.speed = s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *penguin) GetSpeed() float32 {
|
|
||||||
return p.speed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *penguin) SetMoveAnimation() {
|
|
||||||
if p.velocity.X == 0 && p.velocity.Y == 0 {
|
|
||||||
// Stay facing whatever direction we were facing
|
|
||||||
switch p.direction {
|
|
||||||
case DIR_LEFT:
|
|
||||||
p.SetAnimation(animation.PENGUIN_STATIONARY_LEFT)
|
|
||||||
case DIR_RIGHT:
|
|
||||||
p.SetAnimation(animation.PENGUIN_STATIONARY_RIGHT)
|
|
||||||
case DIR_UP:
|
|
||||||
p.SetAnimation(animation.PENGUIN_STATIONARY_UP)
|
|
||||||
case DIR_DOWN:
|
|
||||||
p.SetAnimation(animation.PENGUIN_STATIONARY_DOWN)
|
|
||||||
default:
|
|
||||||
log.Printf("unknown direction: %v", p.direction)
|
|
||||||
p.SetAnimation(animation.PENGUIN_DEFAULT)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Figure out which way we are facing now that we're moving
|
|
||||||
p.direction = determineClosestDirection(p.velocity)
|
|
||||||
|
|
||||||
switch p.direction {
|
|
||||||
case DIR_LEFT:
|
|
||||||
p.SetAnimation(animation.PENGUIN_WALK_LEFT)
|
|
||||||
case DIR_RIGHT:
|
|
||||||
p.SetAnimation(animation.PENGUIN_WALK_RIGHT)
|
|
||||||
case DIR_UP:
|
|
||||||
p.SetAnimation(animation.PENGUIN_WALK_UP)
|
|
||||||
case DIR_DOWN:
|
|
||||||
p.SetAnimation(animation.PENGUIN_WALK_DOWN)
|
|
||||||
default:
|
|
||||||
log.Printf("unknown direction: %v", p.direction)
|
|
||||||
p.SetAnimation(animation.PENGUIN_DEFAULT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *penguin) Update() error {
|
|
||||||
if p.updateAnimation {
|
|
||||||
p.SetMoveAnimation()
|
|
||||||
p.updateAnimation = false
|
|
||||||
}
|
|
||||||
|
|
||||||
x := p.velocity.X * p.speed
|
|
||||||
y := p.velocity.Y * p.speed
|
|
||||||
|
|
||||||
p.worldPosition.X += x
|
|
||||||
p.worldPosition.Y += y
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,142 +0,0 @@
|
||||||
package game
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"git.wisellama.rocks/Project-Ely/project-ely/internal/channels"
|
|
||||||
"git.wisellama.rocks/Project-Ely/project-ely/internal/game/entity/animation"
|
|
||||||
"git.wisellama.rocks/Project-Ely/project-ely/internal/game/entity/command"
|
|
||||||
"git.wisellama.rocks/Project-Ely/project-ely/internal/game/entity/types"
|
|
||||||
"git.wisellama.rocks/Project-Ely/project-ely/internal/game/player"
|
|
||||||
"git.wisellama.rocks/Project-Ely/project-ely/internal/game/sprite"
|
|
||||||
"git.wisellama.rocks/Wisellama/gosimpleconf"
|
|
||||||
rl "github.com/gen2brain/raylib-go/raylib"
|
|
||||||
)
|
|
||||||
|
|
||||||
type EntityCmdHandler interface {
|
|
||||||
Run()
|
|
||||||
CloseRequests()
|
|
||||||
CommandRequest() chan command.Command
|
|
||||||
DrawRequest() chan struct{}
|
|
||||||
DrawResponse() chan struct{}
|
|
||||||
UpdateRequest() chan struct{}
|
|
||||||
UpdateResponse() chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run is the main function to start the game.
|
|
||||||
func Run(ctx context.Context, configMap gosimpleconf.ConfigMap) error {
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
framerate64 := gosimpleconf.Int64(configMap["game.framerate"])
|
|
||||||
framerate := int32(framerate64)
|
|
||||||
|
|
||||||
// Initialize the RayLib window
|
|
||||||
channels.RL.Do(func() {
|
|
||||||
rl.InitWindow(800, 600, configMap["game.title"])
|
|
||||||
rl.SetTargetFPS(framerate)
|
|
||||||
})
|
|
||||||
defer func() {
|
|
||||||
channels.RL.Do(func() {
|
|
||||||
rl.CloseWindow()
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Initialize our sprites and animations
|
|
||||||
sprite.InitSpriteCache()
|
|
||||||
animation.DefineAnimations()
|
|
||||||
|
|
||||||
entityList := make([]EntityCmdHandler, 0)
|
|
||||||
|
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
|
|
||||||
playerPenguinEntity := types.NewPenguin()
|
|
||||||
playerPenguinEntity.SetSpeed(2)
|
|
||||||
playerPenguinEntity.SetPosition(rl.Vector2{X: 100, Y: -100})
|
|
||||||
playerPenguinCmd := command.NewCommandHandler(ctx, &playerPenguinEntity)
|
|
||||||
playerPenguin := player.NewPlayer(ctx)
|
|
||||||
playerPenguin.SetEntityChan(playerPenguinCmd.CommandRequest())
|
|
||||||
entityList = append(entityList, playerPenguinCmd)
|
|
||||||
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
playerPenguinCmd.Run()
|
|
||||||
}()
|
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
entity := types.NewPenguin()
|
|
||||||
entityCmd := command.NewCommandHandler(ctx, &entity)
|
|
||||||
randomPos := rl.Vector2{X: rand.Float32() * 500, Y: rand.Float32() * -500}
|
|
||||||
entity.SetPosition(randomPos)
|
|
||||||
entity.SetAnimation(rand.Intn(animation.PENGUIN_NUM_ANIMS))
|
|
||||||
entityList = append(entityList, entityCmd)
|
|
||||||
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
entityCmd.Run()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// And now starting the main loop
|
|
||||||
running := true
|
|
||||||
for running {
|
|
||||||
channels.RL.Do(func() {
|
|
||||||
if rl.WindowShouldClose() {
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Allow us to exit early if the context is done
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
running = false
|
|
||||||
default:
|
|
||||||
// Keep running
|
|
||||||
}
|
|
||||||
if !running {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update players
|
|
||||||
playerPenguin.Update()
|
|
||||||
|
|
||||||
// Start drawing
|
|
||||||
channels.RL.Do(func() {
|
|
||||||
rl.BeginDrawing()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Tell everything to Update and Draw
|
|
||||||
for _, e := range entityList {
|
|
||||||
e.UpdateRequest() <- struct{}{}
|
|
||||||
e.DrawRequest() <- struct{}{}
|
|
||||||
}
|
|
||||||
// Wait for each entity to finish their Draw and Update commands before proceeding
|
|
||||||
for _, e := range entityList {
|
|
||||||
<-e.UpdateResponse()
|
|
||||||
<-e.DrawResponse()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finish drawing
|
|
||||||
channels.RL.Do(func() {
|
|
||||||
rl.ClearBackground(rl.Black)
|
|
||||||
rl.DrawText("Some Text!", 190, 200, 20, rl.Blue)
|
|
||||||
rl.DrawText(fmt.Sprintf("%v FPS", rl.GetFPS()), 190, 250, 20, rl.Blue)
|
|
||||||
|
|
||||||
rl.EndDrawing()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range entityList {
|
|
||||||
e.CloseRequests()
|
|
||||||
}
|
|
||||||
sprite.CleanupSpriteCache()
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
package physics
|
||||||
|
|
||||||
|
import rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
|
||||||
|
// Object defines the interface for physical objects.
|
||||||
|
//
|
||||||
|
// This is defined here in the object.go file because after
|
||||||
|
// passing an 'object' to an 'entity' (e.g. 'penguin'),
|
||||||
|
// then to a 'player', as well as the main function,
|
||||||
|
// we needed almost all of the functions for the whole
|
||||||
|
// interface defined in each place.
|
||||||
|
type Object interface {
|
||||||
|
Update() error
|
||||||
|
|
||||||
|
GetPosition() rl.Vector2
|
||||||
|
GetVelocity() rl.Vector2
|
||||||
|
GetAcceleration() rl.Vector2
|
||||||
|
GetBaseAcceleration() rl.Vector2
|
||||||
|
|
||||||
|
SetPosition(pos rl.Vector2)
|
||||||
|
SetVelocity(vel rl.Vector2)
|
||||||
|
SetAcceleration(acc rl.Vector2)
|
||||||
|
SetBaseAcceleration(base rl.Vector2)
|
||||||
|
}
|
||||||
|
|
||||||
|
type object struct {
|
||||||
|
position rl.Vector2
|
||||||
|
velocity rl.Vector2
|
||||||
|
acceleration rl.Vector2
|
||||||
|
baseAcceleration rl.Vector2 // some default speed for this object
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewObject() *object {
|
||||||
|
pos := rl.Vector2{X: 0.0, Y: 0.0}
|
||||||
|
vel := rl.Vector2{X: 0.0, Y: 0.0}
|
||||||
|
acc := rl.Vector2{X: 0.0, Y: 0.0}
|
||||||
|
base := rl.Vector2{X: 1.0, Y: 1.0}
|
||||||
|
|
||||||
|
return &object{
|
||||||
|
position: pos,
|
||||||
|
velocity: vel,
|
||||||
|
acceleration: acc,
|
||||||
|
baseAcceleration: base,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *object) Update() error {
|
||||||
|
p := o.position
|
||||||
|
v := o.velocity
|
||||||
|
a := o.acceleration
|
||||||
|
|
||||||
|
v = rl.Vector2Add(v, a)
|
||||||
|
o.velocity = v
|
||||||
|
|
||||||
|
p = rl.Vector2Add(p, v)
|
||||||
|
o.position = p
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *object) SetPosition(pos rl.Vector2) {
|
||||||
|
o.position = pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *object) SetVelocity(vel rl.Vector2) {
|
||||||
|
o.velocity = vel
|
||||||
|
}
|
||||||
|
func (o *object) SetAcceleration(acc rl.Vector2) {
|
||||||
|
o.acceleration = acc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *object) SetBaseAcceleration(base rl.Vector2) {
|
||||||
|
o.baseAcceleration = base
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *object) GetPosition() rl.Vector2 {
|
||||||
|
return o.position
|
||||||
|
}
|
||||||
|
func (o *object) GetVelocity() rl.Vector2 {
|
||||||
|
return o.velocity
|
||||||
|
}
|
||||||
|
func (o *object) GetAcceleration() rl.Vector2 {
|
||||||
|
return o.acceleration
|
||||||
|
}
|
||||||
|
func (o *object) GetBaseAcceleration() rl.Vector2 {
|
||||||
|
return o.baseAcceleration
|
||||||
|
}
|
|
@ -5,10 +5,13 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.wisellama.rocks/Project-Ely/project-ely/internal/channels"
|
"git.wisellama.rocks/Project-Ely/project-ely/internal/channels"
|
||||||
"git.wisellama.rocks/Project-Ely/project-ely/internal/game/entity/command"
|
|
||||||
rl "github.com/gen2brain/raylib-go/raylib"
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PhysicalObject interface {
|
||||||
|
SetVelocity(vel rl.Vector2)
|
||||||
|
}
|
||||||
|
|
||||||
// A player represents a collection of stuff controlled by the user's input.
|
// A player represents a collection of stuff controlled by the user's input.
|
||||||
// It contains the camera used to view the world,
|
// It contains the camera used to view the world,
|
||||||
// the viewport for the part of the screen to draw to (splitscreen support),
|
// the viewport for the part of the screen to draw to (splitscreen support),
|
||||||
|
@ -17,7 +20,7 @@ type player struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
|
|
||||||
entityChan chan command.Command
|
controlledObject PhysicalObject
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPlayer(ctx context.Context) *player {
|
func NewPlayer(ctx context.Context) *player {
|
||||||
|
@ -30,8 +33,8 @@ func NewPlayer(ctx context.Context) *player {
|
||||||
return &p
|
return &p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *player) SetEntityChan(e chan command.Command) {
|
func (p *player) SetControlledObject(c PhysicalObject) {
|
||||||
p.entityChan = e
|
p.controlledObject = c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *player) Update() {
|
func (p *player) Update() {
|
||||||
|
@ -40,38 +43,43 @@ func (p *player) Update() {
|
||||||
down bool
|
down bool
|
||||||
left bool
|
left bool
|
||||||
right bool
|
right bool
|
||||||
speed bool
|
fast bool
|
||||||
)
|
)
|
||||||
channels.RL.Do(func() {
|
channels.RL.Do(func() {
|
||||||
speed = rl.IsKeyDown(rl.KeyLeftShift)
|
fast = rl.IsKeyDown(rl.KeyLeftShift)
|
||||||
left = rl.IsKeyDown(rl.KeyA) || rl.IsKeyDown(rl.KeyLeft)
|
left = rl.IsKeyDown(rl.KeyA) || rl.IsKeyDown(rl.KeyLeft)
|
||||||
right = rl.IsKeyDown(rl.KeyD) || rl.IsKeyDown(rl.KeyRight)
|
right = rl.IsKeyDown(rl.KeyD) || rl.IsKeyDown(rl.KeyRight)
|
||||||
up = rl.IsKeyDown(rl.KeyW) || rl.IsKeyDown(rl.KeyUp)
|
up = rl.IsKeyDown(rl.KeyW) || rl.IsKeyDown(rl.KeyUp)
|
||||||
down = rl.IsKeyDown(rl.KeyS) || rl.IsKeyDown(rl.KeyDown)
|
down = rl.IsKeyDown(rl.KeyS) || rl.IsKeyDown(rl.KeyDown)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Speed
|
baseVel := rl.Vector2{X: 1.0, Y: 1.0}
|
||||||
if speed {
|
velocity := rl.Vector2{X: 0.0, Y: 0.0}
|
||||||
p.entityChan <- command.NewCommand(command.SET_SPEED, 4)
|
|
||||||
} else {
|
|
||||||
p.entityChan <- command.NewCommand(command.SET_SPEED, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move horizontal
|
// Move horizontal
|
||||||
if right {
|
if right {
|
||||||
p.entityChan <- command.NewCommand(command.MOVE_X, 1.0)
|
velocity.X = baseVel.X
|
||||||
} else if left {
|
} else if left {
|
||||||
p.entityChan <- command.NewCommand(command.MOVE_X, -1.0)
|
velocity.X = baseVel.X * -1
|
||||||
} else if !right && !left {
|
|
||||||
p.entityChan <- command.NewCommand(command.MOVE_X, 0.0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move vertical
|
// Move vertical
|
||||||
if up {
|
if up {
|
||||||
p.entityChan <- command.NewCommand(command.MOVE_Y, 1.0)
|
velocity.Y = baseVel.Y
|
||||||
} else if down {
|
} else if down {
|
||||||
p.entityChan <- command.NewCommand(command.MOVE_Y, -1.0)
|
velocity.Y = baseVel.Y * -1
|
||||||
} else if !up && !down {
|
|
||||||
p.entityChan <- command.NewCommand(command.MOVE_Y, 0.0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're moving diagonal, normalize the vector to avoid weird diagonal super-speed
|
||||||
|
if velocity.X != 0 || velocity.Y != 0 {
|
||||||
|
velocity = rl.Vector2Normalize(velocity)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Faster speed
|
||||||
|
if fast {
|
||||||
|
velocity.X *= 2
|
||||||
|
velocity.Y *= 2
|
||||||
|
}
|
||||||
|
|
||||||
|
p.controlledObject.SetVelocity(velocity)
|
||||||
}
|
}
|
4
main.go
4
main.go
|
@ -4,8 +4,8 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"git.wisellama.rocks/Project-Ely/project-ely/internal"
|
||||||
"git.wisellama.rocks/Project-Ely/project-ely/internal/channels"
|
"git.wisellama.rocks/Project-Ely/project-ely/internal/channels"
|
||||||
"git.wisellama.rocks/Project-Ely/project-ely/internal/game"
|
|
||||||
"git.wisellama.rocks/Wisellama/gopackagebase"
|
"git.wisellama.rocks/Wisellama/gopackagebase"
|
||||||
"git.wisellama.rocks/Wisellama/gosimpleconf"
|
"git.wisellama.rocks/Wisellama/gosimpleconf"
|
||||||
)
|
)
|
||||||
|
@ -36,7 +36,7 @@ func main() {
|
||||||
// Run the program
|
// Run the program
|
||||||
log.Printf("=== Starting %v ===", baseConfig.ConfigMap["game.title"])
|
log.Printf("=== Starting %v ===", baseConfig.ConfigMap["game.title"])
|
||||||
channels.RL.Main(func() {
|
channels.RL.Main(func() {
|
||||||
err = game.Run(baseConfig.Ctx, baseConfig.ConfigMap)
|
err = internal.Run(baseConfig.Ctx, baseConfig.ConfigMap)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%v\n", err)
|
log.Printf("%v\n", err)
|
||||||
|
|
Loading…
Reference in New Issue