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
|
/assetsviewer
|
||||||
/topdown
|
/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)
|
||||||
|
}
|
82
topdown.odin
82
topdown.odin
@ -16,9 +16,21 @@ SCREEN_HEIGHT : i32 = 800
|
|||||||
BW := f32(SCREEN_WIDTH)
|
BW := f32(SCREEN_WIDTH)
|
||||||
BH := f32(SCREEN_HEIGHT)
|
BH := f32(SCREEN_HEIGHT)
|
||||||
|
|
||||||
|
IdleState :: struct {}
|
||||||
|
|
||||||
|
MovingState :: struct {
|
||||||
|
velocity: Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
SoldierState :: union #no_nil {
|
||||||
|
IdleState,
|
||||||
|
MovingState,
|
||||||
|
}
|
||||||
|
|
||||||
Soldier :: struct {
|
Soldier :: struct {
|
||||||
position: Vec2,
|
position: Vec2,
|
||||||
velocity: Vec2,
|
state: SoldierState,
|
||||||
|
anim_player: SpriteAnimationPlayer,
|
||||||
}
|
}
|
||||||
|
|
||||||
GameState :: struct {
|
GameState :: struct {
|
||||||
@ -27,7 +39,8 @@ GameState :: struct {
|
|||||||
reticle: Tex,
|
reticle: Tex,
|
||||||
idle: Tex,
|
idle: Tex,
|
||||||
run: Tex,
|
run: Tex,
|
||||||
player: Soldier,
|
soldier: Soldier,
|
||||||
|
animations: [dynamic]SpriteAnimation,
|
||||||
}
|
}
|
||||||
|
|
||||||
init_state :: proc() -> ^GameState {
|
init_state :: proc() -> ^GameState {
|
||||||
@ -44,34 +57,81 @@ init_state :: proc() -> ^GameState {
|
|||||||
keymap["Up"] = .W
|
keymap["Up"] = .W
|
||||||
keymap["Down"] = .S
|
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 {
|
state := GameState {
|
||||||
reticle = rl.LoadTextureFromImage(reticle),
|
reticle = rl.LoadTextureFromImage(reticle),
|
||||||
idle = rl.LoadTextureFromImage(idle),
|
idle = rl.LoadTextureFromImage(idle),
|
||||||
run = rl.LoadTextureFromImage(run),
|
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) {
|
player_input :: proc(s: ^GameState) {
|
||||||
horizontal := rl.IsKeyDown(s.keymap["Left"]) ? -1 : rl.IsKeyDown(s.keymap["Right"]) ? 1 : 0
|
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
|
vertical := rl.IsKeyDown(s.keymap["Up"]) ? -1 : rl.IsKeyDown(s.keymap["Down"]) ? 1 : 0
|
||||||
|
moving := horizontal != 0 || vertical != 0
|
||||||
|
if moving {
|
||||||
dir := rl.Vector2Normalize({f32(horizontal), f32(vertical)})
|
dir := rl.Vector2Normalize({f32(horizontal), f32(vertical)})
|
||||||
s.player.position += dir * (250 * s.dt)
|
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) {
|
update :: proc(s: ^GameState) {
|
||||||
|
anim_player_tick(&s.soldier.anim_player)
|
||||||
}
|
}
|
||||||
|
|
||||||
draw2d :: proc(s: ^GameState) {
|
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)
|
// rl.DrawTextureEx(s.idle, s.player.position, 0, 4, rl.WHITE)
|
||||||
w :f32 = 16
|
flip := mpos.x < s.soldier.position.x
|
||||||
src := Rect{0, 0, pos.x < s.player.position.x ? -w + 1 : w, w}
|
sprite :Tex
|
||||||
dst := Rect{s.player.position.x, s.player.position.y, w * 4, w * 4}
|
if _, ok := s.soldier.state.(IdleState); ok {
|
||||||
rl.DrawTexturePro(s.idle, src, dst, {0,0}, 0, rl.WHITE)
|
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() {
|
main :: proc() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user