Sprite animation system , state machine
This commit is contained in:
parent
eed7ee178f
commit
ea806f6b22
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
/assetsviewer
|
||||
/topdown
|
||||
/top-down-shooter
|
||||
|
70
sprites.odin
Normal file
70
sprites.odin
Normal file
@ -0,0 +1,70 @@
|
||||
package topdown
|
||||
|
||||
import rl "vendor:raylib"
|
||||
import "core:fmt"
|
||||
|
||||
SpriteAnimation :: struct {
|
||||
speed: f32,
|
||||
width: u16,
|
||||
height: u16,
|
||||
total_width: u16,
|
||||
row: u16,
|
||||
loop: bool,
|
||||
total_frames: u8,
|
||||
}
|
||||
|
||||
SpriteAnimationPlayer :: struct {
|
||||
curr_anim: ^SpriteAnimation,
|
||||
time_elapsed: f32,
|
||||
curr_frame: u8,
|
||||
skip_frame: bool
|
||||
}
|
||||
|
||||
anim_player_start :: proc(player: ^SpriteAnimationPlayer, anim: ^SpriteAnimation) {
|
||||
player.curr_anim = anim
|
||||
player.time_elapsed = 0
|
||||
player.curr_frame = 0
|
||||
player.skip_frame = true
|
||||
}
|
||||
|
||||
get_sprite_rect :: proc(player: ^SpriteAnimationPlayer) -> Rect {
|
||||
return Rect{
|
||||
f32(u16(player.curr_frame) * player.curr_anim.width),
|
||||
f32(player.curr_anim.row),
|
||||
f32(player.curr_anim.width),
|
||||
f32(player.curr_anim.height)
|
||||
}
|
||||
}
|
||||
|
||||
anim_player_tick :: proc(player: ^SpriteAnimationPlayer) {
|
||||
// fmt.println(player.curr_anim)
|
||||
finished := !player.curr_anim.loop && player.curr_frame == player.curr_anim.total_frames
|
||||
if finished || player.skip_frame {
|
||||
player.skip_frame = false
|
||||
return
|
||||
}
|
||||
player.time_elapsed += rl.GetFrameTime()
|
||||
// TODO: We want to subtract to advance a frame in case we dropped frames
|
||||
if player.time_elapsed >= player.curr_anim.speed {
|
||||
player.time_elapsed = 0
|
||||
player.curr_frame += 1
|
||||
if player.curr_frame >= player.curr_anim.total_frames {
|
||||
if player.curr_anim.loop {
|
||||
player.curr_frame = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draw_sprite :: proc(texture: Tex, player: ^SpriteAnimationPlayer, pos: Vec2, scale: f32, flip: bool) {
|
||||
src := get_sprite_rect(player)
|
||||
if flip {
|
||||
src.width *= -1
|
||||
}
|
||||
w := f32(player.curr_anim.width) * scale
|
||||
h := f32(player.curr_anim.height) * scale
|
||||
dst := Rect{pos.x, pos.y, w, h}
|
||||
// fmt.println(src)
|
||||
// fmt.println(dst)
|
||||
rl.DrawTexturePro(texture, src, dst, {0,0}, 0, rl.WHITE)
|
||||
}
|
84
topdown.odin
84
topdown.odin
@ -16,9 +16,21 @@ SCREEN_HEIGHT : i32 = 800
|
||||
BW := f32(SCREEN_WIDTH)
|
||||
BH := f32(SCREEN_HEIGHT)
|
||||
|
||||
IdleState :: struct {}
|
||||
|
||||
MovingState :: struct {
|
||||
velocity: Vec2,
|
||||
}
|
||||
|
||||
SoldierState :: union #no_nil {
|
||||
IdleState,
|
||||
MovingState,
|
||||
}
|
||||
|
||||
Soldier :: struct {
|
||||
position: Vec2,
|
||||
velocity: Vec2,
|
||||
state: SoldierState,
|
||||
anim_player: SpriteAnimationPlayer,
|
||||
}
|
||||
|
||||
GameState :: struct {
|
||||
@ -27,7 +39,8 @@ GameState :: struct {
|
||||
reticle: Tex,
|
||||
idle: Tex,
|
||||
run: Tex,
|
||||
player: Soldier,
|
||||
soldier: Soldier,
|
||||
animations: [dynamic]SpriteAnimation,
|
||||
}
|
||||
|
||||
init_state :: proc() -> ^GameState {
|
||||
@ -44,34 +57,81 @@ init_state :: proc() -> ^GameState {
|
||||
keymap["Up"] = .W
|
||||
keymap["Down"] = .S
|
||||
|
||||
animations := make([dynamic]SpriteAnimation)
|
||||
idle_anim := SpriteAnimation {
|
||||
speed = 1 / 2.0,
|
||||
width = 16,
|
||||
height = 16,
|
||||
total_width = 32,
|
||||
row = 0,
|
||||
loop = true,
|
||||
total_frames = 2,
|
||||
}
|
||||
run_anim := SpriteAnimation {
|
||||
speed = 1 / 9.0,
|
||||
width = 16,
|
||||
height = 16,
|
||||
total_width = 64,
|
||||
row = 0,
|
||||
loop = true,
|
||||
total_frames = 4,
|
||||
}
|
||||
append(&animations, idle_anim)
|
||||
append(&animations, run_anim)
|
||||
soldier := Soldier {
|
||||
position = {100, 100},
|
||||
anim_player = SpriteAnimationPlayer{},
|
||||
}
|
||||
anim_player_start(&soldier.anim_player, &animations[0])
|
||||
|
||||
state := GameState {
|
||||
reticle = rl.LoadTextureFromImage(reticle),
|
||||
idle = rl.LoadTextureFromImage(idle),
|
||||
run = rl.LoadTextureFromImage(run),
|
||||
keymap = keymap
|
||||
keymap = keymap,
|
||||
soldier = soldier,
|
||||
animations = animations
|
||||
}
|
||||
return new_clone(state)
|
||||
cloned := new_clone(state)
|
||||
return cloned
|
||||
}
|
||||
|
||||
player_input :: proc(s: ^GameState) {
|
||||
horizontal := rl.IsKeyDown(s.keymap["Left"]) ? -1 : rl.IsKeyDown(s.keymap["Right"]) ? 1 : 0
|
||||
vertical := rl.IsKeyDown(s.keymap["Up"]) ? -1 : rl.IsKeyDown(s.keymap["Down"]) ? 1 : 0
|
||||
dir := rl.Vector2Normalize({f32(horizontal), f32(vertical)})
|
||||
s.player.position += dir * (250 * s.dt)
|
||||
moving := horizontal != 0 || vertical != 0
|
||||
if moving {
|
||||
dir := rl.Vector2Normalize({f32(horizontal), f32(vertical)})
|
||||
velocity := dir * (250 * s.dt)
|
||||
moving_state := MovingState { velocity = velocity }
|
||||
if _, ok := s.soldier.state.(IdleState); ok {
|
||||
anim_player_start(&s.soldier.anim_player, &s.animations[1])
|
||||
}
|
||||
s.soldier.state = moving_state
|
||||
s.soldier.position += velocity
|
||||
} else if _, ok := s.soldier.state.(MovingState); ok {
|
||||
anim_player_start(&s.soldier.anim_player, &s.animations[0])
|
||||
s.soldier.state = IdleState {}
|
||||
}
|
||||
}
|
||||
|
||||
update :: proc(s: ^GameState) {
|
||||
anim_player_tick(&s.soldier.anim_player)
|
||||
}
|
||||
|
||||
draw2d :: proc(s: ^GameState) {
|
||||
pos := rl.GetMousePosition() - {f32(s.reticle.width) / 2, f32(s.reticle.height) / 2}
|
||||
mpos := rl.GetMousePosition() - {f32(s.reticle.width) / 2, f32(s.reticle.height) / 2}
|
||||
// rl.DrawTextureEx(s.idle, s.player.position, 0, 4, rl.WHITE)
|
||||
w :f32 = 16
|
||||
src := Rect{0, 0, pos.x < s.player.position.x ? -w + 1 : w, w}
|
||||
dst := Rect{s.player.position.x, s.player.position.y, w * 4, w * 4}
|
||||
rl.DrawTexturePro(s.idle, src, dst, {0,0}, 0, rl.WHITE)
|
||||
flip := mpos.x < s.soldier.position.x
|
||||
sprite :Tex
|
||||
if _, ok := s.soldier.state.(IdleState); ok {
|
||||
sprite = s.idle
|
||||
} else {
|
||||
sprite = s.run
|
||||
}
|
||||
draw_sprite(sprite, &s.soldier.anim_player, s.soldier.position, 4, flip)
|
||||
|
||||
rl.DrawTextureV(s.reticle, pos, rl.WHITE);
|
||||
rl.DrawTextureV(s.reticle, mpos, rl.WHITE);
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user